diff options
276 files changed, 6412 insertions, 1388 deletions
diff --git a/SConstruct b/SConstruct index 0ae8f1a387..599db4b8de 100644 --- a/SConstruct +++ b/SConstruct @@ -773,6 +773,10 @@ else: # MSVC doesn't have clear C standard support, /std only covers C++. # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features. env.Prepend(CCFLAGS=["/std:c++17"]) + # MSVC is non-conforming with the C++ standard by default, so we enable more conformance. + # Note that this is still not complete conformance, as certain Windows-related headers + # don't compile under complete conformance. + env.Prepend(CCFLAGS=["/permissive-"]) # Disable exception handling. Godot doesn't use exceptions anywhere, and this # saves around 20% of binary size and very significant build time (GH-80513). diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index f231e4010f..5b04986020 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1561,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/error/error_macros.h b/core/error/error_macros.h index d31adb72be..19c16667d0 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -843,6 +843,6 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, _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) + _physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, ObjectID(UINT64_MAX), m_string) #endif // ERROR_MACROS_H diff --git a/core/input/input.cpp b/core/input/input.cpp index 91378591b0..eba7ded267 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -513,21 +513,49 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_ Vector3 Input::get_gravity() const { _THREAD_SAFE_METHOD_ + +#ifdef DEBUG_ENABLED + if (!gravity_enabled) { + WARN_PRINT_ONCE("`input_devices/sensors/enable_gravity` is not enabled in project settings."); + } +#endif + return gravity; } Vector3 Input::get_accelerometer() const { _THREAD_SAFE_METHOD_ + +#ifdef DEBUG_ENABLED + if (!accelerometer_enabled) { + WARN_PRINT_ONCE("`input_devices/sensors/enable_accelerometer` is not enabled in project settings."); + } +#endif + return accelerometer; } Vector3 Input::get_magnetometer() const { _THREAD_SAFE_METHOD_ + +#ifdef DEBUG_ENABLED + if (!magnetometer_enabled) { + WARN_PRINT_ONCE("`input_devices/sensors/enable_magnetometer` is not enabled in project settings."); + } +#endif + return magnetometer; } Vector3 Input::get_gyroscope() const { _THREAD_SAFE_METHOD_ + +#ifdef DEBUG_ENABLED + if (!gyroscope_enabled) { + WARN_PRINT_ONCE("`input_devices/sensors/enable_gyroscope` is not enabled in project settings."); + } +#endif + return gyroscope; } @@ -1683,6 +1711,11 @@ Input::Input() { // Always use standard behavior in the editor. legacy_just_pressed_behavior = false; } + + accelerometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_accelerometer", false); + gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false); + gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false); + magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false); } Input::~Input() { diff --git a/core/input/input.h b/core/input/input.h index 89e48f53d7..95dd623cc0 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -92,9 +92,13 @@ private: RBSet<JoyButton> joy_buttons_pressed; RBMap<JoyAxis, float> _joy_axis; //RBMap<StringName,int> custom_action_press; + bool gravity_enabled = false; Vector3 gravity; + bool accelerometer_enabled = false; Vector3 accelerometer; + bool magnetometer_enabled = false; Vector3 magnetometer; + bool gyroscope_enabled = false; Vector3 gyroscope; Vector2 mouse_pos; int64_t mouse_window = 0; diff --git a/core/io/image.cpp b/core/io/image.cpp index b35d405662..499364826d 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -3564,6 +3564,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("fix_alpha_edges"), &Image::fix_alpha_edges); ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha); ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear); + ClassDB::bind_method(D_METHOD("linear_to_srgb"), &Image::linear_to_srgb); ClassDB::bind_method(D_METHOD("normal_map_to_xy"), &Image::normal_map_to_xy); ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb); ClassDB::bind_method(D_METHOD("bump_map_to_normal_map", "bump_scale"), &Image::bump_map_to_normal_map, DEFVAL(1.0)); @@ -3831,6 +3832,37 @@ void Image::srgb_to_linear() { } } +void Image::linear_to_srgb() { + if (data.size() == 0) { + return; + } + + static const uint8_t lin2srgb[256] = { 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255 }; + + ERR_FAIL_COND(format != FORMAT_RGB8 && format != FORMAT_RGBA8); + + if (format == FORMAT_RGBA8) { + int len = data.size() / 4; + uint8_t *data_ptr = data.ptrw(); + + for (int i = 0; i < len; i++) { + data_ptr[(i << 2) + 0] = lin2srgb[data_ptr[(i << 2) + 0]]; + data_ptr[(i << 2) + 1] = lin2srgb[data_ptr[(i << 2) + 1]]; + data_ptr[(i << 2) + 2] = lin2srgb[data_ptr[(i << 2) + 2]]; + } + + } else if (format == FORMAT_RGB8) { + int len = data.size() / 3; + uint8_t *data_ptr = data.ptrw(); + + for (int i = 0; i < len; i++) { + data_ptr[(i * 3) + 0] = lin2srgb[data_ptr[(i * 3) + 0]]; + data_ptr[(i * 3) + 1] = lin2srgb[data_ptr[(i * 3) + 1]]; + data_ptr[(i * 3) + 2] = lin2srgb[data_ptr[(i * 3) + 2]]; + } + } +} + void Image::premultiply_alpha() { if (data.size() == 0) { return; diff --git a/core/io/image.h b/core/io/image.h index d55cc39dbb..cc285dabe3 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -383,6 +383,7 @@ public: void fix_alpha_edges(); void premultiply_alpha(); void srgb_to_linear(); + void linear_to_srgb(); void normal_map_to_xy(); Ref<Image> rgbe_to_srgb(); Ref<Image> get_image_from_mipmap(int p_mipmap) const; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 4988e73624..0577ca6bca 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -245,9 +245,9 @@ ResourceLoader::LoadToken::~LoadToken() { Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) { const String &original_path = p_original_path.is_empty() ? p_path : p_original_path; load_nesting++; - if (load_paths_stack->size()) { + if (load_paths_stack.size()) { thread_load_mutex.lock(); - const String &parent_task_path = load_paths_stack->get(load_paths_stack->size() - 1); + const String &parent_task_path = load_paths_stack.get(load_paths_stack.size() - 1); HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(parent_task_path); // Avoid double-tracking, for progress reporting, resources that boil down to a remapped path containing the real payload (e.g., imported resources). bool is_remapped_load = original_path == parent_task_path; @@ -256,7 +256,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin } thread_load_mutex.unlock(); } - load_paths_stack->push_back(original_path); + load_paths_stack.push_back(original_path); // Try all loaders and pick the first match for the type hint bool found = false; @@ -272,7 +272,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin } } - load_paths_stack->resize(load_paths_stack->size() - 1); + load_paths_stack.resize(load_paths_stack.size() - 1); res_ref_overrides.erase(load_nesting); load_nesting--; @@ -280,6 +280,10 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin return res; } + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_COND_V_MSG(found, Ref<Resource>(), vformat("Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once.", p_path)); @@ -306,8 +310,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { // Thread-safe either if it's the current thread or a brand new one. CallQueue *own_mq_override = nullptr; if (load_nesting == 0) { - load_paths_stack = memnew(Vector<String>); - + DEV_ASSERT(load_paths_stack.is_empty()); if (!Thread::is_main_thread()) { // Let the caller thread use its own, for added flexibility. Provide one otherwise. if (MessageQueue::get_singleton() == MessageQueue::get_main_singleton()) { @@ -408,10 +411,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { MessageQueue::set_thread_singleton_override(nullptr); memdelete(own_mq_override); } - if (load_paths_stack) { - memdelete(load_paths_stack); - load_paths_stack = nullptr; - } + DEV_ASSERT(load_paths_stack.is_empty()); } } @@ -1299,7 +1299,7 @@ bool ResourceLoader::timestamp_on_load = false; thread_local int ResourceLoader::load_nesting = 0; thread_local WorkerThreadPool::TaskID ResourceLoader::caller_task_id = 0; -thread_local Vector<String> *ResourceLoader::load_paths_stack = nullptr; +thread_local Vector<String> ResourceLoader::load_paths_stack; thread_local HashMap<int, HashMap<String, Ref<Resource>>> ResourceLoader::res_ref_overrides; template <> diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 5f1831f0d9..9d07964105 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -188,7 +188,7 @@ private: static thread_local int load_nesting; static thread_local WorkerThreadPool::TaskID caller_task_id; static thread_local HashMap<int, HashMap<String, Ref<Resource>>> res_ref_overrides; // Outermost key is nesting level. - static thread_local Vector<String> *load_paths_stack; // A pointer to avoid broken TLS implementations from double-running the destructor. + static thread_local Vector<String> load_paths_stack; static SafeBinaryMutex<BINARY_MUTEX_TAG> thread_load_mutex; static HashMap<String, ThreadLoadTask> thread_load_tasks; static bool cleaning_tasks; diff --git a/core/object/object.cpp b/core/object/object.cpp index ac23bf831f..a2926a478d 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -2097,7 +2097,11 @@ Object::~Object() { // Disconnect signals that connect to this object. while (connections.size()) { Connection c = connections.front()->get(); - bool disconnected = c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true); + Object *obj = c.callable.get_object(); + bool disconnected = false; + if (likely(obj)) { + disconnected = c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true); + } if (unlikely(!disconnected)) { // If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here. connections.pop_front(); diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp index 7b643e4637..73f7ec5a54 100644 --- a/core/object/script_language_extension.cpp +++ b/core/object/script_language_extension.cpp @@ -142,6 +142,7 @@ void ScriptLanguageExtension::_bind_methods() { GDVIRTUAL_BIND(_debug_get_current_stack_info); GDVIRTUAL_BIND(_reload_all_scripts); + GDVIRTUAL_BIND(_reload_scripts, "scripts", "soft_reload"); GDVIRTUAL_BIND(_reload_tool_script, "script", "soft_reload"); GDVIRTUAL_BIND(_get_recognized_extensions); diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 658297d805..5d59d65f92 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -39,19 +39,10 @@ StaticCString StaticCString::create(const char *p_ptr) { return scs; } -StringName::_Data *StringName::_table[STRING_TABLE_LEN]; - StringName _scs_create(const char *p_chr, bool p_static) { return (p_chr[0] ? StringName(StaticCString::create(p_chr), p_static) : StringName()); } -bool StringName::configured = false; -Mutex StringName::mutex; - -#ifdef DEBUG_ENABLED -bool StringName::debug_stringname = false; -#endif - void StringName::setup() { ERR_FAIL_COND(configured); for (int i = 0; i < STRING_TABLE_LEN; i++) { diff --git a/core/string/string_name.h b/core/string/string_name.h index 89b4c07e0e..0eb98cf64b 100644 --- a/core/string/string_name.h +++ b/core/string/string_name.h @@ -67,7 +67,7 @@ class StringName { _Data() {} }; - static _Data *_table[STRING_TABLE_LEN]; + static inline _Data *_table[STRING_TABLE_LEN]; _Data *_data = nullptr; @@ -75,10 +75,10 @@ class StringName { friend void register_core_types(); friend void unregister_core_types(); friend class Main; - static Mutex mutex; + static inline Mutex mutex; static void setup(); static void cleanup(); - static bool configured; + static inline bool configured = false; #ifdef DEBUG_ENABLED struct DebugSortReferences { bool operator()(const _Data *p_left, const _Data *p_right) const { @@ -86,7 +86,7 @@ class StringName { } }; - static bool debug_stringname; + static inline bool debug_stringname = false; #endif StringName(_Data *p_data) { _data = p_data; } diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 5a1f6925aa..2cfc48d395 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -4032,54 +4032,161 @@ String String::format(const Variant &values, const String &placeholder) const { return new_string; } -String String::replace(const String &p_key, const String &p_with) const { - String new_string; +static String _replace_common(const String &p_this, const String &p_key, const String &p_with, bool p_case_insensitive) { + if (p_key.is_empty() || p_this.is_empty()) { + return p_this; + } + + const int key_length = p_key.length(); + int search_from = 0; int result = 0; - while ((result = find(p_key, search_from)) >= 0) { - new_string += substr(search_from, result - search_from); - new_string += p_with; - search_from = result + p_key.length(); + LocalVector<int> found; + + while ((result = (p_case_insensitive ? p_this.findn(p_key, search_from) : p_this.find(p_key, search_from))) >= 0) { + found.push_back(result); + search_from = result + key_length; } - if (search_from == 0) { - return *this; + if (found.is_empty()) { + return p_this; + } + + String new_string; + + const int with_length = p_with.length(); + const int old_length = p_this.length(); + + new_string.resize(old_length + found.size() * (with_length - key_length) + 1); + + char32_t *new_ptrw = new_string.ptrw(); + const char32_t *old_ptr = p_this.ptr(); + const char32_t *with_ptr = p_with.ptr(); + + int last_pos = 0; + + for (const int &pos : found) { + if (last_pos != pos) { + memcpy(new_ptrw, old_ptr + last_pos, (pos - last_pos) * sizeof(char32_t)); + new_ptrw += (pos - last_pos); + } + if (with_length) { + memcpy(new_ptrw, with_ptr, with_length * sizeof(char32_t)); + new_ptrw += with_length; + } + last_pos = pos + key_length; } - new_string += substr(search_from, length() - search_from); + if (last_pos != old_length) { + memcpy(new_ptrw, old_ptr + last_pos, (old_length - last_pos) * sizeof(char32_t)); + new_ptrw += old_length - last_pos; + } + + *new_ptrw = 0; return new_string; } -String String::replace(const char *p_key, const char *p_with) const { - String new_string; +static String _replace_common(const String &p_this, char const *p_key, char const *p_with, bool p_case_insensitive) { + int key_length = strlen(p_key); + + if (key_length == 0 || p_this.is_empty()) { + return p_this; + } + int search_from = 0; int result = 0; - while ((result = find(p_key, search_from)) >= 0) { - new_string += substr(search_from, result - search_from); - new_string += p_with; - int k = 0; - while (p_key[k] != '\0') { - k++; + LocalVector<int> found; + + while ((result = (p_case_insensitive ? p_this.findn(p_key, search_from) : p_this.find(p_key, search_from))) >= 0) { + found.push_back(result); + search_from = result + key_length; + } + + if (found.is_empty()) { + return p_this; + } + + String new_string; + + // Create string to speed up copying as we can't do `memcopy` between `char32_t` and `char`. + const String with_string(p_with); + const int with_length = with_string.length(); + const int old_length = p_this.length(); + + new_string.resize(old_length + found.size() * (with_length - key_length) + 1); + + char32_t *new_ptrw = new_string.ptrw(); + const char32_t *old_ptr = p_this.ptr(); + const char32_t *with_ptr = with_string.ptr(); + + int last_pos = 0; + + for (const int &pos : found) { + if (last_pos != pos) { + memcpy(new_ptrw, old_ptr + last_pos, (pos - last_pos) * sizeof(char32_t)); + new_ptrw += (pos - last_pos); + } + if (with_length) { + memcpy(new_ptrw, with_ptr, with_length * sizeof(char32_t)); + new_ptrw += with_length; } - search_from = result + k; + last_pos = pos + key_length; } - if (search_from == 0) { - return *this; + if (last_pos != old_length) { + memcpy(new_ptrw, old_ptr + last_pos, (old_length - last_pos) * sizeof(char32_t)); + new_ptrw += old_length - last_pos; } - new_string += substr(search_from, length() - search_from); + *new_ptrw = 0; return new_string; } +String String::replace(const String &p_key, const String &p_with) const { + return _replace_common(*this, p_key, p_with, false); +} + +String String::replace(const char *p_key, const char *p_with) const { + return _replace_common(*this, p_key, p_with, false); +} + String String::replace_first(const String &p_key, const String &p_with) const { int pos = find(p_key); if (pos >= 0) { - return substr(0, pos) + p_with + substr(pos + p_key.length(), length()); + const int old_length = length(); + const int key_length = p_key.length(); + const int with_length = p_with.length(); + + String new_string; + new_string.resize(old_length + (with_length - key_length) + 1); + + char32_t *new_ptrw = new_string.ptrw(); + const char32_t *old_ptr = ptr(); + const char32_t *with_ptr = p_with.ptr(); + + if (pos > 0) { + memcpy(new_ptrw, old_ptr, pos * sizeof(char32_t)); + new_ptrw += pos; + } + + if (with_length) { + memcpy(new_ptrw, with_ptr, with_length * sizeof(char32_t)); + new_ptrw += with_length; + } + pos += key_length; + + if (pos != old_length) { + memcpy(new_ptrw, old_ptr + pos, (old_length - pos) * sizeof(char32_t)); + new_ptrw += (old_length - pos); + } + + *new_ptrw = 0; + + return new_string; } return *this; @@ -4088,55 +4195,45 @@ String String::replace_first(const String &p_key, const String &p_with) const { String String::replace_first(const char *p_key, const char *p_with) const { int pos = find(p_key); if (pos >= 0) { - int substring_length = strlen(p_key); - return substr(0, pos) + p_with + substr(pos + substring_length, length()); - } + const int old_length = length(); + const int key_length = strlen(p_key); + const int with_length = strlen(p_with); - return *this; -} - -String String::replacen(const String &p_key, const String &p_with) const { - String new_string; - int search_from = 0; - int result = 0; + String new_string; + new_string.resize(old_length + (with_length - key_length) + 1); - while ((result = findn(p_key, search_from)) >= 0) { - new_string += substr(search_from, result - search_from); - new_string += p_with; - search_from = result + p_key.length(); - } + char32_t *new_ptrw = new_string.ptrw(); + const char32_t *old_ptr = ptr(); - if (search_from == 0) { - return *this; - } + if (pos > 0) { + memcpy(new_ptrw, old_ptr, pos * sizeof(char32_t)); + new_ptrw += pos; + } - new_string += substr(search_from, length() - search_from); - return new_string; -} + for (int i = 0; i < with_length; ++i) { + *new_ptrw++ = p_with[i]; + } + pos += key_length; -String String::replacen(const char *p_key, const char *p_with) const { - String new_string; - int search_from = 0; - int result = 0; - int substring_length = strlen(p_key); + if (pos != old_length) { + memcpy(new_ptrw, old_ptr + pos, (old_length - pos) * sizeof(char32_t)); + new_ptrw += (old_length - pos); + } - if (substring_length == 0) { - return *this; // there's nothing to match or substitute - } + *new_ptrw = 0; - while ((result = findn(p_key, search_from)) >= 0) { - new_string += substr(search_from, result - search_from); - new_string += p_with; - search_from = result + substring_length; + return new_string; } - if (search_from == 0) { - return *this; - } + return *this; +} - new_string += substr(search_from, length() - search_from); +String String::replacen(const String &p_key, const String &p_with) const { + return _replace_common(*this, p_key, p_with, true); +} - return new_string; +String String::replacen(const char *p_key, const char *p_with) const { + return _replace_common(*this, p_key, p_with, true); } String String::repeat(int p_count) const { @@ -4430,10 +4527,7 @@ String String::simplify_path() const { dirs.remove_at(i); i--; } else if (d == "..") { - if (i == 0) { - dirs.remove_at(i); - i--; - } else { + if (i != 0) { dirs.remove_at(i); dirs.remove_at(i - 1); i -= 2; diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml index 596767599c..da79e15be1 100644 --- a/doc/classes/AnimationNodeBlendSpace2D.xml +++ b/doc/classes/AnimationNodeBlendSpace2D.xml @@ -5,7 +5,7 @@ </brief_description> <description> A resource used by [AnimationNodeBlendTree]. - [AnimationNodeBlendSpace1D] represents a virtual 2D space on which [AnimationRootNode]s are placed. Outputs the linear blend of the three adjacent animations using a [Vector2] weight. Adjacent in this context means the three [AnimationRootNode]s making up the triangle that contains the current value. + [AnimationNodeBlendSpace2D] represents a virtual 2D space on which [AnimationRootNode]s are placed. Outputs the linear blend of the three adjacent animations using a [Vector2] weight. Adjacent in this context means the three [AnimationRootNode]s making up the triangle that contains the current value. You can add vertices to the blend space with [method add_blend_point] and automatically triangulate it by setting [member auto_triangles] to [code]true[/code]. Otherwise, use [method add_triangle] and [method remove_triangle] to triangulate the blend space by hand. </description> <tutorials> @@ -93,7 +93,7 @@ <param index="0" name="point" type="int" /> <param index="1" name="pos" type="Vector2" /> <description> - Updates the position of the point at index [param point] on the blend axis. + Updates the position of the point at index [param point] in the blend space. </description> </method> </methods> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index db7796778e..e254fd56e9 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -321,6 +321,12 @@ Returns [code]true[/code] if all the image's pixels have an alpha value of 0. Returns [code]false[/code] if any pixel has an alpha value higher than 0. </description> </method> + <method name="linear_to_srgb"> + <return type="void" /> + <description> + Converts the entire image from the linear colorspace to the sRGB colorspace. Only works on images with [constant FORMAT_RGB8] or [constant FORMAT_RGBA8] formats. + </description> + </method> <method name="load"> <return type="int" enum="Error" /> <param index="0" name="path" type="String" /> @@ -590,7 +596,7 @@ <method name="srgb_to_linear"> <return type="void" /> <description> - Converts the raw data from the sRGB colorspace to a linear scale. + Converts the raw data from the sRGB colorspace to a linear scale. Only works on images with [constant FORMAT_RGB8] or [constant FORMAT_RGBA8] formats. </description> </method> </methods> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 1e7b63ecce..5fdcc0b26b 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -133,6 +133,7 @@ [code]vendor_id[/code]: The USB vendor ID of the device. [code]product_id[/code]: The USB product ID of the device. [code]steam_input_index[/code]: The Steam Input gamepad index, if the device is not a Steam Input device this key won't be present. + [b]Note:[/b] The returned dictionary is always empty on Web, iOS, Android, and macOS. </description> </method> <method name="get_joy_name"> diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index 529912171c..e99694c3f0 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -51,6 +51,24 @@ Returns the [Transform2D] of a specific instance. </description> </method> + <method name="reset_instance_physics_interpolation"> + <return type="void" /> + <param index="0" name="instance" type="int" /> + <description> + When using [i]physics interpolation[/i], this function allows you to prevent interpolation on an instance in the current physics tick. + This allows you to move instances instantaneously, and should usually be used when initially placing an instance such as a bullet to prevent graphical glitches. + </description> + </method> + <method name="set_buffer_interpolated"> + <return type="void" /> + <param index="0" name="buffer_curr" type="PackedFloat32Array" /> + <param index="1" name="buffer_prev" type="PackedFloat32Array" /> + <description> + An alternative to setting the [member buffer] property, which can be used with [i]physics interpolation[/i]. This method takes two arrays, and can set the data for the current and previous tick in one go. The renderer will automatically interpolate the data at each frame. + This is useful for situations where the order of instances may change from physics tick to tick, such as particle systems. + When the order of instances is coherent, the simpler alternative of setting [member buffer] can still be used with interpolation. + </description> + </method> <method name="set_instance_color"> <return type="void" /> <param index="0" name="instance" type="int" /> @@ -109,6 +127,11 @@ [Mesh] resource to be instanced. The looks of the individual instances can be modified using [method set_instance_color] and [method set_instance_custom_data]. </member> + <member name="physics_interpolation_quality" type="int" setter="set_physics_interpolation_quality" getter="get_physics_interpolation_quality" enum="MultiMesh.PhysicsInterpolationQuality" default="0"> + Choose whether to use an interpolation method that favors speed or quality. + When using low physics tick rates (typically below 20) or high rates of object rotation, you may get better results from the high quality setting. + [b]Note:[/b] Fast quality does not equate to low quality. Except in the special cases mentioned above, the quality should be comparable to high quality. + </member> <member name="transform_2d_array" type="PackedVector2Array" setter="_set_transform_2d_array" getter="_get_transform_2d_array" deprecated="Accessing this property is very slow. Use [method set_instance_transform_2d] and [method get_instance_transform_2d] instead."> Array containing each [Transform2D] value used by all instances of this mesh, as a [PackedVector2Array]. Each transform is divided into 3 [Vector2] values corresponding to the transforms' [code]x[/code], [code]y[/code], and [code]origin[/code]. </member> @@ -135,5 +158,11 @@ <constant name="TRANSFORM_3D" value="1" enum="TransformFormat"> Use this when using 3D transforms. </constant> + <constant name="INTERP_QUALITY_FAST" value="0" enum="PhysicsInterpolationQuality"> + Always interpolate using Basis lerping, which can produce warping artifacts in some situations. + </constant> + <constant name="INTERP_QUALITY_HIGH" value="1" enum="PhysicsInterpolationQuality"> + Attempt to interpolate using Basis slerping (spherical linear interpolation) where possible, otherwise fall back to lerping. + </constant> </constants> </class> diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml index 8be8a89543..5898e6841a 100644 --- a/doc/classes/NavigationMesh.xml +++ b/doc/classes/NavigationMesh.xml @@ -139,7 +139,7 @@ The physics layers to scan for static colliders. Only used when [member geometry_parsed_geometry_type] is [constant PARSED_GEOMETRY_STATIC_COLLIDERS] or [constant PARSED_GEOMETRY_BOTH]. </member> - <member name="geometry_parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" enum="NavigationMesh.ParsedGeometryType" default="0"> + <member name="geometry_parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" enum="NavigationMesh.ParsedGeometryType" default="2"> Determines which type of nodes will be parsed as geometry. See [enum ParsedGeometryType] for possible values. </member> <member name="geometry_source_geometry_mode" type="int" setter="set_source_geometry_mode" getter="get_source_geometry_mode" enum="NavigationMesh.SourceGeometryMode" default="0"> diff --git a/doc/classes/NavigationMeshSourceGeometryData2D.xml b/doc/classes/NavigationMeshSourceGeometryData2D.xml index 1d8689420b..82b6e077fe 100644 --- a/doc/classes/NavigationMeshSourceGeometryData2D.xml +++ b/doc/classes/NavigationMeshSourceGeometryData2D.xml @@ -57,6 +57,12 @@ Clears all projected obstructions. </description> </method> + <method name="get_bounds"> + <return type="Rect2" /> + <description> + Returns an axis-aligned bounding box that covers all the stored geometry data. The bounds are calculated when calling this function with the result cached until further geometry changes are made. + </description> + </method> <method name="get_obstruction_outlines" qualifiers="const"> <return type="PackedVector2Array[]" /> <description> diff --git a/doc/classes/NavigationMeshSourceGeometryData3D.xml b/doc/classes/NavigationMeshSourceGeometryData3D.xml index 0b3126a63b..aa43dff52d 100644 --- a/doc/classes/NavigationMeshSourceGeometryData3D.xml +++ b/doc/classes/NavigationMeshSourceGeometryData3D.xml @@ -63,6 +63,12 @@ Clears all projected obstructions. </description> </method> + <method name="get_bounds"> + <return type="AABB" /> + <description> + Returns an axis-aligned bounding box that covers all the stored geometry data. The bounds are calculated when calling this function with the result cached until further geometry changes are made. + </description> + </method> <method name="get_indices" qualifiers="const"> <return type="PackedInt32Array" /> <description> diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml index eebdc817a7..68fbc05931 100644 --- a/doc/classes/NavigationPolygon.xml +++ b/doc/classes/NavigationPolygon.xml @@ -193,6 +193,9 @@ <member name="parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" enum="NavigationPolygon.ParsedGeometryType" default="2"> Determines which type of nodes will be parsed as geometry. See [enum ParsedGeometryType] for possible values. </member> + <member name="sample_partition_type" type="int" setter="set_sample_partition_type" getter="get_sample_partition_type" enum="NavigationPolygon.SamplePartitionType" default="0"> + Partitioning algorithm for creating the navigation mesh polys. See [enum SamplePartitionType] for possible values. + </member> <member name="source_geometry_group_name" type="StringName" setter="set_source_geometry_group_name" getter="get_source_geometry_group_name" default="&"navigation_polygon_source_geometry_group""> The group name of nodes that should be parsed for baking source geometry. Only used when [member source_geometry_mode] is [constant SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN] or [constant SOURCE_GEOMETRY_GROUPS_EXPLICIT]. @@ -202,6 +205,15 @@ </member> </members> <constants> + <constant name="SAMPLE_PARTITION_CONVEX_PARTITION" value="0" enum="SamplePartitionType"> + Convex partitioning that yields navigation mesh with convex polygons. + </constant> + <constant name="SAMPLE_PARTITION_TRIANGULATE" value="1" enum="SamplePartitionType"> + Triangulation partitioning that yields navigation mesh with triangle polygons. + </constant> + <constant name="SAMPLE_PARTITION_MAX" value="2" enum="SamplePartitionType"> + Represents the size of the [enum SamplePartitionType] enum. + </constant> <constant name="PARSED_GEOMETRY_MESH_INSTANCES" value="0" enum="ParsedGeometryType"> Parses mesh instances as obstruction geometry. This includes [Polygon2D], [MeshInstance2D], [MultiMeshInstance2D], and [TileMap] nodes. Meshes are only parsed when they use a 2D vertices surface format. diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index ff3e6fd8a6..42f6235f8b 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -1167,5 +1167,8 @@ <constant name="INFO_EDGE_FREE_COUNT" value="8" enum="ProcessInfo"> Constant to get the number of navigation mesh polygon edges that could not be merged but may be still connected by edge proximity or with links. </constant> + <constant name="INFO_OBSTACLE_COUNT" value="9" enum="ProcessInfo"> + Constant to get the number of active navigation obstacles. + </constant> </constants> </class> diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml index 4d01fe1760..6bb71932dd 100644 --- a/doc/classes/Performance.xml +++ b/doc/classes/Performance.xml @@ -221,7 +221,10 @@ <constant name="NAVIGATION_EDGE_FREE_COUNT" value="32" enum="Monitor"> Number of navigation mesh polygon edges that could not be merged in the [NavigationServer3D]. The edges still may be connected by edge proximity or with links. </constant> - <constant name="MONITOR_MAX" value="33" enum="Monitor"> + <constant name="NAVIGATION_OBSTACLE_COUNT" value="33" enum="Monitor"> + Number of active navigation obstacles in the [NavigationServer3D]. + </constant> + <constant name="MONITOR_MAX" value="34" enum="Monitor"> Represents the size of the [enum Monitor] enum. </constant> </constants> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 8d567f347a..34c41195af 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1428,6 +1428,18 @@ <member name="input_devices/pointing/emulate_touch_from_mouse" type="bool" setter="" getter="" default="false"> If [code]true[/code], sends touch input events when clicking or dragging the mouse. </member> + <member name="input_devices/sensors/enable_accelerometer" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the accelerometer sensor is enabled and [method Input.get_accelerometer] returns valid data. + </member> + <member name="input_devices/sensors/enable_gravity" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the gravity sensor is enabled and [method Input.get_gravity] returns valid data. + </member> + <member name="input_devices/sensors/enable_gyroscope" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the gyroscope sensor is enabled and [method Input.get_gyroscope] returns valid data. + </member> + <member name="input_devices/sensors/enable_magnetometer" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the magnetometer sensor is enabled and [method Input.get_magnetometer] returns valid data. + </member> <member name="internationalization/locale/fallback" type="String" setter="" getter="" default=""en""> The locale to fall back to if a translation isn't available in a given language. If left empty, [code]en[/code] (English) will be used. </member> @@ -2592,6 +2604,10 @@ - Intel GPUs: SYCL libraries If no GPU acceleration is configured on the system, multi-threaded CPU-based denoising will be performed instead. This CPU-based denoising is significantly slower than the JNLM denoiser in most cases. </member> + <member name="rendering/lightmapping/lightmap_gi/use_bicubic_filter" type="bool" setter="" getter="" default="true"> + If [code]true[/code], applies a bicubic filter during lightmap sampling. This makes lightmaps look much smoother, at a moderate performance cost. + [b]Note:[/b] The bicubic filter exaggerates the 'bleeding' effect that occurs when a lightmap's resolution is low enough. + </member> <member name="rendering/lightmapping/primitive_meshes/texel_size" type="float" setter="" getter="" default="0.2"> The texel_size that is used to calculate the [member Mesh.lightmap_size_hint] on [PrimitiveMesh] resources if [member PrimitiveMesh.add_uv2] is enabled. </member> @@ -2935,10 +2951,19 @@ Specify whether to enable eye tracking for this project. Depending on the platform, additional export configuration may be needed. </member> <member name="xr/openxr/extensions/hand_interaction_profile" type="bool" setter="" getter="" default="false"> - If true the hand interaction profile extension will be activated if supported by the platform. + If [code]true[/code] the hand interaction profile extension will be activated if supported by the platform. + </member> + <member name="xr/openxr/extensions/hand_tracking" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the hand tracking extension is enabled if available. + [b]Note:[/b] By default hand tracking will only work for data sources chosen by the XR runtime. For SteamVR this is the controller inferred data source, for most other runtimes this is the unobstructed data source. There is no way to query this. If a runtime supports the OpenXR data source extension you can use the [member xr/openxr/extensions/hand_tracking_controller_data_source] and/or [member xr/openxr/extensions/hand_tracking_unobstructed_data_source] to indicate you wish to enable these data sources. If neither is selected the data source extension is not enabled and the XR runtimes default behavior persists. + </member> + <member name="xr/openxr/extensions/hand_tracking_controller_data_source" type="bool" setter="" getter="" default="false"> + If [code]true[/code], support for the controller inferred data source is requested. If supported, you will receive hand tracking data even if the user has a controller in hand, with finger positions automatically inferred from controller input and/or sensors. + [b]Node:[/b] This requires the OpenXR data source extension and controller inferred handtracking to be supported by the XR runtime. If not supported this setting will be ignored. [member xr/openxr/extensions/hand_tracking] must be enabled for this setting to be used. </member> - <member name="xr/openxr/extensions/hand_tracking" type="bool" setter="" getter="" default="true"> - If true we enable the hand tracking extension if available. + <member name="xr/openxr/extensions/hand_tracking_unobstructed_data_source" type="bool" setter="" getter="" default="false"> + If [code]true[/code], support for the unobstructed data source is requested. If supported, you will receive hand tracking data based on the actual finger positions of the user often determined by optical tracking. + [b]Node:[/b] This requires the OpenXR data source extension and unobstructed handtracking to be supported by the XR runtime. If not supported this setting will be ignored. [member xr/openxr/extensions/hand_tracking] must be enabled for this setting to be used. </member> <member name="xr/openxr/form_factor" type="int" setter="" getter="" default=""0""> Specify whether OpenXR should be configured for an HMD or a hand held device. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index d86b82b72a..c81d5d4fab 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -944,7 +944,8 @@ <param index="1" name="item" type="RID" /> <param index="2" name="mirroring" type="Vector2" /> <description> - A copy of the canvas item will be drawn with a local offset of the mirroring [Vector2]. + A copy of the canvas item will be drawn with a local offset of the [param mirroring]. + [b]Note:[/b] This is equivalent to calling [method canvas_set_item_repeat] like [code]canvas_set_item_repeat(item, mirroring, 1)[/code], with an additional check ensuring [param canvas] is a parent of [param item]. </description> </method> <method name="canvas_set_item_repeat"> @@ -2206,6 +2207,13 @@ Set the textures on the given [param lightmap] GI instance to the texture array pointed to by the [param light] RID. If the lightmap texture was baked with [member LightmapGI.directional] set to [code]true[/code], then [param uses_sh] must also be [code]true[/code]. </description> </method> + <method name="lightmaps_set_bicubic_filter"> + <return type="void" /> + <param index="0" name="enable" type="bool" /> + <description> + Toggles whether a bicubic filter should be used when lightmaps are sampled. This smoothens their appearance at a performance cost. + </description> + </method> <method name="make_sphere_mesh"> <return type="RID" /> <param index="0" name="latitudes" type="int" /> @@ -2558,6 +2566,15 @@ Returns the [Transform2D] of the specified instance. For use when the multimesh is set to use 2D transforms. </description> </method> + <method name="multimesh_instance_reset_physics_interpolation"> + <return type="void" /> + <param index="0" name="multimesh" type="RID" /> + <param index="1" name="index" type="int" /> + <description> + Prevents physics interpolation for the specified instance during the current physics tick. + This is useful when moving an instance to a new location, to give an instantaneous change rather than interpolation from the previous location. + </description> + </method> <method name="multimesh_instance_set_color"> <return type="void" /> <param index="0" name="multimesh" type="RID" /> @@ -2615,6 +2632,16 @@ [/codeblock] </description> </method> + <method name="multimesh_set_buffer_interpolated"> + <return type="void" /> + <param index="0" name="multimesh" type="RID" /> + <param index="1" name="buffer" type="PackedFloat32Array" /> + <param index="2" name="buffer_previous" type="PackedFloat32Array" /> + <description> + Alternative version of [method multimesh_set_buffer] for use with physics interpolation. + Takes both an array of current data and an array of data for the previous physics tick. + </description> + </method> <method name="multimesh_set_custom_aabb"> <return type="void" /> <param index="0" name="multimesh" type="RID" /> @@ -2631,6 +2658,23 @@ Sets the mesh to be drawn by the multimesh. Equivalent to [member MultiMesh.mesh]. </description> </method> + <method name="multimesh_set_physics_interpolated"> + <return type="void" /> + <param index="0" name="multimesh" type="RID" /> + <param index="1" name="interpolated" type="bool" /> + <description> + Turns on and off physics interpolation for this MultiMesh resource. + </description> + </method> + <method name="multimesh_set_physics_interpolation_quality"> + <return type="void" /> + <param index="0" name="multimesh" type="RID" /> + <param index="1" name="quality" type="int" enum="RenderingServer.MultimeshPhysicsInterpolationQuality" /> + <description> + Sets the physics interpolation quality for the [MultiMesh]. + A value of [constant MULTIMESH_INTERP_QUALITY_FAST] gives fast but low quality interpolation, a value of [constant MULTIMESH_INTERP_QUALITY_HIGH] gives slower but higher quality interpolation. + </description> + </method> <method name="multimesh_set_visible_instances"> <return type="void" /> <param index="0" name="multimesh" type="RID" /> @@ -4500,6 +4544,12 @@ <constant name="MULTIMESH_TRANSFORM_3D" value="1" enum="MultimeshTransformFormat"> Use [Transform3D] to store MultiMesh transform. </constant> + <constant name="MULTIMESH_INTERP_QUALITY_FAST" value="0" enum="MultimeshPhysicsInterpolationQuality"> + MultiMesh physics interpolation favors speed over quality. + </constant> + <constant name="MULTIMESH_INTERP_QUALITY_HIGH" value="1" enum="MultimeshPhysicsInterpolationQuality"> + MultiMesh physics interpolation favors quality over speed. + </constant> <constant name="LIGHT_PROJECTOR_FILTER_NEAREST" value="0" enum="LightProjectorFilter"> Nearest-neighbor filter for light projectors (use for pixel art light projectors). No mipmaps are used for rendering, which means light projectors at a distance will look sharp but grainy. This has roughly the same performance cost as using mipmaps. </constant> diff --git a/doc/classes/ResourceImporterImageFont.xml b/doc/classes/ResourceImporterImageFont.xml index 6dbfa1d651..663c906001 100644 --- a/doc/classes/ResourceImporterImageFont.xml +++ b/doc/classes/ResourceImporterImageFont.xml @@ -40,7 +40,7 @@ </member> <member name="kerning_pairs" type="PackedStringArray" setter="" getter="" default="PackedStringArray()"> Kerning pairs for the font. Kerning pair adjust the spacing between two characters. - Each string consist of three space separated values: "from" string, "to" string and integer offset. Each combination form the two string for a kerning pair, e.g, [code]ab cd -3[/code] will create kerning pairs [code]ac[/code], [code]ad[/code], [code]bc[/code], and [code]bd[/code] with offset [code]-3[/code]. + Each string consist of three space separated values: "from" string, "to" string and integer offset. Each combination form the two string for a kerning pair, e.g, [code]ab cd -3[/code] will create kerning pairs [code]ac[/code], [code]ad[/code], [code]bc[/code], and [code]bd[/code] with offset [code]-3[/code]. [code]\uXXXX[/code] escape sequences can be used to add Unicode characters. </member> <member name="rows" type="int" setter="" getter="" default="1"> Number of rows in the font image. See also [member columns]. diff --git a/doc/classes/ScriptLanguageExtension.xml b/doc/classes/ScriptLanguageExtension.xml index cc47ca274d..f9d9e4f513 100644 --- a/doc/classes/ScriptLanguageExtension.xml +++ b/doc/classes/ScriptLanguageExtension.xml @@ -314,6 +314,13 @@ <description> </description> </method> + <method name="_reload_scripts" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="scripts" type="Array" /> + <param index="1" name="soft_reload" type="bool" /> + <description> + </description> + </method> <method name="_reload_tool_script" qualifiers="virtual"> <return type="void" /> <param index="0" name="script" type="Script" /> diff --git a/doc/classes/ShapeCast2D.xml b/doc/classes/ShapeCast2D.xml index 385e3a9285..6f5da28825 100644 --- a/doc/classes/ShapeCast2D.xml +++ b/doc/classes/ShapeCast2D.xml @@ -15,7 +15,7 @@ <return type="void" /> <param index="0" name="node" type="CollisionObject2D" /> <description> - Adds a collision exception so the shape does not report collisions with the specified [CollisionObject2D] node. + Adds a collision exception so the shape does not report collisions with the specified node. </description> </method> <method name="add_exception_rid"> @@ -35,19 +35,19 @@ <return type="void" /> <description> Updates the collision information for the shape immediately, without waiting for the next [code]_physics_process[/code] call. Use this method, for example, when the shape or its parent has changed state. - [b]Note:[/b] [code]enabled == true[/code] is not required for this to work. + [b]Note:[/b] Setting [member enabled] to [code]true[/code] is not required for this to work. </description> </method> <method name="get_closest_collision_safe_fraction" qualifiers="const"> <return type="float" /> <description> - The fraction from the [ShapeCast2D]'s origin to its [member target_position] (between 0 and 1) of how far the shape can move without triggering a collision. + Returns the fraction from this cast's origin to its [member target_position] of how far the shape can move without triggering a collision, as a value between [code]0.0[/code] and [code]1.0[/code]. </description> </method> <method name="get_closest_collision_unsafe_fraction" qualifiers="const"> <return type="float" /> <description> - The fraction from the [ShapeCast2D]'s origin to its [member target_position] (between 0 and 1) of how far the shape must move to trigger a collision. + Returns the fraction from this cast's origin to its [member target_position] of how far the shape must move to trigger a collision, as a value between [code]0.0[/code] and [code]1.0[/code]. In ideal conditions this would be the same as [method get_closest_collision_safe_fraction], however shape casting is calculated in discrete steps, so the precise point of collision can occur between two calculated positions. </description> </method> @@ -97,7 +97,7 @@ <param index="0" name="index" type="int" /> <description> Returns the collision point of one of the multiple collisions at [param index] where the shape intersects the colliding object. - [b]Note:[/b] this point is in the [b]global[/b] coordinate system. + [b]Note:[/b] This point is in the [b]global[/b] coordinate system. </description> </method> <method name="is_colliding" qualifiers="const"> @@ -110,7 +110,7 @@ <return type="void" /> <param index="0" name="node" type="CollisionObject2D" /> <description> - Removes a collision exception so the shape does report collisions with the specified [CollisionObject2D] node. + Removes a collision exception so the shape does report collisions with the specified node. </description> </method> <method name="remove_exception_rid"> @@ -137,7 +137,7 @@ If [code]true[/code], collisions with [PhysicsBody2D]s will be reported. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1"> - The shape's collision mask. Only objects in at least one collision layer enabled in the mask will be detected. + The shape's collision mask. Only objects in at least one collision layer enabled in the mask will be detected. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. </member> <member name="collision_result" type="Array" setter="" getter="get_collision_result" default="[]"> Returns the complete collision information from the collision sweep. The data returned is the same as in the [method PhysicsDirectSpaceState2D.get_rest_info] method. @@ -155,10 +155,10 @@ The number of intersections can be limited with this parameter, to reduce the processing time. </member> <member name="shape" type="Shape2D" setter="set_shape" getter="get_shape"> - The [Shape2D]-derived shape to be used for collision queries. + The shape to be used for collision queries. </member> <member name="target_position" type="Vector2" setter="set_target_position" getter="get_target_position" default="Vector2(0, 50)"> - The shape's destination point, relative to this node's [code]position[/code]. + The shape's destination point, relative to this node's [member Node2D.position]. </member> </members> </class> diff --git a/doc/classes/ShapeCast3D.xml b/doc/classes/ShapeCast3D.xml index f70cf169df..ba0b021b4d 100644 --- a/doc/classes/ShapeCast3D.xml +++ b/doc/classes/ShapeCast3D.xml @@ -15,7 +15,7 @@ <return type="void" /> <param index="0" name="node" type="CollisionObject3D" /> <description> - Adds a collision exception so the shape does not report collisions with the specified [CollisionObject3D] node. + Adds a collision exception so the shape does not report collisions with the specified node. </description> </method> <method name="add_exception_rid"> @@ -28,26 +28,26 @@ <method name="clear_exceptions"> <return type="void" /> <description> - Removes all collision exceptions for this [ShapeCast3D]. + Removes all collision exceptions for this shape. </description> </method> <method name="force_shapecast_update"> <return type="void" /> <description> Updates the collision information for the shape immediately, without waiting for the next [code]_physics_process[/code] call. Use this method, for example, when the shape or its parent has changed state. - [b]Note:[/b] [code]enabled == true[/code] is not required for this to work. + [b]Note:[/b] Setting [member enabled] to [code]true[/code] is not required for this to work. </description> </method> <method name="get_closest_collision_safe_fraction" qualifiers="const"> <return type="float" /> <description> - The fraction from the [ShapeCast3D]'s origin to its [member target_position] (between 0 and 1) of how far the shape can move without triggering a collision. + Returns the fraction from this cast's origin to its [member target_position] of how far the shape can move without triggering a collision, as a value between [code]0.0[/code] and [code]1.0[/code]. </description> </method> <method name="get_closest_collision_unsafe_fraction" qualifiers="const"> <return type="float" /> <description> - The fraction from the [ShapeCast3D]'s origin to its [member target_position] (between 0 and 1) of how far the shape must move to trigger a collision. + Returns the fraction from this cast's origin to its [member target_position] of how far the shape must move to trigger a collision, as a value between [code]0.0[/code] and [code]1.0[/code]. In ideal conditions this would be the same as [method get_closest_collision_safe_fraction], however shape casting is calculated in discrete steps, so the precise point of collision can occur between two calculated positions. </description> </method> @@ -97,7 +97,7 @@ <param index="0" name="index" type="int" /> <description> Returns the collision point of one of the multiple collisions at [param index] where the shape intersects the colliding object. - [b]Note:[/b] this point is in the [b]global[/b] coordinate system. + [b]Note:[/b] This point is in the [b]global[/b] coordinate system. </description> </method> <method name="is_colliding" qualifiers="const"> @@ -110,7 +110,7 @@ <return type="void" /> <param index="0" name="node" type="CollisionObject3D" /> <description> - Removes a collision exception so the shape does report collisions with the specified [CollisionObject3D] node. + Removes a collision exception so the shape does report collisions with the specified node. </description> </method> <method name="remove_exception_rid"> @@ -166,10 +166,10 @@ The number of intersections can be limited with this parameter, to reduce the processing time. </member> <member name="shape" type="Shape3D" setter="set_shape" getter="get_shape"> - The [Shape3D]-derived shape to be used for collision queries. + The shape to be used for collision queries. </member> <member name="target_position" type="Vector3" setter="set_target_position" getter="get_target_position" default="Vector3(0, -1, 0)"> - The shape's destination point, relative to this node's [code]position[/code]. + The shape's destination point, relative to this node's [member Node3D.position]. </member> </members> </class> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index f57185ae87..b24f26a764 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -102,6 +102,7 @@ await RenderingServer.frame_post_draw $Viewport.get_texture().get_image().save_png("user://Screenshot.png") [/codeblock] + [b]Note:[/b] When [member use_hdr_2d] is [code]true[/code] the returned texture will be an HDR image encoded in linear space. </description> </method> <method name="get_viewport_rid" qualifiers="const"> diff --git a/doc/classes/ViewportTexture.xml b/doc/classes/ViewportTexture.xml index ba2352ab61..f6840d6b09 100644 --- a/doc/classes/ViewportTexture.xml +++ b/doc/classes/ViewportTexture.xml @@ -8,6 +8,11 @@ To get a [ViewportTexture] in code, use the [method Viewport.get_texture] method on the target viewport. [b]Note:[/b] A [ViewportTexture] is always local to its scene (see [member Resource.resource_local_to_scene]). If the scene root is not ready, it may return incorrect data (see [signal Node.ready]). [b]Note:[/b] Instantiating scenes containing a high-resolution [ViewportTexture] may cause noticeable stutter. + [b]Note:[/b] When using a [Viewport] with [member Viewport.use_hdr_2d] set to [code]true[/code] the returned texture will be an HDR image encoded in linear space. This may look darker than normal when displayed directly on screen. To convert to gamma space, you can do the following: + [codeblock] + img.convert(Image.FORMAT_RGBA8) + imb.linear_to_srgb() + [/codeblock] </description> <tutorials> <link title="GUI in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2807</link> diff --git a/doc/classes/VisualShaderNodeIntParameter.xml b/doc/classes/VisualShaderNodeIntParameter.xml index ed72584b1b..ba17da49a8 100644 --- a/doc/classes/VisualShaderNodeIntParameter.xml +++ b/doc/classes/VisualShaderNodeIntParameter.xml @@ -15,6 +15,9 @@ <member name="default_value_enabled" type="bool" setter="set_default_value_enabled" getter="is_default_value_enabled" default="false"> If [code]true[/code], the node will have a custom default value. </member> + <member name="enum_names" type="PackedStringArray" setter="set_enum_names" getter="get_enum_names" default="PackedStringArray()"> + The names used for the enum select in the editor. [member hint] must be [constant HINT_ENUM] for this to take effect. + </member> <member name="hint" type="int" setter="set_hint" getter="get_hint" enum="VisualShaderNodeIntParameter.Hint" default="0"> Range hint of this node. Use it to customize valid parameter range. </member> @@ -38,7 +41,10 @@ <constant name="HINT_RANGE_STEP" value="2" enum="Hint"> The parameter's value must be within the specified range, with the given [member step] between values. </constant> - <constant name="HINT_MAX" value="3" enum="Hint"> + <constant name="HINT_ENUM" value="3" enum="Hint"> + The parameter uses an enum to associate preset values to names in the editor. + </constant> + <constant name="HINT_MAX" value="4" enum="Hint"> Represents the size of the [enum Hint] enum. </constant> </constants> diff --git a/doc/classes/XRHandTracker.xml b/doc/classes/XRHandTracker.xml index 636af6625b..79ea237480 100644 --- a/doc/classes/XRHandTracker.xml +++ b/doc/classes/XRHandTracker.xml @@ -107,7 +107,10 @@ <constant name="HAND_TRACKING_SOURCE_CONTROLLER" value="2" enum="HandTrackingSource"> The source of hand tracking data is a controller, meaning that joint positions are inferred from controller inputs. </constant> - <constant name="HAND_TRACKING_SOURCE_MAX" value="3" enum="HandTrackingSource"> + <constant name="HAND_TRACKING_SOURCE_NOT_TRACKED" value="3" enum="HandTrackingSource"> + No hand tracking data is tracked, this either means the hand is obscured, the controller is turned off, or tracking is not supported for the current input type. + </constant> + <constant name="HAND_TRACKING_SOURCE_MAX" value="4" enum="HandTrackingSource"> Represents the size of the [enum HandTrackingSource] enum. </constant> <constant name="HAND_JOINT_PALM" value="0" enum="HandJoint"> diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 941b1a1b28..7a6df3d2a4 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -647,18 +647,17 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou _record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, Point2()); } else { Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2); - Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos; - Point2 pos = start_pos; - - do { - do { - _record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, pos); - pos.y += ci->repeat_size.y; - } while (pos.y < end_pos.y); - - pos.x += ci->repeat_size.x; - pos.y = start_pos.y; - } while (pos.x < end_pos.x); + Point2 offset; + + int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0; + int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0; + for (int ry = 0; ry <= repeat_times_y; ry++) { + offset.y = start_pos.y + ry * ci->repeat_size.y; + for (int rx = 0; rx <= repeat_times_x; rx++) { + offset.x = start_pos.x + rx * ci->repeat_size.x; + _record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, offset); + } + } } } @@ -809,7 +808,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou state.last_item_index += index; } -void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_offset) { +void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_repeat_offset) { RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter; if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) { @@ -826,11 +825,11 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend state.canvas_instance_batches[state.current_batch_index].repeat = texture_repeat; } - Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform; - - if (p_offset.x || p_offset.y) { - base_transform *= Transform2D(0, p_offset / p_item->xform_curr.get_scale()); // TODO: Interpolate or explain why not needed. + Transform2D base_transform = p_item->final_transform; + if (p_item->repeat_source_item && (p_repeat_offset.x || p_repeat_offset.y)) { + base_transform.columns[2] += p_item->repeat_source_item->final_transform.basis_xform(p_repeat_offset); } + base_transform = p_canvas_transform_inverse * base_transform; Transform2D draw_transform; // Used by transform command diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 46ed479a3d..027f717eb7 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -362,7 +362,7 @@ public: void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override; void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr); - void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_offset); + void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_repeat_offset); void _render_batch(Light *p_lights, uint32_t p_index, RenderingMethod::RenderInfo *r_render_info = nullptr); bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization); void _new_batch(bool &r_batch_broken); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 84b6ab4bd8..3ed8042f3f 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -777,7 +777,6 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, ERR_FAIL_COND(p_env.is_null()); Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env)); - ERR_FAIL_NULL(sky); GLES3::SkyMaterialData *material_data = nullptr; RID sky_material; @@ -851,6 +850,15 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::SKY_ENERGY_MULTIPLIER, p_sky_energy_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); + Color fog_color = environment_get_fog_light_color(p_env).srgb_to_linear() * environment_get_fog_light_energy(p_env); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_ENABLED, environment_get_fog_enabled(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_AERIAL_PERSPECTIVE, environment_get_fog_aerial_perspective(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_LIGHT_COLOR, fog_color, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_SUN_SCATTER, environment_get_fog_sun_scatter(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_DENSITY, environment_get_fog_density(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_SKY_AFFECT, environment_get_fog_sky_affect(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::DIRECTIONAL_LIGHT_COUNT, sky_globals.directional_light_count, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); + if (p_use_multiview) { glBindBufferBase(GL_UNIFORM_BUFFER, SKY_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer); glBindBuffer(GL_UNIFORM_BUFFER, 0); @@ -2587,7 +2595,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ scene_state.enable_gl_depth_draw(false); - if (draw_sky) { + if (draw_sky || draw_sky_fog_only) { RENDER_TIMESTAMP("Render Sky"); scene_state.enable_gl_depth_test(true); @@ -3202,6 +3210,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (lm->uses_spherical_harmonics) { spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP; } + + if (lightmap_bicubic_upscale) { + spec_constants |= SceneShaderGLES3::LIGHTMAP_BICUBIC_FILTER; + } } else if (inst->lightmap_sh) { spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE; } else { @@ -3344,6 +3356,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, Vector4 uv_scale(inst->lightmap_uv_scale.position.x, inst->lightmap_uv_scale.position.y, inst->lightmap_uv_scale.size.x, inst->lightmap_uv_scale.size.y); material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_UV_SCALE, uv_scale, shader->version, instance_variant, spec_constants); + if (lightmap_bicubic_upscale) { + Vector2 light_texture_size(lm->light_texture_size.x, lm->light_texture_size.y); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_TEXTURE_SIZE, light_texture_size, shader->version, instance_variant, spec_constants); + } + float exposure_normalization = 1.0; if (p_render_data->camera_attributes.is_valid()) { float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); @@ -4039,6 +4056,10 @@ void RasterizerSceneGLES3::decals_set_filter(RS::DecalFilter p_filter) { void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter p_filter) { } +void RasterizerSceneGLES3::lightmaps_set_bicubic_filter(bool p_enable) { + lightmap_bicubic_upscale = p_enable; +} + RasterizerSceneGLES3::RasterizerSceneGLES3() { singleton = this; @@ -4052,6 +4073,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() { positional_soft_shadow_filter_set_quality((RS::ShadowQuality)(int)GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality")); directional_soft_shadow_filter_set_quality((RS::ShadowQuality)(int)GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality")); + lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter")); { // Setup Lights diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 4c70c43244..e4af8f99e9 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -680,6 +680,8 @@ protected: bool glow_bicubic_upscale = false; RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW; + bool lightmap_bicubic_upscale = false; + /* Sky */ struct SkyGlobals { @@ -863,6 +865,7 @@ public: void decals_set_filter(RS::DecalFilter p_filter) override; void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override; + virtual void lightmaps_set_bicubic_filter(bool p_enable) override; RasterizerSceneGLES3(); ~RasterizerSceneGLES3(); diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index ba70de9e34..3e3b4d11f7 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -36,6 +36,7 @@ ADDITIVE_OMNI = false ADDITIVE_SPOT = false RENDER_MATERIAL = false SECOND_REFLECTION_PROBE = false +LIGHTMAP_BICUBIC_FILTER = false #[vertex] @@ -583,6 +584,8 @@ void main() { #define SHADER_IS_SRGB true +#define FLAGS_NON_UNIFORM_SCALE (1 << 4) + /* Varyings */ #if defined(COLOR_USED) @@ -923,6 +926,10 @@ uniform lowp uint lightmap_slice; uniform highp vec4 lightmap_uv_scale; uniform float lightmap_exposure_normalization; +#ifdef LIGHTMAP_BICUBIC_FILTER +uniform highp vec2 lightmap_texture_size; +#endif + #ifdef USE_SH_LIGHTMAP uniform mediump mat3 lightmap_normal_xform; #endif // USE_SH_LIGHTMAP @@ -955,6 +962,7 @@ ivec2 multiview_uv(ivec2 uv) { uniform highp mat4 world_transform; uniform mediump float opaque_prepass_threshold; +uniform highp uint model_flags; #if defined(RENDER_MATERIAL) layout(location = 0) out vec4 albedo_output_buffer; @@ -1414,6 +1422,67 @@ void reflection_process(samplerCube reflection_map, #endif // !MODE_RENDER_DEPTH +#ifdef LIGHTMAP_BICUBIC_FILTER +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 textureArray_bicubic(sampler2DArray tex, vec3 uv, vec2 texture_size) { + vec2 texel_size = vec2(1.0) / texture_size; + + uv.xy = uv.xy * texture_size + vec2(0.5); + + vec2 iuv = floor(uv.xy); + vec2 fuv = fract(uv.xy); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; + + return (g0(fuv.y) * (g0x * texture(tex, vec3(p0, uv.z)) + g1x * texture(tex, vec3(p1, uv.z)))) + + (g1(fuv.y) * (g0x * texture(tex, vec3(p2, uv.z)) + g1x * texture(tex, vec3(p3, uv.z)))); +} +#endif //LIGHTMAP_BICUBIC_FILTER + void main() { //lay out everything, whatever is unused is optimized away anyway vec3 vertex = vertex_interp; @@ -1521,6 +1590,13 @@ void main() { vec3 light_vertex = vertex; #endif //LIGHT_VERTEX_USED + highp mat3 model_normal_matrix; + if (bool(model_flags & uint(FLAGS_NON_UNIFORM_SCALE))) { + model_normal_matrix = transpose(inverse(mat3(model_matrix))); + } else { + model_normal_matrix = mat3(model_matrix); + } + { #CODE : FRAGMENT } @@ -1609,6 +1685,7 @@ void main() { #ifdef BASE_PASS /////////////////////// LIGHTING ////////////////////////////// +#ifndef AMBIENT_LIGHT_DISABLED // IBL precalculations float ndotv = clamp(dot(normal, view), 0.0, 1.0); vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0); @@ -1721,10 +1798,18 @@ void main() { #ifdef USE_SH_LIGHTMAP uvw.z *= 4.0; // SH textures use 4 times more data. + +#ifdef LIGHTMAP_BICUBIC_FILTER + vec3 lm_light_l0 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), lightmap_texture_size).rgb; + vec3 lm_light_l1n1 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), lightmap_texture_size).rgb; + vec3 lm_light_l1_0 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), lightmap_texture_size).rgb; + vec3 lm_light_l1p1 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), lightmap_texture_size).rgb; +#else vec3 lm_light_l0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; vec3 lm_light_l1n1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; vec3 lm_light_l1_0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; vec3 lm_light_l1p1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; +#endif vec3 n = normalize(lightmap_normal_xform * normal); @@ -1739,25 +1824,25 @@ void main() { specular_light += lm_light_l1p1 * 0.32573 * r.x * lightmap_exposure_normalization; } #else +#ifdef LIGHTMAP_BICUBIC_FILTER + ambient_light += textureArray_bicubic(lightmap_textures, uvw, lightmap_texture_size).rgb * lightmap_exposure_normalization; +#else ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization; #endif +#endif } #endif // USE_LIGHTMAP #endif // USE_LIGHTMAP_CAPTURE #endif // !DISABLE_LIGHTMAP - { -#if defined(AMBIENT_LIGHT_DISABLED) - ambient_light = vec3(0.0, 0.0, 0.0); -#else - ambient_light *= albedo.rgb; - ambient_light *= ao; -#endif // AMBIENT_LIGHT_DISABLED - } + ambient_light *= albedo.rgb; + ambient_light *= ao; + +#endif // !AMBIENT_LIGHT_DISABLED // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); - +#ifndef AMBIENT_LIGHT_DISABLED { #if defined(DIFFUSE_TOON) //simplify for toon, as @@ -1779,6 +1864,8 @@ void main() { #endif } +#endif // !AMBIENT_LIGHT_DISABLED + #ifndef DISABLE_LIGHT_DIRECTIONAL for (uint i = uint(0); i < scene_data.directional_light_count; i++) { #if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index 9de65ba960..f734e4b355 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -108,11 +108,11 @@ uniform float sky_energy_multiplier; uniform float luminance_multiplier; uniform float fog_aerial_perspective; -uniform vec3 fog_light_color; +uniform vec4 fog_light_color; uniform float fog_sun_scatter; uniform bool fog_enabled; uniform float fog_density; -uniform float z_far; +uniform float fog_sky_affect; uniform uint directional_light_count; #ifdef USE_MULTIVIEW @@ -135,6 +135,24 @@ vec3 interleaved_gradient_noise(vec2 pos) { } #endif +#if !defined(DISABLE_FOG) +vec4 fog_process(vec3 view, vec3 sky_color) { + vec3 fog_color = mix(fog_light_color.rgb, sky_color, fog_aerial_perspective); + + if (fog_sun_scatter > 0.001) { + vec4 sun_scatter = vec4(0.0); + float sun_total = 0.0; + for (uint i = 0u; i < directional_light_count; i++) { + vec3 light_color = directional_lights.data[i].color_size.xyz * directional_lights.data[i].direction_energy.w; + float light_amount = pow(max(dot(view, directional_lights.data[i].direction_energy.xyz), 0.0), 8.0); + fog_color += light_color * light_amount * fog_sun_scatter; + } + } + + return vec4(fog_color, 1.0); +} +#endif // !DISABLE_FOG + void main() { vec3 cube_normal; #ifdef USE_MULTIVIEW @@ -203,6 +221,21 @@ void main() { // Convert to Linear for tonemapping so color matches scene shader better color = srgb_to_linear(color); + +#if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS) + + // Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky. + if (fog_enabled) { + vec4 fog = fog_process(cube_normal, color.rgb); + color.rgb = mix(color.rgb, fog.rgb, fog.a * fog_sky_affect); + } + + if (custom_fog.a > 0.0) { + color.rgb = mix(color.rgb, custom_fog.rgb, custom_fog.a); + } + +#endif // DISABLE_FOG + color *= exposure; #ifdef APPLY_TONEMAPPING color = apply_tonemapping(color, white); diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index f9547502f4..aab1aadf02 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -1046,6 +1046,9 @@ void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_use lightmap->light_texture = p_light; lightmap->uses_spherical_harmonics = p_uses_spherical_haromics; + Vector3i light_texture_size = GLES3::TextureStorage::get_singleton()->texture_get_size(lightmap->light_texture); + lightmap->light_texture_size = Vector2i(light_texture_size.x, light_texture_size.y); + GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lightmap->light_texture); glBindTexture(GL_TEXTURE_2D_ARRAY, tex); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index b6e64c9492..81e7220439 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -180,6 +180,7 @@ struct Lightmap { bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); float baked_exposure = 1.0; + Vector2i light_texture_size; int32_t array_index = -1; //unassigned PackedVector3Array points; PackedColorArray point_sh; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 41c23fc3ec..25af7ff691 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -586,11 +586,7 @@ void ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { continue; } - if (E.value.texture_order >= 0) { - filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000)); - } else { - filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order)); - } + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.prop_order)); } int uniform_count = filtered_uniforms.size(); sorter.sort(filtered_uniforms.ptr(), uniform_count); @@ -640,7 +636,7 @@ bool ShaderData::is_parameter_texture(const StringName &p_param) const { return false; } - return uniforms[p_param].texture_order >= 0; + return uniforms[p_param].is_texture(); } /////////////////////////////////////////////////////////////////////////// @@ -719,7 +715,7 @@ void MaterialData::update_uniform_buffer(const HashMap<StringName, ShaderLanguag bool uses_global_buffer = false; for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : p_uniforms) { - if (E.value.order < 0) { + if (E.value.is_texture()) { continue; // texture, does not go here } diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index d8a5b960b8..b55a2e0a8a 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -301,7 +301,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) Vector<uint8_t> ir = new_surface.index_data; wr = wf_indices.ptrw(); - if (new_surface.vertex_count < (1 << 16)) { + if (new_surface.vertex_count <= 65536) { // Read 16 bit indices. const uint16_t *src_idx = (const uint16_t *)ir.ptr(); for (uint32_t i = 0; i + 5 < wf_index_count; i += 6) { @@ -1432,15 +1432,17 @@ void MeshStorage::update_mesh_instances() { /* MULTIMESH API */ -RID MeshStorage::multimesh_allocate() { +RID MeshStorage::_multimesh_allocate() { return multimesh_owner.allocate_rid(); } -void MeshStorage::multimesh_initialize(RID p_rid) { +void MeshStorage::_multimesh_initialize(RID p_rid) { multimesh_owner.initialize_rid(p_rid, MultiMesh()); } -void MeshStorage::multimesh_free(RID p_rid) { +void MeshStorage::_multimesh_free(RID p_rid) { + // Remove from interpolator. + _interpolation_data.notify_free_multimesh(p_rid); _update_dirty_multimeshes(); multimesh_allocate_data(p_rid, 0, RS::MULTIMESH_TRANSFORM_2D); MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid); @@ -1448,7 +1450,7 @@ void MeshStorage::multimesh_free(RID p_rid) { multimesh_owner.free(p_rid); } -void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { +void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); @@ -1495,13 +1497,13 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS:: multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH); } -int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const { +int MeshStorage::_multimesh_get_instance_count(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, 0); return multimesh->instances; } -void MeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { +void MeshStorage::_multimesh_set_mesh(RID p_multimesh, RID p_mesh) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); if (multimesh->mesh == p_mesh || p_mesh.is_null()) { @@ -1651,7 +1653,7 @@ void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p multimesh->aabb = aabb; } -void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { +void MeshStorage::_multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1681,7 +1683,7 @@ void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, _multimesh_mark_dirty(multimesh, p_index, true); } -void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { +void MeshStorage::_multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1707,7 +1709,7 @@ void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_ind _multimesh_mark_dirty(multimesh, p_index, true); } -void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { +void MeshStorage::_multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1727,7 +1729,7 @@ void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, con _multimesh_mark_dirty(multimesh, p_index, false); } -void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { +void MeshStorage::_multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1746,27 +1748,27 @@ void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_inde _multimesh_mark_dirty(multimesh, p_index, false); } -RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const { +RID MeshStorage::_multimesh_get_mesh(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, RID()); return multimesh->mesh; } -void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) { +void MeshStorage::_multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); multimesh->custom_aabb = p_aabb; multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } -AABB MeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const { +AABB MeshStorage::_multimesh_get_custom_aabb(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, AABB()); return multimesh->custom_aabb; } -AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const { +AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, AABB()); if (multimesh->custom_aabb != AABB()) { @@ -1778,7 +1780,7 @@ AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const { return multimesh->aabb; } -Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { +Transform3D MeshStorage::_multimesh_instance_get_transform(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Transform3D()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform3D()); @@ -1809,7 +1811,7 @@ Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p return t; } -Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { +Transform2D MeshStorage::_multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Transform2D()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform2D()); @@ -1834,7 +1836,7 @@ Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, in return t; } -Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const { +Color MeshStorage::_multimesh_instance_get_color(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Color()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); @@ -1858,7 +1860,7 @@ Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) co return c; } -Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { +Color MeshStorage::_multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Color()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); @@ -1882,7 +1884,7 @@ Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_ind return c; } -void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { +void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); @@ -1971,7 +1973,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b } } -Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { +Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Vector<float>()); Vector<float> ret; @@ -2043,7 +2045,7 @@ Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { } } -void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { +void MeshStorage::_multimesh_set_visible_instances(RID p_multimesh, int p_visible) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_COND(p_visible < -1 || p_visible > multimesh->instances); @@ -2065,12 +2067,19 @@ void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES); } -int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const { +int MeshStorage::_multimesh_get_visible_instances(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, 0); return multimesh->visible_instances; } +MeshStorage::MultiMeshInterpolator *MeshStorage::_multimesh_get_interpolator(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_NULL_V_MSG(multimesh, nullptr, "Multimesh not found: " + itos(p_multimesh.get_id())); + + return &multimesh->interpolator; +} + void MeshStorage::_update_dirty_multimeshes() { while (multimesh_dirty_list) { MultiMesh *multimesh = multimesh_dirty_list; diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index d246e7725c..a2edbb9c48 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -205,6 +205,8 @@ struct MultiMesh { bool dirty = false; MultiMesh *dirty_list = nullptr; + RendererMeshStorage::MultiMeshInterpolator interpolator; + Dependency dependency; }; @@ -493,32 +495,34 @@ public: MultiMesh *get_multimesh(RID p_rid) { return multimesh_owner.get_or_null(p_rid); }; bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); }; - virtual RID multimesh_allocate() override; - virtual void multimesh_initialize(RID p_rid) override; - virtual void multimesh_free(RID p_rid) override; - virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; - virtual int multimesh_get_instance_count(RID p_multimesh) const override; - - virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; - virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override; - virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override; - virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override; - virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; - - virtual RID multimesh_get_mesh(RID p_multimesh) const override; - virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override; - virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override; - virtual AABB multimesh_get_aabb(RID p_multimesh) const override; - - virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; - virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; - virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override; - virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; - virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; - virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override; - - virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; - virtual int multimesh_get_visible_instances(RID p_multimesh) const override; + virtual RID _multimesh_allocate() override; + virtual void _multimesh_initialize(RID p_rid) override; + virtual void _multimesh_free(RID p_rid) override; + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; + virtual int _multimesh_get_instance_count(RID p_multimesh) const override; + + virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; + virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override; + virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override; + virtual void _multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override; + virtual void _multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; + + virtual RID _multimesh_get_mesh(RID p_multimesh) const override; + virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override; + virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override; + virtual AABB _multimesh_get_aabb(RID p_multimesh) const override; + + virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; + virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; + virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override; + virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; + virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; + virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override; + + virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; + virtual int _multimesh_get_visible_instances(RID p_multimesh) const override; + + virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override; void _update_dirty_multimeshes(); diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 3b1373c928..8251c8f52e 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1680,6 +1680,14 @@ uint32_t TextureStorage::texture_get_texid(RID p_texture) const { return texture->tex_id; } +Vector3i TextureStorage::texture_get_size(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_NULL_V(texture, Vector3i(0, 0, 0)); + + return Vector3i(texture->width, texture->height, texture->depth); +} + uint32_t TextureStorage::texture_get_width(RID p_texture) const { Texture *texture = texture_owner.get_or_null(p_texture); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 1b83efee32..5569abcc73 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -553,6 +553,7 @@ public: void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); virtual Image::Format texture_get_format(RID p_texture) const override; uint32_t texture_get_texid(RID p_texture) const; + Vector3i texture_get_size(RID p_texture) const; uint32_t texture_get_width(RID p_texture) const; uint32_t texture_get_height(RID p_texture) const; uint32_t texture_get_depth(RID p_texture) const; diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 48b9e01fd8..f74bdd8bb9 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -1083,7 +1083,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { if (I.key == REMOVE_ICON) { if (!read_only) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action("Remove Bezier Track"); + undo_redo->create_action("Remove Bezier Track", UndoRedo::MERGE_DISABLE, animation.ptr()); undo_redo->add_do_method(this, "_update_locked_tracks_after", track); undo_redo->add_do_method(this, "_update_hidden_tracks_after", track); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index d24b1edd70..8664c167b5 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -994,6 +994,9 @@ Ref<Texture2D> CodeTextEditor::_get_completion_icon(const ScriptLanguage::CodeCo tex = get_editor_theme_icon(p_option.display); } else { tex = EditorNode::get_singleton()->get_class_icon(p_option.display); + if (!tex.is_valid()) { + tex = get_editor_theme_icon(SNAME("Object")); + } } } break; case ScriptLanguage::CODE_COMPLETION_KIND_ENUM: diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 02b0eca6aa..8ba5811ffa 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -572,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://", "/"); @@ -609,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. diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 02a95fd836..f75e438582 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -386,6 +386,8 @@ void EditorFileSystem::_scan_filesystem() { // On the first scan, the first_scan_root_dir is created in _first_scan_filesystem. if (first_scan) { sd = first_scan_root_dir; + // Will be updated on scan. + ResourceUID::get_singleton()->clear(); } else { Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES); sd = memnew(ScannedDirectory); @@ -3061,7 +3063,6 @@ EditorFileSystem::EditorFileSystem() { using_fat32_or_exfat = (da->get_filesystem_type() == "FAT32" || da->get_filesystem_type() == "exFAT"); scan_total = 0; - callable_mp(ResourceUID::get_singleton(), &ResourceUID::clear).call_deferred(); // Will be updated on scan. ResourceSaver::set_get_resource_id_for_path(_resource_saver_get_resource_id_for_path); } diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp index ae065aebf8..18f5610655 100644 --- a/editor/editor_folding.cpp +++ b/editor/editor_folding.cpp @@ -93,7 +93,7 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p if (!p_node->get_owner()) { return; //not owned, bye } - if (p_node->get_owner() != p_root && !p_root->is_editable_instance(p_node)) { + if (p_node->get_owner() != p_root && !p_root->is_editable_instance(p_node->get_owner())) { return; } } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index d1dffba2ab..6db8b8d915 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -429,6 +429,7 @@ void EditorNode::_update_from_settings() { RS::get_singleton()->decals_set_filter(RS::DecalFilter(int(GLOBAL_GET("rendering/textures/decals/filter")))); RS::get_singleton()->light_projectors_set_filter(RS::LightProjectorFilter(int(GLOBAL_GET("rendering/textures/light_projectors/filter")))); + RS::get_singleton()->lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter")); SceneTree *tree = get_tree(); tree->set_debug_collisions_color(GLOBAL_GET("debug/shapes/collision/shape_color")); @@ -670,7 +671,10 @@ void EditorNode::_notification(int p_what) { callable_mp(this, &EditorNode::_begin_first_scan).call_deferred(); - DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_update_theme).bind(false)); + last_dark_mode_state = DisplayServer::get_singleton()->is_dark_mode(); + last_system_accent_color = DisplayServer::get_singleton()->get_accent_color(); + last_system_base_color = DisplayServer::get_singleton()->get_base_color(); + DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_check_system_theme_changed)); /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */ } break; @@ -5343,7 +5347,7 @@ bool EditorNode::ensure_main_scene(bool p_from_native) { if (main_scene.is_empty()) { current_menu_option = -1; - pick_main_scene->set_text(TTR("No main scene has ever been defined, select one?\nYou can change it later in \"Project Settings\" under the 'application' category.")); + pick_main_scene->set_text(TTR("No main scene has ever been defined. Select one?\nYou can change it later in \"Project Settings\" under the 'application' category.")); pick_main_scene->popup_centered(); if (editor_data.get_edited_scene_root()) { @@ -5358,14 +5362,14 @@ bool EditorNode::ensure_main_scene(bool p_from_native) { if (!FileAccess::exists(main_scene)) { current_menu_option = -1; - pick_main_scene->set_text(vformat(TTR("Selected scene '%s' does not exist, select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene)); + pick_main_scene->set_text(vformat(TTR("Selected scene '%s' does not exist. Select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene)); pick_main_scene->popup_centered(); return false; } if (ResourceLoader::get_resource_type(main_scene) != "PackedScene") { current_menu_option = -1; - pick_main_scene->set_text(vformat(TTR("Selected scene '%s' is not a scene file, select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene)); + pick_main_scene->set_text(vformat(TTR("Selected scene '%s' is not a scene file. Select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene)); pick_main_scene->popup_centered(); return false; } diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 4cd44e3020..8935b9ad8a 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -618,9 +618,9 @@ void EditorResourcePicker::_ensure_allowed_types() const { const String base = allowed_types[i].strip_edges(); if (base == "BaseMaterial3D") { allowed_types_with_convert.insert("Texture2D"); - } else if (base == "ShaderMaterial") { + } else if (ClassDB::is_parent_class("ShaderMaterial", base)) { allowed_types_with_convert.insert("Shader"); - } else if (base == "Texture2D") { + } else if (ClassDB::is_parent_class("ImageTexture", base)) { allowed_types_with_convert.insert("Image"); } } diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index c3a939c007..b9d530353c 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -65,7 +65,7 @@ bool EditorSettings::_set(const StringName &p_name, const Variant &p_value) { _THREAD_SAFE_METHOD_ bool changed = _set_only(p_name, p_value); - if (changed) { + if (changed && initialized) { changed_settings.insert(p_name); emit_signal(SNAME("settings_changed")); } @@ -330,6 +330,10 @@ bool EditorSettings::has_default_value(const String &p_setting) const { return props[p_setting].has_default_value; } +void EditorSettings::_set_initialized() { + initialized = true; +} + void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _THREAD_SAFE_METHOD_ // Sets up the editor setting with a default value and hint PropertyInfo. @@ -1927,7 +1931,5 @@ EditorSettings::EditorSettings() { last_order = 0; _load_defaults(); -} - -EditorSettings::~EditorSettings() { + callable_mp(this, &EditorSettings::_set_initialized).call_deferred(); } diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 62ac0c60f3..6338f9105c 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -98,6 +98,7 @@ private: bool save_changed_setting = true; bool optimize_save = true; //do not save stuff that came from config but was not set from engine + bool initialized = false; bool _set(const StringName &p_name, const Variant &p_value); bool _set_only(const StringName &p_name, const Variant &p_value); @@ -108,6 +109,7 @@ private: bool _property_can_revert(const StringName &p_name) const; bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + void _set_initialized(); void _load_defaults(Ref<ConfigFile> p_extra_config = Ref<ConfigFile>()); void _load_godot2_text_editor_theme(); void _load_default_visual_shader_editor_theme(); @@ -196,7 +198,6 @@ public: #endif EditorSettings(); - ~EditorSettings(); }; //not a macro any longer diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index b780d42119..a9de8e3bc5 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -685,7 +685,15 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s } Vector<String> FileSystemDock::get_selected_paths() const { - return _tree_get_selected(false); + if (display_mode == DISPLAY_MODE_TREE_ONLY) { + return _tree_get_selected(false); + } else { + Vector<String> selected = _file_list_get_selected(); + if (selected.is_empty()) { + selected.push_back(get_current_directory()); + } + return selected; + } } String FileSystemDock::get_current_path() const { @@ -2050,6 +2058,15 @@ Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion, bo return selected_strings; } +Vector<String> FileSystemDock::_file_list_get_selected() const { + Vector<String> selected; + + for (int idx : files->get_selected_items()) { + selected.push_back(files->get_item_metadata(idx)); + } + return selected; +} + Vector<String> FileSystemDock::_remove_self_included_paths(Vector<String> selected_strings) { // Remove paths or files that are included into another. if (selected_strings.size() > 1) { diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 959ace8eba..2f54cb91db 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -359,6 +359,7 @@ private: void _update_display_mode(bool p_force = false); Vector<String> _tree_get_selected(bool remove_self_inclusion = true, bool p_include_unselected_cursor = false) const; + Vector<String> _file_list_get_selected() const; bool _is_file_type_disabled_by_feature_profile(const StringName &p_class); diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index c6cc0e97dd..87d8ddad09 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -1000,6 +1000,7 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) { TreeItem *item = p_node ? _find(tree->get_root(), p_node->get_path()) : nullptr; if (item) { + selected = p_node; if (auto_expand_selected) { // Make visible when it's collapsed. TreeItem *node = item->get_parent(); @@ -1009,8 +1010,24 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) { } item->select(0); item->set_as_cursor(0); - selected = p_node; tree->ensure_cursor_is_visible(); + } else { + // Ensure the node is selected and visible for the user if the node + // is not collapsed. + bool collapsed = false; + TreeItem *node = item; + while (node && node != tree->get_root()) { + if (node->is_collapsed()) { + collapsed = true; + break; + } + node = node->get_parent(); + } + if (!collapsed) { + item->select(0); + item->set_as_cursor(0); + tree->ensure_cursor_is_visible(); + } } } else { if (!p_node) { diff --git a/editor/icons/AudioStreamPlayer.svg b/editor/icons/AudioStreamPlayer.svg index a3c4ad8e35..561e1c3bb0 100644 --- a/editor/icons/AudioStreamPlayer.svg +++ b/editor/icons/AudioStreamPlayer.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#e0e0e0" stroke="#e0e0e0" stroke-linejoin="round" stroke-width=".176" d="M1.382.335.777.858H.204v.673h.564l.614.531Z"/><path fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-width=".176" d="M1.718.572a1.06 1.06 0 0 1 0 1.256M1.947.343c.402.5.402 1.213 0 1.714"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#e0e0e0" d="M1.252.15a.1.1 0 0 0-.082.03L.6.75H.318C.225.75.15.817.15.9v.6c0 .083.075.15.168.15H.6l.57.57c.066.067.18.02.18-.074V.256A.106.106 0 0 0 1.252.15" paint-order="markers stroke fill"/><path fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-width=".165" d="M1.575.675c.45.525 0 1.05 0 1.05m.3-1.35c.675.825 0 1.65 0 1.65" paint-order="markers stroke fill"/></svg>
\ No newline at end of file diff --git a/editor/icons/AudioStreamPlayer2D.svg b/editor/icons/AudioStreamPlayer2D.svg index fa60e30238..7f557b2cfa 100644 --- a/editor/icons/AudioStreamPlayer2D.svg +++ b/editor/icons/AudioStreamPlayer2D.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#8da5f3" stroke="#8da5f3" stroke-linejoin="round" stroke-width=".176" d="M1.382.335.777.858H.204v.673h.564l.614.531Z"/><path fill="none" stroke="#8da5f3" stroke-linecap="round" stroke-width=".176" d="M1.718.572a1.06 1.06 0 0 1 0 1.256M1.947.343c.402.5.402 1.213 0 1.714"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#8da5f3" d="M1.252.15a.1.1 0 0 0-.082.03L.6.75H.318C.225.75.15.817.15.9v.6c0 .083.075.15.168.15H.6l.57.57c.066.067.18.02.18-.074V.256A.106.106 0 0 0 1.252.15" paint-order="markers stroke fill"/><path fill="none" stroke="#8da5f3" stroke-linecap="round" stroke-linejoin="round" stroke-width=".165" d="M1.575.675c.45.525 0 1.05 0 1.05m.3-1.35c.675.825 0 1.65 0 1.65" paint-order="markers stroke fill"/></svg>
\ No newline at end of file diff --git a/editor/icons/AudioStreamPlayer3D.svg b/editor/icons/AudioStreamPlayer3D.svg index f6be929e61..5f07d68eec 100644 --- a/editor/icons/AudioStreamPlayer3D.svg +++ b/editor/icons/AudioStreamPlayer3D.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#fc7f7f" stroke="#fc7f7f" stroke-linejoin="round" stroke-width=".176" d="M1.382.335.777.858H.204v.673h.564l.614.531Z"/><path fill="none" stroke="#fc7f7f" stroke-linecap="round" stroke-width=".176" d="M1.718.572a1.06 1.06 0 0 1 0 1.256M1.947.343c.402.5.402 1.213 0 1.714"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#fc7f7f" d="M1.252.15a.1.1 0 0 0-.082.03L.6.75H.318C.225.75.15.817.15.9v.6c0 .083.075.15.168.15H.6l.57.57c.066.067.18.02.18-.074V.256A.106.106 0 0 0 1.252.15" paint-order="markers stroke fill"/><path fill="none" stroke="#fc7f7f" stroke-linecap="round" stroke-linejoin="round" stroke-width=".165" d="M1.575.675c.45.525 0 1.05 0 1.05m.3-1.35c.675.825 0 1.65 0 1.65" paint-order="markers stroke fill"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels1.svg b/editor/icons/Panels1.svg index 33a511a22b..c37d974058 100644 --- a/editor/icons/Panels1.svg +++ b/editor/icons/Panels1.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0h16v16h-16z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12.002a2 2 0 0 0 2 2h12.002a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h12v10H2z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels2.svg b/editor/icons/Panels2.svg index 2621c36c57..673ae62aec 100644 --- a/editor/icons/Panels2.svg +++ b/editor/icons/Panels2.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h16v-7zm0 9v7h16v-7z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h12v4H2zm12 6v4H2v-4z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels2Alt.svg b/editor/icons/Panels2Alt.svg index 0a6c6b6c04..e2f27e415e 100644 --- a/editor/icons/Panels2Alt.svg +++ b/editor/icons/Panels2Alt.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v16h7v-16zm9 0v16h7v-16z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h5v10H2zm7 0h5v10H9z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels3.svg b/editor/icons/Panels3.svg index 5ee8b7f120..d5cf987c41 100644 --- a/editor/icons/Panels3.svg +++ b/editor/icons/Panels3.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h16v-7zm0 9v7h7v-7zm9 0v7h7v-7z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h12v4H2zm0 6h5v4H2zm7 0h5v4H9z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels3Alt.svg b/editor/icons/Panels3Alt.svg index dc373c6fc5..ea6745df6f 100644 --- a/editor/icons/Panels3Alt.svg +++ b/editor/icons/Panels3Alt.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h7v-7zm9 0v16h7v-16zm-9 9v7h7v-7z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h5v4H2zm7 0h5v10H9zm-7 6h5v4H2z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels4.svg b/editor/icons/Panels4.svg index e0ff57de4d..407dfba1b6 100644 --- a/editor/icons/Panels4.svg +++ b/editor/icons/Panels4.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h7v-7zm9 0v7h7v-7zm-9 9v7h7v-7zm9 0v7h7v-7z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h5v4H2zm7 0h5v4H9zm-7 6h5v4H2zm7 0h5v4H9z"/></svg>
\ No newline at end of file diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index 27b2af8f77..8ad8e6201e 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -2544,6 +2544,8 @@ 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, raycast_normals); diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp index f1b6d3a6ac..f01381904d 100644 --- a/editor/import/resource_importer_imagefont.cpp +++ b/editor/import/resource_importer_imagefont.cpp @@ -293,18 +293,20 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin } String from_tokens; for (int i = 0; i < kp_tokens[0].length(); i++) { - if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u') { + if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u' && is_hex_digit(kp_tokens[0][i + 2]) && is_hex_digit(kp_tokens[0][i + 3]) && is_hex_digit(kp_tokens[0][i + 4]) && is_hex_digit(kp_tokens[0][i + 5])) { char32_t charcode = kp_tokens[0].substr(i + 2, 4).hex_to_int(); from_tokens += charcode; + i += 5; } else { from_tokens += kp_tokens[0][i]; } } String to_tokens; for (int i = 0; i < kp_tokens[1].length(); i++) { - if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u') { + if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u' && is_hex_digit(kp_tokens[1][i + 2]) && is_hex_digit(kp_tokens[1][i + 3]) && is_hex_digit(kp_tokens[1][i + 4]) && is_hex_digit(kp_tokens[1][i + 5])) { char32_t charcode = kp_tokens[1].substr(i + 2, 4).hex_to_int(); to_tokens += charcode; + i += 5; } else { to_tokens += kp_tokens[1][i]; } diff --git a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp index 19dd45a3ea..8d0222215c 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 camera_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), camera_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/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index de56767929..67d5e44ce5 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -81,6 +81,8 @@ void EditorNode3DGizmo::redraw() { gizmo_plugin->redraw(this); } + _update_bvh(); + if (Node3DEditor::get_singleton()->is_current_selected_gizmo(this)) { Node3DEditor::get_singleton()->update_transform_gizmo(); } @@ -244,6 +246,32 @@ void EditorNode3DGizmo::add_mesh(const Ref<Mesh> &p_mesh, const Ref<Material> &p instances.push_back(ins); } +void EditorNode3DGizmo::_update_bvh() { + ERR_FAIL_NULL(spatial_node); + + Transform3D transform = spatial_node->get_global_transform(); + + float effective_icon_size = selectable_icon_size > 0.0f ? selectable_icon_size : 0.0f; + Vector3 icon_size_vector3 = Vector3(effective_icon_size, effective_icon_size, effective_icon_size); + AABB aabb(spatial_node->get_position() - icon_size_vector3 * 100.0f, icon_size_vector3 * 200.0f); + + for (const Vector3 &segment_end : collision_segments) { + aabb.expand_to(transform.xform(segment_end)); + } + + if (collision_mesh.is_valid()) { + for (const Face3 &face : collision_mesh->get_faces()) { + aabb.expand_to(transform.xform(face.vertex[0])); + aabb.expand_to(transform.xform(face.vertex[1])); + aabb.expand_to(transform.xform(face.vertex[2])); + } + } + + Node3DEditor::get_singleton()->update_gizmo_bvh_node( + bvh_node_id, + aabb); +} + void EditorNode3DGizmo::add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard, const Color &p_modulate) { add_vertices(p_lines, p_material, Mesh::PRIMITIVE_LINES, p_billboard, p_modulate); } @@ -765,6 +793,10 @@ void EditorNode3DGizmo::create() { instances.write[i].create_instance(spatial_node, hidden); } + bvh_node_id = Node3DEditor::get_singleton()->insert_gizmo_bvh_node( + spatial_node, + AABB(spatial_node->get_position(), Vector3(0, 0, 0))); + transform(); } @@ -774,6 +806,8 @@ void EditorNode3DGizmo::transform() { for (int i = 0; i < instances.size(); i++) { RS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform() * instances[i].xform); } + + _update_bvh(); } void EditorNode3DGizmo::free() { @@ -790,6 +824,9 @@ void EditorNode3DGizmo::free() { clear(); + Node3DEditor::get_singleton()->remove_gizmo_bvh_node(bvh_node_id); + bvh_node_id = DynamicBVH::ID(); + valid = false; } diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index d7c368d5d0..c4b275032a 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -31,6 +31,7 @@ #ifndef NODE_3D_EDITOR_GIZMOS_H #define NODE_3D_EDITOR_GIZMOS_H +#include "core/math/dynamic_bvh.h" #include "core/templates/hash_map.h" #include "core/templates/local_vector.h" #include "scene/3d/camera_3d.h" @@ -72,8 +73,12 @@ class EditorNode3DGizmo : public Node3DGizmo { Vector<Instance> instances; Node3D *spatial_node = nullptr; + DynamicBVH::ID bvh_node_id; + void _set_node_3d(Node *p_node) { set_node_3d(Object::cast_to<Node3D>(p_node)); } + void _update_bvh(); + protected: static void _bind_methods(); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 1df9dae767..f5b0a3b51e 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -800,7 +800,6 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) const { RS::get_singleton()->sdfgi_set_debug_probe_select(pos, ray); } - Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario()); HashSet<Ref<EditorNode3DGizmo>> found_gizmos; Node *edited_scene = get_tree()->get_edited_scene_root(); @@ -808,9 +807,9 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) const { Node *item = nullptr; float closest_dist = 1e20; - for (int i = 0; i < instances.size(); i++) { - Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i])); + Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_ray_query(pos, pos + ray * camera->get_far()); + for (Node3D *spat : nodes_with_gizmos) { if (!spat) { continue; } @@ -863,12 +862,11 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayRe Vector3 ray = get_ray(p_pos); Vector3 pos = get_ray_pos(p_pos); - Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario()); - HashSet<Node3D *> found_nodes; + Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_ray_query(pos, pos + ray * camera->get_far()); - for (int i = 0; i < instances.size(); i++) { - Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i])); + HashSet<Node3D *> found_nodes; + for (Node3D *spat : nodes_with_gizmos) { if (!spat) { continue; } @@ -1046,7 +1044,7 @@ void Node3DEditorViewport::_select_region() { _clear_selected(); } - Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world_3d()->get_scenario()); + Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_frustum_query(frustum); HashSet<Node3D *> found_nodes; Vector<Node *> selected; @@ -1055,8 +1053,7 @@ void Node3DEditorViewport::_select_region() { return; } - for (int i = 0; i < instances.size(); i++) { - Node3D *sp = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i])); + for (Node3D *sp : nodes_with_gizmos) { if (!sp || _is_node_locked(sp)) { continue; } @@ -1918,12 +1915,17 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } if (after != EditorPlugin::AFTER_GUI_INPUT_CUSTOM) { - //clicking is always deferred to either move or release - clicked = _select_ray(b->get_position()); + // Single item selection. + Vector<_RayResult> selection; + _find_items_at_pos(b->get_position(), selection, false); + if (!selection.is_empty()) { + clicked = selection[0].item->get_instance_id(); + } + selection_in_progress = true; if (clicked.is_null()) { - //default to regionselect + // Default to region select. cursor.region_select = true; cursor.region_begin = b->get_position(); cursor.region_end = b->get_position(); @@ -2254,6 +2256,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } if (_edit.mode == TRANSFORM_NONE) { + if (_edit.gizmo.is_null() && is_freelook_active() && k->get_keycode() == Key::ESCAPE) { + set_freelook_active(false); + return; + } + if (_edit.gizmo.is_valid() && (k->get_keycode() == Key::ESCAPE || k->get_keycode() == Key::BACKSPACE)) { // Restore. _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true); @@ -3109,6 +3116,7 @@ void Node3DEditorViewport::_notification(int p_what) { case NOTIFICATION_DRAG_END: { // Clear preview material when dropped outside applicable object. if (spatial_editor->get_preview_material().is_valid() && !is_drag_successful()) { + _reset_preview_material(); _remove_preview_material(); } else { _remove_preview_node(); @@ -9225,6 +9233,49 @@ void Node3DEditor::remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin) { _update_gizmos_menu(); } +DynamicBVH::ID Node3DEditor::insert_gizmo_bvh_node(Node3D *p_node, const AABB &p_aabb) { + return gizmo_bvh.insert(p_aabb, p_node); +} + +void Node3DEditor::update_gizmo_bvh_node(DynamicBVH::ID p_id, const AABB &p_aabb) { + gizmo_bvh.update(p_id, p_aabb); + gizmo_bvh.optimize_incremental(1); +} + +void Node3DEditor::remove_gizmo_bvh_node(DynamicBVH::ID p_id) { + gizmo_bvh.remove(p_id); +} + +Vector<Node3D *> Node3DEditor::gizmo_bvh_ray_query(const Vector3 &p_ray_start, const Vector3 &p_ray_end) { + struct Result { + Vector<Node3D *> nodes; + bool operator()(void *p_data) { + nodes.append((Node3D *)p_data); + return false; + } + } result; + + gizmo_bvh.ray_query(p_ray_start, p_ray_end, result); + + return result.nodes; +} + +Vector<Node3D *> Node3DEditor::gizmo_bvh_frustum_query(const Vector<Plane> &p_frustum) { + Vector<Vector3> points = Geometry3D::compute_convex_mesh_points(&p_frustum[0], p_frustum.size()); + + struct Result { + Vector<Node3D *> nodes; + bool operator()(void *p_data) { + nodes.append((Node3D *)p_data); + return false; + } + } result; + + gizmo_bvh.convex_query(p_frustum.ptr(), p_frustum.size(), points.ptr(), points.size(), result); + + return result.nodes; +} + Node3DEditorPlugin::Node3DEditorPlugin() { spatial_editor = memnew(Node3DEditor); spatial_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 5bd14748c0..9e7d46c5e8 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -31,6 +31,7 @@ #ifndef NODE_3D_EDITOR_PLUGIN_H #define NODE_3D_EDITOR_PLUGIN_H +#include "core/math/dynamic_bvh.h" #include "editor/plugins/editor_plugin.h" #include "editor/plugins/node_3d_editor_gizmos.h" #include "editor/themes/editor_scale.h" @@ -629,6 +630,8 @@ private: int current_hover_gizmo_handle; bool current_hover_gizmo_handle_secondary; + DynamicBVH gizmo_bvh; + real_t snap_translate_value; real_t snap_rotate_value; real_t snap_scale_value; @@ -933,6 +936,12 @@ public: void add_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin); void remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin); + DynamicBVH::ID insert_gizmo_bvh_node(Node3D *p_node, const AABB &p_aabb); + void update_gizmo_bvh_node(DynamicBVH::ID p_id, const AABB &p_aabb); + void remove_gizmo_bvh_node(DynamicBVH::ID p_id); + Vector<Node3D *> gizmo_bvh_ray_query(const Vector3 &p_ray_start, const Vector3 &p_ray_end); + Vector<Node3D *> gizmo_bvh_frustum_query(const Vector<Plane> &p_frustum); + void edit(Node3D *p_spatial); void clear(); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index e442c37edd..f0ea322504 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -1255,44 +1255,43 @@ void Polygon2DEditor::_uv_draw() { //draw skeleton NodePath skeleton_path = node->get_skeleton(); - if (node->has_node(skeleton_path)) { - Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(node->get_node(skeleton_path)); - if (skeleton) { - for (int i = 0; i < skeleton->get_bone_count(); i++) { - Bone2D *bone = skeleton->get_bone(i); - if (bone->get_rest() == Transform2D(0, 0, 0, 0, 0, 0)) { - continue; //not set - } + Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(node->get_node_or_null(skeleton_path)); + if (skeleton) { + Transform2D skeleton_xform = node->get_global_transform().affine_inverse().translated(-node->get_offset()) * skeleton->get_global_transform(); + for (int i = 0; i < skeleton->get_bone_count(); i++) { + Bone2D *bone = skeleton->get_bone(i); + if (bone->get_rest() == Transform2D(0, 0, 0, 0, 0, 0)) { + continue; //not set + } - bool current = bone_path == skeleton->get_path_to(bone); + bool current = bone_path == skeleton->get_path_to(bone); - bool found_child = false; + bool found_child = false; - for (int j = 0; j < bone->get_child_count(); j++) { - Bone2D *n = Object::cast_to<Bone2D>(bone->get_child(j)); - if (!n) { - continue; - } + for (int j = 0; j < bone->get_child_count(); j++) { + Bone2D *n = Object::cast_to<Bone2D>(bone->get_child(j)); + if (!n) { + continue; + } - found_child = true; + found_child = true; - Transform2D bone_xform = node->get_global_transform().affine_inverse().translated(-node->get_offset()) * (skeleton->get_global_transform() * bone->get_skeleton_rest()); - Transform2D endpoint_xform = bone_xform * n->get_transform(); + Transform2D bone_xform = skeleton_xform * bone->get_skeleton_rest(); + Transform2D endpoint_xform = bone_xform * n->get_transform(); - Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5); - uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE)); - uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE)); - } + Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5); + uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE)); + uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE)); + } - if (!found_child) { - //draw normally - Transform2D bone_xform = node->get_global_transform().affine_inverse().translated(-node->get_offset()) * (skeleton->get_global_transform() * bone->get_skeleton_rest()); - Transform2D endpoint_xform = bone_xform * Transform2D(0, Vector2(bone->get_length(), 0)); + if (!found_child) { + //draw normally + Transform2D bone_xform = skeleton_xform * bone->get_skeleton_rest(); + Transform2D endpoint_xform = bone_xform * Transform2D(0, Vector2(bone->get_length(), 0)).rotated(bone->get_bone_angle()); - Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5); - uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE)); - uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE)); - } + Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5); + uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE)); + uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE)); } } } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index c51eb44aee..9da66a0862 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3799,7 +3799,10 @@ void ScriptEditor::_on_find_in_files_result_selected(const String &fpath, int li ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_editor_data().get_editor_by_name("Shader")); shader_editor->edit(res.ptr()); shader_editor->make_visible(true); - shader_editor->get_shader_editor(res)->goto_line_selection(line_number - 1, begin, end); + TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(shader_editor->get_shader_editor(res)); + if (text_shader_editor) { + text_shader_editor->goto_line_selection(line_number - 1, begin, end); + } return; } else if (fpath.get_extension() == "tscn") { Ref<FileAccess> f = FileAccess::open(fpath, FileAccess::READ); diff --git a/editor/plugins/shader/shader_editor.h b/editor/plugins/shader/shader_editor.h new file mode 100644 index 0000000000..32e690015b --- /dev/null +++ b/editor/plugins/shader/shader_editor.h @@ -0,0 +1,50 @@ +/**************************************************************************/ +/* shader_editor.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef SHADER_EDITOR_H +#define SHADER_EDITOR_H + +#include "scene/gui/control.h" +#include "scene/resources/shader.h" + +class ShaderEditor : public Control { + GDCLASS(ShaderEditor, Control); + +public: + virtual void edit_shader(const Ref<Shader> &p_shader) = 0; + virtual void edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) {} + + virtual void apply_shaders() = 0; + virtual bool is_unsaved() const = 0; + virtual void save_external_data(const String &p_str = "") = 0; + virtual void validate_script() = 0; +}; + +#endif // SHADER_EDITOR_H diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 9d1576cdf2..82c436d3a5 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -142,7 +142,7 @@ void ShaderEditorPlugin::edit(Object *p_object) { } es.shader_inc = Ref<ShaderInclude>(si); es.shader_editor = memnew(TextShaderEditor); - es.shader_editor->edit(si); + es.shader_editor->edit_shader_include(si); shader_tabs->add_child(es.shader_editor); } else { Shader *s = Object::cast_to<Shader>(p_object); @@ -156,20 +156,18 @@ void ShaderEditorPlugin::edit(Object *p_object) { es.shader = Ref<Shader>(s); Ref<VisualShader> vs = es.shader; if (vs.is_valid()) { - es.visual_shader_editor = memnew(VisualShaderEditor); - shader_tabs->add_child(es.visual_shader_editor); - es.visual_shader_editor->edit(vs.ptr()); + es.shader_editor = memnew(VisualShaderEditor); } else { es.shader_editor = memnew(TextShaderEditor); - shader_tabs->add_child(es.shader_editor); - es.shader_editor->edit(s); } + shader_tabs->add_child(es.shader_editor); + es.shader_editor->edit_shader(es.shader); } - if (es.shader_editor) { - es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list)); - - CodeTextEditor *cte = es.shader_editor->get_code_editor(); + TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(es.shader_editor); + if (text_shader_editor) { + text_shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list)); + CodeTextEditor *cte = text_shader_editor->get_code_editor(); if (cte) { cte->set_zoom_factor(text_shader_zoom_factor); cte->connect("zoomed", callable_mp(this, &ShaderEditorPlugin::_set_text_shader_zoom_factor)); @@ -194,7 +192,7 @@ void ShaderEditorPlugin::make_visible(bool p_visible) { void ShaderEditorPlugin::selected_notify() { } -TextShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) { +ShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) { for (EditedShader &edited_shader : edited_shaders) { if (edited_shader.shader == p_for_shader) { return edited_shader.shader_editor; @@ -203,15 +201,6 @@ TextShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for return nullptr; } -VisualShaderEditor *ShaderEditorPlugin::get_visual_shader_editor(const Ref<Shader> &p_for_shader) { - for (EditedShader &edited_shader : edited_shaders) { - if (edited_shader.shader == p_for_shader) { - return edited_shader.visual_shader_editor; - } - } - return nullptr; -} - void ShaderEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) { if (EDITOR_GET("interface/multi_window/restore_windows_on_load") && window_wrapper->is_window_available() && p_layout->has_section_key("ShaderEditor", "window_rect")) { window_wrapper->restore_window_from_saved_position( @@ -280,7 +269,7 @@ void ShaderEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) { String selected_shader; for (int i = 0; i < shader_tabs->get_tab_count(); i++) { EditedShader edited_shader = edited_shaders[i]; - if (edited_shader.shader_editor || edited_shader.visual_shader_editor) { + if (edited_shader.shader_editor) { String shader_path; if (edited_shader.shader.is_valid()) { shader_path = edited_shader.shader->get_path(); @@ -290,10 +279,9 @@ void ShaderEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) { } shaders.push_back(shader_path); - TextShaderEditor *shader_editor = Object::cast_to<TextShaderEditor>(shader_tabs->get_current_tab_control()); - VisualShaderEditor *visual_shader_editor = Object::cast_to<VisualShaderEditor>(shader_tabs->get_current_tab_control()); + ShaderEditor *shader_editor = Object::cast_to<ShaderEditor>(shader_tabs->get_current_tab_control()); - if ((shader_editor && edited_shader.shader_editor == shader_editor) || (visual_shader_editor && edited_shader.visual_shader_editor == visual_shader_editor)) { + if (shader_editor && edited_shader.shader_editor == shader_editor) { selected_shader = shader_path; } } @@ -366,10 +354,6 @@ void ShaderEditorPlugin::_shader_selected(int p_index) { edited_shaders[p_index].shader_editor->validate_script(); } - if (edited_shaders[p_index].visual_shader_editor) { - edited_shaders[p_index].visual_shader_editor->validate_script(); - } - shader_tabs->set_current_tab(p_index); shader_list->select(p_index); } @@ -440,7 +424,7 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) { case FILE_SAVE: { int index = shader_tabs->get_current_tab(); ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); - TextShaderEditor *editor = edited_shaders[index].shader_editor; + TextShaderEditor *editor = Object::cast_to<TextShaderEditor>(edited_shaders[index].shader_editor); if (editor) { if (editor->get_trim_trailing_whitespace_on_save()) { editor->trim_trailing_whitespace(); @@ -462,7 +446,7 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) { case FILE_SAVE_AS: { int index = shader_tabs->get_current_tab(); ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); - TextShaderEditor *editor = edited_shaders[index].shader_editor; + TextShaderEditor *editor = Object::cast_to<TextShaderEditor>(edited_shaders[index].shader_editor); if (editor) { if (editor->get_trim_trailing_whitespace_on_save()) { editor->trim_trailing_whitespace(); @@ -623,8 +607,9 @@ void ShaderEditorPlugin::_set_text_shader_zoom_factor(float p_zoom_factor) { if (text_shader_zoom_factor != p_zoom_factor) { text_shader_zoom_factor = p_zoom_factor; for (const EditedShader &edited_shader : edited_shaders) { - if (edited_shader.shader_editor) { - CodeTextEditor *cte = edited_shader.shader_editor->get_code_editor(); + TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(edited_shader.shader_editor); + if (text_shader_editor) { + CodeTextEditor *cte = text_shader_editor->get_code_editor(); if (cte && cte->get_zoom_factor() != text_shader_zoom_factor) { cte->set_zoom_factor(text_shader_zoom_factor); } @@ -655,12 +640,13 @@ void ShaderEditorPlugin::_res_saved_callback(const Ref<Resource> &p_res) { } ERR_FAIL_COND(shader_res.is_null()); - if (!edited.shader_editor || !shader_res->is_built_in()) { + TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(edited.shader_editor); + if (!text_shader_editor || !shader_res->is_built_in()) { continue; } if (shader_res->get_path().get_slice("::", 0) == path) { - edited.shader_editor->tag_saved_version(); + text_shader_editor->tag_saved_version(); _update_shader_list(); } } diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 386261d844..f9b9405e45 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -37,6 +37,7 @@ class HSplitContainer; class ItemList; class MenuButton; class ShaderCreateDialog; +class ShaderEditor; class TabContainer; class TextShaderEditor; class VisualShaderEditor; @@ -52,8 +53,7 @@ class ShaderEditorPlugin : public EditorPlugin { struct EditedShader { Ref<Shader> shader; Ref<ShaderInclude> shader_inc; - TextShaderEditor *shader_editor = nullptr; - VisualShaderEditor *visual_shader_editor = nullptr; + ShaderEditor *shader_editor = nullptr; String path; String name; }; @@ -121,8 +121,7 @@ public: virtual void make_visible(bool p_visible) override; virtual void selected_notify() override; - TextShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader); - VisualShaderEditor *get_visual_shader_editor(const Ref<Shader> &p_for_shader); + ShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader); virtual void set_window_layout(Ref<ConfigFile> p_layout) override; virtual void get_window_layout(Ref<ConfigFile> p_layout) override; diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index 276c57533f..6cd92bdf57 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -895,7 +895,7 @@ void TextShaderEditor::_reload() { } } -void TextShaderEditor::edit(const Ref<Shader> &p_shader) { +void TextShaderEditor::edit_shader(const Ref<Shader> &p_shader) { if (p_shader.is_null() || !p_shader->is_text_shader()) { return; } @@ -910,7 +910,7 @@ void TextShaderEditor::edit(const Ref<Shader> &p_shader) { code_editor->set_edited_shader(shader); } -void TextShaderEditor::edit(const Ref<ShaderInclude> &p_shader_inc) { +void TextShaderEditor::edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) { if (p_shader_inc.is_null()) { return; } @@ -1141,6 +1141,7 @@ TextShaderEditor::TextShaderEditor() { context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &TextShaderEditor::_menu_option)); VBoxContainer *main_container = memnew(VBoxContainer); + main_container->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); HBoxContainer *hbc = memnew(HBoxContainer); edit_menu = memnew(MenuButton); diff --git a/editor/plugins/text_shader_editor.h b/editor/plugins/text_shader_editor.h index 61066ed7c6..55efb4e30e 100644 --- a/editor/plugins/text_shader_editor.h +++ b/editor/plugins/text_shader_editor.h @@ -32,6 +32,7 @@ #define TEXT_SHADER_EDITOR_H #include "editor/code_editor.h" +#include "editor/plugins/shader/shader_editor.h" #include "scene/gui/margin_container.h" #include "scene/gui/menu_button.h" #include "scene/gui/rich_text_label.h" @@ -104,8 +105,8 @@ public: ShaderTextEditor(); }; -class TextShaderEditor : public MarginContainer { - GDCLASS(TextShaderEditor, MarginContainer); +class TextShaderEditor : public ShaderEditor { + GDCLASS(TextShaderEditor, ShaderEditor); enum { EDIT_UNDO, @@ -188,19 +189,21 @@ protected: void _bookmark_item_pressed(int p_idx); public: + virtual void edit_shader(const Ref<Shader> &p_shader) override; + virtual void edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) override; + + virtual void apply_shaders() override; + virtual bool is_unsaved() const override; + virtual void save_external_data(const String &p_str = "") override; + virtual void validate_script() override; + bool was_compilation_successful() const { return compilation_success; } bool get_trim_trailing_whitespace_on_save() const { return trim_trailing_whitespace_on_save; } bool get_trim_final_newlines_on_save() const { return trim_final_newlines_on_save; } - void apply_shaders(); void ensure_select_current(); - void edit(const Ref<Shader> &p_shader); - void edit(const Ref<ShaderInclude> &p_shader_inc); void goto_line_selection(int p_line, int p_begin, int p_end); - void save_external_data(const String &p_str = ""); void trim_trailing_whitespace(); void trim_final_newlines(); - void validate_script(); - bool is_unsaved() const; void tag_saved_version(); ShaderTextEditor *get_code_editor() { return code_editor; } diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 7e34a36a6e..224c4e434f 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2612,7 +2612,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { atlas_source_inspector->set_v_size_flags(SIZE_EXPAND_FILL); atlas_source_inspector->set_show_categories(false, true); atlas_source_inspector->set_use_doc_hints(true); - atlas_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin)); middle_vbox_container->add_child(atlas_source_inspector); // -- Right side -- @@ -2740,9 +2739,8 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { add_child(confirm_auto_create_tiles); // Inspector plugin. - Ref<EditorInspectorPluginTileData> tile_data_inspector_plugin; - tile_data_inspector_plugin.instantiate(); - EditorInspector::add_inspector_plugin(tile_data_inspector_plugin); + EditorInspector::add_inspector_plugin(memnew(EditorInspectorPluginTileData)); + EditorInspector::add_inspector_plugin(memnew(TileSourceInspectorPlugin)); } TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() { diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp index 22ef779b8d..305407efdb 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -534,7 +534,6 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() { scenes_collection_source_inspector = memnew(EditorInspector); scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); scenes_collection_source_inspector->set_use_doc_hints(true); - scenes_collection_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin)); middle_vbox_container->add_child(scenes_collection_source_inspector); // Tile inspector. @@ -581,6 +580,8 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() { scene_tile_delete_button->set_disabled(true); scene_tile_delete_button->connect(SceneStringName(pressed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_delete_pressed)); scenes_bottom_actions->add_child(scene_tile_delete_button); + + EditorInspector::add_inspector_plugin(memnew(TileSourceInspectorPlugin)); } TileSetScenesCollectionSourceEditor::~TileSetScenesCollectionSourceEditor() { diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index b9b9a51735..d50f5e51d5 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -115,8 +115,8 @@ void VSGraphNode::_draw_port(int p_slot_index, Point2i p_pos, bool p_left, const icon_offset = -port_icon->get_size() * 0.5; // Draw "shadow"/outline in the connection rim color. - draw_texture_rect(port_icon, Rect2(p_pos + icon_offset - Size2(2, 2), port_icon->get_size() + Size2(4, 4)), false, p_rim_color); - draw_texture(port_icon, p_pos + icon_offset, p_color); + draw_texture_rect(port_icon, Rect2(p_pos + (icon_offset - Size2(2, 2)) * EDSCALE, (port_icon->get_size() + Size2(4, 4)) * EDSCALE), false, p_rim_color); + draw_texture_rect(port_icon, Rect2(p_pos + icon_offset * EDSCALE, port_icon->get_size() * EDSCALE), false, p_color); } void VSGraphNode::draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color) { @@ -1508,17 +1508,18 @@ Vector2 VisualShaderEditor::selection_center; List<VisualShaderEditor::CopyItem> VisualShaderEditor::copy_items_buffer; List<VisualShader::Connection> VisualShaderEditor::copy_connections_buffer; -void VisualShaderEditor::edit(VisualShader *p_visual_shader) { +void VisualShaderEditor::edit_shader(const Ref<Shader> &p_shader) { bool changed = false; - if (p_visual_shader) { + VisualShader *visual_shader_ptr = Object::cast_to<VisualShader>(p_shader.ptr()); + if (visual_shader_ptr) { if (visual_shader.is_null()) { changed = true; } else { - if (visual_shader.ptr() != p_visual_shader) { + if (visual_shader.ptr() != visual_shader_ptr) { changed = true; } } - visual_shader = Ref<VisualShader>(p_visual_shader); + visual_shader = p_shader; graph_plugin->register_shader(visual_shader.ptr()); visual_shader->connect_changed(callable_mp(this, &VisualShaderEditor::_update_preview)); @@ -1545,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(); @@ -6053,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); @@ -7561,7 +7574,7 @@ void EditorPropertyVisualShaderMode::_option_selected(int p_which) { if (!shader_editor) { return; } - VisualShaderEditor *editor = shader_editor->get_visual_shader_editor(visual_shader); + VisualShaderEditor *editor = Object::cast_to<VisualShaderEditor>(shader_editor->get_shader_editor(visual_shader)); if (!editor) { return; } diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 75c9d97b7c..6ce096f821 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -34,6 +34,7 @@ #include "editor/editor_properties.h" #include "editor/plugins/editor_plugin.h" #include "editor/plugins/editor_resource_conversion_plugin.h" +#include "editor/plugins/shader/shader_editor.h" #include "scene/gui/graph_edit.h" #include "scene/resources/syntax_highlighter.h" #include "scene/resources/visual_shader.h" @@ -195,8 +196,8 @@ public: VisualShaderEditedProperty() {} }; -class VisualShaderEditor : public VBoxContainer { - GDCLASS(VisualShaderEditor, VBoxContainer); +class VisualShaderEditor : public ShaderEditor { + GDCLASS(VisualShaderEditor, ShaderEditor); friend class VisualShaderGraphPlugin; PopupPanel *property_editor_popup = nullptr; @@ -596,6 +597,12 @@ protected: static void _bind_methods(); public: + virtual void edit_shader(const Ref<Shader> &p_shader) override; + virtual void apply_shaders() override; + virtual bool is_unsaved() const override; + virtual void save_external_data(const String &p_str = "") override; + virtual void validate_script() override; + void add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin); void remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin); @@ -609,11 +616,8 @@ public: virtual Size2 get_minimum_size() const override; - void edit(VisualShader *p_visual_shader); Ref<VisualShader> get_visual_shader() const { return visual_shader; } - void validate_script(); - VisualShaderEditor(); }; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 56924fc1fe..30050901d9 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1551,7 +1551,7 @@ void SceneTreeDock::_notification(int p_what) { node_shortcuts_toggle->set_tooltip_text(TTR("Toggle the display of favorite nodes.")); node_shortcuts_toggle->set_pressed(EDITOR_GET("_use_favorites_root_selection")); node_shortcuts_toggle->set_anchors_and_offsets_preset(Control::PRESET_CENTER_RIGHT); - node_shortcuts_toggle->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_update_create_root_dialog)); + node_shortcuts_toggle->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_update_create_root_dialog).bind(false)); top_row->add_child(node_shortcuts_toggle); create_root_dialog->add_child(top_row); @@ -1601,7 +1601,7 @@ void SceneTreeDock::_notification(int p_what) { button_clipboard->set_icon(get_editor_theme_icon(SNAME("ActionPaste"))); button_clipboard->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_PASTE, false)); - _update_create_root_dialog(); + _update_create_root_dialog(true); } break; case NOTIFICATION_ENTER_TREE: { @@ -4116,9 +4116,12 @@ void SceneTreeDock::_local_tree_selected() { edit_local->set_pressed(true); } -void SceneTreeDock::_update_create_root_dialog() { - EditorSettings::get_singleton()->set_setting("_use_favorites_root_selection", node_shortcuts_toggle->is_pressed()); - EditorSettings::get_singleton()->save(); +void SceneTreeDock::_update_create_root_dialog(bool p_initializing) { + if (!p_initializing) { + EditorSettings::get_singleton()->set_setting("_use_favorites_root_selection", node_shortcuts_toggle->is_pressed()); + EditorSettings::get_singleton()->save(); + } + if (node_shortcuts_toggle->is_pressed()) { for (int i = 0; i < favorite_node_shortcuts->get_child_count(); i++) { favorite_node_shortcuts->get_child(i)->queue_free(); @@ -4542,7 +4545,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec create_dialog->set_base_type("Node"); add_child(create_dialog); create_dialog->connect("create", callable_mp(this, &SceneTreeDock::_create)); - create_dialog->connect("favorites_updated", callable_mp(this, &SceneTreeDock::_update_create_root_dialog)); + create_dialog->connect("favorites_updated", callable_mp(this, &SceneTreeDock::_update_create_root_dialog).bind(false)); #ifdef MODULE_REGEX_ENABLED rename_dialog = memnew(RenameDialog(scene_tree)); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 5028cd5cc9..f01e6598cf 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -282,7 +282,7 @@ class SceneTreeDock : public VBoxContainer { void _remote_tree_selected(); void _local_tree_selected(); - void _update_create_root_dialog(); + void _update_create_root_dialog(bool p_initializing = false); void _favorite_root_selected(const String &p_class); void _feature_profile_changed(); diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index 9d8cbb053d..b271970f21 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -865,7 +865,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the // CheckBox. { Ref<StyleBoxFlat> checkbox_style = p_config.panel_container_style->duplicate(); - checkbox_style->set_content_margin_all(p_config.base_margin * EDSCALE); + checkbox_style->set_content_margin_individual((p_config.increased_margin + 2) * EDSCALE, p_config.base_margin * EDSCALE, (p_config.increased_margin + 2) * EDSCALE, p_config.base_margin * EDSCALE); p_theme->set_stylebox(CoreStringName(normal), "CheckBox", checkbox_style); p_theme->set_stylebox(SceneStringName(pressed), "CheckBox", checkbox_style); diff --git a/main/main.cpp b/main/main.cpp index 20968d3971..9ee88af60e 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -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; @@ -2509,7 +2512,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true); // OpenXR project extensions settings. - GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking", true); + GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking", false); + GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_unobstructed_data_source", false); // XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT + GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_controller_data_source", false); // XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT GLOBAL_DEF_RST_BASIC("xr/openxr/extensions/hand_interaction_profile", false); GLOBAL_DEF_BASIC("xr/openxr/extensions/eye_gaze_interaction", false); @@ -3046,9 +3051,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"); } diff --git a/main/performance.cpp b/main/performance.cpp index 8eda697b16..0547b3bff0 100644 --- a/main/performance.cpp +++ b/main/performance.cpp @@ -91,6 +91,7 @@ void Performance::_bind_methods() { BIND_ENUM_CONSTANT(NAVIGATION_EDGE_MERGE_COUNT); BIND_ENUM_CONSTANT(NAVIGATION_EDGE_CONNECTION_COUNT); BIND_ENUM_CONSTANT(NAVIGATION_EDGE_FREE_COUNT); + BIND_ENUM_CONSTANT(NAVIGATION_OBSTACLE_COUNT); BIND_ENUM_CONSTANT(MONITOR_MAX); } @@ -141,6 +142,7 @@ String Performance::get_monitor_name(Monitor p_monitor) const { PNAME("navigation/edges_merged"), PNAME("navigation/edges_connected"), PNAME("navigation/edges_free"), + PNAME("navigation/obstacles"), }; @@ -225,6 +227,8 @@ double Performance::get_monitor(Monitor p_monitor) const { return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_CONNECTION_COUNT); case NAVIGATION_EDGE_FREE_COUNT: return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_FREE_COUNT); + case NAVIGATION_OBSTACLE_COUNT: + return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_OBSTACLE_COUNT); default: { } @@ -272,6 +276,7 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const MONITOR_TYPE_QUANTITY, MONITOR_TYPE_QUANTITY, MONITOR_TYPE_QUANTITY, + MONITOR_TYPE_QUANTITY, }; diff --git a/main/performance.h b/main/performance.h index 34162b2da9..05d678fe55 100644 --- a/main/performance.h +++ b/main/performance.h @@ -100,6 +100,7 @@ public: NAVIGATION_EDGE_MERGE_COUNT, NAVIGATION_EDGE_CONNECTION_COUNT, NAVIGATION_EDGE_FREE_COUNT, + NAVIGATION_OBSTACLE_COUNT, MONITOR_MAX }; diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 296cda627a..8777651545 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -460,27 +460,31 @@ void CSGShape3D::_update_shape() { _update_collision_faces(); } -void CSGShape3D::_update_collision_faces() { - if (use_collision && is_root_shape() && root_collision_shape.is_valid()) { - CSGBrush *n = _get_brush(); - ERR_FAIL_NULL_MSG(n, "Cannot get CSGBrush."); - Vector<Vector3> physics_faces; - physics_faces.resize(n->faces.size() * 3); - Vector3 *physicsw = physics_faces.ptrw(); - - for (int i = 0; i < n->faces.size(); i++) { - int order[3] = { 0, 1, 2 }; +Vector<Vector3> CSGShape3D::_get_brush_collision_faces() { + Vector<Vector3> collision_faces; + CSGBrush *n = _get_brush(); + ERR_FAIL_NULL_V_MSG(n, collision_faces, "Cannot get CSGBrush."); + collision_faces.resize(n->faces.size() * 3); + Vector3 *collision_faces_ptrw = collision_faces.ptrw(); - if (n->faces[i].invert) { - SWAP(order[1], order[2]); - } + for (int i = 0; i < n->faces.size(); i++) { + int order[3] = { 0, 1, 2 }; - physicsw[i * 3 + 0] = n->faces[i].vertices[order[0]]; - physicsw[i * 3 + 1] = n->faces[i].vertices[order[1]]; - physicsw[i * 3 + 2] = n->faces[i].vertices[order[2]]; + if (n->faces[i].invert) { + SWAP(order[1], order[2]); } - root_collision_shape->set_faces(physics_faces); + collision_faces_ptrw[i * 3 + 0] = n->faces[i].vertices[order[0]]; + collision_faces_ptrw[i * 3 + 1] = n->faces[i].vertices[order[1]]; + collision_faces_ptrw[i * 3 + 2] = n->faces[i].vertices[order[2]]; + } + + return collision_faces; +} + +void CSGShape3D::_update_collision_faces() { + if (use_collision && is_root_shape() && root_collision_shape.is_valid()) { + root_collision_shape->set_faces(_get_brush_collision_faces()); if (_is_debug_collision_shape_visible()) { _update_debug_collision_shape(); @@ -488,6 +492,26 @@ void CSGShape3D::_update_collision_faces() { } } +Ref<ArrayMesh> CSGShape3D::bake_static_mesh() { + Ref<ArrayMesh> baked_mesh; + if (is_root_shape() && root_mesh.is_valid()) { + baked_mesh = root_mesh; + } + return baked_mesh; +} + +Ref<ConcavePolygonShape3D> CSGShape3D::bake_collision_shape() { + Ref<ConcavePolygonShape3D> baked_collision_shape; + if (is_root_shape() && root_collision_shape.is_valid()) { + baked_collision_shape.instantiate(); + baked_collision_shape->set_faces(root_collision_shape->get_faces()); + } else if (is_root_shape()) { + baked_collision_shape.instantiate(); + baked_collision_shape->set_faces(_get_brush_collision_faces()); + } + return baked_collision_shape; +} + bool CSGShape3D::_is_debug_collision_shape_visible() { return is_inside_tree() && (get_tree()->is_debugging_collisions_hint() || Engine::get_singleton()->is_editor_hint()); } @@ -704,6 +728,9 @@ void CSGShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_meshes"), &CSGShape3D::get_meshes); + ClassDB::bind_method(D_METHOD("bake_static_mesh"), &CSGShape3D::bake_static_mesh); + ClassDB::bind_method(D_METHOD("bake_collision_shape"), &CSGShape3D::bake_collision_shape); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents"); @@ -934,7 +961,8 @@ CSGBrush *CSGMesh3D::_build_brush() { void CSGMesh3D::_mesh_changed() { _make_dirty(); - update_gizmos(); + + callable_mp((Node3D *)this, &Node3D::update_gizmos).call_deferred(); } void CSGMesh3D::set_material(const Ref<Material> &p_material) { diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index bb7c8be431..8f23ae2f9e 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -113,6 +113,7 @@ private: void _update_debug_collision_shape(); void _clear_debug_collision_shape(); void _on_transform_changed(); + Vector<Vector3> _get_brush_collision_faces(); protected: void _notification(int p_what); @@ -161,6 +162,10 @@ public: bool is_calculating_tangents() const; bool is_root_shape() const; + + Ref<ArrayMesh> bake_static_mesh(); + Ref<ConcavePolygonShape3D> bake_collision_shape(); + CSGShape3D(); ~CSGShape3D(); }; diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml index f9017e47c7..ac62d8dd83 100644 --- a/modules/csg/doc_classes/CSGShape3D.xml +++ b/modules/csg/doc_classes/CSGShape3D.xml @@ -5,12 +5,29 @@ </brief_description> <description> This is the CSG base class that provides CSG operation support to the various CSG nodes in Godot. - [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. + [b]Performance:[/b] CSG nodes are only intended for prototyping as they have a significant CPU performance cost. + Consider baking final CSG operation results into static geometry that replaces the CSG nodes. + Individual CSG root node results can be baked to nodes with static resources with the editor menu that appears when a CSG root node is selected. + Individual CSG root nodes can also be baked to static resources with scripts by calling [method bake_static_mesh] for the visual mesh or [method bake_collision_shape] for the physics collision. + Entire scenes of CSG nodes can be baked to static geometry and exported with the editor gltf scene exporter. </description> <tutorials> <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link> </tutorials> <methods> + <method name="bake_collision_shape"> + <return type="ConcavePolygonShape3D" /> + <description> + Returns a baked physics [ConcavePolygonShape3D] of this node's CSG operation result. Returns an empty shape if the node is not a CSG root node or has no valid geometry. + [b]Performance:[/b] If the CSG operation results in a very detailed geometry with many faces physics performance will be very slow. Concave shapes should in general only be used for static level geometry and not with dynamic objects that are moving. + </description> + </method> + <method name="bake_static_mesh"> + <return type="ArrayMesh" /> + <description> + Returns a baked static [ArrayMesh] of this node's CSG operation result. Materials from involved CSG nodes are added as extra mesh surfaces. Returns an empty mesh if the node is not a CSG root node or has no valid geometry. + </description> + </method> <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> <param index="0" name="layer_number" type="int" /> diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp index ea7b6d225e..72676f4a40 100644 --- a/modules/csg/editor/csg_gizmos.cpp +++ b/modules/csg/editor/csg_gizmos.cpp @@ -38,6 +38,135 @@ #include "editor/plugins/gizmos/gizmo_3d_helper.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "scene/3d/camera_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/physics/collision_shape_3d.h" +#include "scene/3d/physics/static_body_3d.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/menu_button.h" + +void CSGShapeEditor::_node_removed(Node *p_node) { + if (p_node == node) { + node = nullptr; + options->hide(); + } +} + +void CSGShapeEditor::edit(CSGShape3D *p_csg_shape) { + node = p_csg_shape; + if (node) { + options->show(); + } else { + options->hide(); + } +} + +void CSGShapeEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + options->set_icon(get_editor_theme_icon(SNAME("CSGCombiner3D"))); + } break; + } +} + +void CSGShapeEditor::_menu_option(int p_option) { + Array meshes = node->get_meshes(); + if (meshes.is_empty()) { + err_dialog->set_text(TTR("CSG operation returned an empty array.")); + err_dialog->popup_centered(); + return; + } + + switch (p_option) { + case MENU_OPTION_BAKE_MESH_INSTANCE: { + _create_baked_mesh_instance(); + } break; + case MENU_OPTION_BAKE_COLLISION_SHAPE: { + _create_baked_collision_shape(); + } break; + } +} + +void CSGShapeEditor::_create_baked_mesh_instance() { + if (node == get_tree()->get_edited_scene_root()) { + err_dialog->set_text(TTR("Can not add a baked mesh as sibling for the scene root.\nMove the CSG root node below a parent node.")); + err_dialog->popup_centered(); + return; + } + + Ref<ArrayMesh> mesh = node->bake_static_mesh(); + if (mesh.is_null()) { + err_dialog->set_text(TTR("CSG operation returned an empty mesh.")); + err_dialog->popup_centered(); + return; + } + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Create baked CSGShape3D Mesh Instance")); + + Node *owner = get_tree()->get_edited_scene_root(); + + MeshInstance3D *mi = memnew(MeshInstance3D); + mi->set_mesh(mesh); + mi->set_name("CSGBakedMeshInstance3D"); + mi->set_transform(node->get_transform()); + ur->add_do_method(node, "add_sibling", mi, true); + ur->add_do_method(mi, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), mi); + + ur->add_do_reference(mi); + ur->add_undo_method(node->get_parent(), "remove_child", mi); + + ur->commit_action(); +} + +void CSGShapeEditor::_create_baked_collision_shape() { + if (node == get_tree()->get_edited_scene_root()) { + err_dialog->set_text(TTR("Can not add a baked collision shape as sibling for the scene root.\nMove the CSG root node below a parent node.")); + err_dialog->popup_centered(); + return; + } + + Ref<Shape3D> shape = node->bake_collision_shape(); + if (shape.is_null()) { + err_dialog->set_text(TTR("CSG operation returned an empty shape.")); + err_dialog->popup_centered(); + return; + } + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Create baked CSGShape3D Collision Shape")); + + Node *owner = get_tree()->get_edited_scene_root(); + + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shape); + cshape->set_name("CSGBakedCollisionShape3D"); + cshape->set_transform(node->get_transform()); + ur->add_do_method(node, "add_sibling", cshape, true); + ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), cshape); + + ur->add_do_reference(cshape); + ur->add_undo_method(node->get_parent(), "remove_child", cshape); + + ur->commit_action(); +} + +CSGShapeEditor::CSGShapeEditor() { + options = memnew(MenuButton); + options->hide(); + options->set_text(TTR("CSG")); + options->set_switch_on_hover(true); + Node3DEditor::get_singleton()->add_control_to_menu_panel(options); + + options->get_popup()->add_item(TTR("Bake Mesh Instance"), MENU_OPTION_BAKE_MESH_INSTANCE); + options->get_popup()->add_item(TTR("Bake Collision Shape"), MENU_OPTION_BAKE_COLLISION_SHAPE); + + options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &CSGShapeEditor::_menu_option)); + + err_dialog = memnew(AcceptDialog); + add_child(err_dialog); +} /////////// @@ -393,9 +522,26 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { } } +void EditorPluginCSG::edit(Object *p_object) { + CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_object); + if (csg_shape && csg_shape->is_root_shape()) { + csg_shape_editor->edit(csg_shape); + } else { + csg_shape_editor->edit(nullptr); + } +} + +bool EditorPluginCSG::handles(Object *p_object) const { + CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_object); + return csg_shape && csg_shape->is_root_shape(); +} + EditorPluginCSG::EditorPluginCSG() { Ref<CSGShape3DGizmoPlugin> gizmo_plugin = Ref<CSGShape3DGizmoPlugin>(memnew(CSGShape3DGizmoPlugin)); Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin); + + csg_shape_editor = memnew(CSGShapeEditor); + EditorNode::get_singleton()->get_main_screen_control()->add_child(csg_shape_editor); } #endif // TOOLS_ENABLED diff --git a/modules/csg/editor/csg_gizmos.h b/modules/csg/editor/csg_gizmos.h index de19b33e7d..c562fe9fe7 100644 --- a/modules/csg/editor/csg_gizmos.h +++ b/modules/csg/editor/csg_gizmos.h @@ -37,8 +37,11 @@ #include "editor/plugins/editor_plugin.h" #include "editor/plugins/node_3d_editor_gizmos.h" +#include "scene/gui/control.h" +class AcceptDialog; class Gizmo3DHelper; +class MenuButton; class CSGShape3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(CSGShape3DGizmoPlugin, EditorNode3DGizmoPlugin); @@ -62,10 +65,43 @@ public: ~CSGShape3DGizmoPlugin(); }; +class CSGShapeEditor : public Control { + GDCLASS(CSGShapeEditor, Control); + + enum Menu { + MENU_OPTION_BAKE_MESH_INSTANCE, + MENU_OPTION_BAKE_COLLISION_SHAPE, + }; + + CSGShape3D *node = nullptr; + MenuButton *options = nullptr; + AcceptDialog *err_dialog = nullptr; + + void _menu_option(int p_option); + + void _create_baked_mesh_instance(); + void _create_baked_collision_shape(); + +protected: + void _node_removed(Node *p_node); + + void _notification(int p_what); + +public: + void edit(CSGShape3D *p_csg_shape); + CSGShapeEditor(); +}; + class EditorPluginCSG : public EditorPlugin { GDCLASS(EditorPluginCSG, EditorPlugin); + CSGShapeEditor *csg_shape_editor = nullptr; + public: + virtual String get_name() const override { return "CSGShape3D"; } + virtual void edit(Object *p_object) override; + virtual bool handles(Object *p_object) const override; + EditorPluginCSG(); }; diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 4938d8bff5..a0578bbd9d 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -149,7 +149,7 @@ void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) { int h = p_image->get_height(); bool is_ldr = (p_image->get_format() <= Image::FORMAT_RGBA8); - bool is_hdr = (p_image->get_format() >= Image::FORMAT_RH) && (p_image->get_format() <= Image::FORMAT_RGBE9995); + bool is_hdr = (p_image->get_format() >= Image::FORMAT_RF) && (p_image->get_format() <= Image::FORMAT_RGBE9995); if (!is_ldr && !is_hdr) { return; // Not a usable source format diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index b2de6b656e..6ea44c5fc3 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -42,14 +42,18 @@ enum { DDSD_PITCH = 0x00000008, DDSD_LINEARSIZE = 0x00080000, DDSD_MIPMAPCOUNT = 0x00020000, - DDPF_FOURCC = 0x00000004, DDPF_ALPHAPIXELS = 0x00000001, - DDPF_RGB = 0x00000040 + DDPF_ALPHAONLY = 0x00000002, + DDPF_FOURCC = 0x00000004, + DDPF_RGB = 0x00000040, + DDPF_RG_SNORM = 0x00080000 }; enum DDSFourCC { DDFCC_DXT1 = PF_FOURCC("DXT1"), + DDFCC_DXT2 = PF_FOURCC("DXT2"), DDFCC_DXT3 = PF_FOURCC("DXT3"), + DDFCC_DXT4 = PF_FOURCC("DXT4"), DDFCC_DXT5 = PF_FOURCC("DXT5"), DDFCC_ATI1 = PF_FOURCC("ATI1"), DDFCC_BC4U = PF_FOURCC("BC4U"), @@ -68,17 +72,25 @@ enum DDSFourCC { // Reference: https://learn.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format enum DXGIFormat { DXGI_R32G32B32A32_FLOAT = 2, + DXGI_R32G32B32_FLOAT = 6, DXGI_R16G16B16A16_FLOAT = 10, DXGI_R32G32_FLOAT = 16, DXGI_R10G10B10A2_UNORM = 24, DXGI_R8G8B8A8_UNORM = 28, + DXGI_R8G8B8A8_UNORM_SRGB = 29, DXGI_R16G16_FLOAT = 34, DXGI_R32_FLOAT = 41, + DXGI_R8G8_UNORM = 49, DXGI_R16_FLOAT = 54, + DXGI_R8_UNORM = 61, + DXGI_A8_UNORM = 65, DXGI_R9G9B9E5 = 67, DXGI_BC1_UNORM = 71, + DXGI_BC1_UNORM_SRGB = 72, DXGI_BC2_UNORM = 74, + DXGI_BC2_UNORM_SRGB = 75, DXGI_BC3_UNORM = 77, + DXGI_BC3_UNORM_SRGB = 78, DXGI_BC4_UNORM = 80, DXGI_BC5_UNORM = 83, DXGI_B5G6R5_UNORM = 85, @@ -87,6 +99,7 @@ enum DXGIFormat { DXGI_BC6H_UF16 = 95, DXGI_BC6H_SF16 = 96, DXGI_BC7_UNORM = 98, + DXGI_BC7_UNORM_SRGB = 99, DXGI_B4G4R4A4_UNORM = 115 }; @@ -100,25 +113,29 @@ enum DDSFormat { DDS_ATI2, DDS_BC6U, DDS_BC6S, - DDS_BC7U, + DDS_BC7, DDS_R16F, DDS_RG16F, DDS_RGBA16F, DDS_R32F, DDS_RG32F, + DDS_RGB32F, DDS_RGBA32F, DDS_RGB9E5, - DDS_BGRA8, - DDS_BGR8, - DDS_RGBA8, DDS_RGB8, + DDS_RGBA8, + DDS_BGR8, + DDS_BGRA8, DDS_BGR5A1, DDS_BGR565, + DDS_B2GR3, + DDS_B2GR3A8, DDS_BGR10A2, DDS_RGB10A2, DDS_BGRA4, DDS_LUMINANCE, DDS_LUMINANCE_ALPHA, + DDS_LUMINANCE_ALPHA_4, DDS_MAX }; @@ -132,31 +149,35 @@ struct DDSFormatInfo { static const DDSFormatInfo dds_format_info[DDS_MAX] = { { "DXT1/BC1", true, 4, 8, Image::FORMAT_DXT1 }, - { "DXT3/BC2", true, 4, 16, Image::FORMAT_DXT3 }, - { "DXT5/BC3", true, 4, 16, Image::FORMAT_DXT5 }, + { "DXT2/DXT3/BC2", true, 4, 16, Image::FORMAT_DXT3 }, + { "DXT4/DXT5/BC3", true, 4, 16, Image::FORMAT_DXT5 }, { "ATI1/BC4", true, 4, 8, Image::FORMAT_RGTC_R }, { "ATI2/A2XY/BC5", true, 4, 16, Image::FORMAT_RGTC_RG }, - { "BC6U", true, 4, 16, Image::FORMAT_BPTC_RGBFU }, - { "BC6S", true, 4, 16, Image::FORMAT_BPTC_RGBF }, - { "BC7U", true, 4, 16, Image::FORMAT_BPTC_RGBA }, + { "BC6UF", true, 4, 16, Image::FORMAT_BPTC_RGBFU }, + { "BC6SF", true, 4, 16, Image::FORMAT_BPTC_RGBF }, + { "BC7", true, 4, 16, Image::FORMAT_BPTC_RGBA }, { "R16F", false, 1, 2, Image::FORMAT_RH }, { "RG16F", false, 1, 4, Image::FORMAT_RGH }, { "RGBA16F", false, 1, 8, Image::FORMAT_RGBAH }, { "R32F", false, 1, 4, Image::FORMAT_RF }, { "RG32F", false, 1, 8, Image::FORMAT_RGF }, + { "RGB32F", false, 1, 12, Image::FORMAT_RGBF }, { "RGBA32F", false, 1, 16, Image::FORMAT_RGBAF }, { "RGB9E5", false, 1, 4, Image::FORMAT_RGBE9995 }, - { "BGRA8", false, 1, 4, Image::FORMAT_RGBA8 }, - { "BGR8", false, 1, 3, Image::FORMAT_RGB8 }, - { "RGBA8", false, 1, 4, Image::FORMAT_RGBA8 }, { "RGB8", false, 1, 3, Image::FORMAT_RGB8 }, + { "RGBA8", false, 1, 4, Image::FORMAT_RGBA8 }, + { "BGR8", false, 1, 3, Image::FORMAT_RGB8 }, + { "BGRA8", false, 1, 4, Image::FORMAT_RGBA8 }, { "BGR5A1", false, 1, 2, Image::FORMAT_RGBA8 }, { "BGR565", false, 1, 2, Image::FORMAT_RGB8 }, + { "B2GR3", false, 1, 1, Image::FORMAT_RGB8 }, + { "B2GR3A8", false, 1, 2, Image::FORMAT_RGBA8 }, { "BGR10A2", false, 1, 4, Image::FORMAT_RGBA8 }, { "RGB10A2", false, 1, 4, Image::FORMAT_RGBA8 }, { "BGRA4", false, 1, 2, Image::FORMAT_RGBA8 }, { "GRAYSCALE", false, 1, 1, Image::FORMAT_L8 }, - { "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 } + { "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 }, + { "GRAYSCALE_ALPHA_4", false, 1, 1, Image::FORMAT_LA8 } }; static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) { @@ -164,6 +185,9 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) { case DXGI_R32G32B32A32_FLOAT: { return DDS_RGBA32F; } + case DXGI_R32G32B32_FLOAT: { + return DDS_RGB32F; + } case DXGI_R16G16B16A16_FLOAT: { return DDS_RGBA16F; } @@ -173,7 +197,8 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) { case DXGI_R10G10B10A2_UNORM: { return DDS_RGB10A2; } - case DXGI_R8G8B8A8_UNORM: { + case DXGI_R8G8B8A8_UNORM: + case DXGI_R8G8B8A8_UNORM_SRGB: { return DDS_RGBA8; } case DXGI_R16G16_FLOAT: { @@ -182,19 +207,29 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) { case DXGI_R32_FLOAT: { return DDS_R32F; } + case DXGI_R8_UNORM: + case DXGI_A8_UNORM: { + return DDS_LUMINANCE; + } case DXGI_R16_FLOAT: { return DDS_R16F; } + case DXGI_R8G8_UNORM: { + return DDS_LUMINANCE_ALPHA; + } case DXGI_R9G9B9E5: { return DDS_RGB9E5; } - case DXGI_BC1_UNORM: { + case DXGI_BC1_UNORM: + case DXGI_BC1_UNORM_SRGB: { return DDS_DXT1; } - case DXGI_BC2_UNORM: { + case DXGI_BC2_UNORM: + case DXGI_BC2_UNORM_SRGB: { return DDS_DXT3; } - case DXGI_BC3_UNORM: { + case DXGI_BC3_UNORM: + case DXGI_BC3_UNORM_SRGB: { return DDS_DXT5; } case DXGI_BC4_UNORM: { @@ -218,8 +253,9 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) { case DXGI_BC6H_SF16: { return DDS_BC6S; } - case DXGI_BC7_UNORM: { - return DDS_BC7U; + case DXGI_BC7_UNORM: + case DXGI_BC7_UNORM_SRGB: { + return DDS_BC7; } case DXGI_B4G4R4A4_UNORM: { return DDS_BGRA4; @@ -299,9 +335,11 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig case DDFCC_DXT1: { dds_format = DDS_DXT1; } break; + case DDFCC_DXT2: case DDFCC_DXT3: { dds_format = DDS_DXT3; } break; + case DDFCC_DXT4: case DDFCC_DXT5: { dds_format = DDS_DXT5; } break; @@ -363,6 +401,8 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig dds_format = DDS_RGB10A2; } else if (format_rgb_bits == 16 && format_red_mask == 0xf00 && format_green_mask == 0xf0 && format_blue_mask == 0xf && format_alpha_mask == 0xf000) { dds_format = DDS_BGRA4; + } else if (format_rgb_bits == 16 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3 && format_alpha_mask == 0xff00) { + dds_format = DDS_B2GR3A8; } } else { @@ -373,18 +413,38 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig dds_format = DDS_RGB8; } else if (format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) { dds_format = DDS_BGR565; + } else if (format_rgb_bits == 8 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3) { + dds_format = DDS_B2GR3; } } } else { // Other formats. - if (format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) { - dds_format = DDS_LUMINANCE_ALPHA; - } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff) { + if (format_flags & DDPF_ALPHAONLY && format_rgb_bits == 8 && format_alpha_mask == 0xff) { + // Alpha only. dds_format = DDS_LUMINANCE; } } + // Depending on the writer, luminance formats may or may not have the DDPF_RGB or DDPF_LUMINANCE flags defined, + // so we check for these formats after everything else failed. + if (dds_format == DDS_MAX) { + if (format_flags & DDPF_ALPHAPIXELS) { + // With alpha. + if (format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) { + dds_format = DDS_LUMINANCE_ALPHA; + } else if (format_rgb_bits == 8 && format_red_mask == 0xf && format_alpha_mask == 0xf0) { + dds_format = DDS_LUMINANCE_ALPHA_4; + } + + } else { + // Without alpha. + if (format_rgb_bits == 8 && format_red_mask == 0xff) { + dds_format = DDS_LUMINANCE; + } + } + } + // No format detected, error. if (dds_format == DDS_MAX) { ERR_FAIL_V_MSG(Ref<Resource>(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'."); @@ -433,10 +493,24 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig } // Calculate the space these formats will take up after decoding. - if (dds_format == DDS_BGR565) { - size = size * 3 / 2; - } else if (dds_format == DDS_BGR5A1 || dds_format == DDS_BGRA4) { - size = size * 2; + switch (dds_format) { + case DDS_BGR565: + size = size * 3 / 2; + break; + + case DDS_BGR5A1: + case DDS_BGRA4: + case DDS_B2GR3A8: + case DDS_LUMINANCE_ALPHA_4: + size = size * 2; + break; + + case DDS_B2GR3: + size = size * 3; + break; + + default: + break; } src_data.resize(size); @@ -503,6 +577,44 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig } } break; + case DDS_B2GR3: { + // To RGB8. + int colcount = size / 3; + + for (int i = colcount - 1; i >= 0; i--) { + int src_ofs = i; + int dst_ofs = i * 3; + + uint8_t b = (wb[src_ofs] & 0x3) << 6; + uint8_t g = (wb[src_ofs] & 0x1C) << 3; + uint8_t r = (wb[src_ofs] & 0xE0); + + wb[dst_ofs] = r; + wb[dst_ofs + 1] = g; + wb[dst_ofs + 2] = b; + } + + } break; + case DDS_B2GR3A8: { + // To RGBA8. + int colcount = size / 4; + + for (int i = colcount - 1; i >= 0; i--) { + int src_ofs = i * 2; + int dst_ofs = i * 4; + + uint8_t b = (wb[src_ofs] & 0x3) << 6; + uint8_t g = (wb[src_ofs] & 0x1C) << 3; + uint8_t r = (wb[src_ofs] & 0xE0); + uint8_t a = wb[src_ofs + 1]; + + wb[dst_ofs] = r; + wb[dst_ofs + 1] = g; + wb[dst_ofs + 2] = b; + wb[dst_ofs + 3] = a; + } + + } break; case DDS_RGB10A2: { // To RGBA8. int colcount = size / 4; @@ -549,6 +661,8 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig } } break; + + // Channel-swapped. case DDS_BGRA8: { // To RGBA8. int colcount = size / 4; @@ -568,6 +682,24 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig } break; + // Grayscale. + case DDS_LUMINANCE_ALPHA_4: { + // To LA8. + int colcount = size / 2; + + for (int i = colcount - 1; i >= 0; i--) { + int src_ofs = i; + int dst_ofs = i * 2; + + uint8_t l = wb[src_ofs] & 0x0F; + uint8_t a = wb[src_ofs] & 0xF0; + + wb[dst_ofs] = (l << 4) | l; + wb[dst_ofs + 1] = a | (a >> 4); + } + + } break; + default: { } } diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py index a7d5c406e9..ecd33a5dac 100644 --- a/modules/gdscript/config.py +++ b/modules/gdscript/config.py @@ -11,6 +11,7 @@ def get_doc_classes(): return [ "@GDScript", "GDScript", + "GDScriptSyntaxHighlighter", ] diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 6e7ac0dec9..104fc15a8c 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -52,9 +52,9 @@ <description> Returns a single character (as a [String]) of the given Unicode code point (which is compatible with ASCII code). [codeblock] - a = char(65) # a is "A" - a = char(65 + 32) # a is "a" - a = char(8364) # a is "€" + var upper = char(65) # upper is "A" + var lower = char(65 + 32) # lower is "a" + var euro = char(8364) # euro is "€" [/codeblock] </description> </method> @@ -150,10 +150,10 @@ <description> Returns the length of the given Variant [param var]. The length can be the character count of a [String] or [StringName], the element count of any array type, or the size of a [Dictionary]. For every other Variant type, a run-time error is generated and execution is stopped. [codeblock] - a = [1, 2, 3, 4] + var a = [1, 2, 3, 4] len(a) # Returns 4 - b = "Hello!" + var b = "Hello!" len(b) # Returns 6 [/codeblock] </description> diff --git a/modules/gdscript/doc_classes/GDScriptSyntaxHighlighter.xml b/modules/gdscript/doc_classes/GDScriptSyntaxHighlighter.xml new file mode 100644 index 0000000000..63a9222901 --- /dev/null +++ b/modules/gdscript/doc_classes/GDScriptSyntaxHighlighter.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="GDScriptSyntaxHighlighter" inherits="EditorSyntaxHighlighter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + A GDScript syntax highlighter that can be used with [TextEdit] and [CodeEdit] nodes. + </brief_description> + <description> + [b]Note:[/b] This class can only be used for editor plugins because it relies on editor settings. + [codeblocks] + [gdscript] + var code_preview = TextEdit.new() + var highlighter = GDScriptSyntaxHighlighter.new() + code_preview.syntax_highlighter = highlighter + [/gdscript] + [csharp] + var codePreview = new TextEdit(); + var highlighter = new GDScriptSyntaxHighlighter(); + codePreview.SyntaxHighlighter = highlighter; + [/csharp] + [/codeblocks] + </description> + <tutorials> + </tutorials> +</class> diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 54bb152f7f..ecef852b4b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -245,8 +245,26 @@ void GDScriptParser::apply_pending_warnings() { } #endif -void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument, bool p_force) { - if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) { +void GDScriptParser::override_completion_context(const Node *p_for_node, CompletionType p_type, Node *p_node, int p_argument) { + if (!for_completion) { + return; + } + if (completion_context.node != p_for_node) { + return; + } + CompletionContext context; + context.type = p_type; + context.current_class = current_class; + context.current_function = current_function; + context.current_suite = current_suite; + context.current_line = tokenizer->get_cursor_line(); + context.current_argument = p_argument; + context.node = p_node; + completion_context = context; +} + +void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument) { + if (!for_completion) { return; } if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) { @@ -264,8 +282,8 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node completion_context = context; } -void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force) { - if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) { +void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type) { + if (!for_completion) { return; } if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) { @@ -1620,7 +1638,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali advance(); // Arguments. push_completion_call(annotation); - make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0, true); + make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0); int argument_index = 0; do { if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) { @@ -1628,7 +1646,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali break; } - make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index, true); + make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index); set_last_completion_call_arg(argument_index++); ExpressionNode *argument = parse_expression(false); if (argument == nullptr) { @@ -2569,8 +2587,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_ } LiteralNode *literal = alloc_node<LiteralNode>(); - complete_extents(literal); literal->value = previous.literal; + reset_extents(literal, p_previous_operand); + update_extents(literal); + make_completion_context(COMPLETION_NONE, literal, -1); + complete_extents(literal); return literal; } @@ -3065,12 +3086,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode * const IdentifierNode *id = static_cast<const IdentifierNode *>(p_previous_operand); Variant::Type builtin_type = get_builtin_type(id->name); if (builtin_type < Variant::VARIANT_MAX) { - make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, builtin_type, true); + make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, builtin_type); is_builtin = true; } } if (!is_builtin) { - make_completion_context(COMPLETION_ATTRIBUTE, attribute, -1, true); + make_completion_context(COMPLETION_ATTRIBUTE, attribute, -1); } } @@ -3195,23 +3216,24 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre push_completion_call(call); int argument_index = 0; do { - make_completion_context(ct, call, argument_index++, true); + make_completion_context(ct, call, argument_index); if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) { // Allow for trailing comma. break; } - bool use_identifier_completion = current.cursor_place == GDScriptTokenizerText::CURSOR_END || current.cursor_place == GDScriptTokenizerText::CURSOR_MIDDLE; ExpressionNode *argument = parse_expression(false); if (argument == nullptr) { push_error(R"(Expected expression as the function argument.)"); } else { call->arguments.push_back(argument); - if (argument->type == Node::IDENTIFIER && use_identifier_completion) { - completion_context.type = COMPLETION_IDENTIFIER; + if (argument->type == Node::LITERAL) { + override_completion_context(argument, ct, call, argument_index); } } + ct = COMPLETION_CALL_ARGUMENTS; + argument_index++; } while (match(GDScriptTokenizer::Token::COMMA)); pop_completion_call(); @@ -3224,7 +3246,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) { // We want code completion after a DOLLAR even if the current code is invalid. - make_completion_context(COMPLETION_GET_NODE, nullptr, -1, true); + make_completion_context(COMPLETION_GET_NODE, nullptr, -1); if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); @@ -3281,7 +3303,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p path_state = PATH_STATE_SLASH; } - make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++, true); + make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++); if (match(GDScriptTokenizer::Token::LITERAL)) { if (previous.literal.get_type() != Variant::STRING) { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 60ee477656..2999fb11e4 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1455,9 +1455,11 @@ private: } void apply_pending_warnings(); #endif - - void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = false); - void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force = false); + void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1); + void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type); + // In some cases it might become necessary to alter the completion context after parsing a subexpression. + // For example to not override COMPLETE_CALL_ARGUMENTS with COMPLETION_NONE from string literals. + void override_completion_context(const Node *p_for_node, CompletionType p_type, Node *p_node, int p_argument = -1); void push_completion_call(Node *p_call); void pop_completion_call(); void set_last_completion_call_arg(int p_argument); diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 59e387eece..055f8e4110 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -165,6 +165,13 @@ void initialize_gdscript_module(ModuleInitializationLevel p_level) { gdscript_translation_parser_plugin.instantiate(); EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); + } else if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { + ClassDB::APIType prev_api = ClassDB::get_current_api(); + ClassDB::set_current_api(ClassDB::API_EDITOR); + + GDREGISTER_CLASS(GDScriptSyntaxHighlighter); + + ClassDB::set_current_api(prev_api); } #endif // TOOLS_ENABLED } diff --git a/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.cfg b/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.cfg new file mode 100644 index 0000000000..be9bd510e1 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.cfg @@ -0,0 +1,5 @@ +[output] +include=[ + {"insert_text": "\"property_of_a\""}, + {"insert_text": "\"name\""}, +] diff --git a/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.gd b/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.gd new file mode 100644 index 0000000000..a8e04a62a7 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.gd @@ -0,0 +1,8 @@ +extends Node + +const A = preload ("res://completion/class_a.notest.gd") + +func _ready() -> void: + var a := A.new() + var tween := get_tree().create_tween() + tween.tween_property(a, "➡") diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers.cfg b/modules/gdscript/tests/scripts/completion/common/identifiers_in_call.cfg index 871a404e3a..5f08f9c265 100644 --- a/modules/gdscript/tests/scripts/completion/common/identifiers.cfg +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_call.cfg @@ -11,11 +11,15 @@ include=[ {"display": "func_of_a"}, {"display": "signal_of_a"}, - ; GDScript: self.gd + ; GDScript: identifiers.gd {"display": "test_signal_1"}, {"display": "test_signal_2"}, {"display": "test_var_1"}, {"display": "test_var_2"}, {"display": "test_func_1"}, {"display": "test_func_2"}, + {"display": "test_parameter_1"}, + {"display": "test_parameter_2"}, + {"display": "local_test_var_1"}, + {"display": "local_test_var_2"}, ] diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers_in_call.gd b/modules/gdscript/tests/scripts/completion/common/identifiers_in_call.gd new file mode 100644 index 0000000000..91488c25aa --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_call.gd @@ -0,0 +1,18 @@ +extends "res://completion/class_a.notest.gd" + +signal test_signal_1(a) +signal test_signal_2(a: int) + +var test_var_1 +var test_var_2: int + +func test_func_1(t): + pass + +func test_func_2(t: int) -> void: + pass + +func _init(test_parameter_1, test_parameter_2: String): + var local_test_var_1 + var local_test_var_2: int + print(t➡) diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.cfg b/modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.cfg new file mode 100644 index 0000000000..5f08f9c265 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.cfg @@ -0,0 +1,25 @@ +scene="res://completion/get_node/get_node.tscn" +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, + + ; GDScript: identifiers.gd + {"display": "test_signal_1"}, + {"display": "test_signal_2"}, + {"display": "test_var_1"}, + {"display": "test_var_2"}, + {"display": "test_func_1"}, + {"display": "test_func_2"}, + {"display": "test_parameter_1"}, + {"display": "test_parameter_2"}, + {"display": "local_test_var_1"}, + {"display": "local_test_var_2"}, +] diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers.gd b/modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.gd index efbafbee8e..a2f5b7bc23 100644 --- a/modules/gdscript/tests/scripts/completion/common/identifiers.gd +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.gd @@ -12,5 +12,7 @@ func test_func_1(t): func test_func_2(t: int) -> void: pass -func _init(): +func _init(test_parameter_1, test_parameter_2: String): + var local_test_var_1 + var local_test_var_2: int t➡ diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.cfg b/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.cfg new file mode 100644 index 0000000000..5f08f9c265 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.cfg @@ -0,0 +1,25 @@ +scene="res://completion/get_node/get_node.tscn" +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, + + ; GDScript: identifiers.gd + {"display": "test_signal_1"}, + {"display": "test_signal_2"}, + {"display": "test_var_1"}, + {"display": "test_var_2"}, + {"display": "test_func_1"}, + {"display": "test_func_2"}, + {"display": "test_parameter_1"}, + {"display": "test_parameter_2"}, + {"display": "local_test_var_1"}, + {"display": "local_test_var_2"}, +] diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.gd b/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.gd new file mode 100644 index 0000000000..fed0b869c4 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.gd @@ -0,0 +1,22 @@ +# godotengine/godot#92226 +extends "res://completion/class_a.notest.gd" + +signal test_signal_1(a) +signal test_signal_2(a: int) + +var test_var_1 +var test_var_2: int + +func test_func_1(t): + pass + +func test_func_2(t: int) -> void: + pass + +func _init(test_parameter_1, test_parameter_2: String): + var local_test_var_1 + var local_test_var_2: int + print(t➡ + + if true: + pass diff --git a/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.cfg b/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.cfg new file mode 100644 index 0000000000..462846c9b2 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.cfg @@ -0,0 +1,26 @@ +scene="res://completion/get_node/get_node.tscn" +[output] +exclude=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + {"display": "add_child"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, + + ; GDScript: no_completion_in_string.gd + {"display": "test_signal_1"}, + {"display": "test_signal_2"}, + {"display": "test_var_1"}, + {"display": "test_var_2"}, + {"display": "test_func_1"}, + {"display": "test_func_2"}, + {"display": "test_parameter_1"}, + {"display": "test_parameter_2"}, + {"display": "local_test_var_1"}, + {"display": "local_test_var_2"}, +] diff --git a/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.gd b/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.gd new file mode 100644 index 0000000000..da52af9fe3 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.gd @@ -0,0 +1,19 @@ +# godotengine/godot#62945 +extends "res://completion/class_a.notest.gd" + +signal test_signal_1(a) +signal test_signal_2(a: int) + +var test_var_1 +var test_var_2: int + +func test_func_1(t): + pass + +func test_func_2(t: int) -> void: + pass + +func _init(test_parameter_1, test_parameter_2: String): + var local_test_var_1 + var local_test_var_2: int + var a = "➡" diff --git a/modules/gdscript/tests/scripts/completion/common/self.gd b/modules/gdscript/tests/scripts/completion/common/self.gd index 9ad2fbea51..ed181af0c5 100644 --- a/modules/gdscript/tests/scripts/completion/common/self.gd +++ b/modules/gdscript/tests/scripts/completion/common/self.gd @@ -14,3 +14,4 @@ func test_func_2(t: int) -> void: func _init(): self.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/filter/organized_export.gd b/modules/gdscript/tests/scripts/completion/filter/organized_export.gd index 189608904c..9fa9618cee 100644 --- a/modules/gdscript/tests/scripts/completion/filter/organized_export.gd +++ b/modules/gdscript/tests/scripts/completion/filter/organized_export.gd @@ -5,4 +5,5 @@ extends CPUParticles2D @export_subgroup("Test Subgroup") func _init(): - ➡ + t➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.cfg index ae7d34d87d..ae7d34d87d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd b/modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.gd index 7710c2d13b..7710c2d13b 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.gd diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.cfg index 9c580b711d..9c580b711d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.gd index 6b29bf5526..6b29bf5526 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.gd diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.cfg index 446198dd35..446198dd35 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.gd index 7710c2d13b..7710c2d13b 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.gd diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.cfg index ae7d34d87d..ae7d34d87d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd b/modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.gd index 97b288334e..97b288334e 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.gd diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.cfg index 9c580b711d..9c580b711d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.gd index 402fd1d275..402fd1d275 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.gd diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.cfg index 446198dd35..446198dd35 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.gd index 97b288334e..97b288334e 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.gd diff --git a/modules/gdscript/tests/scripts/completion/types/local/interfered.cfg b/modules/gdscript/tests/scripts/completion/types/local/infered.cfg index 8b68d51a89..8b68d51a89 100644 --- a/modules/gdscript/tests/scripts/completion/types/local/interfered.cfg +++ b/modules/gdscript/tests/scripts/completion/types/local/infered.cfg diff --git a/modules/gdscript/tests/scripts/completion/types/local/interfered.gd b/modules/gdscript/tests/scripts/completion/types/local/infered.gd index f003c366a4..f003c366a4 100644 --- a/modules/gdscript/tests/scripts/completion/types/local/interfered.gd +++ b/modules/gdscript/tests/scripts/completion/types/local/infered.gd diff --git a/modules/gdscript/tests/scripts/completion/types/member/interfered.cfg b/modules/gdscript/tests/scripts/completion/types/member/infered.cfg index 8b68d51a89..8b68d51a89 100644 --- a/modules/gdscript/tests/scripts/completion/types/member/interfered.cfg +++ b/modules/gdscript/tests/scripts/completion/types/member/infered.cfg diff --git a/modules/gdscript/tests/scripts/completion/types/member/interfered.gd b/modules/gdscript/tests/scripts/completion/types/member/infered.gd index 069abd7891..069abd7891 100644 --- a/modules/gdscript/tests/scripts/completion/types/member/interfered.gd +++ b/modules/gdscript/tests/scripts/completion/types/member/infered.gd diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index ec3ea9bcae..0aab6e84b4 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -115,8 +115,15 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ List<String> *r_missing_deps, Error *r_err) { String blender_path = EDITOR_GET("filesystem/import/blender/blender_path"); - if (blender_major_version == -1 || blender_minor_version == -1) { - _get_blender_version(blender_path, blender_major_version, blender_minor_version, nullptr); + ERR_FAIL_COND_V_MSG(blender_path.is_empty(), nullptr, "Blender path is empty, check your Editor Settings."); + ERR_FAIL_COND_V_MSG(!FileAccess::exists(blender_path), nullptr, vformat("Invalid Blender path: %s, check your Editor Settings.", blender_path)); + + if (blender_major_version == -1 || blender_minor_version == -1 || last_tested_blender_path != blender_path) { + String error; + if (!_get_blender_version(blender_path, blender_major_version, blender_minor_version, &error)) { + ERR_FAIL_V_MSG(nullptr, error); + } + last_tested_blender_path = blender_path; } // Get global paths for source and sink. @@ -227,6 +234,18 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ } else { parameters_map["export_normals"] = false; } + + if (blender_major_version > 4 || (blender_major_version == 4 && blender_minor_version >= 1)) { + if (p_options.has(SNAME("blender/meshes/export_geometry_nodes_instances")) && p_options[SNAME("blender/meshes/export_geometry_nodes_instances")]) { + parameters_map["export_gn_mesh"] = true; + if (blender_major_version == 4 && blender_minor_version == 1) { + // There is a bug in Blender 4.1 where it can't export lights and geometry nodes at the same time, one must be disabled. + parameters_map["export_lights"] = false; + } + } else { + parameters_map["export_gn_mesh"] = false; + } + } if (p_options.has(SNAME("blender/meshes/tangents")) && p_options[SNAME("blender/meshes/tangents")]) { parameters_map["export_tangents"] = true; } else { @@ -350,6 +369,7 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li ADD_OPTION_BOOL("blender/meshes/colors", false); ADD_OPTION_BOOL("blender/meshes/uvs", true); ADD_OPTION_BOOL("blender/meshes/normals", true); + ADD_OPTION_BOOL("blender/meshes/export_geometry_nodes_instances", false); ADD_OPTION_BOOL("blender/meshes/tangents", true); ADD_OPTION_ENUM("blender/meshes/skins", "None,4 Influences (Compatible),All Influences", BLEND_BONE_INFLUENCES_ALL); ADD_OPTION_BOOL("blender/meshes/export_bones_deforming_mesh_only", false); diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h index 8a6c65a624..6adace9276 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.h +++ b/modules/gltf/editor/editor_scene_importer_blend.h @@ -45,6 +45,7 @@ class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter { int blender_major_version = -1; int blender_minor_version = -1; + String last_tested_blender_path; public: enum { diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index d98b250538..cd25b93e6c 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -5658,6 +5658,15 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn if (p_scene_root == nullptr) { // If the root node argument is null, this is the root node. p_scene_root = current_node; + // If multiple nodes were generated under the root node, ensure they have the owner set. + if (unlikely(current_node->get_child_count() > 0)) { + Array args; + args.append(p_scene_root); + for (int i = 0; i < current_node->get_child_count(); i++) { + Node *child = current_node->get_child(i); + child->propagate_call(StringName("set_owner"), args); + } + } } else { // Add the node we generated and set the owner to the scene root. p_scene_parent->add_child(current_node, true); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 36c8a40ed9..6d561c1566 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -148,7 +148,7 @@ void CSharpLanguage::finalize() { finalizing = true; - // Make sure all script binding gchandles are released before finalizing GDMono + // Make sure all script binding gchandles are released before finalizing GDMono. for (KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) { CSharpScriptBinding &script_binding = E.value; @@ -156,6 +156,10 @@ void CSharpLanguage::finalize() { script_binding.gchandle.release(); script_binding.inited = false; } + + // Make sure we clear all the instance binding callbacks so they don't get called + // after finalizing the C# language. + script_binding.owner->free_instance_binding(this); } if (gdmono) { @@ -1227,6 +1231,11 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin } GDExtensionBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDExtensionBool p_reference) { + // Instance bindings callbacks can only be called if the C# language is available. + // Failing this assert usually means that we didn't clear the instance binding in some Object + // and the C# language has already been finalized. + DEV_ASSERT(CSharpLanguage::get_singleton() != nullptr); + CRASH_COND(!p_binding); CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)p_binding)->get(); @@ -1662,7 +1671,7 @@ bool CSharpInstance::_reference_owner_unsafe() { // but the managed instance is alive, the refcount will be 1 instead of 0. // See: _unreference_owner_unsafe() - // May not me referenced yet, so we must use init_ref() instead of reference() + // May not be referenced yet, so we must use init_ref() instead of reference() if (static_cast<RefCounted *>(owner)->init_ref()) { CSharpLanguage::get_singleton()->post_unsafe_reference(owner); unsafe_referenced = true; @@ -2351,8 +2360,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg if (!ok) { // Important to clear this before destroying the script instance here instance->script = Ref<CSharpScript>(); - instance->owner = nullptr; p_owner->set_script_instance(nullptr); + instance->owner = nullptr; return nullptr; } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs index 217f467637..9a8b3ea846 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs @@ -11,11 +11,27 @@ partial class ExportDiagnostics_GD0107_OK [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] internal new static global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant> GetGodotPropertyDefaultValues() { - var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(2); + var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(10); global::Godot.Node __NodeProperty_default_value = default; values.Add(PropertyName.@NodeProperty, global::Godot.Variant.From<global::Godot.Node>(__NodeProperty_default_value)); + global::Godot.Node[] __SystemArrayOfNodesProperty_default_value = default; + values.Add(PropertyName.@SystemArrayOfNodesProperty, global::Godot.Variant.CreateFrom(__SystemArrayOfNodesProperty_default_value)); + global::Godot.Collections.Array<global::Godot.Node> __GodotArrayOfNodesProperty_default_value = default; + values.Add(PropertyName.@GodotArrayOfNodesProperty, global::Godot.Variant.CreateFrom(__GodotArrayOfNodesProperty_default_value)); + global::Godot.Collections.Dictionary<global::Godot.Node, string> __GodotDictionaryWithNodeAsKeyProperty_default_value = default; + values.Add(PropertyName.@GodotDictionaryWithNodeAsKeyProperty, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsKeyProperty_default_value)); + global::Godot.Collections.Dictionary<string, global::Godot.Node> __GodotDictionaryWithNodeAsValueProperty_default_value = default; + values.Add(PropertyName.@GodotDictionaryWithNodeAsValueProperty, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsValueProperty_default_value)); global::Godot.Node __NodeField_default_value = default; values.Add(PropertyName.@NodeField, global::Godot.Variant.From<global::Godot.Node>(__NodeField_default_value)); + global::Godot.Node[] __SystemArrayOfNodesField_default_value = default; + values.Add(PropertyName.@SystemArrayOfNodesField, global::Godot.Variant.CreateFrom(__SystemArrayOfNodesField_default_value)); + global::Godot.Collections.Array<global::Godot.Node> __GodotArrayOfNodesField_default_value = default; + values.Add(PropertyName.@GodotArrayOfNodesField, global::Godot.Variant.CreateFrom(__GodotArrayOfNodesField_default_value)); + global::Godot.Collections.Dictionary<global::Godot.Node, string> __GodotDictionaryWithNodeAsKeyField_default_value = default; + values.Add(PropertyName.@GodotDictionaryWithNodeAsKeyField, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsKeyField_default_value)); + global::Godot.Collections.Dictionary<string, global::Godot.Node> __GodotDictionaryWithNodeAsValueField_default_value = default; + values.Add(PropertyName.@GodotDictionaryWithNodeAsValueField, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsValueField_default_value)); return values; } #endif // TOOLS diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs index 067783ea66..4613d883c2 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs @@ -1,4 +1,5 @@ using Godot; +using Godot.Collections; public partial class ExportDiagnostics_GD0107_OK : Node { @@ -6,7 +7,31 @@ public partial class ExportDiagnostics_GD0107_OK : Node public Node NodeField; [Export] + public Node[] SystemArrayOfNodesField; + + [Export] + public Array<Node> GodotArrayOfNodesField; + + [Export] + public Dictionary<Node, string> GodotDictionaryWithNodeAsKeyField; + + [Export] + public Dictionary<string, Node> GodotDictionaryWithNodeAsValueField; + + [Export] public Node NodeProperty { get; set; } + + [Export] + public Node[] SystemArrayOfNodesProperty { get; set; } + + [Export] + public Array<Node> GodotArrayOfNodesProperty { get; set; } + + [Export] + public Dictionary<Node, string> GodotDictionaryWithNodeAsKeyProperty { get; set; } + + [Export] + public Dictionary<string, Node> GodotDictionaryWithNodeAsValueProperty { get; set; } } public partial class ExportDiagnostics_GD0107_KO : Resource @@ -15,5 +40,29 @@ public partial class ExportDiagnostics_GD0107_KO : Resource public Node {|GD0107:NodeField|}; [Export] + public Node[] {|GD0107:SystemArrayOfNodesField|}; + + [Export] + public Array<Node> {|GD0107:GodotArrayOfNodesField|}; + + [Export] + public Dictionary<Node, string> {|GD0107:GodotDictionaryWithNodeAsKeyField|}; + + [Export] + public Dictionary<string, Node> {|GD0107:GodotDictionaryWithNodeAsValueField|}; + + [Export] public Node {|GD0107:NodeProperty|} { get; set; } + + [Export] + public Node[] {|GD0107:SystemArrayOfNodesProperty|} { get; set; } + + [Export] + public Array<Node> {|GD0107:GodotArrayOfNodesProperty|} { get; set; } + + [Export] + public Dictionary<Node, string> {|GD0107:GodotDictionaryWithNodeAsKeyProperty|} { get; set; } + + [Export] + public Dictionary<string, Node> {|GD0107:GodotDictionaryWithNodeAsValueProperty|} { get; set; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs index efe88d8468..626f51ecae 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs @@ -196,16 +196,13 @@ namespace Godot.SourceGenerators continue; } - if (marshalType == MarshalType.GodotObjectOrDerived) + if (!isNode && MemberHasNodeType(propertyType, marshalType.Value)) { - if (!isNode && propertyType.InheritsFrom("GodotSharp", GodotClasses.Node)) - { - context.ReportDiagnostic(Diagnostic.Create( - Common.OnlyNodesShouldExportNodesRule, - property.Locations.FirstLocationWithSourceTreeOrDefault() - )); - continue; - } + context.ReportDiagnostic(Diagnostic.Create( + Common.OnlyNodesShouldExportNodesRule, + property.Locations.FirstLocationWithSourceTreeOrDefault() + )); + continue; } var propertyDeclarationSyntax = property.DeclaringSyntaxReferences @@ -315,16 +312,13 @@ namespace Godot.SourceGenerators continue; } - if (marshalType == MarshalType.GodotObjectOrDerived) + if (!isNode && MemberHasNodeType(fieldType, marshalType.Value)) { - if (!isNode && fieldType.InheritsFrom("GodotSharp", GodotClasses.Node)) - { - context.ReportDiagnostic(Diagnostic.Create( - Common.OnlyNodesShouldExportNodesRule, - field.Locations.FirstLocationWithSourceTreeOrDefault() - )); - continue; - } + context.ReportDiagnostic(Diagnostic.Create( + Common.OnlyNodesShouldExportNodesRule, + field.Locations.FirstLocationWithSourceTreeOrDefault() + )); + continue; } EqualsValueClauseSyntax? initializer = field.DeclaringSyntaxReferences @@ -424,6 +418,27 @@ namespace Godot.SourceGenerators context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8)); } + private static bool MemberHasNodeType(ITypeSymbol memberType, MarshalType marshalType) + { + if (marshalType == MarshalType.GodotObjectOrDerived) + { + return memberType.InheritsFrom("GodotSharp", GodotClasses.Node); + } + if (marshalType == MarshalType.GodotObjectOrDerivedArray) + { + var elementType = ((IArrayTypeSymbol)memberType).ElementType; + return elementType.InheritsFrom("GodotSharp", GodotClasses.Node); + } + if (memberType is INamedTypeSymbol { IsGenericType: true } genericType) + { + return genericType.TypeArguments + .Any(static typeArgument + => typeArgument.InheritsFrom("GodotSharp", GodotClasses.Node)); + } + + return false; + } + private struct ExportedPropertyMetadata { public ExportedPropertyMetadata(string name, MarshalType type, ITypeSymbol typeSymbol, string? value) diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index d3720dcb72..ede0600ac1 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -355,24 +355,23 @@ namespace GodotTools.Export if (outputPaths.Count > 2) { // lipo the simulator binaries together - // TODO: Move this to the native lipo implementation we have in the macos export plugin. - var lipoArgs = new List<string>(); - lipoArgs.Add("-create"); - lipoArgs.AddRange(outputPaths.Skip(1).Select(x => Path.Combine(x, $"{GodotSharpDirs.ProjectAssemblyName}.dylib"))); - lipoArgs.Add("-output"); - lipoArgs.Add(Path.Combine(outputPaths[1], $"{GodotSharpDirs.ProjectAssemblyName}.dylib")); - int lipoExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("lipo"), lipoArgs); - if (lipoExitCode != 0) - throw new InvalidOperationException($"Command 'lipo' exited with code: {lipoExitCode}."); + string outputPath = Path.Combine(outputPaths[1], $"{GodotSharpDirs.ProjectAssemblyName}.dylib"); + string[] files = outputPaths + .Skip(1) + .Select(path => Path.Combine(path, $"{GodotSharpDirs.ProjectAssemblyName}.dylib")) + .ToArray(); + + if (!Internal.LipOCreateFile(outputPath, files)) + { + throw new InvalidOperationException($"Failed to 'lipo' simulator binaries."); + } outputPaths.RemoveRange(2, outputPaths.Count - 2); } - var xcFrameworkPath = Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, publishConfig.BuildConfig, - $"{GodotSharpDirs.ProjectAssemblyName}_aot.xcframework"); - if (!BuildManager.GenerateXCFrameworkBlocking(outputPaths, - Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, publishConfig.BuildConfig, xcFrameworkPath))) + string xcFrameworkPath = Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, publishConfig.BuildConfig, $"{GodotSharpDirs.ProjectAssemblyName}_aot.xcframework"); + if (!BuildManager.GenerateXCFrameworkBlocking(outputPaths, xcFrameworkPath)) { throw new InvalidOperationException("Failed to generate xcframework."); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs b/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs deleted file mode 100644 index 023f46b685..0000000000 --- a/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.IO; - -namespace GodotTools.Export -{ - public static class XcodeHelper - { - private static string? _XcodePath = null; - - public static string XcodePath - { - get - { - if (_XcodePath == null) - { - _XcodePath = FindXcode(); - - if (_XcodePath == null) - throw new FileNotFoundException("Could not find Xcode."); - } - - return _XcodePath; - } - } - - private static string? FindSelectedXcode() - { - var outputWrapper = new Godot.Collections.Array(); - - int exitCode = Godot.OS.Execute("xcode-select", new string[] { "--print-path" }, output: outputWrapper); - - if (exitCode == 0) - { - string output = (string)outputWrapper[0]; - return output.Trim(); - } - - Console.Error.WriteLine($"'xcode-select --print-path' exited with code: {exitCode}"); - - return null; - } - - public static string? FindXcode() - { - string? selectedXcode = FindSelectedXcode(); - if (selectedXcode != null) - { - if (Directory.Exists(Path.Combine(selectedXcode, "Contents", "Developer"))) - return selectedXcode; - - // The path already pointed to Contents/Developer - var dirInfo = new DirectoryInfo(selectedXcode); - if (dirInfo is not { Parent.Name: "Contents", Name: "Developer" }) - { - Console.WriteLine(Path.GetDirectoryName(selectedXcode)); - Console.WriteLine(System.IO.Directory.GetParent(selectedXcode)?.Name); - Console.Error.WriteLine("Unrecognized path for selected Xcode"); - } - else - { - return System.IO.Path.GetFullPath($"{selectedXcode}/../.."); - } - } - else - { - Console.Error.WriteLine("Could not find the selected Xcode; trying with a hint path"); - } - - const string XcodeHintPath = "/Applications/Xcode.app"; - - if (Directory.Exists(XcodeHintPath)) - { - if (Directory.Exists(Path.Combine(XcodeHintPath, "Contents", "Developer"))) - return XcodeHintPath; - - Console.Error.WriteLine($"Found Xcode at '{XcodeHintPath}' but it's missing the 'Contents/Developer' sub-directory"); - } - - return null; - } - - public static string FindXcodeTool(string toolName) - { - string XcodeDefaultToolchain = Path.Combine(XcodePath, "Contents", "Developer", "Toolchains", "XcodeDefault.xctoolchain"); - - string path = Path.Combine(XcodeDefaultToolchain, "usr", "bin", toolName); - if (File.Exists(path)) - return path; - - throw new FileNotFoundException($"Cannot find Xcode tool: {toolName}"); - } - } -} diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index 175bb78051..225ac4073b 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -35,6 +35,13 @@ namespace GodotTools.Internals return godot_icall_Internal_IsMacOSAppBundleInstalled(bundleIdIn); } + public static bool LipOCreateFile(string outputPath, string[] files) + { + using godot_string outputPathIn = Marshaling.ConvertStringToNative(outputPath); + using godot_packed_string_array filesIn = Marshaling.ConvertSystemArrayToNativePackedStringArray(files); + return godot_icall_Internal_LipOCreateFile(outputPathIn, filesIn); + } + public static bool GodotIs32Bits() => godot_icall_Internal_GodotIs32Bits(); public static bool GodotIsRealTDouble() => godot_icall_Internal_GodotIsRealTDouble(); @@ -121,6 +128,8 @@ namespace GodotTools.Internals private static partial bool godot_icall_Internal_IsMacOSAppBundleInstalled(in godot_string bundleId); + private static partial bool godot_icall_Internal_LipOCreateFile(in godot_string outputPath, in godot_packed_string_array files); + private static partial bool godot_icall_Internal_GodotIs32Bits(); private static partial bool godot_icall_Internal_GodotIsRealTDouble(); diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 03d8b4eab6..7322a47630 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -44,6 +44,7 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_settings.h" +#include "editor/export/lipo.h" #include "editor/gui/editor_run_bar.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/themes/editor_scale.h" @@ -117,6 +118,13 @@ bool godot_icall_Internal_IsMacOSAppBundleInstalled(const godot_string *p_bundle #endif } +bool godot_icall_Internal_LipOCreateFile(const godot_string *p_output_path, const godot_packed_array *p_files) { + String output_path = *reinterpret_cast<const String *>(p_output_path); + PackedStringArray files = *reinterpret_cast<const PackedStringArray *>(p_files); + LipO lip; + return lip.create_file(output_path, files); +} + bool godot_icall_Internal_GodotIs32Bits() { return sizeof(void *) == 4; } @@ -258,6 +266,7 @@ static const void *unmanaged_callbacks[]{ (void *)godot_icall_EditorProgress_Step, (void *)godot_icall_Internal_FullExportTemplatesDir, (void *)godot_icall_Internal_IsMacOSAppBundleInstalled, + (void *)godot_icall_Internal_LipOCreateFile, (void *)godot_icall_Internal_GodotIs32Bits, (void *)godot_icall_Internal_GodotIsRealTDouble, (void *)godot_icall_Internal_GodotMainIteration, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 5cc2a8026e..901700067d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -241,11 +241,17 @@ namespace Godot.Bridge if (outIconPath != null) { - var iconAttr = scriptType.GetCustomAttributes(inherit: false) + IconAttribute? iconAttr = scriptType.GetCustomAttributes(inherit: false) .OfType<IconAttribute>() .FirstOrDefault(); - *outIconPath = Marshaling.ConvertStringToNative(iconAttr?.Path); + if (!string.IsNullOrEmpty(iconAttr?.Path)) + { + string iconPath = iconAttr.Path.IsAbsolutePath() + ? iconAttr.Path.SimplifyPath() + : scriptPathStr.GetBaseDir().PathJoin(iconAttr.Path).SimplifyPath(); + *outIconPath = Marshaling.ConvertStringToNative(iconPath); + } } if (outBaseType != null) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index a5fa89d3bf..f943a3049d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -47,7 +47,7 @@ namespace Godot { get { - real_t detSign = Mathf.Sign(BasisDeterminant()); + real_t detSign = Mathf.Sign(Determinant()); return new Vector2(X.Length(), detSign * Y.Length()); } } @@ -59,7 +59,7 @@ namespace Godot { get { - real_t detSign = Mathf.Sign(BasisDeterminant()); + real_t detSign = Mathf.Sign(Determinant()); return Mathf.Acos(X.Normalized().Dot(detSign * Y.Normalized())) - Mathf.Pi * 0.5f; } } @@ -135,7 +135,7 @@ namespace Godot /// <returns>The inverse transformation matrix.</returns> public readonly Transform2D AffineInverse() { - real_t det = BasisDeterminant(); + real_t det = Determinant(); if (det == 0) throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); @@ -157,15 +157,16 @@ namespace Godot /// <summary> /// Returns the determinant of the basis matrix. If the basis is - /// uniformly scaled, its determinant is the square of the scale. + /// uniformly scaled, then its determinant equals the square of the + /// scale factor. /// - /// A negative determinant means the Y scale is negative. - /// A zero determinant means the basis isn't invertible, - /// and is usually considered invalid. + /// A negative determinant means the basis was flipped, so one part of + /// the scale is negative. A zero determinant means the basis isn't + /// invertible, and is usually considered invalid. /// </summary> /// <returns>The determinant of the basis matrix.</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)] - private readonly real_t BasisDeterminant() + public readonly real_t Determinant() { return (X.X * Y.Y) - (X.Y * Y.X); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index 27f2713efa..ef66d5bbe0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -692,10 +692,18 @@ namespace Godot // Zero length vectors have no angle, so the best we can do is either lerp or throw an error. return Lerp(to, weight); } + Vector3 axis = Cross(to); + real_t axisLengthSquared = axis.LengthSquared(); + if (axisLengthSquared == 0.0) + { + // Colinear vectors have no rotation axis or angle between them, so the best we can do is lerp. + return Lerp(to, weight); + } + axis /= Mathf.Sqrt(axisLengthSquared); real_t startLength = Mathf.Sqrt(startLengthSquared); real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight); real_t angle = AngleTo(to); - return Rotated(Cross(to).Normalized(), angle * weight) * (resultLength / startLength); + return Rotated(axis, angle * weight) * (resultLength / startLength); } /// <summary> diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp index 8c2fb42463..33b92f6266 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.cpp +++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp @@ -1042,10 +1042,32 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation } TPPLPartition tpart; - if (tpart.ConvexPartition_HM(&tppl_in_polygon, &tppl_out_polygon) == 0) { //failed! - ERR_PRINT("NavigationPolygon Convex partition failed. Unable to create a valid NavigationMesh from defined polygon outline paths."); - p_navigation_mesh->clear(); - return; + + NavigationPolygon::SamplePartitionType sample_partition_type = p_navigation_mesh->get_sample_partition_type(); + + switch (sample_partition_type) { + case NavigationPolygon::SamplePartitionType::SAMPLE_PARTITION_CONVEX_PARTITION: + if (tpart.ConvexPartition_HM(&tppl_in_polygon, &tppl_out_polygon) == 0) { + ERR_PRINT("NavigationPolygon polygon convex partition failed. Unable to create a valid navigation mesh polygon layout from provided source geometry."); + p_navigation_mesh->set_vertices(Vector<Vector2>()); + p_navigation_mesh->clear_polygons(); + return; + } + break; + case NavigationPolygon::SamplePartitionType::SAMPLE_PARTITION_TRIANGULATE: + if (tpart.Triangulate_EC(&tppl_in_polygon, &tppl_out_polygon) == 0) { + ERR_PRINT("NavigationPolygon polygon triangulation failed. Unable to create a valid navigation mesh polygon layout from provided source geometry."); + p_navigation_mesh->set_vertices(Vector<Vector2>()); + p_navigation_mesh->clear_polygons(); + return; + } + break; + default: { + ERR_PRINT("NavigationPolygon polygon partitioning failed. Unrecognized partition type."); + p_navigation_mesh->set_vertices(Vector<Vector2>()); + p_navigation_mesh->clear_polygons(); + return; + } } Vector<Vector2> new_vertices; diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp index 430d527844..11a5de608b 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.cpp +++ b/modules/navigation/3d/godot_navigation_server_3d.cpp @@ -509,22 +509,31 @@ void GodotNavigationServer3D::region_bake_navigation_mesh(Ref<NavigationMesh> p_ int GodotNavigationServer3D::region_get_connections_count(RID p_region) const { NavRegion *region = region_owner.get_or_null(p_region); ERR_FAIL_NULL_V(region, 0); - - return region->get_connections_count(); + NavMap *map = region->get_map(); + if (map) { + return map->get_region_connections_count(region); + } + return 0; } Vector3 GodotNavigationServer3D::region_get_connection_pathway_start(RID p_region, int p_connection_id) const { NavRegion *region = region_owner.get_or_null(p_region); ERR_FAIL_NULL_V(region, Vector3()); - - return region->get_connection_pathway_start(p_connection_id); + NavMap *map = region->get_map(); + if (map) { + return map->get_region_connection_pathway_start(region, p_connection_id); + } + return Vector3(); } Vector3 GodotNavigationServer3D::region_get_connection_pathway_end(RID p_region, int p_connection_id) const { NavRegion *region = region_owner.get_or_null(p_region); ERR_FAIL_NULL_V(region, Vector3()); - - return region->get_connection_pathway_end(p_connection_id); + NavMap *map = region->get_map(); + if (map) { + return map->get_region_connection_pathway_end(region, p_connection_id); + } + return Vector3(); } Vector3 GodotNavigationServer3D::region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const { @@ -1298,6 +1307,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 +1325,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 +1343,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 +1578,9 @@ int GodotNavigationServer3D::get_process_info(ProcessInfo p_info) const { case INFO_EDGE_FREE_COUNT: { return pm_edge_free_count; } break; + case INFO_OBSTACLE_COUNT: { + return pm_obstacle_count; + } break; } return 0; diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h index 5ba7ed1088..12a1132f07 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.h +++ b/modules/navigation/3d/godot_navigation_server_3d.h @@ -95,6 +95,7 @@ class GodotNavigationServer3D : public NavigationServer3D { int pm_edge_merge_count = 0; int pm_edge_connection_count = 0; int pm_edge_free_count = 0; + int pm_obstacle_count = 0; public: GodotNavigationServer3D(); diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index f917c988ea..0c91e8dea3 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) { @@ -936,8 +937,9 @@ void NavMap::sync() { _new_pm_edge_free_count = 0; // Remove regions connections. + region_external_connections.clear(); for (NavRegion *region : regions) { - region->get_connections().clear(); + region_external_connections[region] = LocalVector<gd::Edge::Connection>(); } // Resize the polygon count. @@ -1071,7 +1073,7 @@ void NavMap::sync() { free_edge.polygon->edges[free_edge.edge].connections.push_back(new_connection); // Add the connection to the region_connection map. - ((NavRegion *)free_edge.polygon->owner)->get_connections().push_back(new_connection); + region_external_connections[(NavRegion *)free_edge.polygon->owner].push_back(new_connection); _new_pm_edge_connection_count += 1; } } @@ -1219,6 +1221,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() { @@ -1426,6 +1429,40 @@ void NavMap::_update_merge_rasterizer_cell_dimensions() { merge_rasterizer_cell_height = cell_height * merge_rasterizer_cell_scale; } +int NavMap::get_region_connections_count(NavRegion *p_region) const { + ERR_FAIL_NULL_V(p_region, 0); + + HashMap<NavRegion *, LocalVector<gd::Edge::Connection>>::ConstIterator found_connections = region_external_connections.find(p_region); + if (found_connections) { + return found_connections->value.size(); + } + return 0; +} + +Vector3 NavMap::get_region_connection_pathway_start(NavRegion *p_region, int p_connection_id) const { + ERR_FAIL_NULL_V(p_region, Vector3()); + + HashMap<NavRegion *, LocalVector<gd::Edge::Connection>>::ConstIterator found_connections = region_external_connections.find(p_region); + if (found_connections) { + ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector3()); + return found_connections->value[p_connection_id].pathway_start; + } + + return Vector3(); +} + +Vector3 NavMap::get_region_connection_pathway_end(NavRegion *p_region, int p_connection_id) const { + ERR_FAIL_NULL_V(p_region, Vector3()); + + HashMap<NavRegion *, LocalVector<gd::Edge::Connection>>::ConstIterator found_connections = region_external_connections.find(p_region); + if (found_connections) { + ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector3()); + return found_connections->value[p_connection_id].pathway_end; + } + + return Vector3(); +} + NavMap::NavMap() { avoidance_use_multiple_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_multiple_threads"); avoidance_use_high_priority_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_high_priority_threads"); diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h index d6215ea57f..82e8854b7a 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -123,6 +123,9 @@ 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; + + HashMap<NavRegion *, LocalVector<gd::Edge::Connection>> region_external_connections; public: NavMap(); @@ -216,6 +219,11 @@ public: int get_pm_edge_merge_count() const { return pm_edge_merge_count; } int get_pm_edge_connection_count() const { return pm_edge_connection_count; } int get_pm_edge_free_count() const { return pm_edge_free_count; } + int get_pm_obstacle_count() const { return pm_obstacle_count; } + + int get_region_connections_count(NavRegion *p_region) const; + Vector3 get_region_connection_pathway_start(NavRegion *p_region, int p_connection_id) const; + Vector3 get_region_connection_pathway_end(NavRegion *p_region, int p_connection_id) const; private: void compute_single_step(uint32_t index, NavAgent **agent); diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp index f30855d697..85510bd416 100644 --- a/modules/navigation/nav_region.cpp +++ b/modules/navigation/nav_region.cpp @@ -44,8 +44,6 @@ void NavRegion::set_map(NavMap *p_map) { map = p_map; polygons_dirty = true; - connections.clear(); - if (map) { map->add_region(this); } @@ -105,25 +103,6 @@ void NavRegion::set_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) { polygons_dirty = true; } -int NavRegion::get_connections_count() const { - if (!map) { - return 0; - } - return connections.size(); -} - -Vector3 NavRegion::get_connection_pathway_start(int p_connection_id) const { - ERR_FAIL_NULL_V(map, Vector3()); - ERR_FAIL_INDEX_V(p_connection_id, connections.size(), Vector3()); - return connections[p_connection_id].pathway_start; -} - -Vector3 NavRegion::get_connection_pathway_end(int p_connection_id) const { - ERR_FAIL_NULL_V(map, Vector3()); - ERR_FAIL_INDEX_V(p_connection_id, connections.size(), Vector3()); - return connections[p_connection_id].pathway_end; -} - Vector3 NavRegion::get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const { if (!get_enabled()) { return Vector3(); diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h index ebc082bd2f..662a32c47a 100644 --- a/modules/navigation/nav_region.h +++ b/modules/navigation/nav_region.h @@ -40,7 +40,6 @@ class NavRegion : public NavBase { NavMap *map = nullptr; Transform3D transform; - Vector<gd::Edge::Connection> connections; bool enabled = true; bool use_edge_connections = true; @@ -85,13 +84,6 @@ public: void set_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh); - Vector<gd::Edge::Connection> &get_connections() { - return connections; - } - int get_connections_count() const; - Vector3 get_connection_pathway_start(int p_connection_id) const; - Vector3 get_connection_pathway_end(int p_connection_id) const; - LocalVector<gd::Polygon> const &get_polygons() const { return polygons; } diff --git a/modules/openxr/config.py b/modules/openxr/config.py index 6c6b9fdb8b..559aa2acf6 100644 --- a/modules/openxr/config.py +++ b/modules/openxr/config.py @@ -22,6 +22,7 @@ def get_doc_classes(): "OpenXRInteractionProfileMetadata", "OpenXRIPBinding", "OpenXRHand", + "OpenXRVisibilityMask", "OpenXRCompositionLayer", "OpenXRCompositionLayerQuad", "OpenXRCompositionLayerCylinder", diff --git a/modules/openxr/doc_classes/OpenXRVisibilityMask.xml b/modules/openxr/doc_classes/OpenXRVisibilityMask.xml new file mode 100644 index 0000000000..90ee889c7c --- /dev/null +++ b/modules/openxr/doc_classes/OpenXRVisibilityMask.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="OpenXRVisibilityMask" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + Draws a stereo correct visibility mask. + </brief_description> + <description> + The visibility mask allows us to black out the part of the render result that is invisible due to lens distortion. + As this is rendered first, it prevents fragments with expensive lighting calculations to be processed as they are discarded through z-checking. + </description> + <tutorials> + </tutorials> +</class> diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp index d9a66aa827..6d6231f6fa 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp @@ -58,9 +58,14 @@ OpenXRHandTrackingExtension::~OpenXRHandTrackingExtension() { HashMap<String, bool *> OpenXRHandTrackingExtension::get_requested_extensions() { HashMap<String, bool *> request_extensions; + unobstructed_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_unobstructed_data_source"); + controller_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_controller_data_source"); + request_extensions[XR_EXT_HAND_TRACKING_EXTENSION_NAME] = &hand_tracking_ext; request_extensions[XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME] = &hand_motion_range_ext; - request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext; + if (unobstructed_data_source || controller_data_source) { + request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext; + } return request_extensions; } @@ -141,10 +146,18 @@ void OpenXRHandTrackingExtension::on_process() { void *next_pointer = nullptr; // Originally not all XR runtimes supported hand tracking data sourced both from controllers and normal hand tracking. - // With this extension we can indicate we accept input from both sources so hand tracking data is consistently provided - // on runtimes that support this. - XrHandTrackingDataSourceEXT data_sources[2] = { XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT, XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT }; - XrHandTrackingDataSourceInfoEXT data_source_info = { XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, next_pointer, 2, data_sources }; + // With this extension we can indicate we wish to accept input from either or both sources. + // This functionality is subject to the abilities of the XR runtime and requires the data source extension. + // Note: If the data source extension is not available, no guarantees can be made on what the XR runtime supports. + uint32_t data_source_count = 0; + XrHandTrackingDataSourceEXT data_sources[2]; + if (unobstructed_data_source) { + data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT; + } + if (controller_data_source) { + data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT; + } + XrHandTrackingDataSourceInfoEXT data_source_info = { XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, next_pointer, data_source_count, data_sources }; if (hand_tracking_source_ext) { // If supported include this info next_pointer = &data_source_info; @@ -224,7 +237,9 @@ void OpenXRHandTrackingExtension::on_process() { if (XR_FAILED(result)) { // not successful? then we do nothing. print_line("OpenXR: Failed to get tracking for hand", i, "[", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + godot_tracker->set_hand_tracking_source(XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN); godot_tracker->set_has_tracking_data(false); + godot_tracker->invalidate_pose("default"); continue; } @@ -235,8 +250,6 @@ void OpenXRHandTrackingExtension::on_process() { } if (hand_trackers[i].locations.isActive) { - godot_tracker->set_has_tracking_data(true); - // SKELETON_RIG_HUMANOID bone adjustment. This rotation performs: // OpenXR Z+ -> Godot Humanoid Y- (Back along the bone) // OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand) @@ -245,7 +258,6 @@ void OpenXRHandTrackingExtension::on_process() { for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) { const XrHandJointLocationEXT &location = hand_trackers[i].joint_locations[joint]; const XrHandJointVelocityEXT &velocity = hand_trackers[i].joint_velocities[joint]; - const XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source; const XrPosef &pose = location.pose; Transform3D transform; @@ -285,23 +297,35 @@ void OpenXRHandTrackingExtension::on_process() { godot_tracker->set_hand_joint_radius((XRHandTracker::HandJoint)joint, location.radius); if (joint == XR_HAND_JOINT_PALM_EXT) { - XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN; - if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) { - source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED; - } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) { - source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER; - } - - godot_tracker->set_hand_tracking_source(source); if (location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) { + XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source; + + XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN; + if (hand_tracking_source_ext) { + if (!data_source.isActive) { + source = XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED; + } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) { + source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED; + } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) { + source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER; + } else { + // Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them. + WARN_PRINT_ONCE("Unknown active data source found!"); + source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN; + } + } + godot_tracker->set_hand_tracking_source(source); + godot_tracker->set_has_tracking_data(true); godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity); } else { + godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN); godot_tracker->set_has_tracking_data(false); godot_tracker->invalidate_pose("default"); } } } } else { + godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN); godot_tracker->set_has_tracking_data(false); godot_tracker->invalidate_pose("default"); } @@ -349,16 +373,17 @@ XrHandJointsMotionRangeEXT OpenXRHandTrackingExtension::get_motion_range(HandTra OpenXRHandTrackingExtension::HandTrackedSource OpenXRHandTrackingExtension::get_hand_tracking_source(HandTrackedHands p_hand) const { ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, OPENXR_SOURCE_UNKNOWN); - if (hand_tracking_source_ext && hand_trackers[p_hand].data_source.isActive) { - switch (hand_trackers[p_hand].data_source.dataSource) { - case XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT: - return OPENXR_SOURCE_UNOBSTRUCTED; - - case XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT: - return OPENXR_SOURCE_CONTROLLER; - - default: - return OPENXR_SOURCE_UNKNOWN; + if (hand_tracking_source_ext) { + if (!hand_trackers[p_hand].data_source.isActive) { + return OPENXR_SOURCE_NOT_TRACKED; + } else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) { + return OPENXR_SOURCE_UNOBSTRUCTED; + } else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) { + return OPENXR_SOURCE_CONTROLLER; + } else { + // Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them. + WARN_PRINT_ONCE("Unknown active data source found!"); + return OPENXR_SOURCE_UNKNOWN; } } diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.h b/modules/openxr/extensions/openxr_hand_tracking_extension.h index f709bc05c2..2c34ff7f21 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.h +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.h @@ -48,6 +48,7 @@ public: OPENXR_SOURCE_UNKNOWN, OPENXR_SOURCE_UNOBSTRUCTED, OPENXR_SOURCE_CONTROLLER, + OPENXR_SOURCE_NOT_TRACKED, OPENXR_SOURCE_MAX }; @@ -110,6 +111,8 @@ private: bool hand_tracking_ext = false; bool hand_motion_range_ext = false; bool hand_tracking_source_ext = false; + bool unobstructed_data_source = false; + bool controller_data_source = false; // functions void cleanup_hand_tracking(); diff --git a/modules/openxr/extensions/openxr_mxink_extension.cpp b/modules/openxr/extensions/openxr_mxink_extension.cpp new file mode 100644 index 0000000000..fe48583c27 --- /dev/null +++ b/modules/openxr/extensions/openxr_mxink_extension.cpp @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* openxr_mxink_extension.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "openxr_mxink_extension.h" + +#include "../action_map/openxr_interaction_profile_metadata.h" + +// Not in base XR libs needs def +#define XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME "XR_LOGITECH_mx_ink_stylus_interaction" + +HashMap<String, bool *> OpenXRMxInkExtension::get_requested_extensions() { + HashMap<String, bool *> request_extensions; + + request_extensions[XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME] = &available; + + return request_extensions; +} + +bool OpenXRMxInkExtension::is_available() { + return available; +} + +void OpenXRMxInkExtension::on_register_metadata() { + OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton(); + ERR_FAIL_NULL(metadata); + + // Logitech MX Ink Stylus + metadata->register_interaction_profile("Logitech MX Ink Stylus", "/interaction_profiles/logitech/mx_ink_stylus_logitech", XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME); + + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip Force", "/user/hand/left", "/user/hand/left/input/tip_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Middle force", "/user/hand/left", "/user/hand/left/input/cluster_middle_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front click", "/user/hand/left", "/user/hand/left/input/cluster_front_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front double", "/user/hand/left", "/user/hand/left/input/cluster_front_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back click", "/user/hand/left", "/user/hand/left/input/cluster_back_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back double", "/user/hand/left", "/user/hand/left/input/cluster_back_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "System click", "/user/hand/left", "/user/hand/left/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Docked", "/user/hand/left", "/user/hand/left/input/dock_logitech/docked_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip pose", "/user/hand/left", "/user/hand/left/input/tip_logitech/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip Force", "/user/hand/right", "/user/hand/right/input/tip_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Middle force", "/user/hand/right", "/user/hand/right/input/cluster_middle_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front click", "/user/hand/right", "/user/hand/right/input/cluster_front_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front double", "/user/hand/right", "/user/hand/right/input/cluster_front_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back click", "/user/hand/right", "/user/hand/right/input/cluster_back_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back double", "/user/hand/right", "/user/hand/right/input/cluster_back_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Docked", "/user/hand/right", "/user/hand/right/input/dock_logitech/docked_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip pose", "/user/hand/right", "/user/hand/right/input/tip_logitech/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC); + metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC); +} diff --git a/modules/openxr/extensions/openxr_mxink_extension.h b/modules/openxr/extensions/openxr_mxink_extension.h new file mode 100644 index 0000000000..fe0cf866aa --- /dev/null +++ b/modules/openxr/extensions/openxr_mxink_extension.h @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* openxr_mxink_extension.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef OPENXR_MXINK_EXTENSION_H +#define OPENXR_MXINK_EXTENSION_H + +#include "openxr_extension_wrapper.h" + +class OpenXRMxInkExtension : public OpenXRExtensionWrapper { +public: + virtual HashMap<String, bool *> get_requested_extensions() override; + + bool is_available(); + + virtual void on_register_metadata() override; + +private: + bool available = false; +}; + +#endif // OPENXR_MXINK_EXTENSION_H diff --git a/modules/openxr/extensions/openxr_visibility_mask_extension.cpp b/modules/openxr/extensions/openxr_visibility_mask_extension.cpp new file mode 100644 index 0000000000..3e193d7d50 --- /dev/null +++ b/modules/openxr/extensions/openxr_visibility_mask_extension.cpp @@ -0,0 +1,279 @@ +/**************************************************************************/ +/* openxr_visibility_mask_extension.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "openxr_visibility_mask_extension.h" + +#include "../openxr_api.h" +#include "core/string/print_string.h" +#include "core/variant/array.h" +#include "core/variant/variant.h" +#include "servers/rendering_server.h" + +static const char *VISIBILITY_MASK_SHADER_CODE = + "shader_type spatial;\n" + "render_mode unshaded, shadows_disabled, cull_disabled;\n" + "void vertex() {\n" + "\tif (int(VERTEX.z) == VIEW_INDEX) {\n" + "\t\tVERTEX.z = -1.0;\n" + "\t\tVERTEX += EYE_OFFSET;\n" + "\t\tPOSITION = PROJECTION_MATRIX * vec4(VERTEX, 1.0);\n" + "\t\tPOSITION.xy /= POSITION.w;\n" + "\t\tPOSITION.z = 1.0;\n" + "\t\tPOSITION.w = 1.0;\n" + "\t} else {\n" + "\t\tPOSITION = vec4(2.0, 2.0, 2.0, 1.0);\n" + "\t}\n" + "}\n" + "void fragment() {\n" + "\tALBEDO = vec3(0.0, 0.0, 0.0);\n" + "}\n"; + +OpenXRVisibilityMaskExtension *OpenXRVisibilityMaskExtension::singleton = nullptr; + +OpenXRVisibilityMaskExtension *OpenXRVisibilityMaskExtension::get_singleton() { + return singleton; +} + +OpenXRVisibilityMaskExtension::OpenXRVisibilityMaskExtension() { + singleton = this; +} + +OpenXRVisibilityMaskExtension::~OpenXRVisibilityMaskExtension() { + singleton = nullptr; +} + +HashMap<String, bool *> OpenXRVisibilityMaskExtension::get_requested_extensions() { + HashMap<String, bool *> request_extensions; + + request_extensions[XR_KHR_VISIBILITY_MASK_EXTENSION_NAME] = &available; + + return request_extensions; +} + +void OpenXRVisibilityMaskExtension::on_instance_created(const XrInstance p_instance) { + if (available) { + EXT_INIT_XR_FUNC(xrGetVisibilityMaskKHR); + } +} + +void OpenXRVisibilityMaskExtension::on_session_created(const XrSession p_instance) { + if (available) { + RS *rendering_server = RS::get_singleton(); + ERR_FAIL_NULL(rendering_server); + + OpenXRAPI *openxr_api = (OpenXRAPI *)OpenXRAPI::get_singleton(); + ERR_FAIL_NULL(openxr_api); + + // Create our shader. + shader = rendering_server->shader_create(); + rendering_server->shader_set_code(shader, VISIBILITY_MASK_SHADER_CODE); + + // Create our material. + material = rendering_server->material_create(); + rendering_server->material_set_shader(material, shader); + rendering_server->material_set_render_priority(material, 99); + + // Create our mesh. + mesh = rendering_server->mesh_create(); + + // Get our initial mesh data. + mesh_count = openxr_api->get_view_count(); // We need a mesh for each view. + for (uint32_t i = 0; i < mesh_count; i++) { + _update_mesh_data(i); + } + + // And update our mesh + _update_mesh(); + } +} + +void OpenXRVisibilityMaskExtension::on_session_destroyed() { + RS *rendering_server = RS::get_singleton(); + ERR_FAIL_NULL(rendering_server); + + // Free our mesh. + if (mesh.is_valid()) { + rendering_server->free(mesh); + mesh = RID(); + } + + // Free our material. + if (material.is_valid()) { + rendering_server->free(material); + material = RID(); + } + + // Free our shader. + if (shader.is_valid()) { + rendering_server->free(shader); + shader = RID(); + } + + mesh_count = 0; +} + +void OpenXRVisibilityMaskExtension::on_pre_render() { + // Update mesh data if its dirty. + // Here we call this from the rendering thread however as we're going through the rendering server this is safe. + _update_mesh(); +} + +bool OpenXRVisibilityMaskExtension::on_event_polled(const XrEventDataBuffer &event) { + if (event.type == XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR) { + XrEventDataVisibilityMaskChangedKHR *vismask_event = (XrEventDataVisibilityMaskChangedKHR *)&event; + + print_verbose("OpenXR EVENT: Visibility mask changed for view " + String::num_uint64(vismask_event->viewIndex)); + + if (available) { // This event won't be called if this extension is not available but better safe than sorry. + _update_mesh_data(vismask_event->viewIndex); + } + + return true; + } + + return false; +} + +bool OpenXRVisibilityMaskExtension::is_available() { + return available; +} + +RID OpenXRVisibilityMaskExtension::get_mesh() { + return mesh; +} + +void OpenXRVisibilityMaskExtension::_update_mesh_data(uint32_t p_view) { + if (available) { + ERR_FAIL_UNSIGNED_INDEX(p_view, 4); + + OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); + ERR_FAIL_NULL(openxr_api); + + XrSession session = openxr_api->get_session(); + XrViewConfigurationType view_configuration_type = openxr_api->get_view_configuration(); + + // Figure out how much data we're getting. + XrVisibilityMaskKHR visibility_mask_data = { + XR_TYPE_VISIBILITY_MASK_KHR, + nullptr, + 0, + 0, + nullptr, + 0, + 0, + nullptr, + }; + + XrResult result = xrGetVisibilityMaskKHR(session, view_configuration_type, p_view, XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR, &visibility_mask_data); + if (XR_FAILED(result)) { + print_line("OpenXR: Unable to obtain visibility mask metrics [", openxr_api->get_error_string(result), "]"); + return; + } + + // Resize buffers + mesh_data[p_view].vertices.resize(visibility_mask_data.vertexCountOutput); + mesh_data[p_view].indices.resize(visibility_mask_data.indexCountOutput); + + visibility_mask_data.vertexCapacityInput = visibility_mask_data.vertexCountOutput; + visibility_mask_data.vertices = mesh_data[p_view].vertices.ptrw(); + visibility_mask_data.indexCapacityInput = visibility_mask_data.indexCountOutput; + visibility_mask_data.indices = mesh_data[p_view].indices.ptrw(); + + result = xrGetVisibilityMaskKHR(session, view_configuration_type, p_view, XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR, &visibility_mask_data); + if (XR_FAILED(result)) { + print_line("OpenXR: Unable to obtain visibility mask data [", openxr_api->get_error_string(result), "]"); + return; + } + + // Mark as dirty, we have updated mesh data. + is_dirty = true; + } +} + +void OpenXRVisibilityMaskExtension::_update_mesh() { + if (available && is_dirty && mesh_count > 0) { + RS *rendering_server = RS::get_singleton(); + ERR_FAIL_NULL(rendering_server); + + OpenXRAPI *openxr_api = (OpenXRAPI *)OpenXRAPI::get_singleton(); + ERR_FAIL_NULL(openxr_api); + + // Combine all vertex and index buffers into one. + PackedVector3Array vertices; + PackedInt32Array indices; + + uint64_t vertice_count = 0; + uint64_t index_count = 0; + + for (uint32_t i = 0; i < mesh_count; i++) { + vertice_count += mesh_data[i].vertices.size(); + index_count += mesh_data[i].indices.size(); + } + + vertices.resize(vertice_count); + indices.resize(index_count); + uint64_t offset = 0; + + Vector3 *v_out = vertices.ptrw(); + int32_t *i_out = indices.ptrw(); + + for (uint32_t i = 0; i < mesh_count; i++) { + const XrVector2f *v_in = mesh_data[i].vertices.ptr(); + for (uint32_t j = 0; j < mesh_data[i].vertices.size(); j++) { + v_out->x = v_in->x; + v_out->y = v_in->y; + v_out->z = float(i); // We store our view in our Z component, our shader will filter the right faces out. + v_out++; + v_in++; + } + const uint32_t *i_in = mesh_data[i].indices.ptr(); + for (uint32_t j = 0; j < mesh_data[i].indices.size(); j++) { + *i_out = offset + *i_in; + i_out++; + i_in++; + } + + offset += mesh_data[i].vertices.size(); + } + + // Update our mesh. + Array arr; + arr.resize(RS::ARRAY_MAX); + arr[RS::ARRAY_VERTEX] = vertices; + arr[RS::ARRAY_INDEX] = indices; + + rendering_server->mesh_clear(mesh); + rendering_server->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr); + rendering_server->mesh_surface_set_material(mesh, 0, material); + + // Set no longer dirty. + is_dirty = false; + } +} diff --git a/modules/openxr/extensions/openxr_visibility_mask_extension.h b/modules/openxr/extensions/openxr_visibility_mask_extension.h new file mode 100644 index 0000000000..ee80dd02ff --- /dev/null +++ b/modules/openxr/extensions/openxr_visibility_mask_extension.h @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* openxr_visibility_mask_extension.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef OPENXR_VISIBILITY_MASK_EXTENSION_H +#define OPENXR_VISIBILITY_MASK_EXTENSION_H + +#include "../util.h" + +#include "core/templates/vector.h" +#include "openxr_extension_wrapper.h" +#include "scene/resources/mesh.h" + +// The OpenXR visibility mask extension provides a mesh for each eye that +// can be used as a mask to determine which part of our rendered result +// is actually visible to the user. Due to lens distortion the edges of +// the rendered image are never used in the final result output on the HMD. +// +// Blacking out this are of the render result can remove a fair amount of +// overhead in rendering part of the screen that is unused. +// +// https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_KHR_visibility_mask + +class OpenXRVisibilityMaskExtension : public OpenXRExtensionWrapper { +public: + static OpenXRVisibilityMaskExtension *get_singleton(); + + OpenXRVisibilityMaskExtension(); + virtual ~OpenXRVisibilityMaskExtension() override; + + virtual HashMap<String, bool *> get_requested_extensions() override; + + virtual void on_instance_created(const XrInstance p_instance) override; + + virtual void on_session_created(const XrSession p_instance) override; + virtual void on_session_destroyed() override; + + virtual void on_pre_render() override; + virtual bool on_event_polled(const XrEventDataBuffer &event) override; + + bool is_available(); + RID get_mesh(); + +private: + static OpenXRVisibilityMaskExtension *singleton; + + bool available = false; + bool is_dirty = false; + + RID shader; + RID material; + RID mesh; + + struct MeshData { + Vector<XrVector2f> vertices; + Vector<uint32_t> indices; + }; + + uint32_t mesh_count = 0; + MeshData mesh_data[4]; + + void _update_mesh_data(uint32_t p_view); + void _update_mesh(); + + // OpenXR API call wrappers + EXT_PROTO_XRRESULT_FUNC5(xrGetVisibilityMaskKHR, (XrSession), session, (XrViewConfigurationType), viewConfigurationType, (uint32_t), viewIndex, (XrVisibilityMaskTypeKHR), visibilityMaskType, (XrVisibilityMaskKHR *), visibilityMask); +}; + +#endif // OPENXR_VISIBILITY_MASK_EXTENSION_H diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 98e5484157..db5c04fdef 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; @@ -1459,6 +1459,10 @@ void OpenXRAPI::set_form_factor(XrFormFactor p_form_factor) { form_factor = p_form_factor; } +uint32_t OpenXRAPI::get_view_count() { + return view_count; +} + void OpenXRAPI::set_view_configuration(XrViewConfigurationType p_view_configuration) { ERR_FAIL_COND(is_initialized()); @@ -1914,15 +1918,6 @@ bool OpenXRAPI::poll_events() { // We probably didn't poll fast enough, just output warning WARN_PRINT("OpenXR EVENT: " + itos(event->lostEventCount) + " event data lost!"); } break; - case XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR: { - // XrEventDataVisibilityMaskChangedKHR *event = (XrEventDataVisibilityMaskChangedKHR *)&runtimeEvent; - - // TODO implement this in the future, we should call xrGetVisibilityMaskKHR to obtain a mask, - // this will allow us to prevent rendering the part of our view which is never displayed giving us - // a decent performance improvement. - - print_verbose("OpenXR EVENT: STUB: visibility mask changed"); - } break; case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: { XrEventDataInstanceLossPending *event = (XrEventDataInstanceLossPending *)&runtimeEvent; diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index 81e9deb7ab..88455379b9 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -434,6 +434,7 @@ public: void set_form_factor(XrFormFactor p_form_factor); XrFormFactor get_form_factor() const { return form_factor; } + uint32_t get_view_count(); void set_view_configuration(XrViewConfigurationType p_view_configuration); XrViewConfigurationType get_view_configuration() const { return view_configuration; } diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index cce9c09361..73ac529537 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -36,7 +36,8 @@ #include "extensions/openxr_eye_gaze_interaction.h" #include "extensions/openxr_hand_interaction_extension.h" -#include "thirdparty/openxr/include/openxr/openxr.h" + +#include <openxr/openxr.h> void OpenXRInterface::_bind_methods() { // lifecycle signals diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp index 85514737f2..61c294eff5 100644 --- a/modules/openxr/register_types.cpp +++ b/modules/openxr/register_types.cpp @@ -44,6 +44,7 @@ #include "scene/openxr_composition_layer_equirect.h" #include "scene/openxr_composition_layer_quad.h" #include "scene/openxr_hand.h" +#include "scene/openxr_visibility_mask.h" #include "extensions/openxr_composition_layer_depth_extension.h" #include "extensions/openxr_composition_layer_extension.h" @@ -57,8 +58,10 @@ #include "extensions/openxr_local_floor_extension.h" #include "extensions/openxr_meta_controller_extension.h" #include "extensions/openxr_ml2_controller_extension.h" +#include "extensions/openxr_mxink_extension.h" #include "extensions/openxr_palm_pose_extension.h" #include "extensions/openxr_pico_controller_extension.h" +#include "extensions/openxr_visibility_mask_extension.h" #include "extensions/openxr_wmr_controller_extension.h" #ifdef TOOLS_ENABLED @@ -126,6 +129,8 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { OpenXRAPI::register_extension_wrapper(memnew(OpenXRMetaControllerExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXREyeGazeInteractionExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRHandInteractionExtension)); + OpenXRAPI::register_extension_wrapper(memnew(OpenXRMxInkExtension)); + OpenXRAPI::register_extension_wrapper(memnew(OpenXRVisibilityMaskExtension)); // register gated extensions if (GLOBAL_GET("xr/openxr/extensions/hand_tracking")) { @@ -179,6 +184,8 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(OpenXRHand); + GDREGISTER_CLASS(OpenXRVisibilityMask); + XRServer *xr_server = XRServer::get_singleton(); if (xr_server) { openxr_interface.instantiate(); diff --git a/modules/openxr/scene/openxr_visibility_mask.cpp b/modules/openxr/scene/openxr_visibility_mask.cpp new file mode 100644 index 0000000000..7214fac327 --- /dev/null +++ b/modules/openxr/scene/openxr_visibility_mask.cpp @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* openxr_visibility_mask.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "openxr_visibility_mask.h" + +#include "../extensions/openxr_visibility_mask_extension.h" +#include "../openxr_interface.h" +#include "scene/3d/xr_nodes.h" + +void OpenXRVisibilityMask::_bind_methods() { +} + +void OpenXRVisibilityMask::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + OpenXRVisibilityMaskExtension *vis_mask_ext = OpenXRVisibilityMaskExtension::get_singleton(); + if (vis_mask_ext && vis_mask_ext->is_available()) { + set_base(vis_mask_ext->get_mesh()); + } + } break; + case NOTIFICATION_EXIT_TREE: { + set_base(RID()); + } break; + } +} + +void OpenXRVisibilityMask::_on_openxr_session_begun() { + if (is_inside_tree()) { + OpenXRVisibilityMaskExtension *vis_mask_ext = OpenXRVisibilityMaskExtension::get_singleton(); + if (vis_mask_ext && vis_mask_ext->is_available()) { + set_base(vis_mask_ext->get_mesh()); + } + } +} + +void OpenXRVisibilityMask::_on_openxr_session_stopping() { + set_base(RID()); +} + +PackedStringArray OpenXRVisibilityMask::get_configuration_warnings() const { + PackedStringArray warnings = VisualInstance3D::get_configuration_warnings(); + + if (is_visible() && is_inside_tree()) { + XRCamera3D *camera = Object::cast_to<XRCamera3D>(get_parent()); + if (camera == nullptr) { + warnings.push_back(RTR("OpenXR visibility mask must have an XRCamera3D node as their parent.")); + } + } + + return warnings; +} + +AABB OpenXRVisibilityMask::get_aabb() const { + AABB ret; + + // Make sure it's always visible, this is positioned through its shader. + ret.position = Vector3(-1000.0, -1000.0, -1000.0); + ret.size = Vector3(2000.0, 2000.0, 2000.0); + + return ret; +} + +OpenXRVisibilityMask::OpenXRVisibilityMask() { + Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR"); + if (openxr_interface.is_valid()) { + openxr_interface->connect("session_begun", callable_mp(this, &OpenXRVisibilityMask::_on_openxr_session_begun)); + openxr_interface->connect("session_stopping", callable_mp(this, &OpenXRVisibilityMask::_on_openxr_session_stopping)); + } + + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(get_instance(), RS::SHADOW_CASTING_SETTING_OFF); +} + +OpenXRVisibilityMask::~OpenXRVisibilityMask() { + Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR"); + if (openxr_interface.is_valid()) { + openxr_interface->disconnect("session_begun", callable_mp(this, &OpenXRVisibilityMask::_on_openxr_session_begun)); + openxr_interface->disconnect("session_stopping", callable_mp(this, &OpenXRVisibilityMask::_on_openxr_session_stopping)); + } +} diff --git a/modules/openxr/scene/openxr_visibility_mask.h b/modules/openxr/scene/openxr_visibility_mask.h new file mode 100644 index 0000000000..0acb30fc0a --- /dev/null +++ b/modules/openxr/scene/openxr_visibility_mask.h @@ -0,0 +1,56 @@ +/**************************************************************************/ +/* openxr_visibility_mask.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef OPENXR_VISIBILITY_MASK_H +#define OPENXR_VISIBILITY_MASK_H + +#include "scene/3d/visual_instance_3d.h" + +class OpenXRVisibilityMask : public VisualInstance3D { + GDCLASS(OpenXRVisibilityMask, VisualInstance3D); + +protected: + static void _bind_methods(); + + void _notification(int p_what); + + void _on_openxr_session_begun(); + void _on_openxr_session_stopping(); + +public: + virtual PackedStringArray get_configuration_warnings() const override; + + virtual AABB get_aabb() const override; + + OpenXRVisibilityMask(); + ~OpenXRVisibilityMask(); +}; + +#endif // OPENXR_VISIBILITY_MASK_H diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index fa39ccb546..111cd48405 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -39,8 +39,6 @@ import android.content.res.Configuration import android.content.res.Resources import android.graphics.Color import android.hardware.Sensor -import android.hardware.SensorEvent -import android.hardware.SensorEventListener import android.hardware.SensorManager import android.os.* import android.util.Log @@ -53,6 +51,7 @@ import androidx.core.view.WindowInsetsAnimationCompat import androidx.core.view.WindowInsetsCompat import com.google.android.vending.expansion.downloader.* import org.godotengine.godot.input.GodotEditText +import org.godotengine.godot.input.GodotInputHandler import org.godotengine.godot.io.directory.DirectoryAccessHandler import org.godotengine.godot.io.file.FileAccessHandler import org.godotengine.godot.plugin.GodotPluginRegistry @@ -73,6 +72,7 @@ import java.io.InputStream import java.lang.Exception import java.security.MessageDigest import java.util.* +import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference /** @@ -81,7 +81,7 @@ import java.util.concurrent.atomic.AtomicReference * Can be hosted by [Activity], [Fragment] or [Service] android components, so long as its * lifecycle methods are properly invoked. */ -class Godot(private val context: Context) : SensorEventListener { +class Godot(private val context: Context) { private companion object { private val TAG = Godot::class.java.simpleName @@ -99,15 +99,23 @@ class Godot(private val context: Context) : SensorEventListener { private val pluginRegistry: GodotPluginRegistry by lazy { GodotPluginRegistry.getPluginRegistry() } + + private val accelerometer_enabled = AtomicBoolean(false) private val mAccelerometer: Sensor? by lazy { mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) } + + private val gravity_enabled = AtomicBoolean(false) private val mGravity: Sensor? by lazy { mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) } + + private val magnetometer_enabled = AtomicBoolean(false) private val mMagnetometer: Sensor? by lazy { mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) } + + private val gyroscope_enabled = AtomicBoolean(false) private val mGyroscope: Sensor? by lazy { mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) } @@ -127,6 +135,7 @@ class Godot(private val context: Context) : SensorEventListener { val fileAccessHandler = FileAccessHandler(context) val netUtils = GodotNetUtils(context) private val commandLineFileParser = CommandLineFileParser() + private val godotInputHandler = GodotInputHandler(context, this) /** * Task to run when the engine terminates. @@ -154,6 +163,17 @@ class Godot(private val context: Context) : SensorEventListener { private var renderViewInitialized = false private var primaryHost: GodotHost? = null + /** + * Tracks whether we're in the RESUMED lifecycle state. + * See [onResume] and [onPause] + */ + private var resumed = false + + /** + * Tracks whether [onGodotSetupCompleted] fired. + */ + private val godotMainLoopStarted = AtomicBoolean(false) + var io: GodotIO? = null private var commandLine : MutableList<String> = ArrayList<String>() @@ -416,10 +436,10 @@ class Godot(private val context: Context) : SensorEventListener { if (!meetsVulkanRequirements(activity.packageManager)) { throw IllegalStateException(activity.getString(R.string.error_missing_vulkan_requirements_message)) } - GodotVulkanRenderView(host, this) + GodotVulkanRenderView(host, this, godotInputHandler) } else { // Fallback to openGl - GodotGLRenderView(host, this, xrMode, useDebugOpengl) + GodotGLRenderView(host, this, godotInputHandler, xrMode, useDebugOpengl) } if (host == primaryHost) { @@ -520,23 +540,13 @@ class Godot(private val context: Context) : SensorEventListener { fun onResume(host: GodotHost) { Log.v(TAG, "OnResume: $host") + resumed = true if (host != primaryHost) { return } renderView?.onActivityResumed() - if (mAccelerometer != null) { - mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME) - } - if (mGravity != null) { - mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME) - } - if (mMagnetometer != null) { - mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME) - } - if (mGyroscope != null) { - mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME) - } + registerSensorsIfNeeded() if (useImmersive) { val window = requireActivity().window window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or @@ -551,14 +561,34 @@ class Godot(private val context: Context) : SensorEventListener { } } + private fun registerSensorsIfNeeded() { + if (!resumed || !godotMainLoopStarted.get()) { + return + } + + if (accelerometer_enabled.get() && mAccelerometer != null) { + mSensorManager.registerListener(godotInputHandler, mAccelerometer, SensorManager.SENSOR_DELAY_GAME) + } + if (gravity_enabled.get() && mGravity != null) { + mSensorManager.registerListener(godotInputHandler, mGravity, SensorManager.SENSOR_DELAY_GAME) + } + if (magnetometer_enabled.get() && mMagnetometer != null) { + mSensorManager.registerListener(godotInputHandler, mMagnetometer, SensorManager.SENSOR_DELAY_GAME) + } + if (gyroscope_enabled.get() && mGyroscope != null) { + mSensorManager.registerListener(godotInputHandler, mGyroscope, SensorManager.SENSOR_DELAY_GAME) + } + } + fun onPause(host: GodotHost) { Log.v(TAG, "OnPause: $host") + resumed = false if (host != primaryHost) { return } renderView?.onActivityPaused() - mSensorManager.unregisterListener(this) + mSensorManager.unregisterListener(godotInputHandler) for (plugin in pluginRegistry.allPlugins) { plugin.onMainPause() } @@ -659,6 +689,16 @@ class Godot(private val context: Context) : SensorEventListener { */ private fun onGodotMainLoopStarted() { Log.v(TAG, "OnGodotMainLoopStarted") + godotMainLoopStarted.set(true) + + accelerometer_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_accelerometer"))) + gravity_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_gravity"))) + gyroscope_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_gyroscope"))) + magnetometer_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_magnetometer"))) + + runOnUiThread { + registerSensorsIfNeeded() + } for (plugin in pluginRegistry.allPlugins) { plugin.onGodotMainLoopStarted() @@ -858,77 +898,6 @@ class Godot(private val context: Context) : SensorEventListener { } } - private fun getRotatedValues(values: FloatArray?): FloatArray? { - if (values == null || values.size != 3) { - return null - } - val rotatedValues = FloatArray(3) - when (windowManager.defaultDisplay.rotation) { - Surface.ROTATION_0 -> { - rotatedValues[0] = values[0] - rotatedValues[1] = values[1] - rotatedValues[2] = values[2] - } - Surface.ROTATION_90 -> { - rotatedValues[0] = -values[1] - rotatedValues[1] = values[0] - rotatedValues[2] = values[2] - } - Surface.ROTATION_180 -> { - rotatedValues[0] = -values[0] - rotatedValues[1] = -values[1] - rotatedValues[2] = values[2] - } - Surface.ROTATION_270 -> { - rotatedValues[0] = values[1] - rotatedValues[1] = -values[0] - rotatedValues[2] = values[2] - } - } - return rotatedValues - } - - override fun onSensorChanged(event: SensorEvent) { - if (renderView == null) { - return - } - - val rotatedValues = getRotatedValues(event.values) - - when (event.sensor.type) { - Sensor.TYPE_ACCELEROMETER -> { - rotatedValues?.let { - renderView?.queueOnRenderThread { - GodotLib.accelerometer(-it[0], -it[1], -it[2]) - } - } - } - Sensor.TYPE_GRAVITY -> { - rotatedValues?.let { - renderView?.queueOnRenderThread { - GodotLib.gravity(-it[0], -it[1], -it[2]) - } - } - } - Sensor.TYPE_MAGNETIC_FIELD -> { - rotatedValues?.let { - renderView?.queueOnRenderThread { - GodotLib.magnetometer(-it[0], -it[1], -it[2]) - } - } - } - Sensor.TYPE_GYROSCOPE -> { - rotatedValues?.let { - renderView?.queueOnRenderThread { - GodotLib.gyroscope(it[0], it[1], it[2]) - } - } - } - } - } - - override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} - /** * Used by the native code (java_godot_wrapper.h) to vibrate the device. * @param durationMs @@ -1063,7 +1032,7 @@ class Godot(private val context: Context) : SensorEventListener { @Keep private fun initInputDevices() { - renderView?.initInputDevices() + godotInputHandler.initInputDevices() } @Keep diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index 7fbdb34047..15a811ce83 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -83,12 +83,12 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { private final GodotRenderer godotRenderer; private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>(); - public GodotGLRenderView(GodotHost host, Godot godot, XRMode xrMode, boolean useDebugOpengl) { + public GodotGLRenderView(GodotHost host, Godot godot, GodotInputHandler inputHandler, XRMode xrMode, boolean useDebugOpengl) { super(host.getActivity()); this.host = host; this.godot = godot; - this.inputHandler = new GodotInputHandler(this); + this.inputHandler = inputHandler; this.godotRenderer = new GodotRenderer(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT)); @@ -102,11 +102,6 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { } @Override - public void initInputDevices() { - this.inputHandler.initInputDevices(); - } - - @Override public void queueOnRenderThread(Runnable event) { queueEvent(event); } @@ -145,11 +140,6 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { } @Override - public void onBackPressed() { - godot.onBackPressed(); - } - - @Override public GodotInputHandler getInputHandler() { return inputHandler; } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java index 19ec0fd1a4..30821eaa8e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -37,8 +37,6 @@ import android.view.SurfaceView; public interface GodotRenderView { SurfaceView getView(); - void initInputDevices(); - /** * Starts the thread that will drive Godot's rendering. */ @@ -59,8 +57,6 @@ public interface GodotRenderView { void onActivityDestroyed(); - void onBackPressed(); - GodotInputHandler getInputHandler(); void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java index f4411ddf2c..d5b05913d8 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -57,12 +57,12 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { private final VkRenderer mRenderer; private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>(); - public GodotVulkanRenderView(GodotHost host, Godot godot) { + public GodotVulkanRenderView(GodotHost host, Godot godot, GodotInputHandler inputHandler) { super(host.getActivity()); this.host = host; this.godot = godot; - mInputHandler = new GodotInputHandler(this); + mInputHandler = inputHandler; mRenderer = new VkRenderer(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT)); @@ -81,11 +81,6 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { } @Override - public void initInputDevices() { - mInputHandler.initInputDevices(); - } - - @Override public void queueOnRenderThread(Runnable event) { queueOnVkThread(event); } @@ -124,11 +119,6 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { } @Override - public void onBackPressed() { - godot.onBackPressed(); - } - - @Override public GodotInputHandler getInputHandler() { return mInputHandler; } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 273774a33d..fb41cd00c0 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -32,10 +32,14 @@ package org.godotengine.godot.input; import static org.godotengine.godot.utils.GLUtils.DEBUG; +import org.godotengine.godot.Godot; import org.godotengine.godot.GodotLib; import org.godotengine.godot.GodotRenderView; import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; import android.hardware.input.InputManager; import android.os.Build; import android.util.Log; @@ -46,6 +50,10 @@ import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ScaleGestureDetector; +import android.view.Surface; +import android.view.WindowManager; + +import androidx.annotation.NonNull; import java.util.Collections; import java.util.HashSet; @@ -54,7 +62,7 @@ import java.util.Set; /** * Handles input related events for the {@link GodotRenderView} view. */ -public class GodotInputHandler implements InputManager.InputDeviceListener { +public class GodotInputHandler implements InputManager.InputDeviceListener, SensorEventListener { private static final String TAG = GodotInputHandler.class.getSimpleName(); private static final int ROTARY_INPUT_VERTICAL_AXIS = 1; @@ -64,8 +72,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4); private final HashSet<Integer> mHardwareKeyboardIds = new HashSet<>(); - private final GodotRenderView mRenderView; + private final Godot godot; private final InputManager mInputManager; + private final WindowManager windowManager; private final GestureDetector gestureDetector; private final ScaleGestureDetector scaleGestureDetector; private final GodotGestureHandler godotGestureHandler; @@ -77,12 +86,13 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS; - public GodotInputHandler(GodotRenderView godotView) { - final Context context = godotView.getView().getContext(); - mRenderView = godotView; + public GodotInputHandler(Context context, Godot godot) { + this.godot = godot; mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE); mInputManager.registerInputDeviceListener(this, null); + windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); + this.godotGestureHandler = new GodotGestureHandler(this); this.gestureDetector = new GestureDetector(context, godotGestureHandler); this.gestureDetector.setIsLongpressEnabled(false); @@ -174,7 +184,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { public boolean onKeyDown(final int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - mRenderView.onBackPressed(); + godot.onBackPressed(); // press 'back' button should not terminate program //normal handle 'back' event in game logic return true; @@ -507,7 +517,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return handleTouchEvent(event, eventActionOverride, doubleTap); } - private static float getEventTiltX(MotionEvent event) { + static float getEventTiltX(MotionEvent event) { // Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise. final float orientation = event.getOrientation(); @@ -520,7 +530,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return (float)-Math.sin(orientation) * tiltMult; } - private static float getEventTiltY(MotionEvent event) { + static float getEventTiltY(MotionEvent event) { // Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise. final float orientation = event.getOrientation(); @@ -579,6 +589,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) { + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return false; + } + // Fix the buttonsMask switch (eventAction) { case MotionEvent.ACTION_CANCEL: @@ -594,7 +609,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { break; } - final int updatedButtonsMask = buttonsMask; // We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically // follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate // stream of events to the engine. @@ -607,11 +621,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { case MotionEvent.ACTION_HOVER_MOVE: case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_SCROLL: { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY)); - } else { - GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY); - } + runnable.setMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY); + dispatchInputEventRunnable(runnable); return true; } } @@ -627,22 +638,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) { - final int pointerCount = event.getPointerCount(); - if (pointerCount == 0) { + if (event.getPointerCount() == 0) { return true; } - final float[] positions = new float[pointerCount * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc... - - for (int i = 0; i < pointerCount; i++) { - positions[i * 6 + 0] = event.getPointerId(i); - positions[i * 6 + 1] = event.getX(i); - positions[i * 6 + 2] = event.getY(i); - positions[i * 6 + 3] = event.getPressure(i); - positions[i * 6 + 4] = getEventTiltX(event); - positions[i * 6 + 5] = getEventTiltY(event); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return false; } - final int actionPointerId = event.getPointerId(event.getActionIndex()); switch (eventActionOverride) { case MotionEvent.ACTION_DOWN: @@ -651,11 +654,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_DOWN: { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap)); - } else { - GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap); - } + runnable.setTouchEvent(event, eventActionOverride, doubleTap); + dispatchInputEventRunnable(runnable); return true; } } @@ -663,58 +663,128 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } void handleMagnifyEvent(float x, float y, float factor) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.magnify(x, y, factor)); - } else { - GodotLib.magnify(x, y, factor); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setMagnifyEvent(x, y, factor); + dispatchInputEventRunnable(runnable); } void handlePanEvent(float x, float y, float deltaX, float deltaY) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.pan(x, y, deltaX, deltaY)); - } else { - GodotLib.pan(x, y, deltaX, deltaY); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setPanEvent(x, y, deltaX, deltaY); + dispatchInputEventRunnable(runnable); } private void handleJoystickButtonEvent(int device, int button, boolean pressed) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.joybutton(device, button, pressed)); - } else { - GodotLib.joybutton(device, button, pressed); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setJoystickButtonEvent(device, button, pressed); + dispatchInputEventRunnable(runnable); } private void handleJoystickAxisEvent(int device, int axis, float value) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.joyaxis(device, axis, value)); - } else { - GodotLib.joyaxis(device, axis, value); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setJoystickAxisEvent(device, axis, value); + dispatchInputEventRunnable(runnable); } private void handleJoystickHatEvent(int device, int hatX, int hatY) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.joyhat(device, hatX, hatY)); - } else { - GodotLib.joyhat(device, hatX, hatY); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setJoystickHatEvent(device, hatX, hatY); + dispatchInputEventRunnable(runnable); } private void handleJoystickConnectionChangedEvent(int device, boolean connected, String name) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.joyconnectionchanged(device, connected, name)); - } else { - GodotLib.joyconnectionchanged(device, connected, name); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setJoystickConnectionChangedEvent(device, connected, name); + dispatchInputEventRunnable(runnable); } void handleKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) { + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; + } + + runnable.setKeyEvent(physicalKeycode, unicode, keyLabel, pressed, echo); + dispatchInputEventRunnable(runnable); + } + + private void dispatchInputEventRunnable(@NonNull InputEventRunnable runnable) { if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo)); + godot.runOnRenderThread(runnable); } else { - GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo); + runnable.run(); + } + } + + @Override + public void onSensorChanged(SensorEvent event) { + final float[] values = event.values; + if (values == null || values.length != 3) { + return; } + + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; + } + + float rotatedValue0 = 0f; + float rotatedValue1 = 0f; + float rotatedValue2 = 0f; + switch (windowManager.getDefaultDisplay().getRotation()) { + case Surface.ROTATION_0: + rotatedValue0 = values[0]; + rotatedValue1 = values[1]; + rotatedValue2 = values[2]; + break; + + case Surface.ROTATION_90: + rotatedValue0 = -values[1]; + rotatedValue1 = values[0]; + rotatedValue2 = values[2]; + break; + + case Surface.ROTATION_180: + rotatedValue0 = -values[0]; + rotatedValue1 = -values[1]; + rotatedValue2 = values[2]; + break; + + case Surface.ROTATION_270: + rotatedValue0 = values[1]; + rotatedValue1 = -values[0]; + rotatedValue2 = values[2]; + break; + } + + runnable.setSensorEvent(event.sensor.getType(), rotatedValue0, rotatedValue1, rotatedValue2); + godot.runOnRenderThread(runnable); } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) {} } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java new file mode 100644 index 0000000000..a282791b2e --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java @@ -0,0 +1,353 @@ +/**************************************************************************/ +/* InputEventRunnable.java */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +package org.godotengine.godot.input; + +import org.godotengine.godot.GodotLib; + +import android.hardware.Sensor; +import android.util.Log; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Pools; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Used to dispatch input events. + * + * This is a specialized version of @{@link Runnable} which allows to allocate a finite pool of + * objects for input events dispatching, thus avoid the creation (and garbage collection) of + * spurious @{@link Runnable} objects. + */ +final class InputEventRunnable implements Runnable { + private static final String TAG = InputEventRunnable.class.getSimpleName(); + + private static final int MAX_TOUCH_POINTER_COUNT = 10; // assuming 10 fingers as max supported concurrent touch pointers + + private static final Pools.Pool<InputEventRunnable> POOL = new Pools.Pool<>() { + private static final int MAX_POOL_SIZE = 120 * 10; // up to 120Hz input events rate for up to 5 secs (ANR limit) * 2 + + private final ArrayBlockingQueue<InputEventRunnable> queue = new ArrayBlockingQueue<>(MAX_POOL_SIZE); + private final AtomicInteger createdCount = new AtomicInteger(); + + @Nullable + @Override + public InputEventRunnable acquire() { + InputEventRunnable instance = queue.poll(); + if (instance == null) { + int creationCount = createdCount.incrementAndGet(); + if (creationCount <= MAX_POOL_SIZE) { + instance = new InputEventRunnable(creationCount - 1); + } + } + + return instance; + } + + @Override + public boolean release(@NonNull InputEventRunnable instance) { + return queue.offer(instance); + } + }; + + @Nullable + static InputEventRunnable obtain() { + InputEventRunnable runnable = POOL.acquire(); + if (runnable == null) { + Log.w(TAG, "Input event pool is at capacity"); + } + return runnable; + } + + /** + * Used to track when this instance was created and added to the pool. Primarily used for + * debug purposes. + */ + private final int creationRank; + + private InputEventRunnable(int creationRank) { + this.creationRank = creationRank; + } + + /** + * Set of supported input events. + */ + private enum EventType { + MOUSE, + TOUCH, + MAGNIFY, + PAN, + JOYSTICK_BUTTON, + JOYSTICK_AXIS, + JOYSTICK_HAT, + JOYSTICK_CONNECTION_CHANGED, + KEY, + SENSOR + } + + private EventType currentEventType = null; + + // common event fields + private float eventX; + private float eventY; + private float eventDeltaX; + private float eventDeltaY; + private boolean eventPressed; + + // common touch / mouse fields + private int eventAction; + private boolean doubleTap; + + // Mouse event fields and setter + private int buttonsMask; + private boolean sourceMouseRelative; + private float pressure; + private float tiltX; + private float tiltY; + void setMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) { + this.currentEventType = EventType.MOUSE; + this.eventAction = eventAction; + this.buttonsMask = buttonsMask; + this.eventX = x; + this.eventY = y; + this.eventDeltaX = deltaX; + this.eventDeltaY = deltaY; + this.doubleTap = doubleClick; + this.sourceMouseRelative = sourceMouseRelative; + this.pressure = pressure; + this.tiltX = tiltX; + this.tiltY = tiltY; + } + + // Touch event fields and setter + private int actionPointerId; + private int pointerCount; + private final float[] positions = new float[MAX_TOUCH_POINTER_COUNT * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc... + void setTouchEvent(MotionEvent event, int eventAction, boolean doubleTap) { + this.currentEventType = EventType.TOUCH; + this.eventAction = eventAction; + this.doubleTap = doubleTap; + this.actionPointerId = event.getPointerId(event.getActionIndex()); + this.pointerCount = Math.min(event.getPointerCount(), MAX_TOUCH_POINTER_COUNT); + for (int i = 0; i < pointerCount; i++) { + positions[i * 6 + 0] = event.getPointerId(i); + positions[i * 6 + 1] = event.getX(i); + positions[i * 6 + 2] = event.getY(i); + positions[i * 6 + 3] = event.getPressure(i); + positions[i * 6 + 4] = GodotInputHandler.getEventTiltX(event); + positions[i * 6 + 5] = GodotInputHandler.getEventTiltY(event); + } + } + + // Magnify event fields and setter + private float magnifyFactor; + void setMagnifyEvent(float x, float y, float factor) { + this.currentEventType = EventType.MAGNIFY; + this.eventX = x; + this.eventY = y; + this.magnifyFactor = factor; + } + + // Pan event setter + void setPanEvent(float x, float y, float deltaX, float deltaY) { + this.currentEventType = EventType.PAN; + this.eventX = x; + this.eventY = y; + this.eventDeltaX = deltaX; + this.eventDeltaY = deltaY; + } + + // common joystick field + private int joystickDevice; + + // Joystick button event fields and setter + private int button; + void setJoystickButtonEvent(int device, int button, boolean pressed) { + this.currentEventType = EventType.JOYSTICK_BUTTON; + this.joystickDevice = device; + this.button = button; + this.eventPressed = pressed; + } + + // Joystick axis event fields and setter + private int axis; + private float value; + void setJoystickAxisEvent(int device, int axis, float value) { + this.currentEventType = EventType.JOYSTICK_AXIS; + this.joystickDevice = device; + this.axis = axis; + this.value = value; + } + + // Joystick hat event fields and setter + private int hatX; + private int hatY; + void setJoystickHatEvent(int device, int hatX, int hatY) { + this.currentEventType = EventType.JOYSTICK_HAT; + this.joystickDevice = device; + this.hatX = hatX; + this.hatY = hatY; + } + + // Joystick connection changed event fields and setter + private boolean connected; + private String joystickName; + void setJoystickConnectionChangedEvent(int device, boolean connected, String name) { + this.currentEventType = EventType.JOYSTICK_CONNECTION_CHANGED; + this.joystickDevice = device; + this.connected = connected; + this.joystickName = name; + } + + // Key event fields and setter + private int physicalKeycode; + private int unicode; + private int keyLabel; + private boolean echo; + void setKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) { + this.currentEventType = EventType.KEY; + this.physicalKeycode = physicalKeycode; + this.unicode = unicode; + this.keyLabel = keyLabel; + this.eventPressed = pressed; + this.echo = echo; + } + + // Sensor event fields and setter + private int sensorType; + private float rotatedValue0; + private float rotatedValue1; + private float rotatedValue2; + void setSensorEvent(int sensorType, float rotatedValue0, float rotatedValue1, float rotatedValue2) { + this.currentEventType = EventType.SENSOR; + this.sensorType = sensorType; + this.rotatedValue0 = rotatedValue0; + this.rotatedValue1 = rotatedValue1; + this.rotatedValue2 = rotatedValue2; + } + + @Override + public void run() { + try { + if (currentEventType == null) { + Log.w(TAG, "Invalid event type"); + return; + } + + switch (currentEventType) { + case MOUSE: + GodotLib.dispatchMouseEvent( + eventAction, + buttonsMask, + eventX, + eventY, + eventDeltaX, + eventDeltaY, + doubleTap, + sourceMouseRelative, + pressure, + tiltX, + tiltY); + break; + + case TOUCH: + GodotLib.dispatchTouchEvent( + eventAction, + actionPointerId, + pointerCount, + positions, + doubleTap); + break; + + case MAGNIFY: + GodotLib.magnify(eventX, eventY, magnifyFactor); + break; + + case PAN: + GodotLib.pan(eventX, eventY, eventDeltaX, eventDeltaY); + break; + + case JOYSTICK_BUTTON: + GodotLib.joybutton(joystickDevice, button, eventPressed); + break; + + case JOYSTICK_AXIS: + GodotLib.joyaxis(joystickDevice, axis, value); + break; + + case JOYSTICK_HAT: + GodotLib.joyhat(joystickDevice, hatX, hatY); + break; + + case JOYSTICK_CONNECTION_CHANGED: + GodotLib.joyconnectionchanged(joystickDevice, connected, joystickName); + break; + + case KEY: + GodotLib.key(physicalKeycode, unicode, keyLabel, eventPressed, echo); + break; + + case SENSOR: + switch (sensorType) { + case Sensor.TYPE_ACCELEROMETER: + GodotLib.accelerometer(-rotatedValue0, -rotatedValue1, -rotatedValue2); + break; + + case Sensor.TYPE_GRAVITY: + GodotLib.gravity(-rotatedValue0, -rotatedValue1, -rotatedValue2); + break; + + case Sensor.TYPE_MAGNETIC_FIELD: + GodotLib.magnetometer(-rotatedValue0, -rotatedValue1, -rotatedValue2); + break; + + case Sensor.TYPE_GYROSCOPE: + GodotLib.gyroscope(rotatedValue0, rotatedValue1, rotatedValue2); + break; + } + break; + } + } finally { + recycle(); + } + } + + /** + * Release the current instance back to the pool + */ + private void recycle() { + currentEventType = null; + POOL.release(this); + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java index 711bca02e7..8976dd65db 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java @@ -43,6 +43,7 @@ import androidx.annotation.Nullable; import java.lang.reflect.Constructor; import java.util.Collection; +import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -82,6 +83,9 @@ public final class GodotPluginRegistry { * Retrieve the full set of loaded plugins. */ public Collection<GodotPlugin> getAllPlugins() { + if (registry.isEmpty()) { + return Collections.emptyList(); + } return registry.values(); } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index ec42e5bbd8..a4a425f685 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -51,6 +51,7 @@ #include "core/config/project_settings.h" #include "core/input/input.h" #include "main/main.h" +#include "servers/xr_server.h" #ifdef TOOLS_ENABLED #include "editor/editor_settings.h" @@ -270,7 +271,18 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, } if (step.get() == STEP_SHOW_LOGO) { - Main::setup_boot_logo(); + bool xr_enabled; + if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) { + xr_enabled = GLOBAL_GET("xr/shaders/enabled"); + } else { + xr_enabled = XRServer::get_xr_mode() == XRServer::XRMODE_ON; + } + // Unlike PCVR, there's no additional 2D screen onto which to render the boot logo, + // so we skip this step if xr is enabled. + if (!xr_enabled) { + Main::setup_boot_logo(); + } + step.increment(); return true; } diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 70ea4b09c1..91bf7b48a6 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -213,25 +213,27 @@ bool GodotJavaWrapper::has_get_clipboard() { } String GodotJavaWrapper::get_clipboard() { + String clipboard; if (_get_clipboard) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, String()); jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard); - return jstring_to_string(s, env); - } else { - return String(); + clipboard = jstring_to_string(s, env); + env->DeleteLocalRef(s); } + return clipboard; } String GodotJavaWrapper::get_input_fallback_mapping() { + String input_fallback_mapping; if (_get_input_fallback_mapping) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, String()); jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping); - return jstring_to_string(fallback_mapping, env); - } else { - return String(); + input_fallback_mapping = jstring_to_string(fallback_mapping, env); + env->DeleteLocalRef(fallback_mapping); } + return input_fallback_mapping; } bool GodotJavaWrapper::has_set_clipboard() { @@ -324,14 +326,15 @@ Vector<String> GodotJavaWrapper::get_gdextension_list_config_file() const { } String GodotJavaWrapper::get_ca_certificates() const { + String ca_certificates; if (_get_ca_certificates) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, String()); jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_ca_certificates); - return jstring_to_string(s, env); - } else { - return String(); + ca_certificates = jstring_to_string(s, env); + env->DeleteLocalRef(s); } + return ca_certificates; } void GodotJavaWrapper::init_input_devices() { diff --git a/platform/ios/godot_app_delegate.h b/platform/ios/godot_app_delegate.h index a9bfcbb0b2..85dc6bb390 100644 --- a/platform/ios/godot_app_delegate.h +++ b/platform/ios/godot_app_delegate.h @@ -32,7 +32,7 @@ typedef NSObject<UIApplicationDelegate> ApplicationDelegateService; -@interface GodotApplicalitionDelegate : NSObject <UIApplicationDelegate> +@interface GodotApplicationDelegate : NSObject <UIApplicationDelegate> @property(class, readonly, strong) NSArray<ApplicationDelegateService *> *services; diff --git a/platform/ios/godot_app_delegate.m b/platform/ios/godot_app_delegate.m index 74e8705bc3..53e53cd0c6 100644 --- a/platform/ios/godot_app_delegate.m +++ b/platform/ios/godot_app_delegate.m @@ -32,11 +32,11 @@ #import "app_delegate.h" -@interface GodotApplicalitionDelegate () +@interface GodotApplicationDelegate () @end -@implementation GodotApplicalitionDelegate +@implementation GodotApplicationDelegate static NSMutableArray<ApplicationDelegateService *> *services = nil; diff --git a/platform/ios/main.m b/platform/ios/main.m index 33b1034d98..89a00c9ae9 100644 --- a/platform/ios/main.m +++ b/platform/ios/main.m @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { gargv = argv; @autoreleasepool { - NSString *className = NSStringFromClass([GodotApplicalitionDelegate class]); + NSString *className = NSStringFromClass([GodotApplicationDelegate class]); UIApplicationMain(argc, argv, nil, className); } return 0; diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp index 936adddda3..0032b898d2 100644 --- a/platform/linuxbsd/export/export_plugin.cpp +++ b/platform/linuxbsd/export/export_plugin.cpp @@ -61,6 +61,20 @@ Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportP } Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + String custom_debug = p_preset->get("custom_template/debug"); + String custom_release = p_preset->get("custom_template/release"); + String arch = p_preset->get("binary_format/architecture"); + + String template_path = p_debug ? custom_debug : custom_release; + template_path = template_path.strip_edges(); + if (!template_path.is_empty()) { + String exe_arch = _get_exe_arch(template_path); + if (arch != exe_arch) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Mismatching custom export template executable architecture, found \"%s\", expected \"%s\"."), exe_arch, arch)); + return ERR_CANT_CREATE; + } + } + bool export_as_zip = p_path.ends_with("zip"); String pkg_name; @@ -205,8 +219,76 @@ bool EditorExportPlatformLinuxBSD::is_executable(const String &p_path) const { return is_elf(p_path) || is_shebang(p_path); } +bool EditorExportPlatformLinuxBSD::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { + String err; + bool valid = EditorExportPlatformPC::has_valid_export_configuration(p_preset, err, r_missing_templates, p_debug); + + String custom_debug = p_preset->get("custom_template/debug").operator String().strip_edges(); + String custom_release = p_preset->get("custom_template/release").operator String().strip_edges(); + String arch = p_preset->get("binary_format/architecture"); + + if (!custom_debug.is_empty() && FileAccess::exists(custom_debug)) { + String exe_arch = _get_exe_arch(custom_debug); + if (arch != exe_arch) { + err += vformat(TTR("Mismatching custom debug export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n"; + } + } + if (!custom_release.is_empty() && FileAccess::exists(custom_release)) { + String exe_arch = _get_exe_arch(custom_release); + if (arch != exe_arch) { + err += vformat(TTR("Mismatching custom release export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n"; + } + } + + if (!err.is_empty()) { + r_error = err; + } + + return valid; +} + +String EditorExportPlatformLinuxBSD::_get_exe_arch(const String &p_path) const { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { + return "invalid"; + } + + // Read and check ELF magic number. + { + uint32_t magic = f->get_32(); + if (magic != 0x464c457f) { // 0x7F + "ELF" + return "invalid"; + } + } + + // Process header. + int64_t header_pos = f->get_position(); + f->seek(header_pos + 14); + uint16_t machine = f->get_16(); + f->close(); + + switch (machine) { + case 0x0003: + return "x86_32"; + case 0x003e: + return "x86_64"; + case 0x0014: + return "ppc32"; + case 0x0015: + return "ppc64"; + case 0x0028: + return "arm32"; + case 0x00b7: + return "arm64"; + case 0x00f3: + return "rv64"; + default: + return "unknown"; + } +} + Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) { - // Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data + // Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data. Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ_WRITE); if (f.is_null()) { @@ -214,7 +296,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int return ERR_CANT_OPEN; } - // Read and check ELF magic number + // Read and check ELF magic number. { uint32_t magic = f->get_32(); if (magic != 0x464c457f) { // 0x7F + "ELF" @@ -223,7 +305,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int } } - // Read program architecture bits from class field + // Read program architecture bits from class field. int bits = f->get_8() * 32; @@ -231,7 +313,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("32-bit executables cannot have embedded data >= 4 GiB.")); } - // Get info about the section header table + // Get info about the section header table. int64_t section_table_pos; int64_t section_header_size; @@ -249,13 +331,13 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int int num_sections = f->get_16(); int string_section_idx = f->get_16(); - // Load the strings table + // Load the strings table. uint8_t *strings; { - // Jump to the strings section header + // Jump to the strings section header. f->seek(section_table_pos + string_section_idx * section_header_size); - // Read strings data size and offset + // Read strings data size and offset. int64_t string_data_pos; int64_t string_data_size; if (bits == 32) { @@ -268,7 +350,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int string_data_size = f->get_64(); } - // Read strings data + // Read strings data. f->seek(string_data_pos); strings = (uint8_t *)memalloc(string_data_size); if (!strings) { @@ -277,7 +359,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int f->get_buffer(strings, string_data_size); } - // Search for the "pck" section + // Search for the "pck" section. bool found = false; for (int i = 0; i < num_sections; ++i) { diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h index 21bd81ed2f..bbc55b82ce 100644 --- a/platform/linuxbsd/export/export_plugin.h +++ b/platform/linuxbsd/export/export_plugin.h @@ -69,11 +69,13 @@ class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC { bool is_shebang(const String &p_path) const; Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path); + String _get_exe_arch(const String &p_path) const; public: virtual void get_export_options(List<ExportOption> *r_options) const override; virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override; + virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; virtual String get_template_file_name(const String &p_target, const String &p_arch) const override; virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override; diff --git a/platform/web/serve.py b/platform/web/serve.py index f0b0ec9622..4e1521449b 100755 --- a/platform/web/serve.py +++ b/platform/web/serve.py @@ -6,7 +6,7 @@ import os import socket import subprocess import sys -from http.server import HTTPServer, SimpleHTTPRequestHandler, test # type: ignore +from http.server import HTTPServer, SimpleHTTPRequestHandler from pathlib import Path @@ -38,12 +38,24 @@ def shell_open(url): def serve(root, port, run_browser): os.chdir(root) + address = ("", port) + httpd = DualStackServer(address, CORSRequestHandler) + + url = f"http://127.0.0.1:{port}" if run_browser: # Open the served page in the user's default browser. - print("Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this).") - shell_open(f"http://127.0.0.1:{port}") + print(f"Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this): {url}") + shell_open(url) + else: + print(f"Serving at: {url}") - test(CORSRequestHandler, DualStackServer, port=port) + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\nKeyboard interrupt received, stopping server.") + finally: + # Clean-up server + httpd.server_close() if __name__ == "__main__": diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index f0fe56a9c8..635e8326e2 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -250,6 +250,14 @@ void DisplayServerWindows::tts_stop() { tts->stop(); } +Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { + return _file_dialog_with_options_show(p_title, p_current_directory, String(), p_filename, p_show_hidden, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false); +} + +Error DisplayServerWindows::file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) { + return _file_dialog_with_options_show(p_title, p_current_directory, p_root, p_filename, p_show_hidden, p_mode, p_filters, p_options, p_callback, true); +} + // Silence warning due to a COM API weirdness. #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -377,22 +385,85 @@ public: #pragma GCC diagnostic pop #endif -Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { - return _file_dialog_with_options_show(p_title, p_current_directory, String(), p_filename, p_show_hidden, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false); +LRESULT CALLBACK WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton()); + if (ds_win) { + return ds_win->WndProcFileDialog(hWnd, uMsg, wParam, lParam); + } else { + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } } -Error DisplayServerWindows::file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) { - return _file_dialog_with_options_show(p_title, p_current_directory, p_root, p_filename, p_show_hidden, p_mode, p_filters, p_options, p_callback, true); +LRESULT DisplayServerWindows::WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + MutexLock lock(file_dialog_mutex); + if (file_dialog_wnd.has(hWnd)) { + if (file_dialog_wnd[hWnd]->close_requested.is_set()) { + IPropertyStore *prop_store; + HRESULT hr = SHGetPropertyStoreForWindow(hWnd, IID_IPropertyStore, (void **)&prop_store); + if (hr == S_OK) { + PROPVARIANT val; + PropVariantInit(&val); + prop_store->SetValue(PKEY_AppUserModel_ID, val); + prop_store->Release(); + } + DestroyWindow(hWnd); + file_dialog_wnd.erase(hWnd); + } + } + return DefWindowProcW(hWnd, uMsg, wParam, lParam); } -Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb) { - _THREAD_SAFE_METHOD_ +void DisplayServerWindows::_thread_fd_monitor(void *p_ud) { + DisplayServerWindows *ds = static_cast<DisplayServerWindows *>(get_singleton()); + FileDialogData *fd = (FileDialogData *)p_ud; - ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED); + if (fd->mode < 0 && fd->mode >= DisplayServer::FILE_DIALOG_MODE_SAVE_MAX) { + fd->finished.set(); + return; + } + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + int64_t x = fd->wrect.position.x; + int64_t y = fd->wrect.position.y; + int64_t w = fd->wrect.size.x; + int64_t h = fd->wrect.size.y; + + WNDCLASSW wc = {}; + wc.lpfnWndProc = (WNDPROC)::WndProcFileDialog; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = L"Engine File Dialog"; + RegisterClassW(&wc); + + HWND hwnd_dialog = CreateWindowExW(WS_EX_APPWINDOW, L"Engine File Dialog", L"", WS_OVERLAPPEDWINDOW, x, y, w, h, nullptr, nullptr, GetModuleHandle(nullptr), nullptr); + if (hwnd_dialog) { + { + MutexLock lock(ds->file_dialog_mutex); + ds->file_dialog_wnd[hwnd_dialog] = fd; + } + + HICON mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_SMALL, 0); + if (mainwindow_icon) { + SendMessage(hwnd_dialog, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon); + } + mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_BIG, 0); + if (mainwindow_icon) { + SendMessage(hwnd_dialog, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon); + } + IPropertyStore *prop_store; + HRESULT hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store); + if (hr == S_OK) { + PROPVARIANT val; + InitPropVariantFromString((PCWSTR)fd->appid.utf16().get_data(), &val); + prop_store->SetValue(PKEY_AppUserModel_ID, val); + prop_store->Release(); + } + } + + SetCurrentProcessExplicitAppUserModelID((PCWSTR)fd->appid.utf16().get_data()); Vector<Char16String> filter_names; Vector<Char16String> filter_exts; - for (const String &E : p_filters) { + for (const String &E : fd->filters) { Vector<String> tokens = E.split(";"); if (tokens.size() >= 1) { String flt = tokens[0].strip_edges(); @@ -425,11 +496,9 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() }); } - WindowID prev_focus = last_focused_window; - HRESULT hr = S_OK; IFileDialog *pfd = nullptr; - if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) { + if (fd->mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) { hr = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileSaveDialog, (void **)&pfd); } else { hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void **)&pfd); @@ -445,40 +514,32 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title IFileDialogCustomize *pfdc = nullptr; hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc)); - for (int i = 0; i < p_options.size(); i++) { - const Dictionary &item = p_options[i]; + for (int i = 0; i < fd->options.size(); i++) { + const Dictionary &item = fd->options[i]; if (!item.has("name") || !item.has("values") || !item.has("default")) { continue; } - const String &name = item["name"]; - const Vector<String> &options = item["values"]; - int default_idx = item["default"]; - - event_handler->add_option(pfdc, name, options, default_idx); + event_handler->add_option(pfdc, item["name"], item["values"], item["default_idx"]); } - event_handler->set_root(p_root); + event_handler->set_root(fd->root); pfdc->Release(); DWORD flags; pfd->GetOptions(&flags); - if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) { + if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) { flags |= FOS_ALLOWMULTISELECT; } - if (p_mode == FILE_DIALOG_MODE_OPEN_DIR) { + if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR) { flags |= FOS_PICKFOLDERS; } - if (p_show_hidden) { + if (fd->show_hidden) { flags |= FOS_FORCESHOWHIDDEN; } pfd->SetOptions(flags | FOS_FORCEFILESYSTEM); - pfd->SetTitle((LPCWSTR)p_title.utf16().ptr()); + pfd->SetTitle((LPCWSTR)fd->title.utf16().ptr()); - String dir = ProjectSettings::get_singleton()->globalize_path(p_current_directory); - if (dir == ".") { - dir = OS::get_singleton()->get_executable_path().get_base_dir(); - } - dir = dir.replace("/", "\\"); + String dir = fd->current_directory.replace("/", "\\"); IShellItem *shellitem = nullptr; hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem); @@ -487,16 +548,11 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title pfd->SetFolder(shellitem); } - pfd->SetFileName((LPCWSTR)p_filename.utf16().ptr()); + pfd->SetFileName((LPCWSTR)fd->filename.utf16().ptr()); pfd->SetFileTypes(filters.size(), filters.ptr()); pfd->SetFileTypeIndex(0); - WindowID window_id = _get_focused_window_or_popup(); - if (!windows.has(window_id)) { - window_id = MAIN_WINDOW_ID; - } - - hr = pfd->Show(windows[window_id].hWnd); + hr = pfd->Show(hwnd_dialog); pfd->Unadvise(cookie); Dictionary options = event_handler->get_selected(); @@ -513,7 +569,7 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title if (SUCCEEDED(hr)) { Vector<String> file_names; - if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) { + if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) { IShellItemArray *results; hr = static_cast<IFileOpenDialog *>(pfd)->GetResults(&results); if (SUCCEEDED(hr)) { @@ -546,73 +602,148 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title result->Release(); } } - if (p_callback.is_valid()) { - if (p_options_in_cb) { + if (fd->callback.is_valid()) { + if (fd->options_in_cb) { Variant v_result = true; Variant v_files = file_names; Variant v_index = index; Variant v_opt = options; - Variant ret; - Callable::CallError ce; - const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt }; + const Variant *cb_args[4] = { &v_result, &v_files, &v_index, &v_opt }; - p_callback.callp(args, 4, ret, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 4, ce))); - } + fd->callback.call_deferredp(cb_args, 4); } else { Variant v_result = true; Variant v_files = file_names; Variant v_index = index; - Variant ret; - Callable::CallError ce; - const Variant *args[3] = { &v_result, &v_files, &v_index }; + const Variant *cb_args[3] = { &v_result, &v_files, &v_index }; - p_callback.callp(args, 3, ret, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 3, ce))); - } + fd->callback.call_deferredp(cb_args, 3); } } } else { - if (p_callback.is_valid()) { - if (p_options_in_cb) { + if (fd->callback.is_valid()) { + if (fd->options_in_cb) { Variant v_result = false; Variant v_files = Vector<String>(); - Variant v_index = index; - Variant v_opt = options; - Variant ret; - Callable::CallError ce; - const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt }; + Variant v_index = 0; + Variant v_opt = Dictionary(); + const Variant *cb_args[4] = { &v_result, &v_files, &v_index, &v_opt }; - p_callback.callp(args, 4, ret, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 4, ce))); - } + fd->callback.call_deferredp(cb_args, 4); } else { Variant v_result = false; Variant v_files = Vector<String>(); - Variant v_index = index; - Variant ret; - Callable::CallError ce; - const Variant *args[3] = { &v_result, &v_files, &v_index }; + Variant v_index = 0; + const Variant *cb_args[3] = { &v_result, &v_files, &v_index }; - p_callback.callp(args, 3, ret, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 3, ce))); - } + fd->callback.call_deferredp(cb_args, 3); } } } pfd->Release(); - if (prev_focus != INVALID_WINDOW_ID) { - callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus); + } else { + if (fd->callback.is_valid()) { + if (fd->options_in_cb) { + Variant v_result = false; + Variant v_files = Vector<String>(); + Variant v_index = 0; + Variant v_opt = Dictionary(); + const Variant *cb_args[4] = { &v_result, &v_files, &v_index, &v_opt }; + + fd->callback.call_deferredp(cb_args, 4); + } else { + Variant v_result = false; + Variant v_files = Vector<String>(); + Variant v_index = 0; + const Variant *cb_args[3] = { &v_result, &v_files, &v_index }; + + fd->callback.call_deferredp(cb_args, 3); + } } + } + { + MutexLock lock(ds->file_dialog_mutex); + if (hwnd_dialog && ds->file_dialog_wnd.has(hwnd_dialog)) { + IPropertyStore *prop_store; + hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store); + if (hr == S_OK) { + PROPVARIANT val; + PropVariantInit(&val); + prop_store->SetValue(PKEY_AppUserModel_ID, val); + prop_store->Release(); + } + DestroyWindow(hwnd_dialog); + ds->file_dialog_wnd.erase(hwnd_dialog); + } + } + UnregisterClassW(L"Engine File Dialog", GetModuleHandle(nullptr)); + CoUninitialize(); + + fd->finished.set(); - return OK; + if (fd->window_id != INVALID_WINDOW_ID) { + callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd->window_id); + } +} + +Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED); + + WindowID window_id = _get_focused_window_or_popup(); + if (!windows.has(window_id)) { + window_id = MAIN_WINDOW_ID; + } + String appname; + if (Engine::get_singleton()->is_editor_hint()) { + appname = "Godot.GodotEditor." + String(VERSION_BRANCH); } else { - return ERR_CANT_OPEN; + String name = GLOBAL_GET("application/config/name"); + String version = GLOBAL_GET("application/config/version"); + if (version.is_empty()) { + version = "0"; + } + String clean_app_name = name.to_pascal_case(); + for (int i = 0; i < clean_app_name.length(); i++) { + if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') { + clean_app_name[i] = '_'; + } + } + clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix("."); + appname = "Godot." + clean_app_name + "." + version; + } + + FileDialogData *fd = memnew(FileDialogData); + if (window_id != INVALID_WINDOW_ID) { + fd->hwnd_owner = windows[window_id].hWnd; + RECT crect; + GetWindowRect(fd->hwnd_owner, &crect); + fd->wrect = Rect2i(crect.left, crect.top, crect.right - crect.left, crect.bottom - crect.top); + } else { + fd->hwnd_owner = 0; + fd->wrect = Rect2i(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT); } + fd->appid = appname; + fd->title = p_title; + fd->current_directory = p_current_directory; + fd->root = p_root; + fd->filename = p_filename; + fd->show_hidden = p_show_hidden; + fd->mode = p_mode; + fd->window_id = window_id; + fd->filters = p_filters; + fd->options = p_options; + fd->callback = p_callback; + fd->options_in_cb = p_options_in_cb; + fd->finished.clear(); + fd->close_requested.clear(); + + fd->listener_thread.start(DisplayServerWindows::_thread_fd_monitor, fd); + + file_dialogs.push_back(fd); + + return OK; } void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) { @@ -3022,6 +3153,21 @@ void DisplayServerWindows::process_events() { _process_key_events(); Input::get_singleton()->flush_buffered_events(); } + + LocalVector<List<FileDialogData *>::Element *> to_remove; + for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) { + FileDialogData *fd = E->get(); + if (fd->finished.is_set()) { + if (fd->listener_thread.is_started()) { + fd->listener_thread.wait_to_finish(); + } + to_remove.push_back(E); + } + } + for (List<FileDialogData *>::Element *E : to_remove) { + memdelete(E->get()); + E->erase(); + } } void DisplayServerWindows::force_process_and_drop_events() { @@ -5703,12 +5849,6 @@ Vector2i _get_device_ids(const String &p_device_name) { return ids; } -typedef enum _SHC_PROCESS_DPI_AWARENESS { - SHC_PROCESS_DPI_UNAWARE = 0, - SHC_PROCESS_SYSTEM_DPI_AWARE = 1, - SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2 -} SHC_PROCESS_DPI_AWARENESS; - bool DisplayServerWindows::is_dark_mode_supported() const { return ux_theme_available; } @@ -6260,6 +6400,20 @@ void DisplayServerWindows::register_windows_driver() { } DisplayServerWindows::~DisplayServerWindows() { + LocalVector<List<FileDialogData *>::Element *> to_remove; + for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) { + FileDialogData *fd = E->get(); + if (fd->listener_thread.is_started()) { + fd->close_requested.set(); + fd->listener_thread.wait_to_finish(); + } + to_remove.push_back(E); + } + for (List<FileDialogData *>::Element *E : to_remove) { + memdelete(E->get()); + E->erase(); + } + delete joypad; touch_state.clear(); diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 26328ba876..8a2f9f81a6 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -350,6 +350,12 @@ typedef struct { ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em) } ICONDIR, *LPICONDIR; +typedef enum _SHC_PROCESS_DPI_AWARENESS { + SHC_PROCESS_DPI_UNAWARE = 0, + SHC_PROCESS_SYSTEM_DPI_AWARE = 1, + SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2, +} SHC_PROCESS_DPI_AWARENESS; + class DisplayServerWindows : public DisplayServer { // No need to register with GDCLASS, it's platform-specific and nothing is added. @@ -544,6 +550,31 @@ class DisplayServerWindows : public DisplayServer { IndicatorID indicator_id_counter = 0; HashMap<IndicatorID, IndicatorData> indicators; + struct FileDialogData { + HWND hwnd_owner = 0; + Rect2i wrect; + String appid; + String title; + String current_directory; + String root; + String filename; + bool show_hidden = false; + DisplayServer::FileDialogMode mode = FileDialogMode::FILE_DIALOG_MODE_OPEN_ANY; + Vector<String> filters; + TypedArray<Dictionary> options; + WindowID window_id = DisplayServer::INVALID_WINDOW_ID; + Callable callback; + bool options_in_cb = false; + Thread listener_thread; + SafeFlag close_requested; + SafeFlag finished; + }; + Mutex file_dialog_mutex; + List<FileDialogData *> file_dialogs; + HashMap<HWND, FileDialogData *> file_dialog_wnd; + + static void _thread_fd_monitor(void *p_ud); + HashMap<int64_t, MouseButton> pointer_prev_button; HashMap<int64_t, MouseButton> pointer_button; HashMap<int64_t, LONG> pointer_down_time; @@ -605,6 +636,7 @@ class DisplayServerWindows : public DisplayServer { String _get_klid(HKL p_hkl) const; public: + LRESULT WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT MouseProc(int code, WPARAM wParam, LPARAM lParam); diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index 12694b0155..b465bd4ecd 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -187,6 +187,12 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> template_path = template_path.strip_edges(); if (template_path.is_empty()) { template_path = find_export_template(get_template_file_name(p_debug ? "debug" : "release", arch)); + } else { + String exe_arch = _get_exe_arch(template_path); + if (arch != exe_arch) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Mismatching custom export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch)); + return ERR_CANT_CREATE; + } } int export_angle = p_preset->get("application/export_angle"); @@ -753,9 +759,26 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p } bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { - String err = ""; + String err; bool valid = EditorExportPlatformPC::has_valid_export_configuration(p_preset, err, r_missing_templates, p_debug); + String custom_debug = p_preset->get("custom_template/debug").operator String().strip_edges(); + String custom_release = p_preset->get("custom_template/release").operator String().strip_edges(); + String arch = p_preset->get("binary_format/architecture"); + + if (!custom_debug.is_empty() && FileAccess::exists(custom_debug)) { + String exe_arch = _get_exe_arch(custom_debug); + if (arch != exe_arch) { + err += vformat(TTR("Mismatching custom debug export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n"; + } + } + if (!custom_release.is_empty() && FileAccess::exists(custom_release)) { + String exe_arch = _get_exe_arch(custom_release); + if (arch != exe_arch) { + err += vformat(TTR("Mismatching custom release export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n"; + } + } + String rcedit_path = EDITOR_GET("export/windows/rcedit"); if (p_preset->get("application/modify_resources") && rcedit_path.is_empty()) { err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > rcedit) to change the icon or app information data.") + "\n"; @@ -769,7 +792,7 @@ bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref<Edito } bool EditorExportPlatformWindows::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { - String err = ""; + String err; bool valid = true; List<ExportOption> options; @@ -793,6 +816,43 @@ bool EditorExportPlatformWindows::has_valid_project_configuration(const Ref<Edit return valid; } +String EditorExportPlatformWindows::_get_exe_arch(const String &p_path) const { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { + return "invalid"; + } + + // Jump to the PE header and check the magic number. + { + f->seek(0x3c); + uint32_t pe_pos = f->get_32(); + + f->seek(pe_pos); + uint32_t magic = f->get_32(); + if (magic != 0x00004550) { + return "invalid"; + } + } + + // Process header. + uint16_t machine = f->get_16(); + f->close(); + + switch (machine) { + case 0x014c: + return "x86_32"; + case 0x8664: + return "x86_64"; + case 0x01c0: + case 0x01c4: + return "arm32"; + case 0xaa64: + return "arm64"; + default: + return "unknown"; + } +} + Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) { // Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index c644b1f9e1..6ccb4a15a7 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -73,6 +73,8 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC { Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon); Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); + String _get_exe_arch(const String &p_path) const; + public: virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) override; diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index dfe321a435..023e9201fc 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -78,13 +78,7 @@ void ParallaxLayer::_update_mirroring() { } void ParallaxLayer::set_mirroring(const Size2 &p_mirroring) { - mirroring = p_mirroring; - if (mirroring.x < 0) { - mirroring.x = 0; - } - if (mirroring.y < 0) { - mirroring.y = 0; - } + mirroring = p_mirroring.maxf(0); _update_mirroring(); } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 8aa50668eb..f9e8c831d1 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -39,19 +39,17 @@ #endif //TOOLS_ENABLED bool Bone2D::_set(const StringName &p_path, const Variant &p_value) { - String path = p_path; - - if (path.begins_with("auto_calculate_length_and_angle")) { + if (p_path == SNAME("auto_calculate_length_and_angle")) { set_autocalculate_length_and_angle(p_value); - } else if (path.begins_with("length")) { + } else if (p_path == SNAME("length")) { set_length(p_value); - } else if (path.begins_with("bone_angle")) { + } else if (p_path == SNAME("bone_angle")) { set_bone_angle(Math::deg_to_rad(real_t(p_value))); - } else if (path.begins_with("default_length")) { + } else if (p_path == SNAME("default_length")) { set_length(p_value); } #ifdef TOOLS_ENABLED - else if (path.begins_with("editor_settings/show_bone_gizmo")) { + else if (p_path == SNAME("editor_settings/show_bone_gizmo")) { _editor_set_show_bone_gizmo(p_value); } #endif // TOOLS_ENABLED @@ -63,19 +61,17 @@ bool Bone2D::_set(const StringName &p_path, const Variant &p_value) { } bool Bone2D::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - - if (path.begins_with("auto_calculate_length_and_angle")) { + if (p_path == SNAME("auto_calculate_length_and_angle")) { r_ret = get_autocalculate_length_and_angle(); - } else if (path.begins_with("length")) { + } else if (p_path == SNAME("length")) { r_ret = get_length(); - } else if (path.begins_with("bone_angle")) { + } else if (p_path == SNAME("bone_angle")) { r_ret = Math::rad_to_deg(get_bone_angle()); - } else if (path.begins_with("default_length")) { + } else if (p_path == SNAME("default_length")) { r_ret = get_length(); } #ifdef TOOLS_ENABLED - else if (path.begins_with("editor_settings/show_bone_gizmo")) { + else if (p_path == SNAME("editor_settings/show_bone_gizmo")) { r_ret = _editor_get_show_bone_gizmo(); } #endif // TOOLS_ENABLED @@ -330,9 +326,7 @@ bool Bone2D::_editor_get_bone_shape(Vector<Vector2> *p_shape, Vector<Vector2> *p rel = (p_other_bone->get_global_position() - get_global_position()); rel = rel.rotated(-get_global_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation } else { - real_t angle_to_use = get_rotation() + bone_angle; - rel = Vector2(cos(angle_to_use), sin(angle_to_use)) * (length * MIN(get_global_scale().x, get_global_scale().y)); - rel = rel.rotated(-get_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation + rel = Vector2(Math::cos(bone_angle), Math::sin(bone_angle)) * length * get_global_scale(); } Vector2 relt = rel.rotated(Math_PI * 0.5).normalized() * bone_width; @@ -518,23 +512,19 @@ Bone2D::~Bone2D() { ////////////////////////////////////// bool Skeleton2D::_set(const StringName &p_path, const Variant &p_value) { - String path = p_path; - - if (path.begins_with("modification_stack")) { + if (p_path == SNAME("modification_stack")) { set_modification_stack(p_value); return true; } - return true; + return false; } bool Skeleton2D::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - - if (path.begins_with("modification_stack")) { + if (p_path == SNAME("modification_stack")) { r_ret = get_modification_stack(); return true; } - return true; + return false; } void Skeleton2D::_get_property_list(List<PropertyInfo> *p_list) const { diff --git a/scene/3d/multimesh_instance_3d.cpp b/scene/3d/multimesh_instance_3d.cpp index 55d6e49e6c..2eef1dbbf4 100644 --- a/scene/3d/multimesh_instance_3d.cpp +++ b/scene/3d/multimesh_instance_3d.cpp @@ -30,16 +30,35 @@ #include "multimesh_instance_3d.h" +void MultiMeshInstance3D::_refresh_interpolated() { + if (is_inside_tree() && multimesh.is_valid()) { + bool interpolated = is_physics_interpolated_and_enabled(); + multimesh->set_physics_interpolated(interpolated); + } +} + +void MultiMeshInstance3D::_physics_interpolated_changed() { + VisualInstance3D::_physics_interpolated_changed(); + _refresh_interpolated(); +} + void MultiMeshInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_multimesh", "multimesh"), &MultiMeshInstance3D::set_multimesh); ClassDB::bind_method(D_METHOD("get_multimesh"), &MultiMeshInstance3D::get_multimesh); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multimesh", PROPERTY_HINT_RESOURCE_TYPE, "MultiMesh"), "set_multimesh", "get_multimesh"); } +void MultiMeshInstance3D::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + _refresh_interpolated(); + } +} + void MultiMeshInstance3D::set_multimesh(const Ref<MultiMesh> &p_multimesh) { multimesh = p_multimesh; if (multimesh.is_valid()) { set_base(multimesh->get_rid()); + _refresh_interpolated(); } else { set_base(RID()); } diff --git a/scene/3d/multimesh_instance_3d.h b/scene/3d/multimesh_instance_3d.h index 404f31d1e3..c9507b1047 100644 --- a/scene/3d/multimesh_instance_3d.h +++ b/scene/3d/multimesh_instance_3d.h @@ -39,9 +39,12 @@ class MultiMeshInstance3D : public GeometryInstance3D { Ref<MultiMesh> multimesh; + void _refresh_interpolated(); + protected: + virtual void _physics_interpolated_changed() override; static void _bind_methods(); - // bind helpers + void _notification(int p_what); public: void set_multimesh(const Ref<MultiMesh> &p_multimesh); diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 40e04f0fb4..d7397a932d 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -686,6 +686,8 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { Vector<Vector3> vertex_array; vertex_array.resize(connections_count * 6); + Vector3 *vertex_array_ptrw = vertex_array.ptrw(); + int vertex_array_index = 0; for (int i = 0; i < connections_count; i++) { Vector3 connection_pathway_start = NavigationServer3D::get_singleton()->region_get_connection_pathway_start(region, i); @@ -705,13 +707,12 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { Vector3 left_end_pos = connection_pathway_end + (end_right_dir * half_edge_connection_margin); Vector3 right_end_pos = connection_pathway_end + (end_left_dir * half_edge_connection_margin); - vertex_array.push_back(right_end_pos); - vertex_array.push_back(left_start_pos); - vertex_array.push_back(right_start_pos); - - vertex_array.push_back(left_end_pos); - vertex_array.push_back(right_end_pos); - vertex_array.push_back(right_start_pos); + vertex_array_ptrw[vertex_array_index++] = connection_pathway_start; + vertex_array_ptrw[vertex_array_index++] = connection_pathway_end; + vertex_array_ptrw[vertex_array_index++] = left_start_pos; + vertex_array_ptrw[vertex_array_index++] = right_start_pos; + vertex_array_ptrw[vertex_array_index++] = left_end_pos; + vertex_array_ptrw[vertex_array_index++] = right_end_pos; } if (vertex_array.size() == 0) { @@ -724,7 +725,7 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { mesh_array.resize(Mesh::ARRAY_MAX); mesh_array[Mesh::ARRAY_VERTEX] = vertex_array; - debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_array); + debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, mesh_array); debug_edge_connections_mesh->surface_set_material(0, edge_connections_material); RS::get_singleton()->instance_set_base(debug_edge_connections_instance, debug_edge_connections_mesh->get_rid()); diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 5a4e176e99..c6ece84cdd 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -69,13 +69,13 @@ SkinReference::~SkinReference() { /////////////////////////////////////// bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { - String path = p_path; - #ifndef DISABLE_DEPRECATED - if (path.begins_with("animate_physical_bones")) { + if (p_path == SNAME("animate_physical_bones")) { set_animate_physical_bones(p_value); + return true; } #endif + String path = p_path; if (!path.begins_with("bones/")) { return false; @@ -139,13 +139,13 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { } bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - #ifndef DISABLE_DEPRECATED - if (path.begins_with("animate_physical_bones")) { + if (p_path == SNAME("animate_physical_bones")) { r_ret = get_animate_physical_bones(); + return true; } #endif + String path = p_path; if (!path.begins_with("bones/")) { return false; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 3f4b962641..b71f9bc0c4 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -303,6 +303,8 @@ StringName XRNode3D::get_pose_name() const { void XRNode3D::set_show_when_tracked(bool p_show) { show_when_tracked = p_show; + + _update_visibility(); } bool XRNode3D::get_show_when_tracked() const { @@ -361,6 +363,9 @@ void XRNode3D::_bind_tracker() { if (pose.is_valid()) { set_transform(pose->get_adjusted_transform()); _set_has_tracking_data(pose->get_has_tracking_data()); + } else { + // Pose has been invalidated or was never set. + _set_has_tracking_data(false); } } } @@ -407,6 +412,10 @@ void XRNode3D::_pose_lost_tracking(const Ref<XRPose> &p_pose) { } void XRNode3D::_set_has_tracking_data(bool p_has_tracking_data) { + // Always update our visibility, we may have set our tracking data + // when conditions weren't right. + _update_visibility(); + // Ignore if the has_tracking_data state isn't changing. if (p_has_tracking_data == has_tracking_data) { return; @@ -415,10 +424,19 @@ void XRNode3D::_set_has_tracking_data(bool p_has_tracking_data) { // Handle change of has_tracking_data. has_tracking_data = p_has_tracking_data; emit_signal(SNAME("tracking_changed"), has_tracking_data); +} +void XRNode3D::_update_visibility() { // If configured, show or hide the node based on tracking data. if (show_when_tracked) { - set_visible(has_tracking_data); + // Only react to this if we have a primary interface. + XRServer *xr_server = XRServer::get_singleton(); + if (xr_server != nullptr) { + Ref<XRInterface> xr_interface = xr_server->get_primary_interface(); + if (xr_interface.is_valid()) { + set_visible(has_tracking_data); + } + } } } diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index a42f6d470f..94c3923433 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -95,6 +95,8 @@ protected: void _pose_lost_tracking(const Ref<XRPose> &p_pose); void _set_has_tracking_data(bool p_has_tracking_data); + void _update_visibility(); + public: void _validate_property(PropertyInfo &p_property) const; void set_tracker(const StringName &p_tracker_name); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 00b9a3478a..412eb83515 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -765,7 +765,7 @@ void CodeEdit::_backspace_internal(int p_caret) { continue; } - if (to_line > 0 && _is_line_hidden(to_line - 1)) { + if (to_line > 0 && to_column == 0 && _is_line_hidden(to_line - 1)) { unfold_line(to_line - 1); } diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 4f8d818a6c..3264733548 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -121,8 +121,9 @@ void MenuBar::_open_popup(int p_index, bool p_focus_item) { } Rect2 item_rect = _get_menu_item_rect(p_index); - Point2 screen_pos = get_screen_position() + item_rect.position * get_viewport()->get_canvas_transform().get_scale(); - Size2 screen_size = item_rect.size * get_viewport()->get_canvas_transform().get_scale(); + Size2 canvas_scale = get_canvas_transform().get_scale(); + Point2 screen_pos = get_screen_position() + item_rect.position * canvas_scale; + Size2 screen_size = item_rect.size * canvas_scale; active_menu = p_index; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 5cdb0c3d44..422783b01b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -4328,25 +4328,12 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_ return Point2i(text[row].length(), row); } - int col = 0; int colx = p_pos.x - (theme_cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding); colx += first_visible_col; if (!editable) { colx -= theme_cache.style_readonly->get_offset().x / 2; colx += theme_cache.style_normal->get_offset().x / 2; } - col = _get_char_pos_for_line(colx, row, wrap_index); - if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && wrap_index < get_line_wrap_count(row)) { - // Move back one if we are at the end of the row. - Vector<String> rows2 = get_line_wrapped_text(row); - int row_end_col = 0; - for (int i = 0; i < wrap_index + 1; i++) { - row_end_col += rows2[i].length(); - } - if (col >= row_end_col) { - col -= 1; - } - } RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index); float wrap_indent = (text.is_indent_wrapped_lines() && wrap_index > 0) ? get_indent_level(row) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width : 0.0; @@ -4355,7 +4342,7 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_ } else { colx -= wrap_indent; } - col = TS->shaped_text_hit_test_position(text_rid, colx); + int col = TS->shaped_text_hit_test_position(text_rid, colx); if (!caret_mid_grapheme_enabled) { col = TS->shaped_text_closest_character_pos(text_rid, col); } @@ -7531,7 +7518,7 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line, int p_column int row = 0; Vector<Vector2i> rows2 = text.get_line_wrap_ranges(p_line); for (int i = 0; i < rows2.size(); i++) { - if ((p_char >= rows2[i].x) && (p_char <= rows2[i].y)) { + if ((p_char >= rows2[i].x) && (p_char < rows2[i].y || (i == rows2.size() - 1 && p_char == rows2[i].y))) { row = i; break; } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 5c46abc732..de6d49761b 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2809,9 +2809,11 @@ Node *Node::duplicate(int p_flags) const { ERR_THREAD_GUARD_V(nullptr); Node *dupe = _duplicate(p_flags); + ERR_FAIL_NULL_V_MSG(dupe, nullptr, "Failed to duplicate node."); + _duplicate_properties(this, this, dupe, p_flags); - if (dupe && (p_flags & DUPLICATE_SIGNALS)) { + if (p_flags & DUPLICATE_SIGNALS) { _duplicate_signals(this, dupe); } @@ -2827,6 +2829,8 @@ Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, con int flags = DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANTIATION | DUPLICATE_FROM_EDITOR; Node *dupe = _duplicate(flags, &r_duplimap); + ERR_FAIL_NULL_V_MSG(dupe, nullptr, "Failed to duplicate node."); + _duplicate_properties(this, this, dupe, flags); // This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated. diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index c85fda2371..0036247625 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -5070,6 +5070,7 @@ void SubViewport::_internal_set_size(const Size2i &p_size, bool p_force) { if (c) { c->update_minimum_size(); + c->queue_redraw(); } } diff --git a/scene/main/window.cpp b/scene/main/window.cpp index c2704a3848..19fb62df2e 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -1602,7 +1602,7 @@ Size2 Window::_get_contents_minimum_size() const { } } - return max; + return max * content_scale_factor; } void Window::child_controls_changed() { diff --git a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp index 686560829b..ee6fc61af3 100644 --- a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp +++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp @@ -37,6 +37,7 @@ void NavigationMeshSourceGeometryData2D::clear() { traversable_outlines.clear(); obstruction_outlines.clear(); _projected_obstructions.clear(); + bounds_dirty = true; } bool NavigationMeshSourceGeometryData2D::has_data() { @@ -47,16 +48,19 @@ bool NavigationMeshSourceGeometryData2D::has_data() { void NavigationMeshSourceGeometryData2D::clear_projected_obstructions() { RWLockWrite write_lock(geometry_rwlock); _projected_obstructions.clear(); + bounds_dirty = true; } void NavigationMeshSourceGeometryData2D::_set_traversable_outlines(const Vector<Vector<Vector2>> &p_traversable_outlines) { RWLockWrite write_lock(geometry_rwlock); traversable_outlines = p_traversable_outlines; + bounds_dirty = true; } void NavigationMeshSourceGeometryData2D::_set_obstruction_outlines(const Vector<Vector<Vector2>> &p_obstruction_outlines) { RWLockWrite write_lock(geometry_rwlock); obstruction_outlines = p_obstruction_outlines; + bounds_dirty = true; } const Vector<Vector<Vector2>> &NavigationMeshSourceGeometryData2D::_get_traversable_outlines() const { @@ -73,6 +77,7 @@ void NavigationMeshSourceGeometryData2D::_add_traversable_outline(const Vector<V if (p_shape_outline.size() > 1) { RWLockWrite write_lock(geometry_rwlock); traversable_outlines.push_back(p_shape_outline); + bounds_dirty = true; } } @@ -80,6 +85,7 @@ void NavigationMeshSourceGeometryData2D::_add_obstruction_outline(const Vector<V if (p_shape_outline.size() > 1) { RWLockWrite write_lock(geometry_rwlock); obstruction_outlines.push_back(p_shape_outline); + bounds_dirty = true; } } @@ -89,6 +95,7 @@ void NavigationMeshSourceGeometryData2D::set_traversable_outlines(const TypedArr for (int i = 0; i < p_traversable_outlines.size(); i++) { traversable_outlines.write[i] = p_traversable_outlines[i]; } + bounds_dirty = true; } TypedArray<Vector<Vector2>> NavigationMeshSourceGeometryData2D::get_traversable_outlines() const { @@ -108,6 +115,7 @@ void NavigationMeshSourceGeometryData2D::set_obstruction_outlines(const TypedArr for (int i = 0; i < p_obstruction_outlines.size(); i++) { obstruction_outlines.write[i] = p_obstruction_outlines[i]; } + bounds_dirty = true; } TypedArray<Vector<Vector2>> NavigationMeshSourceGeometryData2D::get_obstruction_outlines() const { @@ -128,6 +136,7 @@ void NavigationMeshSourceGeometryData2D::append_traversable_outlines(const Typed for (int i = traversable_outlines_size; i < p_traversable_outlines.size(); i++) { traversable_outlines.write[i] = p_traversable_outlines[i]; } + bounds_dirty = true; } void NavigationMeshSourceGeometryData2D::append_obstruction_outlines(const TypedArray<Vector<Vector2>> &p_obstruction_outlines) { @@ -137,6 +146,7 @@ void NavigationMeshSourceGeometryData2D::append_obstruction_outlines(const Typed for (int i = obstruction_outlines_size; i < p_obstruction_outlines.size(); i++) { obstruction_outlines.write[i] = p_obstruction_outlines[i]; } + bounds_dirty = true; } void NavigationMeshSourceGeometryData2D::add_traversable_outline(const PackedVector2Array &p_shape_outline) { @@ -148,6 +158,7 @@ void NavigationMeshSourceGeometryData2D::add_traversable_outline(const PackedVec traversable_outline.write[i] = p_shape_outline[i]; } traversable_outlines.push_back(traversable_outline); + bounds_dirty = true; } } @@ -160,6 +171,7 @@ void NavigationMeshSourceGeometryData2D::add_obstruction_outline(const PackedVec obstruction_outline.write[i] = p_shape_outline[i]; } obstruction_outlines.push_back(obstruction_outline); + bounds_dirty = true; } } @@ -176,6 +188,7 @@ void NavigationMeshSourceGeometryData2D::merge(const Ref<NavigationMeshSourceGeo traversable_outlines.append_array(other_traversable_outlines); obstruction_outlines.append_array(other_obstruction_outlines); _projected_obstructions.append_array(other_projected_obstructions); + bounds_dirty = true; } void NavigationMeshSourceGeometryData2D::add_projected_obstruction(const Vector<Vector2> &p_vertices, bool p_carve) { @@ -195,6 +208,7 @@ void NavigationMeshSourceGeometryData2D::add_projected_obstruction(const Vector< RWLockWrite write_lock(geometry_rwlock); _projected_obstructions.push_back(projected_obstruction); + bounds_dirty = true; } void NavigationMeshSourceGeometryData2D::set_projected_obstructions(const Array &p_array) { @@ -217,6 +231,7 @@ void NavigationMeshSourceGeometryData2D::set_projected_obstructions(const Array RWLockWrite write_lock(geometry_rwlock); _projected_obstructions.push_back(projected_obstruction); + bounds_dirty = true; } } @@ -266,6 +281,7 @@ void NavigationMeshSourceGeometryData2D::set_data(const Vector<Vector<Vector2>> traversable_outlines = p_traversable_outlines; obstruction_outlines = p_obstruction_outlines; _projected_obstructions = p_projected_obstructions; + bounds_dirty = true; } void NavigationMeshSourceGeometryData2D::get_data(Vector<Vector<Vector2>> &r_traversable_outlines, Vector<Vector<Vector2>> &r_obstruction_outlines, Vector<ProjectedObstruction> &r_projected_obstructions) { @@ -275,6 +291,58 @@ void NavigationMeshSourceGeometryData2D::get_data(Vector<Vector<Vector2>> &r_tra r_projected_obstructions = _projected_obstructions; } +Rect2 NavigationMeshSourceGeometryData2D::get_bounds() { + geometry_rwlock.read_lock(); + + if (bounds_dirty) { + geometry_rwlock.read_unlock(); + RWLockWrite write_lock(geometry_rwlock); + + bounds_dirty = false; + bounds = Rect2(); + bool first_vertex = true; + + for (const Vector<Vector2> &traversable_outline : traversable_outlines) { + for (const Vector2 &traversable_point : traversable_outline) { + if (first_vertex) { + first_vertex = false; + bounds.position = traversable_point; + } else { + bounds.expand_to(traversable_point); + } + } + } + + for (const Vector<Vector2> &obstruction_outline : obstruction_outlines) { + for (const Vector2 &obstruction_point : obstruction_outline) { + if (first_vertex) { + first_vertex = false; + bounds.position = obstruction_point; + } else { + bounds.expand_to(obstruction_point); + } + } + } + + for (const ProjectedObstruction &projected_obstruction : _projected_obstructions) { + for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) { + const Vector2 vertex = Vector2(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]); + if (first_vertex) { + first_vertex = false; + bounds.position = vertex; + } else { + bounds.expand_to(vertex); + } + } + } + } else { + geometry_rwlock.read_unlock(); + } + + RWLockRead read_lock(geometry_rwlock); + return bounds; +} + void NavigationMeshSourceGeometryData2D::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &NavigationMeshSourceGeometryData2D::clear); ClassDB::bind_method(D_METHOD("has_data"), &NavigationMeshSourceGeometryData2D::has_data); @@ -298,6 +366,8 @@ void NavigationMeshSourceGeometryData2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_projected_obstructions", "projected_obstructions"), &NavigationMeshSourceGeometryData2D::set_projected_obstructions); ClassDB::bind_method(D_METHOD("get_projected_obstructions"), &NavigationMeshSourceGeometryData2D::get_projected_obstructions); + ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationMeshSourceGeometryData2D::get_bounds); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "traversable_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_traversable_outlines", "get_traversable_outlines"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "obstruction_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_obstruction_outlines", "get_obstruction_outlines"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "projected_obstructions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_projected_obstructions", "get_projected_obstructions"); diff --git a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h index 01e97eee48..b29c106fb5 100644 --- a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h +++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h @@ -42,6 +42,9 @@ class NavigationMeshSourceGeometryData2D : public Resource { Vector<Vector<Vector2>> traversable_outlines; Vector<Vector<Vector2>> obstruction_outlines; + Rect2 bounds; + bool bounds_dirty = true; + public: struct ProjectedObstruction; @@ -103,6 +106,8 @@ public: void set_data(const Vector<Vector<Vector2>> &p_traversable_outlines, const Vector<Vector<Vector2>> &p_obstruction_outlines, Vector<ProjectedObstruction> &p_projected_obstructions); void get_data(Vector<Vector<Vector2>> &r_traversable_outlines, Vector<Vector<Vector2>> &r_obstruction_outlines, Vector<ProjectedObstruction> &r_projected_obstructions); + Rect2 get_bounds(); + NavigationMeshSourceGeometryData2D() {} ~NavigationMeshSourceGeometryData2D() { clear(); } }; diff --git a/scene/resources/2d/navigation_polygon.cpp b/scene/resources/2d/navigation_polygon.cpp index 4a290db86b..3dfa906e3b 100644 --- a/scene/resources/2d/navigation_polygon.cpp +++ b/scene/resources/2d/navigation_polygon.cpp @@ -104,7 +104,7 @@ void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array } polygons.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - polygons.write[i].indices = p_array[i]; + polygons.write[i] = p_array[i]; } } @@ -113,7 +113,7 @@ TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const { TypedArray<Vector<int32_t>> ret; ret.resize(polygons.size()); for (int i = 0; i < ret.size(); i++) { - ret[i] = polygons[i].indices; + ret[i] = polygons[i]; } return ret; @@ -141,9 +141,7 @@ TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const { void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) { RWLockWrite write_lock(rwlock); - Polygon polygon; - polygon.indices = p_polygon; - polygons.push_back(polygon); + polygons.push_back(p_polygon); { MutexLock lock(navigation_mesh_generation); navigation_mesh.unref(); @@ -164,7 +162,7 @@ int NavigationPolygon::get_polygon_count() const { Vector<int> NavigationPolygon::get_polygon(int p_idx) { RWLockRead read_lock(rwlock); ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>()); - return polygons[p_idx].indices; + return polygons[p_idx]; } void NavigationPolygon::clear_polygons() { @@ -189,10 +187,19 @@ void NavigationPolygon::clear() { void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons) { RWLockWrite write_lock(rwlock); vertices = p_vertices; - polygons.resize(p_polygons.size()); - for (int i = 0; i < p_polygons.size(); i++) { - polygons.write[i].indices = p_polygons[i]; + polygons = p_polygons; + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); } +} + +void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons, const Vector<Vector<Vector2>> &p_outlines) { + RWLockWrite write_lock(rwlock); + vertices = p_vertices; + polygons = p_polygons; + outlines = p_outlines; + rect_cache_dirty = true; { MutexLock lock(navigation_mesh_generation); navigation_mesh.unref(); @@ -202,10 +209,14 @@ void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector void NavigationPolygon::get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons) { RWLockRead read_lock(rwlock); r_vertices = vertices; - r_polygons.resize(polygons.size()); - for (int i = 0; i < polygons.size(); i++) { - r_polygons.write[i] = polygons[i].indices; - } + r_polygons = polygons; +} + +void NavigationPolygon::get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons, Vector<Vector<Vector2>> &r_outlines) { + RWLockRead read_lock(rwlock); + r_vertices = vertices; + r_polygons = polygons; + r_outlines = outlines; } Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() { @@ -237,6 +248,31 @@ Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() { return navigation_mesh; } +void NavigationPolygon::set_outlines(const Vector<Vector<Vector2>> &p_outlines) { + RWLockWrite write_lock(rwlock); + outlines = p_outlines; + rect_cache_dirty = true; +} + +Vector<Vector<Vector2>> NavigationPolygon::get_outlines() const { + RWLockRead read_lock(rwlock); + return outlines; +} + +void NavigationPolygon::set_polygons(const Vector<Vector<int>> &p_polygons) { + RWLockWrite write_lock(rwlock); + polygons = p_polygons; + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } +} + +Vector<Vector<int>> NavigationPolygon::get_polygons() const { + RWLockRead read_lock(rwlock); + return polygons; +} + void NavigationPolygon::add_outline(const Vector<Vector2> &p_outline) { RWLockWrite write_lock(rwlock); outlines.push_back(p_outline); @@ -364,7 +400,7 @@ void NavigationPolygon::make_polygons_from_outlines() { for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { TPPLPoly &tp = I->get(); - struct Polygon p; + Vector<int> p; for (int64_t i = 0; i < tp.GetNumPoints(); i++) { HashMap<Vector2, int>::Iterator E = points.find(tp[i]); @@ -372,7 +408,7 @@ void NavigationPolygon::make_polygons_from_outlines() { E = points.insert(tp[i], vertices.size()); vertices.push_back(tp[i]); } - p.indices.push_back(E->value); + p.push_back(E->value); } polygons.push_back(p); @@ -400,6 +436,15 @@ real_t NavigationPolygon::get_border_size() const { return border_size; } +void NavigationPolygon::set_sample_partition_type(SamplePartitionType p_value) { + ERR_FAIL_INDEX(p_value, SAMPLE_PARTITION_MAX); + partition_type = p_value; +} + +NavigationPolygon::SamplePartitionType NavigationPolygon::get_sample_partition_type() const { + return partition_type; +} + void NavigationPolygon::set_parsed_geometry_type(ParsedGeometryType p_geometry_type) { ERR_FAIL_INDEX(p_geometry_type, PARSED_GEOMETRY_MAX); parsed_geometry_type = p_geometry_type; @@ -514,6 +559,9 @@ void NavigationPolygon::_bind_methods() { ClassDB::bind_method(D_METHOD("set_border_size", "border_size"), &NavigationPolygon::set_border_size); ClassDB::bind_method(D_METHOD("get_border_size"), &NavigationPolygon::get_border_size); + ClassDB::bind_method(D_METHOD("set_sample_partition_type", "sample_partition_type"), &NavigationPolygon::set_sample_partition_type); + ClassDB::bind_method(D_METHOD("get_sample_partition_type"), &NavigationPolygon::get_sample_partition_type); + ClassDB::bind_method(D_METHOD("set_parsed_geometry_type", "geometry_type"), &NavigationPolygon::set_parsed_geometry_type); ClassDB::bind_method(D_METHOD("get_parsed_geometry_type"), &NavigationPolygon::get_parsed_geometry_type); @@ -543,6 +591,8 @@ void NavigationPolygon::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); + ADD_GROUP("Sampling", "sample_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type", PROPERTY_HINT_ENUM, "Convex Partition,Triangulate"), "set_sample_partition_type", "get_sample_partition_type"); ADD_GROUP("Geometry", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Meshes and Static Colliders"), "set_parsed_geometry_type", "get_parsed_geometry_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "parsed_collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_parsed_collision_mask", "get_parsed_collision_mask"); @@ -559,6 +609,10 @@ void NavigationPolygon::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::RECT2, "baking_rect"), "set_baking_rect", "get_baking_rect"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "baking_rect_offset"), "set_baking_rect_offset", "get_baking_rect_offset"); + BIND_ENUM_CONSTANT(SAMPLE_PARTITION_CONVEX_PARTITION); + BIND_ENUM_CONSTANT(SAMPLE_PARTITION_TRIANGULATE); + BIND_ENUM_CONSTANT(SAMPLE_PARTITION_MAX); + BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES); BIND_ENUM_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS); BIND_ENUM_CONSTANT(PARSED_GEOMETRY_BOTH); diff --git a/scene/resources/2d/navigation_polygon.h b/scene/resources/2d/navigation_polygon.h index 4e99660b0e..86bda47ace 100644 --- a/scene/resources/2d/navigation_polygon.h +++ b/scene/resources/2d/navigation_polygon.h @@ -39,10 +39,7 @@ class NavigationPolygon : public Resource { RWLock rwlock; Vector<Vector2> vertices; - struct Polygon { - Vector<int> indices; - }; - Vector<Polygon> polygons; + Vector<Vector<int>> polygons; Vector<Vector<Vector2>> outlines; Vector<Vector<Vector2>> baked_outlines; @@ -74,6 +71,11 @@ public: Rect2 _edit_get_rect() const; bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; #endif + enum SamplePartitionType { + SAMPLE_PARTITION_CONVEX_PARTITION = 0, + SAMPLE_PARTITION_TRIANGULATE, + SAMPLE_PARTITION_MAX + }; enum ParsedGeometryType { PARSED_GEOMETRY_MESH_INSTANCES = 0, @@ -91,6 +93,7 @@ public: real_t agent_radius = 10.0f; + SamplePartitionType partition_type = SAMPLE_PARTITION_CONVEX_PARTITION; ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_BOTH; uint32_t parsed_collision_mask = 0xFFFFFFFF; @@ -109,6 +112,8 @@ public: Vector<Vector2> get_outline(int p_idx) const; void remove_outline(int p_idx); int get_outline_count() const; + void set_outlines(const Vector<Vector<Vector2>> &p_outlines); + Vector<Vector<Vector2>> get_outlines() const; void clear_outlines(); #ifndef DISABLE_DEPRECATED @@ -116,10 +121,13 @@ public: #endif // DISABLE_DEPRECATED void set_polygons(const Vector<Vector<int>> &p_polygons); - const Vector<Vector<int>> &get_polygons() const; + Vector<Vector<int>> get_polygons() const; Vector<int> get_polygon(int p_idx); void clear_polygons(); + void set_sample_partition_type(SamplePartitionType p_value); + SamplePartitionType get_sample_partition_type() const; + void set_parsed_geometry_type(ParsedGeometryType p_geometry_type); ParsedGeometryType get_parsed_geometry_type() const; @@ -155,12 +163,15 @@ public: void clear(); void set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons); + void set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons, const Vector<Vector<Vector2>> &p_outlines); void get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons); + void get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons, Vector<Vector<Vector2>> &r_outlines); NavigationPolygon() {} ~NavigationPolygon() {} }; +VARIANT_ENUM_CAST(NavigationPolygon::SamplePartitionType); VARIANT_ENUM_CAST(NavigationPolygon::ParsedGeometryType); VARIANT_ENUM_CAST(NavigationPolygon::SourceGeometryMode); diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp index 6a799e90ce..dd6ae5096a 100644 --- a/scene/resources/2d/tile_set.cpp +++ b/scene/resources/2d/tile_set.cpp @@ -6502,18 +6502,19 @@ Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id, bool p_f transformed_polygon.instantiate(); PackedVector2Array new_points = get_transformed_vertices(layer_tile_data.navigation_polygon->get_vertices(), p_flip_h, p_flip_v, p_transpose); - transformed_polygon->set_vertices(new_points); - int num_polygons = layer_tile_data.navigation_polygon->get_polygon_count(); - for (int i = 0; i < num_polygons; ++i) { - transformed_polygon->add_polygon(layer_tile_data.navigation_polygon->get_polygon(i)); - } + const Vector<Vector<Vector2>> outlines = layer_tile_data.navigation_polygon->get_outlines(); + int outline_count = outlines.size(); + + Vector<Vector<Vector2>> new_outlines; + new_outlines.resize(outline_count); - 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); + 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/3d/importer_mesh.cpp b/scene/resources/3d/importer_mesh.cpp index 91531699b4..b0633c06b9 100644 --- a/scene/resources/3d/importer_mesh.cpp +++ b/scene/resources/3d/importer_mesh.cpp @@ -256,6 +256,33 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma mesh.unref(); } +void ImporterMesh::optimize_indices_for_cache() { + if (!SurfaceTool::optimize_vertex_cache_func) { + return; + } + + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) { + continue; + } + + Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX]; + PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX]; + + unsigned int index_count = indices.size(); + unsigned int vertex_count = vertices.size(); + + if (index_count == 0) { + continue; + } + + int *indices_ptr = indices.ptrw(); + SurfaceTool::optimize_vertex_cache_func((unsigned int *)indices_ptr, (const unsigned int *)indices_ptr, index_count, vertex_count); + + surfaces.write[i].arrays[RS::ARRAY_INDEX] = indices; + } +} + #define VERTEX_SKIN_FUNC(bone_count, vert_idx, read_array, write_array, transform_array, bone_array, weight_array) \ Vector3 transformed_vert; \ for (unsigned int weight_idx = 0; weight_idx < bone_count; weight_idx++) { \ @@ -822,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. @@ -840,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; } } @@ -1073,9 +1108,12 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { } HashMap<Vector3, int> unique_vertices; - LocalVector<int> face_indices; + Vector<Vector<int>> face_polygons; + face_polygons.resize(faces.size()); for (int i = 0; i < faces.size(); i++) { + Vector<int> face_indices; + face_indices.resize(3); for (int j = 0; j < 3; j++) { Vector3 v = faces[i].vertex[j]; int idx; @@ -1085,8 +1123,9 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { idx = unique_vertices.size(); unique_vertices[v] = idx; } - face_indices.push_back(idx); + face_indices.write[j] = idx; } + face_polygons.write[i] = face_indices; } Vector<Vector3> vertices; @@ -1097,16 +1136,7 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { Ref<NavigationMesh> nm; nm.instantiate(); - nm->set_vertices(vertices); - - Vector<int> v3; - v3.resize(3); - for (uint32_t i = 0; i < face_indices.size(); i += 3) { - v3.write[0] = face_indices[i + 0]; - v3.write[1] = face_indices[i + 1]; - v3.write[2] = face_indices[i + 2]; - nm->add_polygon(v3); - } + nm->set_data(vertices, face_polygons); return nm; } diff --git a/scene/resources/3d/importer_mesh.h b/scene/resources/3d/importer_mesh.h index 777f936030..5eb4ee884e 100644 --- a/scene/resources/3d/importer_mesh.h +++ b/scene/resources/3d/importer_mesh.h @@ -114,6 +114,8 @@ public: void set_surface_material(int p_surface, const Ref<Material> &p_material); + 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(); diff --git a/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp index 4c9f381aba..c55e25fcae 100644 --- a/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp +++ b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp @@ -33,6 +33,7 @@ void NavigationMeshSourceGeometryData3D::set_vertices(const Vector<float> &p_vertices) { RWLockWrite write_lock(geometry_rwlock); vertices = p_vertices; + bounds_dirty = true; } const Vector<float> &NavigationMeshSourceGeometryData3D::get_vertices() const { @@ -44,6 +45,7 @@ void NavigationMeshSourceGeometryData3D::set_indices(const Vector<int> &p_indice ERR_FAIL_COND(vertices.size() < p_indices.size()); RWLockWrite write_lock(geometry_rwlock); indices = p_indices; + bounds_dirty = true; } const Vector<int> &NavigationMeshSourceGeometryData3D::get_indices() const { @@ -63,6 +65,7 @@ void NavigationMeshSourceGeometryData3D::append_arrays(const Vector<float> &p_ve for (int64_t i = number_of_indices_before_merge; i < indices.size(); i++) { indices.set(i, indices[i] + number_of_vertices_before_merge / 3); } + bounds_dirty = true; } bool NavigationMeshSourceGeometryData3D::has_data() { @@ -75,11 +78,13 @@ void NavigationMeshSourceGeometryData3D::clear() { vertices.clear(); indices.clear(); _projected_obstructions.clear(); + bounds_dirty = true; } void NavigationMeshSourceGeometryData3D::clear_projected_obstructions() { RWLockWrite write_lock(geometry_rwlock); _projected_obstructions.clear(); + bounds_dirty = true; } void NavigationMeshSourceGeometryData3D::_add_vertex(const Vector3 &p_vec3) { @@ -207,12 +212,14 @@ void NavigationMeshSourceGeometryData3D::add_mesh_array(const Array &p_mesh_arra ERR_FAIL_COND(p_mesh_array.size() != Mesh::ARRAY_MAX); RWLockWrite write_lock(geometry_rwlock); _add_mesh_array(p_mesh_array, root_node_transform * p_xform); + bounds_dirty = true; } void NavigationMeshSourceGeometryData3D::add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform) { ERR_FAIL_COND(p_faces.size() % 3 != 0); RWLockWrite write_lock(geometry_rwlock); _add_faces(p_faces, root_node_transform * p_xform); + bounds_dirty = true; } void NavigationMeshSourceGeometryData3D::merge(const Ref<NavigationMeshSourceGeometryData3D> &p_other_geometry) { @@ -236,6 +243,7 @@ void NavigationMeshSourceGeometryData3D::merge(const Ref<NavigationMeshSourceGeo } _projected_obstructions.append_array(other_projected_obstructions); + bounds_dirty = true; } void NavigationMeshSourceGeometryData3D::add_projected_obstruction(const Vector<Vector3> &p_vertices, float p_elevation, float p_height, bool p_carve) { @@ -259,6 +267,7 @@ void NavigationMeshSourceGeometryData3D::add_projected_obstruction(const Vector< RWLockWrite write_lock(geometry_rwlock); _projected_obstructions.push_back(projected_obstruction); + bounds_dirty = true; } void NavigationMeshSourceGeometryData3D::set_projected_obstructions(const Array &p_array) { @@ -285,6 +294,7 @@ void NavigationMeshSourceGeometryData3D::set_projected_obstructions(const Array RWLockWrite write_lock(geometry_rwlock); _projected_obstructions.push_back(projected_obstruction); + bounds_dirty = true; } } @@ -336,6 +346,7 @@ void NavigationMeshSourceGeometryData3D::set_data(const Vector<float> &p_vertice vertices = p_vertices; indices = p_indices; _projected_obstructions = p_projected_obstructions; + bounds_dirty = true; } void NavigationMeshSourceGeometryData3D::get_data(Vector<float> &r_vertices, Vector<int> &r_indices, Vector<ProjectedObstruction> &r_projected_obstructions) { @@ -345,6 +356,45 @@ void NavigationMeshSourceGeometryData3D::get_data(Vector<float> &r_vertices, Vec r_projected_obstructions = _projected_obstructions; } +AABB NavigationMeshSourceGeometryData3D::get_bounds() { + geometry_rwlock.read_lock(); + + if (bounds_dirty) { + geometry_rwlock.read_unlock(); + RWLockWrite write_lock(geometry_rwlock); + + bounds_dirty = false; + bounds = AABB(); + bool first_vertex = true; + + for (int i = 0; i < vertices.size() / 3; i++) { + const Vector3 vertex = Vector3(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]); + if (first_vertex) { + first_vertex = false; + bounds.position = vertex; + } else { + bounds.expand_to(vertex); + } + } + for (const ProjectedObstruction &projected_obstruction : _projected_obstructions) { + for (int i = 0; i < projected_obstruction.vertices.size() / 3; i++) { + const Vector3 vertex = Vector3(projected_obstruction.vertices[i * 3], projected_obstruction.vertices[i * 3 + 1], projected_obstruction.vertices[i * 3 + 2]); + if (first_vertex) { + first_vertex = false; + bounds.position = vertex; + } else { + bounds.expand_to(vertex); + } + } + } + } else { + geometry_rwlock.read_unlock(); + } + + RWLockRead read_lock(geometry_rwlock); + return bounds; +} + void NavigationMeshSourceGeometryData3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationMeshSourceGeometryData3D::set_vertices); ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationMeshSourceGeometryData3D::get_vertices); @@ -367,6 +417,8 @@ void NavigationMeshSourceGeometryData3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_projected_obstructions", "projected_obstructions"), &NavigationMeshSourceGeometryData3D::set_projected_obstructions); ClassDB::bind_method(D_METHOD("get_projected_obstructions"), &NavigationMeshSourceGeometryData3D::get_projected_obstructions); + ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationMeshSourceGeometryData3D::get_bounds); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_indices", "get_indices"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "projected_obstructions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_projected_obstructions", "get_projected_obstructions"); diff --git a/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h index a8e613a51d..d7e3c3071c 100644 --- a/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h +++ b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h @@ -41,6 +41,9 @@ class NavigationMeshSourceGeometryData3D : public Resource { Vector<float> vertices; Vector<int> indices; + AABB bounds; + bool bounds_dirty = true; + public: struct ProjectedObstruction; @@ -101,6 +104,8 @@ public: void set_data(const Vector<float> &p_vertices, const Vector<int> &p_indices, Vector<ProjectedObstruction> &p_projected_obstructions); void get_data(Vector<float> &r_vertices, Vector<int> &r_indices, Vector<ProjectedObstruction> &r_projected_obstructions); + AABB get_bounds(); + NavigationMeshSourceGeometryData3D() {} ~NavigationMeshSourceGeometryData3D() { clear(); } }; diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index 8cddfb5840..bf3caa1edd 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -202,6 +202,10 @@ Vector<float> MultiMesh::get_buffer() const { return RS::get_singleton()->multimesh_get_buffer(multimesh); } +void MultiMesh::set_buffer_interpolated(const Vector<float> &p_buffer_curr, const Vector<float> &p_buffer_prev) { + RS::get_singleton()->multimesh_set_buffer_interpolated(multimesh, p_buffer_curr, p_buffer_prev); +} + void MultiMesh::set_mesh(const Ref<Mesh> &p_mesh) { mesh = p_mesh; if (!mesh.is_null()) { @@ -236,6 +240,11 @@ int MultiMesh::get_visible_instance_count() const { return visible_instance_count; } +void MultiMesh::set_physics_interpolation_quality(PhysicsInterpolationQuality p_quality) { + _physics_interpolation_quality = p_quality; + RenderingServer::get_singleton()->multimesh_set_physics_interpolation_quality(multimesh, (RS::MultimeshPhysicsInterpolationQuality)p_quality); +} + void MultiMesh::set_instance_transform(int p_instance, const Transform3D &p_transform) { RenderingServer::get_singleton()->multimesh_instance_set_transform(multimesh, p_instance, p_transform); } @@ -269,6 +278,14 @@ Color MultiMesh::get_instance_custom_data(int p_instance) const { return RenderingServer::get_singleton()->multimesh_instance_get_custom_data(multimesh, p_instance); } +void MultiMesh::reset_instance_physics_interpolation(int p_instance) { + RenderingServer::get_singleton()->multimesh_instance_reset_physics_interpolation(multimesh, p_instance); +} + +void MultiMesh::set_physics_interpolated(bool p_interpolated) { + RenderingServer::get_singleton()->multimesh_set_physics_interpolated(multimesh, p_interpolated); +} + void MultiMesh::set_custom_aabb(const AABB &p_custom) { custom_aabb = p_custom; RS::get_singleton()->multimesh_set_custom_aabb(multimesh, custom_aabb); @@ -328,6 +345,8 @@ void MultiMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_instance_count"), &MultiMesh::get_instance_count); ClassDB::bind_method(D_METHOD("set_visible_instance_count", "count"), &MultiMesh::set_visible_instance_count); ClassDB::bind_method(D_METHOD("get_visible_instance_count"), &MultiMesh::get_visible_instance_count); + ClassDB::bind_method(D_METHOD("set_physics_interpolation_quality", "quality"), &MultiMesh::set_physics_interpolation_quality); + ClassDB::bind_method(D_METHOD("get_physics_interpolation_quality"), &MultiMesh::get_physics_interpolation_quality); ClassDB::bind_method(D_METHOD("set_instance_transform", "instance", "transform"), &MultiMesh::set_instance_transform); ClassDB::bind_method(D_METHOD("set_instance_transform_2d", "instance", "transform"), &MultiMesh::set_instance_transform_2d); ClassDB::bind_method(D_METHOD("get_instance_transform", "instance"), &MultiMesh::get_instance_transform); @@ -336,6 +355,7 @@ void MultiMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_instance_color", "instance"), &MultiMesh::get_instance_color); ClassDB::bind_method(D_METHOD("set_instance_custom_data", "instance", "custom_data"), &MultiMesh::set_instance_custom_data); ClassDB::bind_method(D_METHOD("get_instance_custom_data", "instance"), &MultiMesh::get_instance_custom_data); + ClassDB::bind_method(D_METHOD("reset_instance_physics_interpolation", "instance"), &MultiMesh::reset_instance_physics_interpolation); ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &MultiMesh::set_custom_aabb); ClassDB::bind_method(D_METHOD("get_custom_aabb"), &MultiMesh::get_custom_aabb); ClassDB::bind_method(D_METHOD("get_aabb"), &MultiMesh::get_aabb); @@ -343,6 +363,8 @@ void MultiMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_buffer"), &MultiMesh::get_buffer); ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &MultiMesh::set_buffer); + ClassDB::bind_method(D_METHOD("set_buffer_interpolated", "buffer_curr", "buffer_prev"), &MultiMesh::set_buffer_interpolated); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_colors"), "set_use_colors", "is_using_colors"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_custom_data"), "set_use_custom_data", "is_using_custom_data"); @@ -369,8 +391,14 @@ void MultiMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "custom_data_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_custom_data_array", "_get_custom_data_array"); #endif + ADD_GROUP("Physics Interpolation", "physics_interpolation"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_interpolation_quality", PROPERTY_HINT_ENUM, "Fast,High"), "set_physics_interpolation_quality", "get_physics_interpolation_quality"); + BIND_ENUM_CONSTANT(TRANSFORM_2D); BIND_ENUM_CONSTANT(TRANSFORM_3D); + + BIND_ENUM_CONSTANT(INTERP_QUALITY_FAST); + BIND_ENUM_CONSTANT(INTERP_QUALITY_HIGH); } MultiMesh::MultiMesh() { diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h index d7bcb13162..03505bb4d1 100644 --- a/scene/resources/multimesh.h +++ b/scene/resources/multimesh.h @@ -44,6 +44,11 @@ public: TRANSFORM_3D = RS::MULTIMESH_TRANSFORM_3D }; + enum PhysicsInterpolationQuality { + INTERP_QUALITY_FAST, + INTERP_QUALITY_HIGH, + }; + private: Ref<Mesh> mesh; RID multimesh; @@ -53,6 +58,7 @@ private: bool use_custom_data = false; int instance_count = 0; int visible_instance_count = -1; + PhysicsInterpolationQuality _physics_interpolation_quality = INTERP_QUALITY_FAST; protected: static void _bind_methods(); @@ -74,6 +80,8 @@ protected: void set_buffer(const Vector<float> &p_buffer); Vector<float> get_buffer() const; + void set_buffer_interpolated(const Vector<float> &p_buffer_curr, const Vector<float> &p_buffer_prev); + public: void set_mesh(const Ref<Mesh> &p_mesh); Ref<Mesh> get_mesh() const; @@ -93,6 +101,9 @@ public: void set_visible_instance_count(int p_count); int get_visible_instance_count() const; + void set_physics_interpolation_quality(PhysicsInterpolationQuality p_quality); + PhysicsInterpolationQuality get_physics_interpolation_quality() const { return _physics_interpolation_quality; } + void set_instance_transform(int p_instance, const Transform3D &p_transform); void set_instance_transform_2d(int p_instance, const Transform2D &p_transform); Transform3D get_instance_transform(int p_instance) const; @@ -104,6 +115,10 @@ public: void set_instance_custom_data(int p_instance, const Color &p_custom_data); Color get_instance_custom_data(int p_instance) const; + void reset_instance_physics_interpolation(int p_instance); + + void set_physics_interpolated(bool p_interpolated); + void set_custom_aabb(const AABB &p_custom); AABB get_custom_aabb() const; @@ -116,5 +131,6 @@ public: }; VARIANT_ENUM_CAST(MultiMesh::TransformFormat); +VARIANT_ENUM_CAST(MultiMesh::PhysicsInterpolationQuality); #endif // MULTIMESH_H diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 2866ae7219..67ed65df0d 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -61,12 +61,12 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { int rlen = iarr.size(); const int *r = iarr.ptr(); + Vector<int> polygon; for (int j = 0; j < rlen; j += 3) { - Polygon polygon; - polygon.indices.resize(3); - polygon.indices.write[0] = r[j + 0] + from; - polygon.indices.write[1] = r[j + 1] + from; - polygon.indices.write[2] = r[j + 2] + from; + polygon.resize(3); + polygon.write[0] = r[j + 0] + from; + polygon.write[1] = r[j + 1] + from; + polygon.write[2] = r[j + 2] + from; polygons.push_back(polygon); } } @@ -318,7 +318,7 @@ void NavigationMesh::_set_polygons(const Array &p_array) { RWLockWrite write_lock(rwlock); polygons.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - polygons.write[i].indices = p_array[i]; + polygons.write[i] = p_array[i]; } notify_property_list_changed(); } @@ -328,17 +328,26 @@ Array NavigationMesh::_get_polygons() const { Array ret; ret.resize(polygons.size()); for (int i = 0; i < ret.size(); i++) { - ret[i] = polygons[i].indices; + ret[i] = polygons[i]; } return ret; } +void NavigationMesh::set_polygons(const Vector<Vector<int>> &p_polygons) { + RWLockWrite write_lock(rwlock); + polygons = p_polygons; + notify_property_list_changed(); +} + +Vector<Vector<int>> NavigationMesh::get_polygons() const { + RWLockRead read_lock(rwlock); + return polygons; +} + void NavigationMesh::add_polygon(const Vector<int> &p_polygon) { RWLockWrite write_lock(rwlock); - Polygon polygon; - polygon.indices = p_polygon; - polygons.push_back(polygon); + polygons.push_back(p_polygon); notify_property_list_changed(); } @@ -350,7 +359,7 @@ int NavigationMesh::get_polygon_count() const { Vector<int> NavigationMesh::get_polygon(int p_idx) { RWLockRead read_lock(rwlock); ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>()); - return polygons[p_idx].indices; + return polygons[p_idx]; } void NavigationMesh::clear_polygons() { @@ -367,19 +376,13 @@ void NavigationMesh::clear() { void NavigationMesh::set_data(const Vector<Vector3> &p_vertices, const Vector<Vector<int>> &p_polygons) { RWLockWrite write_lock(rwlock); vertices = p_vertices; - polygons.resize(p_polygons.size()); - for (int i = 0; i < p_polygons.size(); i++) { - polygons.write[i].indices = p_polygons[i]; - } + polygons = p_polygons; } void NavigationMesh::get_data(Vector<Vector3> &r_vertices, Vector<Vector<int>> &r_polygons) { RWLockRead read_lock(rwlock); r_vertices = vertices; - r_polygons.resize(polygons.size()); - for (int i = 0; i < polygons.size(); i++) { - r_polygons.write[i] = polygons[i].indices; - } + r_polygons = polygons; } #ifdef DEBUG_ENABLED diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index 0ec2cc1bb1..741cea0791 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -39,10 +39,7 @@ class NavigationMesh : public Resource { RWLock rwlock; Vector<Vector3> vertices; - struct Polygon { - Vector<int> indices; - }; - Vector<Polygon> polygons; + Vector<Vector<int>> polygons; Ref<ArrayMesh> debug_mesh; protected: @@ -96,7 +93,7 @@ protected: float detail_sample_max_error = 1.0f; SamplePartitionType partition_type = SAMPLE_PARTITION_WATERSHED; - ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES; + ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_BOTH; uint32_t collision_mask = 0xFFFFFFFF; SourceGeometryMode source_geometry_mode = SOURCE_GEOMETRY_ROOT_NODE_CHILDREN; @@ -194,6 +191,8 @@ public: int get_polygon_count() const; Vector<int> get_polygon(int p_idx); void clear_polygons(); + void set_polygons(const Vector<Vector<int>> &p_polygons); + Vector<Vector<int>> get_polygons() const; void clear(); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 5f70a24fcd..5e148c9276 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -5368,6 +5368,20 @@ String VisualShaderNodeIntParameter::generate_global(Shader::Mode p_mode, Visual code += _get_qual_str() + "uniform int " + get_parameter_name() + " : hint_range(" + itos(hint_range_min) + ", " + itos(hint_range_max) + ")"; } else if (hint == HINT_RANGE_STEP) { code += _get_qual_str() + "uniform int " + get_parameter_name() + " : hint_range(" + itos(hint_range_min) + ", " + itos(hint_range_max) + ", " + itos(hint_range_step) + ")"; + } else if (hint == HINT_ENUM) { + code += _get_qual_str() + "uniform int " + get_parameter_name() + " : hint_enum("; + + bool first = true; + for (const String &_name : hint_enum_names) { + if (first) { + first = false; + } else { + code += ", "; + } + code += "\"" + _name.c_escape() + "\""; + } + + code += ")"; } else { code += _get_qual_str() + "uniform int " + get_parameter_name(); } @@ -5439,6 +5453,18 @@ int VisualShaderNodeIntParameter::get_step() const { return hint_range_step; } +void VisualShaderNodeIntParameter::set_enum_names(const PackedStringArray &p_names) { + if (hint_enum_names == p_names) { + return; + } + hint_enum_names = p_names; + emit_changed(); +} + +PackedStringArray VisualShaderNodeIntParameter::get_enum_names() const { + return hint_enum_names; +} + void VisualShaderNodeIntParameter::set_default_value_enabled(bool p_default_value_enabled) { if (default_value_enabled == p_default_value_enabled) { return; @@ -5476,22 +5502,27 @@ void VisualShaderNodeIntParameter::_bind_methods() { ClassDB::bind_method(D_METHOD("set_step", "value"), &VisualShaderNodeIntParameter::set_step); ClassDB::bind_method(D_METHOD("get_step"), &VisualShaderNodeIntParameter::get_step); + ClassDB::bind_method(D_METHOD("set_enum_names", "names"), &VisualShaderNodeIntParameter::set_enum_names); + ClassDB::bind_method(D_METHOD("get_enum_names"), &VisualShaderNodeIntParameter::get_enum_names); + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeIntParameter::set_default_value_enabled); ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeIntParameter::is_default_value_enabled); ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeIntParameter::set_default_value); ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeIntParameter::get_default_value); - ADD_PROPERTY(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,Range + Step"), "set_hint", "get_hint"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,Range + Step,Enum"), "set_hint", "get_hint"); ADD_PROPERTY(PropertyInfo(Variant::INT, "min"), "set_min", "get_min"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max"), "set_max", "get_max"); ADD_PROPERTY(PropertyInfo(Variant::INT, "step"), "set_step", "get_step"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "enum_names"), "set_enum_names", "get_enum_names"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "default_value"), "set_default_value", "get_default_value"); BIND_ENUM_CONSTANT(HINT_NONE); BIND_ENUM_CONSTANT(HINT_RANGE); BIND_ENUM_CONSTANT(HINT_RANGE_STEP); + BIND_ENUM_CONSTANT(HINT_ENUM); BIND_ENUM_CONSTANT(HINT_MAX); } @@ -5513,6 +5544,9 @@ Vector<StringName> VisualShaderNodeIntParameter::get_editable_properties() const if (hint == HINT_RANGE_STEP) { props.push_back("step"); } + if (hint == HINT_ENUM) { + props.push_back("enum_names"); + } props.push_back("default_value_enabled"); if (default_value_enabled) { props.push_back("default_value"); diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 7a37ffa0e0..279599ef9c 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -2115,6 +2115,7 @@ public: HINT_NONE, HINT_RANGE, HINT_RANGE_STEP, + HINT_ENUM, HINT_MAX, }; @@ -2123,6 +2124,7 @@ private: int hint_range_min = 0; int hint_range_max = 100; int hint_range_step = 1; + PackedStringArray hint_enum_names; bool default_value_enabled = false; int default_value = 0; @@ -2158,6 +2160,9 @@ public: void set_step(int p_value); int get_step() const; + void set_enum_names(const PackedStringArray &p_names); + PackedStringArray get_enum_names() const; + void set_default_value_enabled(bool p_enabled); bool is_default_value_enabled() const; diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp index 9df05ba94a..aebed4b432 100644 --- a/servers/movie_writer/movie_writer.cpp +++ b/servers/movie_writer/movie_writer.cpp @@ -185,6 +185,10 @@ void MovieWriter::add_frame() { RID main_vp_rid = RenderingServer::get_singleton()->viewport_find_from_screen_attachment(DisplayServer::MAIN_WINDOW_ID); RID main_vp_texture = RenderingServer::get_singleton()->viewport_get_texture(main_vp_rid); Ref<Image> vp_tex = RenderingServer::get_singleton()->texture_2d_get(main_vp_texture); + if (RenderingServer::get_singleton()->viewport_is_using_hdr_2d(main_vp_rid)) { + vp_tex->convert(Image::FORMAT_RGBA8); + vp_tex->linear_to_srgb(); + } RenderingServer::get_singleton()->viewport_set_measure_render_time(main_vp_rid, true); cpu_time += RenderingServer::get_singleton()->viewport_get_measured_render_time_cpu(main_vp_rid); diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index b21c6b60f0..398ff1e1f3 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -216,6 +216,7 @@ void NavigationServer3D::_bind_methods() { BIND_ENUM_CONSTANT(INFO_EDGE_MERGE_COUNT); BIND_ENUM_CONSTANT(INFO_EDGE_CONNECTION_COUNT); BIND_ENUM_CONSTANT(INFO_EDGE_FREE_COUNT); + BIND_ENUM_CONSTANT(INFO_OBSTACLE_COUNT); } NavigationServer3D *NavigationServer3D::get_singleton() { diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 17c0771732..cdacf8e7e4 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -369,6 +369,7 @@ public: INFO_EDGE_MERGE_COUNT, INFO_EDGE_CONNECTION_COUNT, INFO_EDGE_FREE_COUNT, + INFO_OBSTACLE_COUNT, }; virtual int get_process_info(ProcessInfo p_info) const = 0; diff --git a/servers/rendering/dummy/rasterizer_scene_dummy.h b/servers/rendering/dummy/rasterizer_scene_dummy.h index 083493003f..a699a58b1f 100644 --- a/servers/rendering/dummy/rasterizer_scene_dummy.h +++ b/servers/rendering/dummy/rasterizer_scene_dummy.h @@ -186,6 +186,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) override {} virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override {} + virtual void lightmaps_set_bicubic_filter(bool p_enable) override {} RasterizerSceneDummy() {} ~RasterizerSceneDummy() {} diff --git a/servers/rendering/dummy/storage/material_storage.cpp b/servers/rendering/dummy/storage/material_storage.cpp index 64f6b55172..e8b553ca76 100644 --- a/servers/rendering/dummy/storage/material_storage.cpp +++ b/servers/rendering/dummy/storage/material_storage.cpp @@ -102,11 +102,7 @@ void MaterialStorage::get_shader_parameter_list(RID p_shader, List<PropertyInfo> if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { continue; } - if (E.value.texture_order >= 0) { - filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000)); - } else { - filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order)); - } + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.prop_order)); } int uniform_count = filtered_uniforms.size(); sorter.sort(filtered_uniforms.ptr(), uniform_count); diff --git a/servers/rendering/dummy/storage/mesh_storage.cpp b/servers/rendering/dummy/storage/mesh_storage.cpp index 0b7ade1762..3f90c80826 100644 --- a/servers/rendering/dummy/storage/mesh_storage.cpp +++ b/servers/rendering/dummy/storage/mesh_storage.cpp @@ -64,22 +64,22 @@ void MeshStorage::mesh_clear(RID p_mesh) { m->surfaces.clear(); } -RID MeshStorage::multimesh_allocate() { +RID MeshStorage::_multimesh_allocate() { return multimesh_owner.allocate_rid(); } -void MeshStorage::multimesh_initialize(RID p_rid) { +void MeshStorage::_multimesh_initialize(RID p_rid) { multimesh_owner.initialize_rid(p_rid, DummyMultiMesh()); } -void MeshStorage::multimesh_free(RID p_rid) { +void MeshStorage::_multimesh_free(RID p_rid) { DummyMultiMesh *multimesh = multimesh_owner.get_or_null(p_rid); ERR_FAIL_NULL(multimesh); multimesh_owner.free(p_rid); } -void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { +void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { DummyMultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); multimesh->buffer.resize(p_buffer.size()); @@ -87,7 +87,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b memcpy(cache_data, p_buffer.ptr(), p_buffer.size() * sizeof(float)); } -Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { +Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const { DummyMultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Vector<float>()); diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h index d98b2e2ee7..ec19562147 100644 --- a/servers/rendering/dummy/storage/mesh_storage.h +++ b/servers/rendering/dummy/storage/mesh_storage.h @@ -146,34 +146,36 @@ public: bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); } - virtual RID multimesh_allocate() override; - virtual void multimesh_initialize(RID p_rid) override; - virtual void multimesh_free(RID p_rid) override; - - virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override {} - virtual int multimesh_get_instance_count(RID p_multimesh) const override { return 0; } - - virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override {} - virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override {} - virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override {} - virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override {} - virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override {} - - virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override {} - virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override { return AABB(); } - - virtual RID multimesh_get_mesh(RID p_multimesh) const override { return RID(); } - virtual AABB multimesh_get_aabb(RID p_multimesh) const override { return AABB(); } - - virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform3D(); } - virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); } - virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); } - virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); } - virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; - virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override; - - virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override {} - virtual int multimesh_get_visible_instances(RID p_multimesh) const override { return 0; } + virtual RID _multimesh_allocate() override; + virtual void _multimesh_initialize(RID p_rid) override; + virtual void _multimesh_free(RID p_rid) override; + + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override {} + virtual int _multimesh_get_instance_count(RID p_multimesh) const override { return 0; } + + virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override {} + virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override {} + virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override {} + virtual void _multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override {} + virtual void _multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override {} + + virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override {} + virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override { return AABB(); } + + virtual RID _multimesh_get_mesh(RID p_multimesh) const override { return RID(); } + virtual AABB _multimesh_get_aabb(RID p_multimesh) const override { return AABB(); } + + virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform3D(); } + virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); } + virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); } + virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); } + virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; + virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override; + + virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override {} + virtual int _multimesh_get_visible_instances(RID p_multimesh) const override { return 0; } + + MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override { return nullptr; } /* SKELETON API */ diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index c4286dcc0c..0ec161d8cf 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -51,7 +51,7 @@ void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas memset(z_last_list, 0, z_range * sizeof(RendererCanvasRender::Item *)); for (int i = 0; i < p_child_item_count; i++) { - _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask, p_child_items[i].mirror, 1); + _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask, Point2(), 1, nullptr); } RendererCanvasRender::Item *list = nullptr; @@ -97,6 +97,7 @@ void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, const Tran if (!child_items[i]->repeat_source) { child_items[i]->repeat_size = p_canvas_item->repeat_size; child_items[i]->repeat_times = p_canvas_item->repeat_times; + child_items[i]->repeat_source_item = p_canvas_item->repeat_source_item; } // Y sorted canvas items are flattened into r_items. Calculate their absolute z index to use when rendering r_items. @@ -229,10 +230,13 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item * ci->visibility_notifier->visible_in_frame = RSG::rasterizer->get_frame_number(); } + } else if (ci->repeat_source) { + // If repeat source does not draw itself it still needs transform updated as its child items' repeat offsets are relative to it. + ci->final_transform = p_transform; } } -void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times) { +void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item) { Item *ci = p_canvas_item; if (!ci->visible) { @@ -268,19 +272,16 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 Point2 repeat_size = p_repeat_size; int repeat_times = p_repeat_times; + RendererCanvasRender::Item *repeat_source_item = p_repeat_source_item; if (ci->repeat_source) { repeat_size = ci->repeat_size; repeat_times = ci->repeat_times; + repeat_source_item = ci; } else { ci->repeat_size = repeat_size; ci->repeat_times = repeat_times; - } - - if (repeat_size.x || repeat_size.y) { - Size2 scale = final_xform.get_scale(); - rect.size += repeat_size * repeat_times / scale; - rect.position -= repeat_size / scale * (repeat_times / 2); + ci->repeat_source_item = repeat_source_item; } if (snapping_2d_transforms_to_pixel) { @@ -291,6 +292,25 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 final_xform = parent_xform * final_xform; Rect2 global_rect = final_xform.xform(rect); + if (repeat_source_item && (repeat_size.x || repeat_size.y)) { + // Top-left repeated rect. + Rect2 corner_rect = global_rect; + corner_rect.position -= repeat_source_item->final_transform.basis_xform((repeat_times / 2) * repeat_size); + global_rect = corner_rect; + + // Plus top-right repeated rect. + Size2 size_x_offset = repeat_source_item->final_transform.basis_xform(repeat_times * Size2(repeat_size.x, 0)); + corner_rect.position += size_x_offset; + global_rect = global_rect.merge(corner_rect); + + // Plus bottom-right repeated rect. + corner_rect.position += repeat_source_item->final_transform.basis_xform(repeat_times * Size2(0, repeat_size.y)); + global_rect = global_rect.merge(corner_rect); + + // Plus bottom-left repeated rect. + corner_rect.position -= size_x_offset; + global_rect = global_rect.merge(corner_rect); + } global_rect.position += p_clip_rect.position; if (ci->use_parent_material && p_material_owner) { @@ -357,7 +377,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 sorter.sort(child_items, child_item_count); for (i = 0; i < child_item_count; i++) { - _cull_canvas_item(child_items[i], final_xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask, child_items[i]->repeat_size, child_items[i]->repeat_times); + _cull_canvas_item(child_items[i], final_xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask, child_items[i]->repeat_size, child_items[i]->repeat_times, child_items[i]->repeat_source_item); } } else { RendererCanvasRender::Item *canvas_group_from = nullptr; @@ -381,14 +401,14 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 if (!child_items[i]->behind && !use_canvas_group) { continue; } - _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times); + _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item); } _attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, final_xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from); for (int i = 0; i < child_item_count; i++) { if (child_items[i]->behind || use_canvas_group) { continue; } - _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times); + _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item); } } } @@ -431,14 +451,22 @@ void RendererCanvasCull::canvas_set_item_mirroring(RID p_canvas, RID p_item, con int idx = canvas->find_item(canvas_item); ERR_FAIL_COND(idx == -1); - canvas->child_items.write[idx].mirror = p_mirroring; + + bool is_repeat_source = (p_mirroring.x || p_mirroring.y); + canvas_item->repeat_source = is_repeat_source; + canvas_item->repeat_source_item = is_repeat_source ? canvas_item : nullptr; + canvas_item->repeat_size = p_mirroring; + canvas_item->repeat_times = 1; } void RendererCanvasCull::canvas_set_item_repeat(RID p_item, const Point2 &p_repeat_size, int p_repeat_times) { + ERR_FAIL_COND(p_repeat_times < 0); Item *canvas_item = canvas_item_owner.get_or_null(p_item); ERR_FAIL_NULL(canvas_item); - canvas_item->repeat_source = true; + bool is_repeat_source = (p_repeat_size.x || p_repeat_size.y) && p_repeat_times; + canvas_item->repeat_source = is_repeat_source; + canvas_item->repeat_source_item = is_repeat_source ? canvas_item : nullptr; canvas_item->repeat_size = p_repeat_size; canvas_item->repeat_times = p_repeat_times; } diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index 9f8cbea2e9..91c03054f7 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -126,7 +126,6 @@ public: struct Canvas : public RendererViewport::CanvasBase { HashSet<RID> viewports; struct ChildItem { - Point2 mirror; Item *item = nullptr; bool operator<(const ChildItem &p_item) const { return item->index < p_item.item->index; @@ -188,7 +187,7 @@ public: private: void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info = nullptr); - void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times); + void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item); static constexpr int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1; diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h index cb8180f989..c57abee165 100644 --- a/servers/rendering/renderer_canvas_render.h +++ b/servers/rendering/renderer_canvas_render.h @@ -364,6 +364,7 @@ public: bool repeat_source; Point2 repeat_size; int repeat_times = 1; + Item *repeat_source_item = nullptr; Rect2 global_rect_cache; diff --git a/servers/rendering/renderer_rd/effects/fsr2.cpp b/servers/rendering/renderer_rd/effects/fsr2.cpp index 925352a7d1..551ea5dd97 100644 --- a/servers/rendering/renderer_rd/effects/fsr2.cpp +++ b/servers/rendering/renderer_rd/effects/fsr2.cpp @@ -800,9 +800,6 @@ FSR2Effect::~FSR2Effect() { RD::get_singleton()->free(device.linear_clamp_sampler); for (uint32_t i = 0; i < FFX_FSR2_PASS_COUNT; i++) { - if (device.passes[i].pipeline.pipeline_rid.is_valid()) { - RD::get_singleton()->free(device.passes[i].pipeline.pipeline_rid); - } device.passes[i].shader->version_free(device.passes[i].shader_version); } } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index ad19f36257..b97e38da4d 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1103,9 +1103,17 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]); + // Transform (for directional lightmaps). Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform); + + // Light texture size. + Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap); + scene_state.lightmaps[i].texture_size[0] = lightmap_size[0]; + scene_state.lightmaps[i].texture_size[1] = lightmap_size[1]; + + // Exposure. scene_state.lightmaps[i].exposure_normalization = 1.0; if (p_render_data->camera_attributes.is_valid()) { float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); @@ -1749,7 +1757,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 +1835,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 +1845,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())); } @@ -4242,6 +4250,11 @@ void RenderForwardClustered::_update_shader_quality_settings() { spec_constants.push_back(sc); + sc.constant_id = SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER; + sc.bool_value = lightmap_filter_bicubic_get(); + + spec_constants.push_back(sc); + scene_shader.set_default_specialization_constants(spec_constants); base_uniforms_changed(); //also need this diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 0aa4a0667e..5d14653db6 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -73,6 +73,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { SPEC_CONSTANT_DECAL_FILTER = 10, SPEC_CONSTANT_PROJECTOR_FILTER = 11, SPEC_CONSTANT_USE_DEPTH_FOG = 12, + SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 13, }; enum { @@ -235,8 +236,9 @@ class RenderForwardClustered : public RendererSceneRenderRD { struct LightmapData { float normal_xform[12]; - float pad[3]; + float texture_size[2]; float exposure_normalization; + float pad; }; struct LightmapCaptureData { diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index c03dd96062..412fa643b1 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -563,9 +563,17 @@ void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, co RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]); + // Transform (for directional lightmaps). Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform); + + // Light texture size. + Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap); + scene_state.lightmaps[i].texture_size[0] = lightmap_size[0]; + scene_state.lightmaps[i].texture_size[1] = lightmap_size[1]; + + // Exposure. scene_state.lightmaps[i].exposure_normalization = 1.0; if (p_render_data->camera_attributes.is_valid()) { float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); @@ -2781,6 +2789,11 @@ void RenderForwardMobile::_update_shader_quality_settings() { spec_constants.push_back(sc); + sc.constant_id = SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER; + sc.bool_value = lightmap_filter_bicubic_get(); + + spec_constants.push_back(sc); + scene_shader.set_default_specialization_constants(spec_constants); base_uniforms_changed(); //also need this diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 34260bd701..fc60c770e8 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -82,7 +82,7 @@ private: SPEC_CONSTANT_DISABLE_FOG = 14, SPEC_CONSTANT_USE_DEPTH_FOG = 16, SPEC_CONSTANT_IS_MULTIMESH = 17, - + SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 18, }; enum { @@ -208,8 +208,9 @@ private: struct LightmapData { float normal_xform[12]; - float pad[3]; + float texture_size[2]; float exposure_normalization; + float pad; }; struct LightmapCaptureData { diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 5c68fb82b1..5e4721dfb5 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -407,7 +407,7 @@ _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primit return (p_indices - subtractor[p_primitive]) / divisor[p_primitive]; } -void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info) { +void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_repeat_offset, RenderingMethod::RenderInfo *r_render_info) { //create an empty push constant RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); @@ -425,11 +425,11 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend } PushConstant push_constant; - Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform; - - if (p_offset.x || p_offset.y) { - base_transform *= Transform2D(0, p_offset / p_item->xform_curr.get_scale()); // TODO: Interpolate or explain why not needed. + Transform2D base_transform = p_item->final_transform; + if (p_item->repeat_source_item && (p_repeat_offset.x || p_repeat_offset.y)) { + base_transform.columns[2] += p_item->repeat_source_item->final_transform.basis_xform(p_repeat_offset); } + base_transform = p_canvas_transform_inverse * base_transform; Transform2D draw_transform; _update_transform_2d_to_mat2x3(base_transform, push_constant.world); @@ -1250,18 +1250,17 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, Point2(), r_render_info); } else { Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2); - Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos; - Point2 pos = start_pos; - - do { - do { - _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, pos, r_render_info); - pos.y += ci->repeat_size.y; - } while (pos.y < end_pos.y); - - pos.x += ci->repeat_size.x; - pos.y = start_pos.y; - } while (pos.x < end_pos.x); + Point2 offset; + + int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0; + int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0; + for (int ry = 0; ry <= repeat_times_y; ry++) { + offset.y = start_pos.y + ry * ci->repeat_size.y; + for (int rx = 0; rx <= repeat_times_x; rx++) { + offset.x = start_pos.x + rx * ci->repeat_size.x; + _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, offset, r_render_info); + } + } } prev_material = material; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index a78ced271a..9deb4814c7 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -423,7 +423,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { double debug_redraw_time = 1.0; inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead. - void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info = nullptr); + void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_repeat_offset, RenderingMethod::RenderInfo *r_render_info = nullptr); void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr); _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 0ebed49ee9..7d6d5018d0 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -1040,6 +1040,14 @@ void RendererSceneRenderRD::light_projectors_set_filter(RenderingServer::LightPr _update_shader_quality_settings(); } +void RendererSceneRenderRD::lightmaps_set_bicubic_filter(bool p_enable) { + if (lightmap_filter_bicubic == p_enable) { + return; + } + lightmap_filter_bicubic = p_enable; + _update_shader_quality_settings(); +} + int RendererSceneRenderRD::get_roughness_layers() const { return sky.roughness_layers; } @@ -1483,6 +1491,7 @@ void RendererSceneRenderRD::init() { decals_set_filter(RS::DecalFilter(int(GLOBAL_GET("rendering/textures/decals/filter")))); light_projectors_set_filter(RS::LightProjectorFilter(int(GLOBAL_GET("rendering/textures/light_projectors/filter")))); + lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter")); cull_argument.set_page_pool(&cull_argument_pool); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index a8e8e638cd..022a4560f8 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -133,6 +133,7 @@ private: float *directional_soft_shadow_kernel = nullptr; float *penumbra_shadow_kernel = nullptr; float *soft_shadow_kernel = nullptr; + bool lightmap_filter_bicubic = false; int directional_penumbra_shadow_samples = 0; int directional_soft_shadow_samples = 0; int penumbra_shadow_samples = 0; @@ -262,6 +263,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) override; virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override; + virtual void lightmaps_set_bicubic_filter(bool p_enable) override; _FORCE_INLINE_ RS::ShadowQuality shadows_quality_get() const { return shadows_quality; @@ -292,6 +294,9 @@ public: _FORCE_INLINE_ int directional_penumbra_shadow_samples_get() const { return directional_penumbra_shadow_samples; } + _FORCE_INLINE_ bool lightmap_filter_bicubic_get() const { + return lightmap_filter_bicubic; + } _FORCE_INLINE_ int directional_soft_shadow_samples_get() const { return directional_soft_shadow_samples; } diff --git a/servers/rendering/renderer_rd/shaders/environment/sky.glsl b/servers/rendering/renderer_rd/shaders/environment/sky.glsl index 35457a2482..5aa3735494 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sky.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sky.glsl @@ -255,10 +255,6 @@ void main() { frag_color.rgb = color; frag_color.a = alpha; - // For mobile renderer we're multiplying by 0.5 as we're using a UNORM buffer. - // For both mobile and clustered, we also bake in the exposure value for the environment and camera. - frag_color.rgb = frag_color.rgb * params.luminance_multiplier; - #if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS) // Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky. @@ -278,6 +274,10 @@ void main() { #endif // DISABLE_FOG + // For mobile renderer we're multiplying by 0.5 as we're using a UNORM buffer. + // For both mobile and clustered, we also bake in the exposure value for the environment and camera. + frag_color.rgb = frag_color.rgb * params.luminance_multiplier; + // Blending is disabled for Sky, so alpha doesn't blend. // Alpha is used for subsurface scattering so make sure it doesn't get applied to Sky. if (!AT_CUBEMAP_PASS && !AT_HALF_RES_PASS && !AT_QUARTER_RES_PASS) { 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 67ee84b01b..5706f670eb 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 @@ -657,6 +657,7 @@ layout(constant_id = 9) const uint sc_directional_penumbra_shadow_samples = 4; layout(constant_id = 10) const bool sc_decal_use_mipmaps = true; layout(constant_id = 11) const bool sc_projector_use_mipmaps = true; layout(constant_id = 12) const bool sc_use_depth_fog = false; +layout(constant_id = 13) const bool sc_use_lightmap_bicubic_filter = false; // not used in clustered renderer but we share some code with the mobile renderer that requires this. const float sc_luminance_multiplier = 1.0; @@ -701,6 +702,67 @@ layout(location = 9) in float dp_clip; layout(location = 10) in flat uint instance_index_interp; +#ifdef USE_LIGHTMAP +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 textureArray_bicubic(texture2DArray tex, vec3 uv, vec2 texture_size) { + vec2 texel_size = vec2(1.0) / texture_size; + + uv.xy = uv.xy * texture_size + vec2(0.5); + + vec2 iuv = floor(uv.xy); + vec2 fuv = fract(uv.xy); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; + + return (g0(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p0, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p1, uv.z)))) + + (g1(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p2, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p3, uv.z)))); +} +#endif //USE_LIGHTMAP + #ifdef USE_MULTIVIEW #ifdef has_VK_KHR_multiview #define ViewIndex gl_ViewIndex @@ -1030,6 +1092,13 @@ void fragment_shader(in SceneData scene_data) { vec3 light_vertex = vertex; #endif //LIGHT_VERTEX_USED + mat3 model_normal_matrix; + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_NON_UNIFORM_SCALE)) { + model_normal_matrix = transpose(inverse(mat3(read_model_matrix))); + } else { + model_normal_matrix = mat3(read_model_matrix); + } + mat4 read_view_matrix = scene_data.view_matrix; vec2 read_viewport_size = scene_data.viewport_size; { @@ -1310,6 +1379,8 @@ void fragment_shader(in SceneData scene_data) { #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) +#ifndef AMBIENT_LIGHT_DISABLED + if (scene_data.use_reflection_cubemap) { #ifdef LIGHT_ANISOTROPY_USED // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy @@ -1397,14 +1468,15 @@ void fragment_shader(in SceneData scene_data) { #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; } -#endif +#endif // LIGHT_CLEARCOAT_USED +#endif // !AMBIENT_LIGHT_DISABLED #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) //radiance /// GI /// #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) - +#ifndef AMBIENT_LIGHT_DISABLED #ifdef USE_LIGHTMAP //lightmap @@ -1439,10 +1511,23 @@ void fragment_shader(in SceneData scene_data) { if (uses_sh) { uvw.z *= 4.0; //SH textures use 4 times more data - vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; - vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; - vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; - vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + + vec3 lm_light_l0; + vec3 lm_light_l1n1; + vec3 lm_light_l1_0; + vec3 lm_light_l1p1; + + if (sc_use_lightmap_bicubic_filter) { + lm_light_l0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 0.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1n1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 1.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1_0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1p1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 3.0), lightmaps.data[ofs].light_texture_size).rgb; + } else { + lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + } vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal); float en = lightmaps.data[ofs].exposure_normalization; @@ -1459,7 +1544,11 @@ void fragment_shader(in SceneData scene_data) { } } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + if (sc_use_lightmap_bicubic_filter) { + ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization; + } else { + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + } } } #else @@ -1698,9 +1787,6 @@ void fragment_shader(in SceneData scene_data) { //finalize ambient light here { -#if defined(AMBIENT_LIGHT_DISABLED) - ambient_light = vec3(0.0, 0.0, 0.0); -#else ambient_light *= albedo.rgb; ambient_light *= ao; @@ -1713,15 +1799,14 @@ void fragment_shader(in SceneData scene_data) { ambient_light *= 1.0 - ssil.a; ambient_light += ssil.rgb * albedo.rgb; } -#endif // AMBIENT_LIGHT_DISABLED } - +#endif // AMBIENT_LIGHT_DISABLED // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); //this saves some VGPRs vec3 f0 = F0(metallic, specular, albedo); - +#ifndef AMBIENT_LIGHT_DISABLED { #if defined(DIFFUSE_TOON) //simplify for toon, as @@ -1743,6 +1828,7 @@ void fragment_shader(in SceneData scene_data) { #endif } +#endif // !AMBIENT_LIGHT_DISABLED #endif //GI !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) #if !defined(MODE_RENDER_DEPTH) diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl index 441cf3c80c..18322a0619 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl @@ -94,8 +94,9 @@ directional_lights; struct Lightmap { mat3 normal_xform; - vec3 pad; + vec2 light_texture_size; float exposure_normalization; + vec2 pad; }; layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { 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 24568d7e94..4e1da64151 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 @@ -521,6 +521,7 @@ layout(constant_id = 9) const bool sc_disable_omni_lights = false; layout(constant_id = 10) const bool sc_disable_spot_lights = false; layout(constant_id = 11) const bool sc_disable_reflection_probes = false; layout(constant_id = 12) const bool sc_disable_directional_lights = false; +layout(constant_id = 18) const bool sc_use_lightmap_bicubic_filter = false; #endif //!MODE_UNSHADED @@ -567,6 +568,67 @@ layout(location = 9) highp in float dp_clip; #endif +#ifdef USE_LIGHTMAP +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 textureArray_bicubic(texture2DArray tex, vec3 uv, vec2 texture_size) { + vec2 texel_size = vec2(1.0) / texture_size; + + uv.xy = uv.xy * texture_size + vec2(0.5); + + vec2 iuv = floor(uv.xy); + vec2 fuv = fract(uv.xy); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; + + return (g0(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p0, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p1, uv.z)))) + + (g1(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p2, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p3, uv.z)))); +} +#endif //USE_LIGHTMAP + #ifdef USE_MULTIVIEW #ifdef has_VK_KHR_multiview #define ViewIndex gl_ViewIndex @@ -839,6 +901,13 @@ void main() { vec3 light_vertex = vertex; #endif //LIGHT_VERTEX_USED + mat3 model_normal_matrix; + if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_NON_UNIFORM_SCALE)) { + model_normal_matrix = transpose(inverse(mat3(read_model_matrix))); + } else { + model_normal_matrix = mat3(read_model_matrix); + } + mat4 read_view_matrix = scene_data.view_matrix; vec2 read_viewport_size = scene_data.viewport_size; @@ -1071,6 +1140,8 @@ void main() { #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) +#ifndef AMBIENT_LIGHT_DISABLED + if (scene_data.use_reflection_cubemap) { #ifdef LIGHT_ANISOTROPY_USED // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy @@ -1158,13 +1229,14 @@ void main() { #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; } -#endif +#endif // LIGHT_CLEARCOAT_USED +#endif // !AMBIENT_LIGHT_DISABLED #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) //radiance #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) - +#ifndef AMBIENT_LIGHT_DISABLED #ifdef USE_LIGHTMAP //lightmap @@ -1199,10 +1271,22 @@ void main() { if (uses_sh) { uvw.z *= 4.0; //SH textures use 4 times more data - vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; - vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; - vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; - vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + vec3 lm_light_l0; + vec3 lm_light_l1n1; + vec3 lm_light_l1_0; + vec3 lm_light_l1p1; + + if (sc_use_lightmap_bicubic_filter) { + lm_light_l0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 0.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1n1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 1.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1_0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1p1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 3.0), lightmaps.data[ofs].light_texture_size).rgb; + } else { + lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + } vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal); float exposure_normalization = lightmaps.data[ofs].exposure_normalization; @@ -1219,7 +1303,11 @@ void main() { } } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + if (sc_use_lightmap_bicubic_filter) { + ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization; + } else { + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + } } } @@ -1274,14 +1362,11 @@ void main() { } //Reflection probes // finalize ambient light here - { -#if defined(AMBIENT_LIGHT_DISABLED) - ambient_light = vec3(0.0, 0.0, 0.0); -#else - ambient_light *= albedo.rgb; - ambient_light *= ao; -#endif // AMBIENT_LIGHT_DISABLED - } + + ambient_light *= albedo.rgb; + ambient_light *= ao; + +#endif // !AMBIENT_LIGHT_DISABLED // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); @@ -1289,6 +1374,7 @@ void main() { //this saves some VGPRs vec3 f0 = F0(metallic, specular, albedo); +#ifndef AMBIENT_LIGHT_DISABLED { #if defined(DIFFUSE_TOON) //simplify for toon, as @@ -1310,6 +1396,7 @@ void main() { #endif } +#endif // !AMBIENT_LIGHT_DISABLED #endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) #if !defined(MODE_RENDER_DEPTH) diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl index 7674e905e1..d971ff04c5 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl @@ -71,8 +71,9 @@ directional_lights; struct Lightmap { mediump mat3 normal_xform; - vec3 pad; + vec2 light_texture_size; float exposure_normalization; + float pad; }; layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 3d294ca8cb..c217c0fa9a 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -1813,6 +1813,7 @@ void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_use } t->lightmap_users.insert(p_lightmap); + lm->light_texture_size = Vector2i(t->width, t->height); if (using_lightmap_array) { if (lm->array_index < 0) { diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index f152cc5dae..94ab219dc2 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -332,6 +332,7 @@ private: bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); float baked_exposure = 1.0; + Vector2i light_texture_size; int32_t array_index = -1; //unassigned PackedVector3Array points; PackedColorArray point_sh; @@ -985,6 +986,10 @@ public: const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); return lm->uses_spherical_harmonics; } + _FORCE_INLINE_ Vector2i lightmap_get_light_texture_size(RID p_lightmap) const { + const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + return lm->light_texture_size; + } _FORCE_INLINE_ uint64_t lightmap_array_get_version() const { ERR_FAIL_COND_V(!using_lightmap_array, 0); //only for arrays return lightmap_array_version; diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index 8b74ebfacd..63dc54e24c 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -580,11 +580,7 @@ void MaterialStorage::ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_ if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { continue; } - if (E.value.texture_order >= 0) { - filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000)); - } else { - filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order)); - } + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.prop_order)); } int uniform_count = filtered_uniforms.size(); sorter.sort(filtered_uniforms.ptr(), uniform_count); @@ -634,7 +630,7 @@ bool MaterialStorage::ShaderData::is_parameter_texture(const StringName &p_param return false; } - return uniforms[p_param].texture_order >= 0; + return uniforms[p_param].is_texture(); } /////////////////////////////////////////////////////////////////////////// @@ -645,7 +641,7 @@ void MaterialStorage::MaterialData::update_uniform_buffer(const HashMap<StringNa bool uses_global_buffer = false; for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : p_uniforms) { - if (E.value.order < 0) { + if (E.value.is_texture()) { continue; // texture, does not go here } diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index c5454e748a..539bdcbbd0 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -1056,8 +1056,9 @@ void MeshStorage::update_mesh_instances() { mi->surfaces[i].previous_buffer = mi->surfaces[i].current_buffer; - if (uses_motion_vectors && (frame - mi->surfaces[i].last_change) == 1) { - // Previous buffer's data can only be one frame old to be able to use motion vectors. + if (uses_motion_vectors && mi->surfaces[i].last_change && (frame - mi->surfaces[i].last_change) <= 2) { + // Use a 2-frame tolerance so that stepped skeletal animations have correct motion vectors + // (stepped animation is common for distant NPCs). uint32_t new_buffer_index = mi->surfaces[i].current_buffer ^ 1; if (mi->surfaces[i].uniform_set[new_buffer_index].is_null()) { @@ -1380,14 +1381,16 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V ////////////////// MULTIMESH -RID MeshStorage::multimesh_allocate() { +RID MeshStorage::_multimesh_allocate() { return multimesh_owner.allocate_rid(); } -void MeshStorage::multimesh_initialize(RID p_rid) { +void MeshStorage::_multimesh_initialize(RID p_rid) { multimesh_owner.initialize_rid(p_rid, MultiMesh()); } -void MeshStorage::multimesh_free(RID p_rid) { +void MeshStorage::_multimesh_free(RID p_rid) { + // Remove from interpolator. + _interpolation_data.notify_free_multimesh(p_rid); _update_dirty_multimeshes(); multimesh_allocate_data(p_rid, 0, RS::MULTIMESH_TRANSFORM_2D); MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid); @@ -1395,7 +1398,7 @@ void MeshStorage::multimesh_free(RID p_rid) { multimesh_owner.free(p_rid); } -void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { +void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); @@ -1505,13 +1508,13 @@ bool MeshStorage::_multimesh_uses_motion_vectors_offsets(RID p_multimesh) { return _multimesh_uses_motion_vectors(multimesh); } -int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const { +int MeshStorage::_multimesh_get_instance_count(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, 0); return multimesh->instances; } -void MeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { +void MeshStorage::_multimesh_set_mesh(RID p_multimesh, RID p_mesh) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); if (multimesh->mesh == p_mesh) { @@ -1701,7 +1704,7 @@ void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p multimesh->aabb = aabb; } -void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { +void MeshStorage::_multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1738,7 +1741,7 @@ void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, _multimesh_mark_dirty(multimesh, p_index, true); } -void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { +void MeshStorage::_multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1765,7 +1768,7 @@ void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_ind _multimesh_mark_dirty(multimesh, p_index, true); } -void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { +void MeshStorage::_multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1788,7 +1791,7 @@ void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, con _multimesh_mark_dirty(multimesh, p_index, false); } -void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { +void MeshStorage::_multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1811,7 +1814,7 @@ void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_inde _multimesh_mark_dirty(multimesh, p_index, false); } -RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const { +RID MeshStorage::_multimesh_get_mesh(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, RID()); @@ -1825,7 +1828,7 @@ Dependency *MeshStorage::multimesh_get_dependency(RID p_multimesh) const { return &multimesh->dependency; } -Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { +Transform3D MeshStorage::_multimesh_instance_get_transform(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Transform3D()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform3D()); @@ -1856,7 +1859,7 @@ Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p return t; } -Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { +Transform2D MeshStorage::_multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Transform2D()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform2D()); @@ -1881,7 +1884,7 @@ Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, in return t; } -Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const { +Color MeshStorage::_multimesh_instance_get_color(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Color()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); @@ -1904,7 +1907,7 @@ Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) co return c; } -Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { +Color MeshStorage::_multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Color()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); @@ -1927,7 +1930,7 @@ Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_ind return c; } -void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { +void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache)); @@ -1974,7 +1977,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b } } -Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { +Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Vector<float>()); if (multimesh->buffer.is_null()) { @@ -1996,7 +1999,7 @@ Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { } } -void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { +void MeshStorage::_multimesh_set_visible_instances(RID p_multimesh, int p_visible) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_COND(p_visible < -1 || p_visible > multimesh->instances); @@ -2018,26 +2021,26 @@ void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES); } -int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const { +int MeshStorage::_multimesh_get_visible_instances(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, 0); return multimesh->visible_instances; } -void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) { +void MeshStorage::_multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); multimesh->custom_aabb = p_aabb; multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } -AABB MeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const { +AABB MeshStorage::_multimesh_get_custom_aabb(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, AABB()); return multimesh->custom_aabb; } -AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const { +AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, AABB()); if (multimesh->custom_aabb != AABB()) { @@ -2050,6 +2053,13 @@ AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const { return multimesh->aabb; } +MeshStorage::MultiMeshInterpolator *MeshStorage::_multimesh_get_interpolator(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_NULL_V_MSG(multimesh, nullptr, "Multimesh not found: " + itos(p_multimesh.get_id())); + + return &multimesh->interpolator; +} + void MeshStorage::_update_dirty_multimeshes() { while (multimesh_dirty_list) { MultiMesh *multimesh = multimesh_dirty_list; diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index 5491f637bc..4344db783d 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -244,6 +244,8 @@ private: bool dirty = false; MultiMesh *dirty_list = nullptr; + RendererMeshStorage::MultiMeshInterpolator interpolator; + Dependency dependency; }; @@ -621,36 +623,38 @@ public: bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); }; - virtual RID multimesh_allocate() override; - virtual void multimesh_initialize(RID p_multimesh) override; - virtual void multimesh_free(RID p_rid) override; + virtual RID _multimesh_allocate() override; + virtual void _multimesh_initialize(RID p_multimesh) override; + virtual void _multimesh_free(RID p_rid) override; + + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; + virtual int _multimesh_get_instance_count(RID p_multimesh) const override; - virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; - virtual int multimesh_get_instance_count(RID p_multimesh) const override; + virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; + virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override; + virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override; + virtual void _multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override; + virtual void _multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; - virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; - virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override; - virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override; - virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override; - virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; + virtual RID _multimesh_get_mesh(RID p_multimesh) const override; - virtual RID multimesh_get_mesh(RID p_multimesh) const override; + virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; + virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; + virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override; + virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; - virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; - virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; - virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override; - virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; + virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; + virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override; - virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; - virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override; + virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; + virtual int _multimesh_get_visible_instances(RID p_multimesh) const override; - virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; - virtual int multimesh_get_visible_instances(RID p_multimesh) const override; + virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override; + virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override; - virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override; - virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override; + virtual AABB _multimesh_get_aabb(RID p_multimesh) const override; - virtual AABB multimesh_get_aabb(RID p_multimesh) const override; + virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override; void _update_dirty_multimeshes(); void _multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset); 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 6ebb85442b..888527e1ef 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h @@ -73,6 +73,8 @@ public: uint32_t directional_light_count = 0; bool directional_light_soft_shadows = false; + bool lightmap_bicubic_filter = false; + RenderingMethod::RenderInfo *render_info = nullptr; /* Viewport data */ diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 06753c3fb7..1d25dec633 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -4392,7 +4392,8 @@ void RendererSceneCull::set_scene_render(RendererSceneRender *p_scene_render) { /* INTERPOLATION API */ void RendererSceneCull::update_interpolation_tick(bool p_process) { - // TODO (MultiMesh): Update interpolation in storage. + // MultiMesh: Update interpolation in storage. + RSG::mesh_storage->update_interpolation_tick(p_process); // INSTANCES @@ -4450,7 +4451,8 @@ void RendererSceneCull::update_interpolation_tick(bool p_process) { } void RendererSceneCull::update_interpolation_frame(bool p_process) { - // TODO (MultiMesh): Update interpolation in storage. + // MultiMesh: Update interpolation in storage. + RSG::mesh_storage->update_interpolation_frame(p_process); if (p_process) { real_t f = Engine::get_singleton()->get_physics_interpolation_fraction(); diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 53f1f2d246..972f66d325 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -1418,6 +1418,7 @@ public: PASS1(decals_set_filter, RS::DecalFilter) PASS1(light_projectors_set_filter, RS::LightProjectorFilter) + PASS1(lightmaps_set_bicubic_filter, bool) virtual void update(); diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 719efa4df2..3446f5dd1b 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -338,6 +338,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) = 0; virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) = 0; + virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0; virtual void update() = 0; virtual ~RendererSceneRender() {} diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 1049cb63cd..7e45eba1de 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -1271,6 +1271,13 @@ void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d RSG::texture_storage->render_target_set_use_hdr(viewport->render_target, p_use_hdr_2d); } +bool RendererViewport::viewport_is_using_hdr_2d(RID p_viewport) const { + Viewport *viewport = viewport_owner.get_or_null(p_viewport); + ERR_FAIL_NULL_V(viewport, false); + + return viewport->use_hdr_2d; +} + void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) { Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_NULL(viewport); diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index b36fc7f57f..bf97905f86 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -262,6 +262,8 @@ public: void viewport_set_transparent_background(RID p_viewport, bool p_enabled); void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d); + bool viewport_is_using_hdr_2d(RID p_viewport) const; + void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform); void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer); diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h index 57fbf97d8c..f6212faf08 100644 --- a/servers/rendering/rendering_method.h +++ b/servers/rendering/rendering_method.h @@ -349,6 +349,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) = 0; virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) = 0; + virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0; virtual bool free(RID p_rid) = 0; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 6e195a8002..60fa546e16 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -344,6 +344,11 @@ public: FUNC2(multimesh_set_buffer, RID, const Vector<float> &) FUNC1RC(Vector<float>, multimesh_get_buffer, RID) + FUNC3(multimesh_set_buffer_interpolated, RID, const Vector<float> &, const Vector<float> &) + FUNC2(multimesh_set_physics_interpolated, RID, bool) + FUNC2(multimesh_set_physics_interpolation_quality, RID, MultimeshPhysicsInterpolationQuality) + FUNC2(multimesh_instance_reset_physics_interpolation, RID, int) + FUNC2(multimesh_set_visible_instances, RID, int) FUNC1RC(int, multimesh_get_visible_instances, RID) @@ -639,6 +644,7 @@ public: FUNC3(viewport_set_canvas_transform, RID, RID, const Transform2D &) FUNC2(viewport_set_transparent_background, RID, bool) FUNC2(viewport_set_use_hdr_2d, RID, bool) + FUNC1RC(bool, viewport_is_using_hdr_2d, RID) FUNC2(viewport_set_snap_2d_transforms_to_pixel, RID, bool) FUNC2(viewport_set_snap_2d_vertices_to_pixel, RID, bool) @@ -761,6 +767,7 @@ public: FUNC1(directional_soft_shadow_filter_set_quality, ShadowQuality); FUNC1(decals_set_filter, RS::DecalFilter); FUNC1(light_projectors_set_filter, RS::LightProjectorFilter); + FUNC1(lightmaps_set_bicubic_filter, bool); /* CAMERA ATTRIBUTES */ diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 2542f2eed7..49e005ca96 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -922,7 +922,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene if (shader->uniforms.has(vnode->name)) { //its a uniform! const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[vnode->name]; - if (u.texture_order >= 0) { + if (u.is_texture()) { StringName name; if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) { name = "color_buffer"; @@ -1039,7 +1039,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene if (shader->uniforms.has(anode->name)) { //its a uniform! const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[anode->name]; - if (u.texture_order >= 0) { + if (u.is_texture()) { code = _mkid(anode->name); //texture, use as is } else { //a scalar or vector diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 66fcefe228..f8d00ba7ec 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -96,6 +96,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "FLOAT_CONSTANT", "INT_CONSTANT", "UINT_CONSTANT", + "STRING_CONSTANT", "TYPE_VOID", "TYPE_BOOL", "TYPE_BVEC2", @@ -212,6 +213,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "HINT_ANISOTROPY_TEXTURE", "HINT_SOURCE_COLOR", "HINT_RANGE", + "HINT_ENUM", "HINT_INSTANCE_INDEX", "HINT_SCREEN_TEXTURE", "HINT_NORMAL_ROUGHNESS_TEXTURE", @@ -365,6 +367,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_HINT_SOURCE_COLOR, "source_color", CF_UNSPECIFIED, {}, {} }, { TK_HINT_RANGE, "hint_range", CF_UNSPECIFIED, {}, {} }, + { TK_HINT_ENUM, "hint_enum", CF_UNSPECIFIED, {}, {} }, { TK_HINT_INSTANCE_INDEX, "instance_index", CF_UNSPECIFIED, {}, {} }, // sampler hints @@ -512,7 +515,54 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { return _make_token(TK_OP_NOT); } break; - //case '"' //string - no strings in shader + case '"': { + String _content = ""; + bool _previous_backslash = false; + + while (true) { + bool _ended = false; + char32_t c = GETCHAR(0); + if (c == 0) { + return _make_token(TK_ERROR, "EOF reached before string termination."); + } + switch (c) { + case '"': { + if (_previous_backslash) { + _content += '"'; + _previous_backslash = false; + } else { + _ended = true; + } + break; + } + case '\\': { + if (_previous_backslash) { + _content += '\\'; + } + _previous_backslash = !_previous_backslash; + break; + } + case '\n': { + return _make_token(TK_ERROR, "Unexpected end of string."); + } + default: { + if (!_previous_backslash) { + _content += c; + } else { + return _make_token(TK_ERROR, "Only \\\" and \\\\ escape characters supported."); + } + break; + } + } + + char_idx++; + if (_ended) { + break; + } + } + + return _make_token(TK_STRING_CONSTANT, _content); + } break; //case '\'' //string - no strings in shader case '{': return _make_token(TK_CURLY_BRACKET_OPEN); @@ -1127,6 +1177,9 @@ String ShaderLanguage::get_uniform_hint_name(ShaderNode::Uniform::Hint p_hint) { case ShaderNode::Uniform::HINT_RANGE: { result = "hint_range"; } break; + case ShaderNode::Uniform::HINT_ENUM: { + result = "hint_enum"; + } break; case ShaderNode::Uniform::HINT_SOURCE_COLOR: { result = "source_color"; } break; @@ -4146,6 +4199,11 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform if (p_uniform.array_size > 0) { pi.type = Variant::PACKED_INT32_ARRAY; // TODO: Handle range and encoding for for unsigned values. + } else if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_ENUM) { + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_ENUM; + String hint_string; + pi.hint_string = String(",").join(p_uniform.hint_enum_names); } else { pi.type = Variant::INT; pi.hint = PROPERTY_HINT_RANGE; @@ -8228,6 +8286,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f int texture_binding = 0; int uniforms = 0; int instance_index = 0; + int prop_index = 0; #ifdef DEBUG_ENABLED uint64_t uniform_buffer_size = 0; uint64_t max_uniform_buffer_size = 0; @@ -8782,6 +8841,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f ++texture_binding; } uniform.order = -1; + uniform.prop_order = prop_index++; } else { if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE && (type == TYPE_MAT2 || type == TYPE_MAT3 || type == TYPE_MAT4)) { _set_error(vformat(RTR("The '%s' qualifier is not supported for matrix types."), "SCOPE_INSTANCE")); @@ -8790,6 +8850,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f uniform.texture_order = -1; if (uniform_scope != ShaderNode::Uniform::SCOPE_INSTANCE) { uniform.order = uniforms++; + uniform.prop_order = prop_index++; #ifdef DEBUG_ENABLED if (check_device_limit_warnings) { if (uniform.array_size > 0) { @@ -8985,6 +9046,40 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f new_hint = ShaderNode::Uniform::HINT_RANGE; } break; + case TK_HINT_ENUM: { + if (type != TYPE_INT) { + _set_error(vformat(RTR("Enum hint is for '%s' only."), "int")); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_expected_after_error("(", "hint_enum"); + return ERR_PARSE_ERROR; + } + + while (true) { + tk = _get_token(); + + if (tk.type != TK_STRING_CONSTANT) { + _set_error(RTR("Expected a string constant.")); + return ERR_PARSE_ERROR; + } + + uniform.hint_enum_names.push_back(tk.text); + + tk = _get_token(); + + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } else if (tk.type != TK_COMMA) { + _set_error(RTR("Expected ',' or ')' after string constant.")); + return ERR_PARSE_ERROR; + } + } + + new_hint = ShaderNode::Uniform::HINT_ENUM; + } break; case TK_HINT_INSTANCE_INDEX: { if (custom_instance_index != -1) { _set_error(vformat(RTR("Can only specify '%s' once."), "instance_index")); @@ -9079,7 +9174,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f default: break; } - if (((new_filter != FILTER_DEFAULT || new_repeat != REPEAT_DEFAULT) || (new_hint != ShaderNode::Uniform::HINT_NONE && new_hint != ShaderNode::Uniform::HINT_SOURCE_COLOR && new_hint != ShaderNode::Uniform::HINT_RANGE)) && !is_sampler_type(type)) { + + bool is_sampler_hint = new_hint != ShaderNode::Uniform::HINT_NONE && new_hint != ShaderNode::Uniform::HINT_SOURCE_COLOR && new_hint != ShaderNode::Uniform::HINT_RANGE && new_hint != ShaderNode::Uniform::HINT_ENUM; + if (((new_filter != FILTER_DEFAULT || new_repeat != REPEAT_DEFAULT) || is_sampler_hint) && !is_sampler_type(type)) { _set_error(RTR("This hint is only for sampler types.")); return ERR_PARSE_ERROR; } @@ -10782,15 +10879,21 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ } } else if ((completion_base == DataType::TYPE_INT || completion_base == DataType::TYPE_FLOAT) && !completion_base_array) { if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) { - ScriptLanguage::CodeCompletionOption option("hint_range", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + Vector<String> options; if (completion_base == DataType::TYPE_INT) { - option.insert_text = "hint_range(0, 100, 1)"; + options.push_back("hint_range(0, 100, 1)"); + options.push_back("hint_enum(\"Zero\", \"One\", \"Two\")"); } else { - option.insert_text = "hint_range(0.0, 1.0, 0.1)"; + options.push_back("hint_range(0.0, 1.0, 0.1)"); } - r_options->push_back(option); + for (const String &option_text : options) { + String hint_name = option_text.substr(0, option_text.find_char(char32_t('('))); + ScriptLanguage::CodeCompletionOption option(hint_name, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + option.insert_text = option_text; + r_options->push_back(option); + } } } else if ((int(completion_base) > int(TYPE_MAT4) && int(completion_base) < int(TYPE_STRUCT))) { Vector<String> options; diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index ad3b78546d..1b5df7e90f 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -59,6 +59,7 @@ public: TK_FLOAT_CONSTANT, TK_INT_CONSTANT, TK_UINT_CONSTANT, + TK_STRING_CONSTANT, TK_TYPE_VOID, TK_TYPE_BOOL, TK_TYPE_BVEC2, @@ -175,6 +176,7 @@ public: TK_HINT_ANISOTROPY_TEXTURE, TK_HINT_SOURCE_COLOR, TK_HINT_RANGE, + TK_HINT_ENUM, TK_HINT_INSTANCE_INDEX, TK_HINT_SCREEN_TEXTURE, TK_HINT_NORMAL_ROUGHNESS_TEXTURE, @@ -623,6 +625,7 @@ public: enum Hint { HINT_NONE, HINT_RANGE, + HINT_ENUM, HINT_SOURCE_COLOR, HINT_NORMAL, HINT_ROUGHNESS_NORMAL, @@ -648,6 +651,7 @@ public: }; int order = 0; + int prop_order = 0; int texture_order = 0; int texture_binding = 0; DataType type = TYPE_VOID; @@ -660,10 +664,16 @@ public: TextureFilter filter = FILTER_DEFAULT; TextureRepeat repeat = REPEAT_DEFAULT; float hint_range[3]; + PackedStringArray hint_enum_names; int instance_index = 0; String group; String subgroup; + _FORCE_INLINE_ bool is_texture() const { + // Order is assigned to -1 for texture uniforms. + return order < 0; + } + Uniform() { hint_range[0] = 0.0f; hint_range[1] = 1.0f; diff --git a/servers/rendering/storage/mesh_storage.cpp b/servers/rendering/storage/mesh_storage.cpp new file mode 100644 index 0000000000..221ebaa277 --- /dev/null +++ b/servers/rendering/storage/mesh_storage.cpp @@ -0,0 +1,485 @@ +/**************************************************************************/ +/* mesh_storage.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 "mesh_storage.h" + +#include "core/math/transform_interpolator.h" + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) +#include "core/config/project_settings.h" +#endif + +RID RendererMeshStorage::multimesh_allocate() { + return _multimesh_allocate(); +} + +void RendererMeshStorage::multimesh_initialize(RID p_rid) { + _multimesh_initialize(p_rid); +} + +void RendererMeshStorage::multimesh_free(RID p_rid) { + _multimesh_free(p_rid); +} + +void RendererMeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi) { + mmi->_transform_format = p_transform_format; + mmi->_use_colors = p_use_colors; + mmi->_use_custom_data = p_use_custom_data; + mmi->_num_instances = p_instances; + + mmi->_vf_size_xform = p_transform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12; + mmi->_vf_size_color = p_use_colors ? 4 : 0; + mmi->_vf_size_data = p_use_custom_data ? 4 : 0; + + mmi->_stride = mmi->_vf_size_xform + mmi->_vf_size_color + mmi->_vf_size_data; + + int size_in_floats = p_instances * mmi->_stride; + mmi->_data_curr.resize_zeroed(size_in_floats); + mmi->_data_prev.resize_zeroed(size_in_floats); + mmi->_data_interpolated.resize_zeroed(size_in_floats); + } + + _multimesh_allocate_data(p_multimesh, p_instances, p_transform_format, p_use_colors, p_use_custom_data); +} + +int RendererMeshStorage::multimesh_get_instance_count(RID p_multimesh) const { + return _multimesh_get_instance_count(p_multimesh); +} + +void RendererMeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { + _multimesh_set_mesh(p_multimesh, p_mesh); +} + +void RendererMeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi && mmi->interpolated) { + ERR_FAIL_COND(p_index >= mmi->_num_instances); + ERR_FAIL_COND(mmi->_vf_size_xform != 12); + + int start = p_index * mmi->_stride; + float *ptr = mmi->_data_curr.ptrw(); + ptr += start; + + const Transform3D &t = p_transform; + ptr[0] = t.basis.rows[0][0]; + ptr[1] = t.basis.rows[0][1]; + ptr[2] = t.basis.rows[0][2]; + ptr[3] = t.origin.x; + ptr[4] = t.basis.rows[1][0]; + ptr[5] = t.basis.rows[1][1]; + ptr[6] = t.basis.rows[1][2]; + ptr[7] = t.origin.y; + ptr[8] = t.basis.rows[2][0]; + ptr[9] = t.basis.rows[2][1]; + ptr[10] = t.basis.rows[2][2]; + ptr[11] = t.origin.z; + + _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues"); + } +#endif + + return; + } + + _multimesh_instance_set_transform(p_multimesh, p_index, p_transform); +} + +void RendererMeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { + _multimesh_instance_set_transform_2d(p_multimesh, p_index, p_transform); +} + +void RendererMeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi && mmi->interpolated) { + ERR_FAIL_COND(p_index >= mmi->_num_instances); + ERR_FAIL_COND(mmi->_vf_size_color == 0); + + int start = (p_index * mmi->_stride) + mmi->_vf_size_xform; + float *ptr = mmi->_data_curr.ptrw(); + ptr += start; + + if (mmi->_vf_size_color == 4) { + for (int n = 0; n < 4; n++) { + ptr[n] = p_color.components[n]; + } + } else { +#ifdef DEV_ENABLED + // The options are currently 4 or zero, but just in case this changes in future... + ERR_FAIL_COND(mmi->_vf_size_color != 0); +#endif + } + _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); + return; + } + + _multimesh_instance_set_color(p_multimesh, p_index, p_color); +} + +void RendererMeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi && mmi->interpolated) { + ERR_FAIL_COND(p_index >= mmi->_num_instances); + ERR_FAIL_COND(mmi->_vf_size_data == 0); + + int start = (p_index * mmi->_stride) + mmi->_vf_size_xform + mmi->_vf_size_color; + float *ptr = mmi->_data_curr.ptrw(); + ptr += start; + + if (mmi->_vf_size_data == 4) { + for (int n = 0; n < 4; n++) { + ptr[n] = p_color.components[n]; + } + } else { +#ifdef DEV_ENABLED + // The options are currently 4 or zero, but just in case this changes in future... + ERR_FAIL_COND(mmi->_vf_size_data != 0); +#endif + } + _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); + return; + } + + _multimesh_instance_set_custom_data(p_multimesh, p_index, p_color); +} + +void RendererMeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) { + _multimesh_set_custom_aabb(p_multimesh, p_aabb); +} + +AABB RendererMeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const { + return _multimesh_get_custom_aabb(p_multimesh); +} + +RID RendererMeshStorage::multimesh_get_mesh(RID p_multimesh) const { + return _multimesh_get_mesh(p_multimesh); +} + +Transform3D RendererMeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { + return _multimesh_instance_get_transform(p_multimesh, p_index); +} + +Transform2D RendererMeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { + return _multimesh_instance_get_transform_2d(p_multimesh, p_index); +} + +Color RendererMeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const { + return _multimesh_instance_get_color(p_multimesh, p_index); +} + +Color RendererMeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { + return _multimesh_instance_get_custom_data(p_multimesh, p_index); +} + +void RendererMeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi && mmi->interpolated) { + ERR_FAIL_COND_MSG(p_buffer.size() != mmi->_data_curr.size(), vformat("Buffer should have %d elements, got %d instead.", mmi->_data_curr.size(), p_buffer.size())); + + mmi->_data_curr = p_buffer; + _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues"); + } +#endif + + return; + } + + _multimesh_set_buffer(p_multimesh, p_buffer); +} + +Vector<float> RendererMeshStorage::multimesh_get_buffer(RID p_multimesh) const { + return _multimesh_get_buffer(p_multimesh); +} + +void RendererMeshStorage::multimesh_set_buffer_interpolated(RID p_multimesh, const Vector<float> &p_buffer, const Vector<float> &p_buffer_prev) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi) { + ERR_FAIL_COND_MSG(p_buffer.size() != mmi->_data_curr.size(), vformat("Buffer for current frame should have %d elements, got %d instead.", mmi->_data_curr.size(), p_buffer.size())); + ERR_FAIL_COND_MSG(p_buffer_prev.size() != mmi->_data_prev.size(), vformat("Buffer for previous frame should have %d elements, got %d instead.", mmi->_data_prev.size(), p_buffer_prev.size())); + + // We are assuming that mmi->interpolated is the case. (Can possibly assert this?) + // Even if this flag hasn't been set - just calling this function suggests interpolation is desired. + mmi->_data_prev = p_buffer_prev; + mmi->_data_curr = p_buffer; + _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues"); + } +#endif + } +} + +void RendererMeshStorage::multimesh_set_physics_interpolated(RID p_multimesh, bool p_interpolated) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi) { + mmi->interpolated = p_interpolated; + } +} + +void RendererMeshStorage::multimesh_set_physics_interpolation_quality(RID p_multimesh, RS::MultimeshPhysicsInterpolationQuality p_quality) { + ERR_FAIL_COND((p_quality < 0) || (p_quality > 1)); + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi) { + mmi->quality = (int)p_quality; + } +} + +void RendererMeshStorage::multimesh_instance_reset_physics_interpolation(RID p_multimesh, int p_index) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi) { + ERR_FAIL_INDEX(p_index, mmi->_num_instances); + + float *w = mmi->_data_prev.ptrw(); + const float *r = mmi->_data_curr.ptr(); + int start = p_index * mmi->_stride; + + for (int n = 0; n < mmi->_stride; n++) { + w[start + n] = r[start + n]; + } + } +} + +void RendererMeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { + return _multimesh_set_visible_instances(p_multimesh, p_visible); +} + +int RendererMeshStorage::multimesh_get_visible_instances(RID p_multimesh) const { + return _multimesh_get_visible_instances(p_multimesh); +} + +AABB RendererMeshStorage::multimesh_get_aabb(RID p_multimesh) const { + return _multimesh_get_aabb(p_multimesh); +} + +void RendererMeshStorage::_multimesh_add_to_interpolation_lists(RID p_multimesh, MultiMeshInterpolator &r_mmi) { + if (!r_mmi.on_interpolate_update_list) { + r_mmi.on_interpolate_update_list = true; + _interpolation_data.multimesh_interpolate_update_list.push_back(p_multimesh); + } + + if (!r_mmi.on_transform_update_list) { + r_mmi.on_transform_update_list = true; + _interpolation_data.multimesh_transform_update_list_curr->push_back(p_multimesh); + } +} + +void RendererMeshStorage::InterpolationData::notify_free_multimesh(RID p_rid) { + // If the instance was on any of the lists, remove. + multimesh_interpolate_update_list.erase_multiple_unordered(p_rid); + multimesh_transform_update_lists[0].erase_multiple_unordered(p_rid); + multimesh_transform_update_lists[1].erase_multiple_unordered(p_rid); +} + +void RendererMeshStorage::update_interpolation_tick(bool p_process) { + // Detect any that were on the previous transform list that are no longer active, + // we should remove them from the interpolate list. + + for (unsigned int n = 0; n < _interpolation_data.multimesh_transform_update_list_prev->size(); n++) { + const RID &rid = (*_interpolation_data.multimesh_transform_update_list_prev)[n]; + + bool active = true; + + // No longer active? (Either the instance deleted or no longer being transformed.) + + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid); + if (mmi && !mmi->on_transform_update_list) { + active = false; + mmi->on_interpolate_update_list = false; + + // Make sure the most recent transform is set... + mmi->_data_interpolated = mmi->_data_curr; // TODO: Copy data rather than use Packed = function? + + // ... and that both prev and current are the same, just in case of any interpolations. + mmi->_data_prev = mmi->_data_curr; + } + + if (!mmi) { + active = false; + } + + if (!active) { + _interpolation_data.multimesh_interpolate_update_list.erase(rid); + } + } + + if (p_process) { + for (unsigned int i = 0; i < _interpolation_data.multimesh_transform_update_list_curr->size(); i++) { + const RID &rid = (*_interpolation_data.multimesh_transform_update_list_curr)[i]; + + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid); + if (mmi) { + // Reset for next tick. + mmi->on_transform_update_list = false; + mmi->_data_prev = mmi->_data_curr; + } + } + } + + // If any have left the transform list, remove from the interpolate list. + + // 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.multimesh_transform_update_list_curr, _interpolation_data.multimesh_transform_update_list_prev); + + // Prepare for the next iteration. + _interpolation_data.multimesh_transform_update_list_curr->clear(); +} + +void RendererMeshStorage::update_interpolation_frame(bool p_process) { + if (p_process) { + // Only need 32 bits for interpolation, don't use real_t. + float f = Engine::get_singleton()->get_physics_interpolation_fraction(); + + for (unsigned int c = 0; c < _interpolation_data.multimesh_interpolate_update_list.size(); c++) { + const RID &rid = _interpolation_data.multimesh_interpolate_update_list[c]; + + // We could use the TransformInterpolator here to slerp transforms, but that might be too expensive, + // so just using a Basis lerp for now. + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid); + if (mmi) { + // Make sure arrays are the correct size. + DEV_ASSERT(mmi->_data_prev.size() == mmi->_data_curr.size()); + + if (mmi->_data_interpolated.size() < mmi->_data_curr.size()) { + mmi->_data_interpolated.resize(mmi->_data_curr.size()); + } + DEV_ASSERT(mmi->_data_interpolated.size() >= mmi->_data_curr.size()); + + DEV_ASSERT((mmi->_data_curr.size() % mmi->_stride) == 0); + int num = mmi->_data_curr.size() / mmi->_stride; + + const float *pf_prev = mmi->_data_prev.ptr(); + const float *pf_curr = mmi->_data_curr.ptr(); + float *pf_int = mmi->_data_interpolated.ptrw(); + + bool use_lerp = mmi->quality == 0; + + // Temporary transform (needed for swizzling). + Transform3D tp, tc, tr; // (transform prev, curr and result) + + // Test for cache friendliness versus doing branchless. + for (int n = 0; n < num; n++) { + // Transform. + if (use_lerp) { + for (int i = 0; i < mmi->_vf_size_xform; i++) { + pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f); + } + } else { + // Silly swizzling, this will slow things down. + // No idea why it is using this format... + // ... maybe due to the shader. + tp.basis.rows[0][0] = pf_prev[0]; + tp.basis.rows[0][1] = pf_prev[1]; + tp.basis.rows[0][2] = pf_prev[2]; + tp.basis.rows[1][0] = pf_prev[4]; + tp.basis.rows[1][1] = pf_prev[5]; + tp.basis.rows[1][2] = pf_prev[6]; + tp.basis.rows[2][0] = pf_prev[8]; + tp.basis.rows[2][1] = pf_prev[9]; + tp.basis.rows[2][2] = pf_prev[10]; + tp.origin.x = pf_prev[3]; + tp.origin.y = pf_prev[7]; + tp.origin.z = pf_prev[11]; + + tc.basis.rows[0][0] = pf_curr[0]; + tc.basis.rows[0][1] = pf_curr[1]; + tc.basis.rows[0][2] = pf_curr[2]; + tc.basis.rows[1][0] = pf_curr[4]; + tc.basis.rows[1][1] = pf_curr[5]; + tc.basis.rows[1][2] = pf_curr[6]; + tc.basis.rows[2][0] = pf_curr[8]; + tc.basis.rows[2][1] = pf_curr[9]; + tc.basis.rows[2][2] = pf_curr[10]; + tc.origin.x = pf_curr[3]; + tc.origin.y = pf_curr[7]; + tc.origin.z = pf_curr[11]; + + TransformInterpolator::interpolate_transform_3d(tp, tc, tr, f); + + pf_int[0] = tr.basis.rows[0][0]; + pf_int[1] = tr.basis.rows[0][1]; + pf_int[2] = tr.basis.rows[0][2]; + pf_int[4] = tr.basis.rows[1][0]; + pf_int[5] = tr.basis.rows[1][1]; + pf_int[6] = tr.basis.rows[1][2]; + pf_int[8] = tr.basis.rows[2][0]; + pf_int[9] = tr.basis.rows[2][1]; + pf_int[10] = tr.basis.rows[2][2]; + pf_int[3] = tr.origin.x; + pf_int[7] = tr.origin.y; + pf_int[11] = tr.origin.z; + } + + pf_prev += mmi->_vf_size_xform; + pf_curr += mmi->_vf_size_xform; + pf_int += mmi->_vf_size_xform; + + // Color. + if (mmi->_vf_size_color == 4) { + for (int i = 0; i < 4; i++) { + pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f); + } + + pf_prev += 4; + pf_curr += 4; + pf_int += 4; + } + + // Custom data. + if (mmi->_vf_size_data == 4) { + for (int i = 0; i < 4; i++) { + pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f); + } + + pf_prev += 4; + pf_curr += 4; + pf_int += 4; + } + } + + _multimesh_set_buffer(rid, mmi->_data_interpolated); + + // TODO: Make sure AABBs are constantly up to date through the interpolation? + // NYI. + } + } + } +} diff --git a/servers/rendering/storage/mesh_storage.h b/servers/rendering/storage/mesh_storage.h index 39fd4f393d..ecd2a967d0 100644 --- a/servers/rendering/storage/mesh_storage.h +++ b/servers/rendering/storage/mesh_storage.h @@ -89,39 +89,110 @@ public: virtual void update_mesh_instances() = 0; /* MULTIMESH API */ + struct MultiMeshInterpolator { + RS::MultimeshTransformFormat _transform_format = RS::MULTIMESH_TRANSFORM_3D; + bool _use_colors = false; + bool _use_custom_data = false; - virtual RID multimesh_allocate() = 0; - virtual void multimesh_initialize(RID p_rid) = 0; - virtual void multimesh_free(RID p_rid) = 0; + // The stride of the buffer in floats. + int _stride = 0; - virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0; + // Vertex format sizes in floats. + int _vf_size_xform = 0; + int _vf_size_color = 0; + int _vf_size_data = 0; - virtual int multimesh_get_instance_count(RID p_multimesh) const = 0; + // Set by allocate, can be used to prevent indexing out of range. + int _num_instances = 0; - virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) = 0; - virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) = 0; - virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) = 0; - virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0; - virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0; + // Quality determines whether to use lerp or slerp etc. + int quality = 0; + bool interpolated = false; + bool on_interpolate_update_list = false; + bool on_transform_update_list = false; - virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) = 0; - virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const = 0; + Vector<float> _data_prev; + Vector<float> _data_curr; + Vector<float> _data_interpolated; + }; - virtual RID multimesh_get_mesh(RID p_multimesh) const = 0; + virtual RID multimesh_allocate(); + virtual void multimesh_initialize(RID p_rid); + virtual void multimesh_free(RID p_rid); - virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0; - virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const = 0; - virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0; - virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0; + virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false); - virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) = 0; - virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const = 0; + virtual int multimesh_get_instance_count(RID p_multimesh) const; - virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0; - virtual int multimesh_get_visible_instances(RID p_multimesh) const = 0; + virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh); + virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform); + virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform); + virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color); + virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color); - virtual AABB multimesh_get_aabb(RID p_multimesh) const = 0; + virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb); + virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const; + virtual RID multimesh_get_mesh(RID p_multimesh) const; + + virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const; + virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const; + virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const; + virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const; + + virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer); + virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const; + + virtual void multimesh_set_buffer_interpolated(RID p_multimesh, const Vector<float> &p_buffer, const Vector<float> &p_buffer_prev); + virtual void multimesh_set_physics_interpolated(RID p_multimesh, bool p_interpolated); + virtual void multimesh_set_physics_interpolation_quality(RID p_multimesh, RS::MultimeshPhysicsInterpolationQuality p_quality); + virtual void multimesh_instance_reset_physics_interpolation(RID p_multimesh, int p_index); + + virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible); + virtual int multimesh_get_visible_instances(RID p_multimesh) const; + + virtual AABB multimesh_get_aabb(RID p_multimesh) const; + + virtual RID _multimesh_allocate() = 0; + virtual void _multimesh_initialize(RID p_rid) = 0; + virtual void _multimesh_free(RID p_rid) = 0; + + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0; + + virtual int _multimesh_get_instance_count(RID p_multimesh) const = 0; + + virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) = 0; + virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) = 0; + virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) = 0; + virtual void _multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0; + virtual void _multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0; + + virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) = 0; + virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const = 0; + + virtual RID _multimesh_get_mesh(RID p_multimesh) const = 0; + + virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0; + virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const = 0; + virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0; + virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0; + + virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) = 0; + virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const = 0; + + virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0; + virtual int _multimesh_get_visible_instances(RID p_multimesh) const = 0; + + virtual AABB _multimesh_get_aabb(RID p_multimesh) const = 0; + + // Multimesh is responsible for allocating / destroying a MultiMeshInterpolator object. + // This allows shared functionality for interpolation across backends. + virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const = 0; + +private: + void _multimesh_add_to_interpolation_lists(RID p_multimesh, MultiMeshInterpolator &r_mmi); + +public: /* SKELETON API */ virtual RID skeleton_allocate() = 0; @@ -137,6 +208,19 @@ public: virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0; virtual void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) = 0; + + /* INTERPOLATION */ + + struct InterpolationData { + void notify_free_multimesh(RID p_rid); + LocalVector<RID> multimesh_interpolate_update_list; + LocalVector<RID> multimesh_transform_update_lists[2]; + LocalVector<RID> *multimesh_transform_update_list_curr = &multimesh_transform_update_lists[0]; + LocalVector<RID> *multimesh_transform_update_list_prev = &multimesh_transform_update_lists[1]; + } _interpolation_data; + + void update_interpolation_tick(bool p_process = true); + void update_interpolation_frame(bool p_process = true); }; #endif // MESH_STORAGE_H diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 9fc67b04b1..0f0d15a52f 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2439,8 +2439,15 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("multimesh_set_buffer", "multimesh", "buffer"), &RenderingServer::multimesh_set_buffer); ClassDB::bind_method(D_METHOD("multimesh_get_buffer", "multimesh"), &RenderingServer::multimesh_get_buffer); + ClassDB::bind_method(D_METHOD("multimesh_set_buffer_interpolated", "multimesh", "buffer", "buffer_previous"), &RenderingServer::multimesh_set_buffer_interpolated); + ClassDB::bind_method(D_METHOD("multimesh_set_physics_interpolated", "multimesh", "interpolated"), &RenderingServer::multimesh_set_physics_interpolated); + ClassDB::bind_method(D_METHOD("multimesh_set_physics_interpolation_quality", "multimesh", "quality"), &RenderingServer::multimesh_set_physics_interpolation_quality); + ClassDB::bind_method(D_METHOD("multimesh_instance_reset_physics_interpolation", "multimesh", "index"), &RenderingServer::multimesh_instance_reset_physics_interpolation); + BIND_ENUM_CONSTANT(MULTIMESH_TRANSFORM_2D); BIND_ENUM_CONSTANT(MULTIMESH_TRANSFORM_3D); + BIND_ENUM_CONSTANT(MULTIMESH_INTERP_QUALITY_FAST); + BIND_ENUM_CONSTANT(MULTIMESH_INTERP_QUALITY_HIGH); /* SKELETON API */ @@ -2477,6 +2484,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("light_directional_set_sky_mode", "light", "mode"), &RenderingServer::light_directional_set_sky_mode); ClassDB::bind_method(D_METHOD("light_projectors_set_filter", "filter"), &RenderingServer::light_projectors_set_filter); + ClassDB::bind_method(D_METHOD("lightmaps_set_bicubic_filter", "enable"), &RenderingServer::lightmaps_set_bicubic_filter); BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST); BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_LINEAR); @@ -3618,6 +3626,7 @@ void RenderingServer::init() { GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/probe_capture/update_speed", PROPERTY_HINT_RANGE, "0.001,256,0.001"), 15); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/primitive_meshes/texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2); + GLOBAL_DEF("rendering/lightmapping/lightmap_gi/use_bicubic_filter", true); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/sdfgi/probe_ray_count", PROPERTY_HINT_ENUM, "8 (Fastest),16,32,64,96,128 (Slowest)"), 1); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/sdfgi/frames_to_converge", PROPERTY_HINT_ENUM, "5 (Less Latency but Lower Quality),10,15,20,25,30 (More Latency but Higher Quality)"), 5); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 8450cb0198..d8b6651833 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -426,6 +426,11 @@ public: MULTIMESH_TRANSFORM_3D, }; + enum MultimeshPhysicsInterpolationQuality { + MULTIMESH_INTERP_QUALITY_FAST, + MULTIMESH_INTERP_QUALITY_HIGH, + }; + virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0; virtual int multimesh_get_instance_count(RID p_multimesh) const = 0; @@ -449,6 +454,12 @@ public: virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) = 0; virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const = 0; + // Interpolation. + virtual void multimesh_set_buffer_interpolated(RID p_multimesh, const Vector<float> &p_buffer_curr, const Vector<float> &p_buffer_prev) = 0; + virtual void multimesh_set_physics_interpolated(RID p_multimesh, bool p_interpolated) = 0; + virtual void multimesh_set_physics_interpolation_quality(RID p_multimesh, MultimeshPhysicsInterpolationQuality p_quality) = 0; + virtual void multimesh_instance_reset_physics_interpolation(RID p_multimesh, int p_index) = 0; + virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0; virtual int multimesh_get_visible_instances(RID p_multimesh) const = 0; @@ -687,6 +698,8 @@ public: virtual void lightmap_set_probe_capture_update_speed(float p_speed) = 0; + virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0; + /* PARTICLES API */ virtual RID particles_create() = 0; @@ -924,6 +937,7 @@ public: virtual void viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset) = 0; virtual void viewport_set_transparent_background(RID p_viewport, bool p_enabled) = 0; virtual void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr) = 0; + virtual bool viewport_is_using_hdr_2d(RID p_viewport) const = 0; virtual void viewport_set_snap_2d_transforms_to_pixel(RID p_viewport, bool p_enabled) = 0; virtual void viewport_set_snap_2d_vertices_to_pixel(RID p_viewport, bool p_enabled) = 0; @@ -1788,6 +1802,7 @@ VARIANT_ENUM_CAST(RenderingServer::ArrayCustomFormat); VARIANT_ENUM_CAST(RenderingServer::PrimitiveType); VARIANT_ENUM_CAST(RenderingServer::BlendShapeMode); VARIANT_ENUM_CAST(RenderingServer::MultimeshTransformFormat); +VARIANT_ENUM_CAST(RenderingServer::MultimeshPhysicsInterpolationQuality); VARIANT_ENUM_CAST(RenderingServer::LightType); VARIANT_ENUM_CAST(RenderingServer::LightParam); VARIANT_ENUM_CAST(RenderingServer::LightBakeMode); diff --git a/servers/xr/xr_hand_tracker.cpp b/servers/xr/xr_hand_tracker.cpp index abfe2e9867..2f64737ed6 100644 --- a/servers/xr/xr_hand_tracker.cpp +++ b/servers/xr/xr_hand_tracker.cpp @@ -60,6 +60,7 @@ void XRHandTracker::_bind_methods() { BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNKNOWN); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNOBSTRUCTED); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_CONTROLLER); + BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_NOT_TRACKED); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_MAX); BIND_ENUM_CONSTANT(HAND_JOINT_PALM); diff --git a/servers/xr/xr_hand_tracker.h b/servers/xr/xr_hand_tracker.h index c7c18a31f8..7837df3e0a 100644 --- a/servers/xr/xr_hand_tracker.h +++ b/servers/xr/xr_hand_tracker.h @@ -42,6 +42,7 @@ public: HAND_TRACKING_SOURCE_UNKNOWN, HAND_TRACKING_SOURCE_UNOBSTRUCTED, HAND_TRACKING_SOURCE_CONTROLLER, + HAND_TRACKING_SOURCE_NOT_TRACKED, HAND_TRACKING_SOURCE_MAX }; diff --git a/tests/core/config/test_project_settings.h b/tests/core/config/test_project_settings.h index 8fc2489f8b..0e1058a626 100644 --- a/tests/core/config/test_project_settings.h +++ b/tests/core/config/test_project_settings.h @@ -126,10 +126,9 @@ TEST_CASE("[ProjectSettings] localize_path") { CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\.\\filename"), "res://path/filename"); #endif - // FIXME?: These checks pass, but that doesn't seems correct - CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "res://filename"); - CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "res://path/filename"); - CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "../filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "../path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "../path/filename"); CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/filename"), "/testroot/filename"); CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/filename"), "/testroot/path/filename"); diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index 393ae16d0a..933eeff524 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -1620,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/scene/test_code_edit.h b/tests/scene/test_code_edit.h index a166002cdd..9ec1b812df 100644 --- a/tests/scene/test_code_edit.h +++ b/tests/scene/test_code_edit.h @@ -4609,6 +4609,26 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") { CHECK(code_edit->get_text() == "line 1\nline 2\nline 3"); CHECK(code_edit->get_caret_line() == 0); CHECK(code_edit->get_caret_column() == 0); + + // Unfold previous folded line on backspace if the caret is at the first column. + code_edit->set_line_folding_enabled(true); + code_edit->set_text("line 1\n\tline 2\nline 3"); + code_edit->set_caret_line(2); + code_edit->set_caret_column(0); + code_edit->fold_line(0); + code_edit->backspace(); + CHECK_FALSE(code_edit->is_line_folded(0)); + code_edit->set_line_folding_enabled(false); + + // Do not unfold previous line on backspace if the caret is not at the first column. + code_edit->set_line_folding_enabled(true); + code_edit->set_text("line 1\n\tline 2\nline 3"); + code_edit->set_caret_line(2); + code_edit->set_caret_column(4); + code_edit->fold_line(0); + code_edit->backspace(); + CHECK(code_edit->is_line_folded(0)); + code_edit->set_line_folding_enabled(false); } SUBCASE("[TextEdit] cut") { diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index b2d9f5100e..69e27fe7a0 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -1763,6 +1763,28 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 4); + + // Wrapped lines. + text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); + text_edit->set_text("this is some text\nfor selection"); + text_edit->set_size(Size2(110, 100)); + MessageQueue::get_singleton()->flush(); + + // Line 0 wraps: 'this is ', 'some text'. + // Line 1 wraps: 'for ', 'selection'. + CHECK(text_edit->is_line_wrapped(0)); + + // Select to the first character of a wrapped line. + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 11).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 8).get_center(), MouseButtonMask::LEFT, Key::NONE); + CHECK(text_edit->has_selection()); + CHECK(text_edit->get_selected_text() == "so"); + CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER); + CHECK(text_edit->get_selection_origin_line() == 0); + CHECK(text_edit->get_selection_origin_column() == 10); + CHECK(text_edit->get_caret_line() == 0); + CHECK(text_edit->get_caret_column() == 8); + CHECK(text_edit->is_dragging_cursor()); } SUBCASE("[TextEdit] mouse word select") { @@ -5713,6 +5735,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_caret_count() == 2); MessageQueue::get_singleton()->flush(); + // Lines 0 and 4 are wrapped into 2 parts: 'this is ' and 'some'. CHECK(text_edit->is_line_wrapped(0)); SIGNAL_DISCARD("text_set"); SIGNAL_DISCARD("text_changed"); @@ -5762,9 +5785,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); - text_edit->set_caret_column(12, false); // Normal up over wrapped line to line 0. + text_edit->set_caret_column(12, false); SEND_GUI_ACTION("ui_text_caret_up"); CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); @@ -5777,6 +5800,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + + // Normal up from column 0 to a wrapped line. + text_edit->remove_secondary_carets(); + text_edit->set_caret_line(5); + text_edit->set_caret_column(0); + SEND_GUI_ACTION("ui_text_caret_up"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 4); + CHECK(text_edit->get_caret_column() == 8); + CHECK_FALSE(text_edit->has_selection(0)); + + // Normal up to column 0 of a wrapped line. + SEND_GUI_ACTION("ui_text_caret_up"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 4); + CHECK(text_edit->get_caret_column() == 0); + CHECK_FALSE(text_edit->has_selection(0)); } SUBCASE("[TextEdit] ui_text_caret_down") { @@ -5792,6 +5832,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { MessageQueue::get_singleton()->flush(); + // Lines 3 and 7 are wrapped into 2 parts: 'this is ' and 'some'. CHECK(text_edit->is_line_wrapped(3)); SIGNAL_DISCARD("text_set"); SIGNAL_DISCARD("text_changed"); @@ -5841,9 +5882,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); - text_edit->set_caret_column(7, false); // Normal down over wrapped line to last wrapped line. + text_edit->set_caret_column(7, false); SEND_GUI_ACTION("ui_text_caret_down"); CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 3); @@ -5856,6 +5897,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + + // Normal down to column 0 of a wrapped line. + text_edit->remove_secondary_carets(); + text_edit->set_caret_line(3); + text_edit->set_caret_column(0); + SEND_GUI_ACTION("ui_text_caret_down"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 3); + CHECK(text_edit->get_caret_column() == 8); + CHECK_FALSE(text_edit->has_selection(0)); + + // Normal down out of visual column 0 of a wrapped line moves to start of next line. + SEND_GUI_ACTION("ui_text_caret_down"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 4); + CHECK(text_edit->get_caret_column() == 0); + CHECK_FALSE(text_edit->has_selection(0)); } SUBCASE("[TextEdit] ui_text_caret_document_start") { @@ -7162,7 +7220,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") { CHECK(text_edit->get_caret_line(0) == 2); CHECK(text_edit->get_caret_column(0) == 5); CHECK(text_edit->get_caret_line(1) == 2); - CHECK(text_edit->get_caret_column(1) == 10); + CHECK(text_edit->get_caret_column(1) == 6); // Cannot add caret below from last line last line wrap. text_edit->add_caret_at_carets(true); @@ -7171,7 +7229,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") { CHECK(text_edit->get_caret_line(0) == 2); CHECK(text_edit->get_caret_column(0) == 5); CHECK(text_edit->get_caret_line(1) == 2); - CHECK(text_edit->get_caret_column(1) == 10); + CHECK(text_edit->get_caret_column(1) == 6); // Add caret above from not first line wrap. text_edit->remove_secondary_carets(); diff --git a/thirdparty/README.md b/thirdparty/README.md index 47618d675b..f241ce56d1 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: @@ -894,7 +894,7 @@ number and run the script. ## ufbx - Upstream: https://github.com/ufbx/ufbx -- Version: 0.14.0 (80ff790ab36507b99ec7e4ef55b9cfb076ce821b, 2024) +- Version: 0.14.3 (19bdb7e7ef02eb914d5e7211a3685f50ee6d27e3, 2024) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/openxr/include/openxr/openxr.h b/thirdparty/openxr/include/openxr/openxr.h index c0c826b981..9ac66d8c69 100644 --- a/thirdparty/openxr/include/openxr/openxr.h +++ b/thirdparty/openxr/include/openxr/openxr.h @@ -19,13 +19,17 @@ extern "C" { +// XR_VERSION_1_0 is a preprocessor guard. Do not pass it to API calls. #define XR_VERSION_1_0 1 #include "openxr_platform_defines.h" #define XR_MAKE_VERSION(major, minor, patch) \ ((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL)) // OpenXR current version number. -#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 34) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 38) + +// OpenXR 1.0 version number +#define XR_API_VERSION_1_0 XR_MAKE_VERSION(1, 0, XR_VERSION_PATCH(XR_CURRENT_API_VERSION)) #define XR_VERSION_MAJOR(version) (uint16_t)(((uint64_t)(version) >> 48)& 0xffffULL) #define XR_VERSION_MINOR(version) (uint16_t)(((uint64_t)(version) >> 32) & 0xffffULL) @@ -41,7 +45,6 @@ extern "C" { #define XR_NULL_HANDLE 0 #endif #endif - #define XR_NULL_SYSTEM_ID 0 @@ -96,13 +99,20 @@ extern "C" { #define XR_DEFINE_HANDLE(object) typedef uint64_t object; #endif #endif - + + +#if !defined(XR_DEFINE_OPAQUE_64) + #if (XR_PTR_SIZE == 8) + #define XR_DEFINE_OPAQUE_64(object) typedef struct object##_T* object; + #else + #define XR_DEFINE_OPAQUE_64(object) typedef uint64_t object; + #endif +#endif #if !defined(XR_DEFINE_ATOM) #define XR_DEFINE_ATOM(object) typedef uint64_t object; #endif - typedef uint64_t XrVersion; typedef uint64_t XrFlags64; @@ -190,6 +200,8 @@ typedef enum XrResult { XR_ERROR_LOCALIZED_NAME_INVALID = -49, XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING = -50, XR_ERROR_RUNTIME_UNAVAILABLE = -51, + XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED = -1000710001, + XR_ERROR_PERMISSION_INSUFFICIENT = -1000710000, XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR = -1000003000, XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR = -1000003001, XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT = -1000039001, @@ -239,10 +251,15 @@ typedef enum XrResult { XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB = -1000169003, XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB = -1000169004, XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META = -1000266000, + XR_ENVIRONMENT_DEPTH_NOT_AVAILABLE_META = 1000291000, XR_ERROR_HINT_ALREADY_SET_QCOM = -1000306000, XR_ERROR_NOT_AN_ANCHOR_HTC = -1000319000, XR_ERROR_SPACE_NOT_LOCATABLE_EXT = -1000429000, XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT = -1000429001, + XR_ERROR_FUTURE_PENDING_EXT = -1000469001, + XR_ERROR_FUTURE_INVALID_EXT = -1000469002, + XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED_KHR = XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED, + XR_ERROR_PERMISSION_INSUFFICIENT_KHR = XR_ERROR_PERMISSION_INSUFFICIENT, XR_RESULT_MAX_ENUM = 0x7FFFFFFF } XrResult; @@ -297,6 +314,9 @@ typedef enum XrStructureType { XR_TYPE_ACTIONS_SYNC_INFO = 61, XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO = 62, XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO = 63, + XR_TYPE_SPACES_LOCATE_INFO = 1000471000, + XR_TYPE_SPACE_LOCATIONS = 1000471001, + XR_TYPE_SPACE_VELOCITIES = 1000471002, XR_TYPE_COMPOSITION_LAYER_CUBE_KHR = 1000006000, XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR = 1000008000, XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR = 1000010000, @@ -562,6 +582,14 @@ typedef enum XrStructureType { XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB = 1000287014, XR_TYPE_FACE_EXPRESSION_INFO2_FB = 1000287015, XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB = 1000287016, + XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META = 1000291000, + XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META = 1000291001, + XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META = 1000291002, + XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META = 1000291003, + XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META = 1000291004, + XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META = 1000291005, + XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META = 1000291006, + XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META = 1000291007, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC = 1000317001, XR_TYPE_PASSTHROUGH_COLOR_HTC = 1000317002, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC = 1000317003, @@ -583,12 +611,19 @@ typedef enum XrStructureType { XR_TYPE_PLANE_DETECTOR_LOCATION_EXT = 1000429005, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT = 1000429006, XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT = 1000429007, + XR_TYPE_FUTURE_CANCEL_INFO_EXT = 1000469000, + XR_TYPE_FUTURE_POLL_INFO_EXT = 1000469001, + XR_TYPE_FUTURE_COMPLETION_EXT = 1000469002, + XR_TYPE_FUTURE_POLL_RESULT_EXT = 1000469003, XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT = 1000470000, XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT = 1000470001, XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR, XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR, XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR, XR_TYPE_DEVICE_PCM_SAMPLE_RATE_GET_INFO_FB = XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB, + XR_TYPE_SPACES_LOCATE_INFO_KHR = XR_TYPE_SPACES_LOCATE_INFO, + XR_TYPE_SPACE_LOCATIONS_KHR = XR_TYPE_SPACE_LOCATIONS, + XR_TYPE_SPACE_VELOCITIES_KHR = XR_TYPE_SPACE_VELOCITIES, XR_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } XrStructureType; @@ -601,8 +636,9 @@ typedef enum XrFormFactor { typedef enum XrViewConfigurationType { XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO = 1, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO = 2, - XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO = 1000037000, + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET = 1000037000, XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT = 1000054000, + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET, XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM = 0x7FFFFFFF } XrViewConfigurationType; @@ -617,10 +653,11 @@ typedef enum XrReferenceSpaceType { XR_REFERENCE_SPACE_TYPE_VIEW = 1, XR_REFERENCE_SPACE_TYPE_LOCAL = 2, XR_REFERENCE_SPACE_TYPE_STAGE = 3, + XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR = 1000426000, XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT = 1000038000, XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO = 1000121000, XR_REFERENCE_SPACE_TYPE_LOCALIZATION_MAP_ML = 1000139000, - XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT = 1000426000, + XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR, XR_REFERENCE_SPACE_TYPE_MAX_ENUM = 0x7FFFFFFF } XrReferenceSpaceType; @@ -683,6 +720,8 @@ typedef enum XrObjectType { XR_OBJECT_TYPE_SPACE_USER_FB = 1000241000, XR_OBJECT_TYPE_PASSTHROUGH_COLOR_LUT_META = 1000266000, XR_OBJECT_TYPE_FACE_TRACKER2_FB = 1000287012, + XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META = 1000291000, + XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META = 1000291001, XR_OBJECT_TYPE_PASSTHROUGH_HTC = 1000317000, XR_OBJECT_TYPE_PLANE_DETECTOR_EXT = 1000429000, XR_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF @@ -734,6 +773,7 @@ typedef XrFlags64 XrCompositionLayerFlags; static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001; static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002; static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT = 0x00000004; +static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_INVERTED_ALPHA_BIT_EXT = 0x00000008; typedef XrFlags64 XrViewStateFlags; @@ -1595,6 +1635,91 @@ XRAPI_ATTR XrResult XRAPI_CALL xrStopHapticFeedback( #endif /* !XR_NO_PROTOTYPES */ +// XR_VERSION_1_1 is a preprocessor guard. Do not pass it to API calls. +#define XR_VERSION_1_1 1 +// OpenXR 1.1 version number +#define XR_API_VERSION_1_1 XR_MAKE_VERSION(1, 1, XR_VERSION_PATCH(XR_CURRENT_API_VERSION)) + +#define XR_UUID_SIZE 16 +typedef struct XrColor3f { + float r; + float g; + float b; +} XrColor3f; + +typedef struct XrExtent3Df { + float width; + float height; + float depth; +} XrExtent3Df; + +typedef struct XrSpheref { + XrPosef center; + float radius; +} XrSpheref; + +typedef struct XrBoxf { + XrPosef center; + XrExtent3Df extents; +} XrBoxf; + +typedef struct XrFrustumf { + XrPosef pose; + XrFovf fov; + float nearZ; + float farZ; +} XrFrustumf; + +typedef struct XrUuid { + uint8_t data[XR_UUID_SIZE]; +} XrUuid; + +typedef struct XrSpacesLocateInfo { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace baseSpace; + XrTime time; + uint32_t spaceCount; + const XrSpace* spaces; +} XrSpacesLocateInfo; + +typedef struct XrSpaceLocationData { + XrSpaceLocationFlags locationFlags; + XrPosef pose; +} XrSpaceLocationData; + +typedef struct XrSpaceLocations { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t locationCount; + XrSpaceLocationData* locations; +} XrSpaceLocations; + +typedef struct XrSpaceVelocityData { + XrSpaceVelocityFlags velocityFlags; + XrVector3f linearVelocity; + XrVector3f angularVelocity; +} XrSpaceVelocityData; + +// XrSpaceVelocities extends XrSpaceLocations +typedef struct XrSpaceVelocities { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t velocityCount; + XrSpaceVelocityData* velocities; +} XrSpaceVelocities; + +typedef XrResult (XRAPI_PTR *PFN_xrLocateSpaces)(XrSession session, const XrSpacesLocateInfo* locateInfo, XrSpaceLocations* spaceLocations); + +#ifndef XR_NO_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpaces( + XrSession session, + const XrSpacesLocateInfo* locateInfo, + XrSpaceLocations* spaceLocations); +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_KHR_composition_layer_cube is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_cube 1 #define XR_KHR_composition_layer_cube_SPEC_VERSION 8 #define XR_KHR_COMPOSITION_LAYER_CUBE_EXTENSION_NAME "XR_KHR_composition_layer_cube" @@ -1611,6 +1736,7 @@ typedef struct XrCompositionLayerCubeKHR { +// XR_KHR_composition_layer_depth is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_depth 1 #define XR_KHR_composition_layer_depth_SPEC_VERSION 6 #define XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME "XR_KHR_composition_layer_depth" @@ -1627,6 +1753,7 @@ typedef struct XrCompositionLayerDepthInfoKHR { +// XR_KHR_composition_layer_cylinder is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_cylinder 1 #define XR_KHR_composition_layer_cylinder_SPEC_VERSION 4 #define XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME "XR_KHR_composition_layer_cylinder" @@ -1645,6 +1772,7 @@ typedef struct XrCompositionLayerCylinderKHR { +// XR_KHR_composition_layer_equirect is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_equirect 1 #define XR_KHR_composition_layer_equirect_SPEC_VERSION 3 #define XR_KHR_COMPOSITION_LAYER_EQUIRECT_EXTENSION_NAME "XR_KHR_composition_layer_equirect" @@ -1663,6 +1791,7 @@ typedef struct XrCompositionLayerEquirectKHR { +// XR_KHR_visibility_mask is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_visibility_mask 1 #define XR_KHR_visibility_mask_SPEC_VERSION 2 #define XR_KHR_VISIBILITY_MASK_EXTENSION_NAME "XR_KHR_visibility_mask" @@ -1706,6 +1835,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVisibilityMaskKHR( #endif /* !XR_NO_PROTOTYPES */ +// XR_KHR_composition_layer_color_scale_bias is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_color_scale_bias 1 #define XR_KHR_composition_layer_color_scale_bias_SPEC_VERSION 5 #define XR_KHR_COMPOSITION_LAYER_COLOR_SCALE_BIAS_EXTENSION_NAME "XR_KHR_composition_layer_color_scale_bias" @@ -1719,6 +1849,7 @@ typedef struct XrCompositionLayerColorScaleBiasKHR { +// XR_KHR_loader_init is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_loader_init 1 #define XR_KHR_loader_init_SPEC_VERSION 2 #define XR_KHR_LOADER_INIT_EXTENSION_NAME "XR_KHR_loader_init" @@ -1737,6 +1868,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrInitializeLoaderKHR( #endif /* !XR_NO_PROTOTYPES */ +// XR_KHR_composition_layer_equirect2 is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_equirect2 1 #define XR_KHR_composition_layer_equirect2_SPEC_VERSION 1 #define XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME "XR_KHR_composition_layer_equirect2" @@ -1756,6 +1888,7 @@ typedef struct XrCompositionLayerEquirect2KHR { +// XR_KHR_binding_modification is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_binding_modification 1 #define XR_KHR_binding_modification_SPEC_VERSION 1 #define XR_KHR_BINDING_MODIFICATION_EXTENSION_NAME "XR_KHR_binding_modification" @@ -1774,11 +1907,55 @@ typedef struct XrBindingModificationsKHR { +// XR_KHR_swapchain_usage_input_attachment_bit is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_swapchain_usage_input_attachment_bit 1 #define XR_KHR_swapchain_usage_input_attachment_bit_SPEC_VERSION 3 #define XR_KHR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_EXTENSION_NAME "XR_KHR_swapchain_usage_input_attachment_bit" +// XR_KHR_locate_spaces is a preprocessor guard. Do not pass it to API calls. +#define XR_KHR_locate_spaces 1 +#define XR_KHR_locate_spaces_SPEC_VERSION 1 +#define XR_KHR_LOCATE_SPACES_EXTENSION_NAME "XR_KHR_locate_spaces" +typedef XrSpacesLocateInfo XrSpacesLocateInfoKHR; + +typedef XrSpaceLocationData XrSpaceLocationDataKHR; + +typedef XrSpaceLocations XrSpaceLocationsKHR; + +typedef XrSpaceVelocityData XrSpaceVelocityDataKHR; + +typedef XrSpaceVelocities XrSpaceVelocitiesKHR; + +typedef XrResult (XRAPI_PTR *PFN_xrLocateSpacesKHR)(XrSession session, const XrSpacesLocateInfo* locateInfo, XrSpaceLocations* spaceLocations); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpacesKHR( + XrSession session, + const XrSpacesLocateInfo* locateInfo, + XrSpaceLocations* spaceLocations); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_KHR_maintenance1 is a preprocessor guard. Do not pass it to API calls. +#define XR_KHR_maintenance1 1 +#define XR_KHR_maintenance1_SPEC_VERSION 1 +#define XR_KHR_MAINTENANCE1_EXTENSION_NAME "XR_KHR_maintenance1" +typedef XrColor3f XrColor3fKHR; + +typedef XrExtent3Df XrExtent3DfKHR; + +typedef XrSpheref XrSpherefKHR; + +typedef XrBoxf XrBoxfKHR; + +typedef XrFrustumf XrFrustumfKHR; + + + +// XR_EXT_performance_settings is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_performance_settings 1 #define XR_EXT_performance_settings_SPEC_VERSION 4 #define XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME "XR_EXT_performance_settings" @@ -1831,6 +2008,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrPerfSettingsSetPerformanceLevelEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_thermal_query is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_thermal_query 1 #define XR_EXT_thermal_query_SPEC_VERSION 2 #define XR_EXT_THERMAL_QUERY_EXTENSION_NAME "XR_EXT_thermal_query" @@ -1848,6 +2026,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrThermalGetTemperatureTrendEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_debug_utils is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_debug_utils 1 XR_DEFINE_HANDLE(XrDebugUtilsMessengerEXT) #define XR_EXT_debug_utils_SPEC_VERSION 5 @@ -1953,6 +2132,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSessionInsertDebugUtilsLabelEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_eye_gaze_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_eye_gaze_interaction 1 #define XR_EXT_eye_gaze_interaction_SPEC_VERSION 2 #define XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME "XR_EXT_eye_gaze_interaction" @@ -1972,6 +2152,7 @@ typedef struct XrEyeGazeSampleTimeEXT { +// XR_EXTX_overlay is a preprocessor guard. Do not pass it to API calls. #define XR_EXTX_overlay 1 #define XR_EXTX_overlay_SPEC_VERSION 5 #define XR_EXTX_OVERLAY_EXTENSION_NAME "XR_EXTX_overlay" @@ -2001,16 +2182,19 @@ typedef struct XrEventDataMainSessionVisibilityChangedEXTX { +// XR_VARJO_quad_views is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_quad_views 1 #define XR_VARJO_quad_views_SPEC_VERSION 1 #define XR_VARJO_QUAD_VIEWS_EXTENSION_NAME "XR_VARJO_quad_views" +// XR_MSFT_unbounded_reference_space is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_unbounded_reference_space 1 #define XR_MSFT_unbounded_reference_space_SPEC_VERSION 1 #define XR_MSFT_UNBOUNDED_REFERENCE_SPACE_EXTENSION_NAME "XR_MSFT_unbounded_reference_space" +// XR_MSFT_spatial_anchor is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_spatial_anchor 1 XR_DEFINE_HANDLE(XrSpatialAnchorMSFT) #define XR_MSFT_spatial_anchor_SPEC_VERSION 2 @@ -2052,6 +2236,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialAnchorMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_composition_layer_image_layout is a preprocessor guard. Do not pass it to API calls. #define XR_FB_composition_layer_image_layout 1 #define XR_FB_composition_layer_image_layout_SPEC_VERSION 1 #define XR_FB_COMPOSITION_LAYER_IMAGE_LAYOUT_EXTENSION_NAME "XR_FB_composition_layer_image_layout" @@ -2069,8 +2254,9 @@ typedef struct XrCompositionLayerImageLayoutFB { +// XR_FB_composition_layer_alpha_blend is a preprocessor guard. Do not pass it to API calls. #define XR_FB_composition_layer_alpha_blend 1 -#define XR_FB_composition_layer_alpha_blend_SPEC_VERSION 2 +#define XR_FB_composition_layer_alpha_blend_SPEC_VERSION 3 #define XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME "XR_FB_composition_layer_alpha_blend" typedef enum XrBlendFactorFB { @@ -2094,16 +2280,19 @@ typedef struct XrCompositionLayerAlphaBlendFB { +// XR_MND_headless is a preprocessor guard. Do not pass it to API calls. #define XR_MND_headless 1 #define XR_MND_headless_SPEC_VERSION 2 #define XR_MND_HEADLESS_EXTENSION_NAME "XR_MND_headless" +// XR_OCULUS_android_session_state_enable is a preprocessor guard. Do not pass it to API calls. #define XR_OCULUS_android_session_state_enable 1 #define XR_OCULUS_android_session_state_enable_SPEC_VERSION 1 #define XR_OCULUS_ANDROID_SESSION_STATE_ENABLE_EXTENSION_NAME "XR_OCULUS_android_session_state_enable" +// XR_EXT_view_configuration_depth_range is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_view_configuration_depth_range 1 #define XR_EXT_view_configuration_depth_range_SPEC_VERSION 1 #define XR_EXT_VIEW_CONFIGURATION_DEPTH_RANGE_EXTENSION_NAME "XR_EXT_view_configuration_depth_range" @@ -2119,6 +2308,7 @@ typedef struct XrViewConfigurationDepthRangeEXT { +// XR_EXT_conformance_automation is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_conformance_automation 1 #define XR_EXT_conformance_automation_SPEC_VERSION 3 #define XR_EXT_CONFORMANCE_AUTOMATION_EXTENSION_NAME "XR_EXT_conformance_automation" @@ -2164,6 +2354,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceLocationEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_spatial_graph_bridge is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_spatial_graph_bridge 1 XR_DEFINE_HANDLE(XrSpatialGraphNodeBindingMSFT) #define XR_GUID_SIZE_MSFT 16 @@ -2231,11 +2422,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialGraphNodeBindingPropertiesMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_hand_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_hand_interaction 1 #define XR_MSFT_hand_interaction_SPEC_VERSION 1 #define XR_MSFT_HAND_INTERACTION_EXTENSION_NAME "XR_MSFT_hand_interaction" +// XR_EXT_hand_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hand_tracking 1 #define XR_HAND_JOINT_COUNT_EXT 26 @@ -2356,6 +2549,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrLocateHandJointsEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_hand_tracking_mesh is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_hand_tracking_mesh 1 #define XR_MSFT_hand_tracking_mesh_SPEC_VERSION 4 #define XR_MSFT_HAND_TRACKING_MESH_EXTENSION_NAME "XR_MSFT_hand_tracking_mesh" @@ -2442,6 +2636,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrUpdateHandMeshMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_secondary_view_configuration is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_secondary_view_configuration 1 #define XR_MSFT_secondary_view_configuration_SPEC_VERSION 1 #define XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME "XR_MSFT_secondary_view_configuration" @@ -2494,11 +2689,13 @@ typedef struct XrSecondaryViewConfigurationSwapchainCreateInfoMSFT { +// XR_MSFT_first_person_observer is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_first_person_observer 1 #define XR_MSFT_first_person_observer_SPEC_VERSION 1 #define XR_MSFT_FIRST_PERSON_OBSERVER_EXTENSION_NAME "XR_MSFT_first_person_observer" +// XR_MSFT_controller_model is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_controller_model 1 #define XR_NULL_CONTROLLER_MODEL_KEY_MSFT 0 @@ -2574,11 +2771,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetControllerModelStateMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_win32_appcontainer_compatible is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_win32_appcontainer_compatible 1 #define XR_EXT_win32_appcontainer_compatible_SPEC_VERSION 1 #define XR_EXT_WIN32_APPCONTAINER_COMPATIBLE_EXTENSION_NAME "XR_EXT_win32_appcontainer_compatible" +// XR_EPIC_view_configuration_fov is a preprocessor guard. Do not pass it to API calls. #define XR_EPIC_view_configuration_fov 1 #define XR_EPIC_view_configuration_fov_SPEC_VERSION 2 #define XR_EPIC_VIEW_CONFIGURATION_FOV_EXTENSION_NAME "XR_EPIC_view_configuration_fov" @@ -2592,6 +2791,7 @@ typedef struct XrViewConfigurationViewFovEPIC { +// XR_MSFT_composition_layer_reprojection is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_composition_layer_reprojection 1 #define XR_MSFT_composition_layer_reprojection_SPEC_VERSION 1 #define XR_MSFT_COMPOSITION_LAYER_REPROJECTION_EXTENSION_NAME "XR_MSFT_composition_layer_reprojection" @@ -2634,11 +2834,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateReprojectionModesMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_HUAWEI_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HUAWEI_controller_interaction 1 #define XR_HUAWEI_controller_interaction_SPEC_VERSION 1 #define XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HUAWEI_controller_interaction" +// XR_FB_swapchain_update_state is a preprocessor guard. Do not pass it to API calls. #define XR_FB_swapchain_update_state 1 #define XR_FB_swapchain_update_state_SPEC_VERSION 3 #define XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME "XR_FB_swapchain_update_state" @@ -2663,6 +2865,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSwapchainStateFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_composition_layer_secure_content is a preprocessor guard. Do not pass it to API calls. #define XR_FB_composition_layer_secure_content 1 #define XR_FB_composition_layer_secure_content_SPEC_VERSION 1 #define XR_FB_COMPOSITION_LAYER_SECURE_CONTENT_EXTENSION_NAME "XR_FB_composition_layer_secure_content" @@ -2681,6 +2884,7 @@ typedef struct XrCompositionLayerSecureContentFB { +// XR_FB_body_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_FB_body_tracking 1 XR_DEFINE_HANDLE(XrBodyTrackerFB) #define XR_FB_body_tracking_SPEC_VERSION 1 @@ -2842,6 +3046,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetBodySkeletonFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_dpad_binding is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_dpad_binding 1 #define XR_EXT_dpad_binding_SPEC_VERSION 1 #define XR_EXT_DPAD_BINDING_EXTENSION_NAME "XR_EXT_dpad_binding" @@ -2861,6 +3066,7 @@ typedef struct XrInteractionProfileDpadBindingEXT { +// XR_VALVE_analog_threshold is a preprocessor guard. Do not pass it to API calls. #define XR_VALVE_analog_threshold 1 #define XR_VALVE_analog_threshold_SPEC_VERSION 2 #define XR_VALVE_ANALOG_THRESHOLD_EXTENSION_NAME "XR_VALVE_analog_threshold" @@ -2877,6 +3083,7 @@ typedef struct XrInteractionProfileAnalogThresholdVALVE { +// XR_EXT_hand_joints_motion_range is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hand_joints_motion_range 1 #define XR_EXT_hand_joints_motion_range_SPEC_VERSION 1 #define XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME "XR_EXT_hand_joints_motion_range" @@ -2895,21 +3102,25 @@ typedef struct XrHandJointsMotionRangeInfoEXT { +// XR_EXT_samsung_odyssey_controller is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_samsung_odyssey_controller 1 #define XR_EXT_samsung_odyssey_controller_SPEC_VERSION 1 #define XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME "XR_EXT_samsung_odyssey_controller" +// XR_EXT_hp_mixed_reality_controller is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hp_mixed_reality_controller 1 #define XR_EXT_hp_mixed_reality_controller_SPEC_VERSION 1 #define XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME "XR_EXT_hp_mixed_reality_controller" +// XR_MND_swapchain_usage_input_attachment_bit is a preprocessor guard. Do not pass it to API calls. #define XR_MND_swapchain_usage_input_attachment_bit 1 #define XR_MND_swapchain_usage_input_attachment_bit_SPEC_VERSION 2 #define XR_MND_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_EXTENSION_NAME "XR_MND_swapchain_usage_input_attachment_bit" +// XR_MSFT_scene_understanding is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_scene_understanding 1 XR_DEFINE_HANDLE(XrSceneObserverMSFT) @@ -3240,6 +3451,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneMeshBuffersMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_scene_understanding_serialization is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_scene_understanding_serialization 1 #define XR_MSFT_scene_understanding_serialization_SPEC_VERSION 2 #define XR_MSFT_SCENE_UNDERSTANDING_SERIALIZATION_EXTENSION_NAME "XR_MSFT_scene_understanding_serialization" @@ -3280,6 +3492,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSerializedSceneFragmentDataMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_display_refresh_rate is a preprocessor guard. Do not pass it to API calls. #define XR_FB_display_refresh_rate 1 #define XR_FB_display_refresh_rate_SPEC_VERSION 1 #define XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME "XR_FB_display_refresh_rate" @@ -3313,11 +3526,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRequestDisplayRefreshRateFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_vive_cosmos_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_vive_cosmos_controller_interaction 1 #define XR_HTC_vive_cosmos_controller_interaction_SPEC_VERSION 1 #define XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_cosmos_controller_interaction" +// XR_HTCX_vive_tracker_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HTCX_vive_tracker_interaction 1 #define XR_HTCX_vive_tracker_interaction_SPEC_VERSION 3 #define XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTCX_vive_tracker_interaction" @@ -3347,6 +3562,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViveTrackerPathsHTCX( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_facial_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_facial_tracking 1 #define XR_FACIAL_EXPRESSION_EYE_COUNT_HTC 14 @@ -3466,21 +3682,25 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialExpressionsHTC( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_vive_focus3_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_vive_focus3_controller_interaction 1 #define XR_HTC_vive_focus3_controller_interaction_SPEC_VERSION 2 #define XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_focus3_controller_interaction" +// XR_HTC_hand_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_hand_interaction 1 #define XR_HTC_hand_interaction_SPEC_VERSION 1 #define XR_HTC_HAND_INTERACTION_EXTENSION_NAME "XR_HTC_hand_interaction" +// XR_HTC_vive_wrist_tracker_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_vive_wrist_tracker_interaction 1 #define XR_HTC_vive_wrist_tracker_interaction_SPEC_VERSION 1 #define XR_HTC_VIVE_WRIST_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_wrist_tracker_interaction" +// XR_FB_color_space is a preprocessor guard. Do not pass it to API calls. #define XR_FB_color_space 1 #define XR_FB_color_space_SPEC_VERSION 3 #define XR_FB_COLOR_SPACE_EXTENSION_NAME "XR_FB_color_space" @@ -3521,6 +3741,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetColorSpaceFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_hand_tracking_mesh is a preprocessor guard. Do not pass it to API calls. #define XR_FB_hand_tracking_mesh 1 #define XR_FB_hand_tracking_mesh_SPEC_VERSION 3 #define XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME "XR_FB_hand_tracking_mesh" @@ -3572,6 +3793,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetHandMeshFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_hand_tracking_aim is a preprocessor guard. Do not pass it to API calls. #define XR_FB_hand_tracking_aim 1 #define XR_FB_hand_tracking_aim_SPEC_VERSION 2 #define XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME "XR_FB_hand_tracking_aim" @@ -3602,6 +3824,7 @@ typedef struct XrHandTrackingAimStateFB { +// XR_FB_hand_tracking_capsules is a preprocessor guard. Do not pass it to API calls. #define XR_FB_hand_tracking_capsules 1 #define XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB 2 #define XR_HAND_TRACKING_CAPSULE_COUNT_FB 19 @@ -3624,9 +3847,9 @@ typedef struct XrHandTrackingCapsulesStateFB { +// XR_FB_spatial_entity is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity 1 XR_DEFINE_ATOM(XrAsyncRequestIdFB) -#define XR_UUID_SIZE_EXT 16 #define XR_FB_spatial_entity_SPEC_VERSION 3 #define XR_FB_SPATIAL_ENTITY_EXTENSION_NAME "XR_FB_spatial_entity" @@ -3672,9 +3895,7 @@ typedef struct XrSpaceComponentStatusFB { XrBool32 changePending; } XrSpaceComponentStatusFB; -typedef struct XrUuidEXT { - uint8_t data[XR_UUID_SIZE_EXT]; -} XrUuidEXT; +typedef XrUuid XrUuidEXT; typedef struct XrEventDataSpatialAnchorCreateCompleteFB { XrStructureType type; @@ -3732,6 +3953,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceComponentStatusFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_foveation is a preprocessor guard. Do not pass it to API calls. #define XR_FB_foveation 1 XR_DEFINE_HANDLE(XrFoveationProfileFB) #define XR_FB_foveation_SPEC_VERSION 1 @@ -3781,6 +4003,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFoveationProfileFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_foveation_configuration is a preprocessor guard. Do not pass it to API calls. #define XR_FB_foveation_configuration 1 #define XR_FB_foveation_configuration_SPEC_VERSION 1 #define XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME "XR_FB_foveation_configuration" @@ -3809,6 +4032,7 @@ typedef struct XrFoveationLevelProfileCreateInfoFB { +// XR_FB_keyboard_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_FB_keyboard_tracking 1 #define XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB 128 #define XR_FB_keyboard_tracking_SPEC_VERSION 1 @@ -3871,6 +4095,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateKeyboardSpaceFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_triangle_mesh is a preprocessor guard. Do not pass it to API calls. #define XR_FB_triangle_mesh 1 XR_DEFINE_HANDLE(XrTriangleMeshFB) #define XR_FB_triangle_mesh_SPEC_VERSION 2 @@ -3943,12 +4168,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshEndVertexBufferUpdateFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_passthrough is a preprocessor guard. Do not pass it to API calls. #define XR_FB_passthrough 1 XR_DEFINE_HANDLE(XrPassthroughFB) XR_DEFINE_HANDLE(XrPassthroughLayerFB) XR_DEFINE_HANDLE(XrGeometryInstanceFB) #define XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB 256 -#define XR_FB_passthrough_SPEC_VERSION 3 +#define XR_FB_passthrough_SPEC_VERSION 4 #define XR_FB_PASSTHROUGH_EXTENSION_NAME "XR_FB_passthrough" typedef enum XrPassthroughLayerPurposeFB { @@ -4007,7 +4233,6 @@ typedef struct XrPassthroughLayerCreateInfoFB { XrPassthroughLayerPurposeFB purpose; } XrPassthroughLayerCreateInfoFB; -// XrCompositionLayerPassthroughFB extends XrCompositionLayerBaseHeader typedef struct XrCompositionLayerPassthroughFB { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -4133,6 +4358,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGeometryInstanceSetTransformFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_render_model is a preprocessor guard. Do not pass it to API calls. #define XR_FB_render_model 1 #define XR_NULL_RENDER_MODEL_KEY_FB 0 @@ -4216,6 +4442,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrLoadRenderModelFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_VARJO_foveated_rendering is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_foveated_rendering 1 #define XR_VARJO_foveated_rendering_SPEC_VERSION 3 #define XR_VARJO_FOVEATED_RENDERING_EXTENSION_NAME "XR_VARJO_foveated_rendering" @@ -4242,6 +4469,7 @@ typedef struct XrSystemFoveatedRenderingPropertiesVARJO { +// XR_VARJO_composition_layer_depth_test is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_composition_layer_depth_test 1 #define XR_VARJO_composition_layer_depth_test_SPEC_VERSION 2 #define XR_VARJO_COMPOSITION_LAYER_DEPTH_TEST_EXTENSION_NAME "XR_VARJO_composition_layer_depth_test" @@ -4255,6 +4483,7 @@ typedef struct XrCompositionLayerDepthTestVARJO { +// XR_VARJO_environment_depth_estimation is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_environment_depth_estimation 1 #define XR_VARJO_environment_depth_estimation_SPEC_VERSION 1 #define XR_VARJO_ENVIRONMENT_DEPTH_ESTIMATION_EXTENSION_NAME "XR_VARJO_environment_depth_estimation" @@ -4269,6 +4498,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetEnvironmentDepthEstimationVARJO( #endif /* !XR_NO_PROTOTYPES */ +// XR_VARJO_marker_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_marker_tracking 1 #define XR_VARJO_marker_tracking_SPEC_VERSION 1 #define XR_VARJO_MARKER_TRACKING_EXTENSION_NAME "XR_VARJO_marker_tracking" @@ -4330,6 +4560,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceVARJO( #endif /* !XR_NO_PROTOTYPES */ +// XR_VARJO_view_offset is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_view_offset 1 #define XR_VARJO_view_offset_SPEC_VERSION 1 #define XR_VARJO_VIEW_OFFSET_EXTENSION_NAME "XR_VARJO_view_offset" @@ -4344,16 +4575,19 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetViewOffsetVARJO( #endif /* !XR_NO_PROTOTYPES */ +// XR_VARJO_xr4_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_xr4_controller_interaction 1 #define XR_VARJO_xr4_controller_interaction_SPEC_VERSION 1 #define XR_VARJO_XR4_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_VARJO_xr4_controller_interaction" +// XR_ML_ml2_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_ML_ml2_controller_interaction 1 #define XR_ML_ml2_controller_interaction_SPEC_VERSION 1 #define XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_ML_ml2_controller_interaction" +// XR_ML_frame_end_info is a preprocessor guard. Do not pass it to API calls. #define XR_ML_frame_end_info 1 #define XR_ML_frame_end_info_SPEC_VERSION 1 #define XR_ML_FRAME_END_INFO_EXTENSION_NAME "XR_ML_frame_end_info" @@ -4373,6 +4607,7 @@ typedef struct XrFrameEndInfoML { +// XR_ML_global_dimmer is a preprocessor guard. Do not pass it to API calls. #define XR_ML_global_dimmer 1 #define XR_ML_global_dimmer_SPEC_VERSION 1 #define XR_ML_GLOBAL_DIMMER_EXTENSION_NAME "XR_ML_global_dimmer" @@ -4391,6 +4626,7 @@ typedef struct XrGlobalDimmerFrameEndInfoML { +// XR_ML_marker_understanding is a preprocessor guard. Do not pass it to API calls. #define XR_ML_marker_understanding 1 XR_DEFINE_ATOM(XrMarkerML) XR_DEFINE_HANDLE(XrMarkerDetectorML) @@ -4619,6 +4855,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceML( #endif /* !XR_NO_PROTOTYPES */ +// XR_ML_localization_map is a preprocessor guard. Do not pass it to API calls. #define XR_ML_localization_map 1 XR_DEFINE_HANDLE(XrExportedLocalizationMapML) #define XR_MAX_LOCALIZATION_MAP_NAME_LENGTH_ML 64 @@ -4745,6 +4982,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetExportedLocalizationMapDataML( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_spatial_anchor_persistence is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_spatial_anchor_persistence 1 XR_DEFINE_HANDLE(XrSpatialAnchorStoreConnectionMSFT) #define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_MSFT 256 @@ -4810,6 +5048,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrClearSpatialAnchorStoreMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_scene_marker is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_scene_marker 1 #define XR_MSFT_scene_marker_SPEC_VERSION 1 #define XR_MSFT_SCENE_MARKER_EXTENSION_NAME "XR_MSFT_scene_marker" @@ -4882,6 +5121,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneMarkerDecodedStringMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_ULTRALEAP_hand_tracking_forearm is a preprocessor guard. Do not pass it to API calls. #define XR_ULTRALEAP_hand_tracking_forearm 1 #define XR_HAND_FOREARM_JOINT_COUNT_ULTRALEAP 27 @@ -4921,6 +5161,7 @@ typedef enum XrHandForearmJointULTRALEAP { } XrHandForearmJointULTRALEAP; +// XR_FB_spatial_entity_query is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_query 1 #define XR_FB_spatial_entity_query_SPEC_VERSION 1 #define XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME "XR_FB_spatial_entity_query" @@ -5020,6 +5261,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRetrieveSpaceQueryResultsFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_spatial_entity_storage is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_storage 1 #define XR_FB_spatial_entity_storage_SPEC_VERSION 1 #define XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME "XR_FB_spatial_entity_storage" @@ -5082,11 +5324,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEraseSpaceFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_touch_controller_pro is a preprocessor guard. Do not pass it to API calls. #define XR_FB_touch_controller_pro 1 #define XR_FB_touch_controller_pro_SPEC_VERSION 1 #define XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME "XR_FB_touch_controller_pro" +// XR_FB_spatial_entity_sharing is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_sharing 1 XR_DEFINE_HANDLE(XrSpaceUserFB) #define XR_FB_spatial_entity_sharing_SPEC_VERSION 1 @@ -5119,6 +5363,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrShareSpacesFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_space_warp is a preprocessor guard. Do not pass it to API calls. #define XR_FB_space_warp 1 #define XR_FB_space_warp_SPEC_VERSION 2 #define XR_FB_SPACE_WARP_EXTENSION_NAME "XR_FB_space_warp" @@ -5151,6 +5396,7 @@ typedef struct XrSystemSpaceWarpPropertiesFB { +// XR_FB_haptic_amplitude_envelope is a preprocessor guard. Do not pass it to API calls. #define XR_FB_haptic_amplitude_envelope 1 #define XR_MAX_HAPTIC_AMPLITUDE_ENVELOPE_SAMPLES_FB 4000u @@ -5167,6 +5413,7 @@ typedef struct XrHapticAmplitudeEnvelopeVibrationFB { +// XR_FB_scene is a preprocessor guard. Do not pass it to API calls. #define XR_FB_scene 1 #define XR_FB_scene_SPEC_VERSION 4 #define XR_FB_SCENE_EXTENSION_NAME "XR_FB_scene" @@ -5177,11 +5424,7 @@ static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_MULTIPLE_ static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_DESK_TO_TABLE_MIGRATION_BIT_FB = 0x00000002; static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_INVISIBLE_WALL_FACE_BIT_FB = 0x00000004; -typedef struct XrExtent3DfFB { - float width; - float height; - float depth; -} XrExtent3DfFB; +typedef XrExtent3Df XrExtent3DfFB; typedef struct XrOffset3DfFB { float x; @@ -5263,11 +5506,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceRoomLayoutFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_palm_pose is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_palm_pose 1 -#define XR_EXT_palm_pose_SPEC_VERSION 2 +#define XR_EXT_palm_pose_SPEC_VERSION 3 #define XR_EXT_PALM_POSE_EXTENSION_NAME "XR_EXT_palm_pose" +// XR_ALMALENCE_digital_lens_control is a preprocessor guard. Do not pass it to API calls. #define XR_ALMALENCE_digital_lens_control 1 #define XR_ALMALENCE_digital_lens_control_SPEC_VERSION 1 #define XR_ALMALENCE_DIGITAL_LENS_CONTROL_EXTENSION_NAME "XR_ALMALENCE_digital_lens_control" @@ -5293,6 +5538,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetDigitalLensControlALMALENCE( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_scene_capture is a preprocessor guard. Do not pass it to API calls. #define XR_FB_scene_capture 1 #define XR_FB_scene_capture_SPEC_VERSION 1 #define XR_FB_SCENE_CAPTURE_EXTENSION_NAME "XR_FB_scene_capture" @@ -5322,6 +5568,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRequestSceneCaptureFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_spatial_entity_container is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_container 1 #define XR_FB_spatial_entity_container_SPEC_VERSION 2 #define XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME "XR_FB_spatial_entity_container" @@ -5345,6 +5592,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceContainerFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_foveation_eye_tracked is a preprocessor guard. Do not pass it to API calls. #define XR_META_foveation_eye_tracked 1 #define XR_FOVEATION_CENTER_SIZE_META 2 #define XR_META_foveation_eye_tracked_SPEC_VERSION 1 @@ -5390,6 +5638,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFoveationEyeTrackedStateMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_face_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_FB_face_tracking 1 #define XR_FACE_EXPRESSSION_SET_DEFAULT_FB XR_FACE_EXPRESSION_SET_DEFAULT_FB @@ -5534,6 +5783,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceExpressionWeightsFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_eye_tracking_social is a preprocessor guard. Do not pass it to API calls. #define XR_FB_eye_tracking_social 1 XR_DEFINE_HANDLE(XrEyeTrackerFB) #define XR_FB_eye_tracking_social_SPEC_VERSION 1 @@ -5599,6 +5849,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetEyeGazesFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_passthrough_keyboard_hands is a preprocessor guard. Do not pass it to API calls. #define XR_FB_passthrough_keyboard_hands 1 #define XR_FB_passthrough_keyboard_hands_SPEC_VERSION 2 #define XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME "XR_FB_passthrough_keyboard_hands" @@ -5620,6 +5871,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerSetKeyboardHandsIntensityFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_composition_layer_settings is a preprocessor guard. Do not pass it to API calls. #define XR_FB_composition_layer_settings 1 #define XR_FB_composition_layer_settings_SPEC_VERSION 1 #define XR_FB_COMPOSITION_LAYER_SETTINGS_EXTENSION_NAME "XR_FB_composition_layer_settings" @@ -5641,11 +5893,13 @@ typedef struct XrCompositionLayerSettingsFB { +// XR_FB_touch_controller_proximity is a preprocessor guard. Do not pass it to API calls. #define XR_FB_touch_controller_proximity 1 #define XR_FB_touch_controller_proximity_SPEC_VERSION 1 #define XR_FB_TOUCH_CONTROLLER_PROXIMITY_EXTENSION_NAME "XR_FB_touch_controller_proximity" +// XR_FB_haptic_pcm is a preprocessor guard. Do not pass it to API calls. #define XR_FB_haptic_pcm 1 #define XR_MAX_HAPTIC_PCM_BUFFER_SIZE_FB 4000 @@ -5682,6 +5936,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetDeviceSampleRateFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_composition_layer_depth_test is a preprocessor guard. Do not pass it to API calls. #define XR_FB_composition_layer_depth_test 1 #define XR_FB_composition_layer_depth_test_SPEC_VERSION 1 #define XR_FB_COMPOSITION_LAYER_DEPTH_TEST_EXTENSION_NAME "XR_FB_composition_layer_depth_test" @@ -5707,6 +5962,7 @@ typedef struct XrCompositionLayerDepthTestFB { +// XR_META_local_dimming is a preprocessor guard. Do not pass it to API calls. #define XR_META_local_dimming 1 #define XR_META_local_dimming_SPEC_VERSION 1 #define XR_META_LOCAL_DIMMING_EXTENSION_NAME "XR_META_local_dimming" @@ -5725,6 +5981,7 @@ typedef struct XrLocalDimmingFrameEndInfoMETA { +// XR_META_passthrough_preferences is a preprocessor guard. Do not pass it to API calls. #define XR_META_passthrough_preferences 1 #define XR_META_passthrough_preferences_SPEC_VERSION 1 #define XR_META_PASSTHROUGH_PREFERENCES_EXTENSION_NAME "XR_META_passthrough_preferences" @@ -5750,6 +6007,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetPassthroughPreferencesMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_virtual_keyboard is a preprocessor guard. Do not pass it to API calls. #define XR_META_virtual_keyboard 1 XR_DEFINE_HANDLE(XrVirtualKeyboardMETA) #define XR_MAX_VIRTUAL_KEYBOARD_COMMIT_TEXT_SIZE_META 3992 @@ -5952,6 +6210,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrChangeVirtualKeyboardTextContextMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_OCULUS_external_camera is a preprocessor guard. Do not pass it to API calls. #define XR_OCULUS_external_camera 1 #define XR_MAX_EXTERNAL_CAMERA_NAME_SIZE_OCULUS 32 #define XR_OCULUS_external_camera_SPEC_VERSION 1 @@ -6009,6 +6268,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateExternalCamerasOCULUS( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_performance_metrics is a preprocessor guard. Do not pass it to API calls. #define XR_META_performance_metrics 1 #define XR_META_performance_metrics_SPEC_VERSION 2 #define XR_META_PERFORMANCE_METRICS_EXTENSION_NAME "XR_META_performance_metrics" @@ -6072,6 +6332,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrQueryPerformanceMetricsCounterMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_spatial_entity_storage_batch is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_storage_batch 1 #define XR_FB_spatial_entity_storage_batch_SPEC_VERSION 1 #define XR_FB_SPATIAL_ENTITY_STORAGE_BATCH_EXTENSION_NAME "XR_FB_spatial_entity_storage_batch" @@ -6102,6 +6363,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpaceListFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_spatial_entity_user is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_user 1 typedef uint64_t XrSpaceUserIdFB; #define XR_FB_spatial_entity_user_SPEC_VERSION 1 @@ -6133,6 +6395,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpaceUserFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_headset_id is a preprocessor guard. Do not pass it to API calls. #define XR_META_headset_id 1 #define XR_META_headset_id_SPEC_VERSION 2 #define XR_META_HEADSET_ID_EXTENSION_NAME "XR_META_headset_id" @@ -6145,6 +6408,7 @@ typedef struct XrSystemHeadsetIdPropertiesMETA { +// XR_META_recommended_layer_resolution is a preprocessor guard. Do not pass it to API calls. #define XR_META_recommended_layer_resolution 1 #define XR_META_recommended_layer_resolution_SPEC_VERSION 1 #define XR_META_RECOMMENDED_LAYER_RESOLUTION_EXTENSION_NAME "XR_META_recommended_layer_resolution" @@ -6174,6 +6438,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetRecommendedLayerResolutionMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_passthrough_color_lut is a preprocessor guard. Do not pass it to API calls. #define XR_META_passthrough_color_lut 1 XR_DEFINE_HANDLE(XrPassthroughColorLutMETA) #define XR_META_passthrough_color_lut_SPEC_VERSION 1 @@ -6248,6 +6513,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrUpdatePassthroughColorLutMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_spatial_entity_mesh is a preprocessor guard. Do not pass it to API calls. #define XR_META_spatial_entity_mesh 1 #define XR_META_spatial_entity_mesh_SPEC_VERSION 1 #define XR_META_SPATIAL_ENTITY_MESH_EXTENSION_NAME "XR_META_spatial_entity_mesh" @@ -6279,16 +6545,19 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceTriangleMeshMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_automatic_layer_filter is a preprocessor guard. Do not pass it to API calls. #define XR_META_automatic_layer_filter 1 #define XR_META_automatic_layer_filter_SPEC_VERSION 1 #define XR_META_AUTOMATIC_LAYER_FILTER_EXTENSION_NAME "XR_META_automatic_layer_filter" +// XR_META_touch_controller_plus is a preprocessor guard. Do not pass it to API calls. #define XR_META_touch_controller_plus 1 #define XR_META_touch_controller_plus_SPEC_VERSION 1 #define XR_META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME "XR_META_touch_controller_plus" +// XR_FB_face_tracking2 is a preprocessor guard. Do not pass it to API calls. #define XR_FB_face_tracking2 1 XR_DEFINE_HANDLE(XrFaceTracker2FB) #define XR_FB_face_tracking2_SPEC_VERSION 1 @@ -6443,16 +6712,147 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceExpressionWeights2FB( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_environment_depth is a preprocessor guard. Do not pass it to API calls. +#define XR_META_environment_depth 1 +XR_DEFINE_HANDLE(XrEnvironmentDepthProviderMETA) +XR_DEFINE_HANDLE(XrEnvironmentDepthSwapchainMETA) +#define XR_META_environment_depth_SPEC_VERSION 1 +#define XR_META_ENVIRONMENT_DEPTH_EXTENSION_NAME "XR_META_environment_depth" +typedef XrFlags64 XrEnvironmentDepthProviderCreateFlagsMETA; + +// Flag bits for XrEnvironmentDepthProviderCreateFlagsMETA + +typedef XrFlags64 XrEnvironmentDepthSwapchainCreateFlagsMETA; + +// Flag bits for XrEnvironmentDepthSwapchainCreateFlagsMETA + +typedef struct XrEnvironmentDepthProviderCreateInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrEnvironmentDepthProviderCreateFlagsMETA createFlags; +} XrEnvironmentDepthProviderCreateInfoMETA; + +typedef struct XrEnvironmentDepthSwapchainCreateInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrEnvironmentDepthSwapchainCreateFlagsMETA createFlags; +} XrEnvironmentDepthSwapchainCreateInfoMETA; + +typedef struct XrEnvironmentDepthSwapchainStateMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t width; + uint32_t height; +} XrEnvironmentDepthSwapchainStateMETA; + +typedef struct XrEnvironmentDepthImageAcquireInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace space; + XrTime displayTime; +} XrEnvironmentDepthImageAcquireInfoMETA; + +typedef struct XrEnvironmentDepthImageViewMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrFovf fov; + XrPosef pose; +} XrEnvironmentDepthImageViewMETA; + +typedef struct XrEnvironmentDepthImageMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t swapchainIndex; + float nearZ; + float farZ; + XrEnvironmentDepthImageViewMETA views[2]; +} XrEnvironmentDepthImageMETA; + +typedef struct XrEnvironmentDepthHandRemovalSetInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBool32 enabled; +} XrEnvironmentDepthHandRemovalSetInfoMETA; + +// XrSystemEnvironmentDepthPropertiesMETA extends XrSystemProperties +typedef struct XrSystemEnvironmentDepthPropertiesMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsEnvironmentDepth; + XrBool32 supportsHandRemoval; +} XrSystemEnvironmentDepthPropertiesMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrCreateEnvironmentDepthProviderMETA)(XrSession session, const XrEnvironmentDepthProviderCreateInfoMETA* createInfo, XrEnvironmentDepthProviderMETA* environmentDepthProvider); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider); +typedef XrResult (XRAPI_PTR *PFN_xrStartEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider); +typedef XrResult (XRAPI_PTR *PFN_xrStopEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider); +typedef XrResult (XRAPI_PTR *PFN_xrCreateEnvironmentDepthSwapchainMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthSwapchainCreateInfoMETA* createInfo, XrEnvironmentDepthSwapchainMETA* swapchain); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyEnvironmentDepthSwapchainMETA)(XrEnvironmentDepthSwapchainMETA swapchain); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateEnvironmentDepthSwapchainImagesMETA)(XrEnvironmentDepthSwapchainMETA swapchain, uint32_t imageCapacityInput, uint32_t* imageCountOutput, XrSwapchainImageBaseHeader* images); +typedef XrResult (XRAPI_PTR *PFN_xrGetEnvironmentDepthSwapchainStateMETA)(XrEnvironmentDepthSwapchainMETA swapchain, XrEnvironmentDepthSwapchainStateMETA* state); +typedef XrResult (XRAPI_PTR *PFN_xrAcquireEnvironmentDepthImageMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthImageAcquireInfoMETA* acquireInfo, XrEnvironmentDepthImageMETA* environmentDepthImage); +typedef XrResult (XRAPI_PTR *PFN_xrSetEnvironmentDepthHandRemovalMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthHandRemovalSetInfoMETA* setInfo); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrCreateEnvironmentDepthProviderMETA( + XrSession session, + const XrEnvironmentDepthProviderCreateInfoMETA* createInfo, + XrEnvironmentDepthProviderMETA* environmentDepthProvider); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEnvironmentDepthProviderMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider); + +XRAPI_ATTR XrResult XRAPI_CALL xrStartEnvironmentDepthProviderMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider); + +XRAPI_ATTR XrResult XRAPI_CALL xrStopEnvironmentDepthProviderMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateEnvironmentDepthSwapchainMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider, + const XrEnvironmentDepthSwapchainCreateInfoMETA* createInfo, + XrEnvironmentDepthSwapchainMETA* swapchain); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEnvironmentDepthSwapchainMETA( + XrEnvironmentDepthSwapchainMETA swapchain); + +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateEnvironmentDepthSwapchainImagesMETA( + XrEnvironmentDepthSwapchainMETA swapchain, + uint32_t imageCapacityInput, + uint32_t* imageCountOutput, + XrSwapchainImageBaseHeader* images); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetEnvironmentDepthSwapchainStateMETA( + XrEnvironmentDepthSwapchainMETA swapchain, + XrEnvironmentDepthSwapchainStateMETA* state); + +XRAPI_ATTR XrResult XRAPI_CALL xrAcquireEnvironmentDepthImageMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider, + const XrEnvironmentDepthImageAcquireInfoMETA* acquireInfo, + XrEnvironmentDepthImageMETA* environmentDepthImage); + +XRAPI_ATTR XrResult XRAPI_CALL xrSetEnvironmentDepthHandRemovalMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider, + const XrEnvironmentDepthHandRemovalSetInfoMETA* setInfo); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_EXT_uuid is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_uuid 1 #define XR_EXT_uuid_SPEC_VERSION 1 #define XR_EXT_UUID_EXTENSION_NAME "XR_EXT_uuid" +#define XR_UUID_SIZE_EXT 16 +// XR_EXT_hand_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hand_interaction 1 #define XR_EXT_hand_interaction_SPEC_VERSION 1 #define XR_EXT_HAND_INTERACTION_EXTENSION_NAME "XR_EXT_hand_interaction" +// XR_QCOM_tracking_optimization_settings is a preprocessor guard. Do not pass it to API calls. #define XR_QCOM_tracking_optimization_settings 1 #define XR_QCOM_tracking_optimization_settings_SPEC_VERSION 1 #define XR_QCOM_TRACKING_OPTIMIZATION_SETTINGS_EXTENSION_NAME "XR_QCOM_tracking_optimization_settings" @@ -6482,6 +6882,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetTrackingOptimizationSettingsHintQCOM( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_passthrough is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_passthrough 1 XR_DEFINE_HANDLE(XrPassthroughHTC) #define XR_HTC_passthrough_SPEC_VERSION 1 @@ -6543,6 +6944,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughHTC( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_foveation is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_foveation 1 #define XR_HTC_foveation_SPEC_VERSION 1 #define XR_HTC_FOVEATION_EXTENSION_NAME "XR_HTC_foveation" @@ -6609,6 +7011,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrApplyFoveationHTC( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_anchor is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_anchor 1 #define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC 256 #define XR_HTC_anchor_SPEC_VERSION 1 @@ -6649,6 +7052,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialAnchorNameHTC( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_active_action_set_priority is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_active_action_set_priority 1 #define XR_EXT_active_action_set_priority_SPEC_VERSION 1 #define XR_EXT_ACTIVE_ACTION_SET_PRIORITY_EXTENSION_NAME "XR_EXT_active_action_set_priority" @@ -6667,6 +7071,7 @@ typedef struct XrActiveActionSetPrioritiesEXT { +// XR_MNDX_force_feedback_curl is a preprocessor guard. Do not pass it to API calls. #define XR_MNDX_force_feedback_curl 1 #define XR_MNDX_force_feedback_curl_SPEC_VERSION 1 #define XR_MNDX_FORCE_FEEDBACK_CURL_EXTENSION_NAME "XR_MNDX_force_feedback_curl" @@ -6709,16 +7114,19 @@ XRAPI_ATTR XrResult XRAPI_CALL xrApplyForceFeedbackCurlMNDX( #endif /* !XR_NO_PROTOTYPES */ +// XR_BD_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_BD_controller_interaction 1 #define XR_BD_controller_interaction_SPEC_VERSION 2 #define XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_BD_controller_interaction" +// XR_EXT_local_floor is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_local_floor 1 #define XR_EXT_local_floor_SPEC_VERSION 1 #define XR_EXT_LOCAL_FLOOR_EXTENSION_NAME "XR_EXT_local_floor" +// XR_EXT_hand_tracking_data_source is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hand_tracking_data_source 1 #define XR_EXT_hand_tracking_data_source_SPEC_VERSION 1 #define XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME "XR_EXT_hand_tracking_data_source" @@ -6746,9 +7154,10 @@ typedef struct XrHandTrackingDataSourceStateEXT { +// XR_EXT_plane_detection is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_plane_detection 1 XR_DEFINE_HANDLE(XrPlaneDetectorEXT) -#define XR_EXT_plane_detection_SPEC_VERSION 1 +#define XR_EXT_plane_detection_SPEC_VERSION 2 #define XR_EXT_PLANE_DETECTION_EXTENSION_NAME "XR_EXT_plane_detection" typedef enum XrPlaneDetectorOrientationEXT { @@ -6805,11 +7214,7 @@ typedef struct XrPlaneDetectorCreateInfoEXT { XrPlaneDetectorFlagsEXT flags; } XrPlaneDetectorCreateInfoEXT; -typedef struct XrExtent3DfEXT { - float width; - float height; - float depth; -} XrExtent3DfEXT; +typedef XrExtent3Df XrExtent3DfEXT; typedef struct XrPlaneDetectorBeginInfoEXT { XrStructureType type; @@ -6900,11 +7305,72 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetPlanePolygonBufferEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_OPPO_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_OPPO_controller_interaction 1 #define XR_OPPO_controller_interaction_SPEC_VERSION 1 #define XR_OPPO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_OPPO_controller_interaction" +// XR_EXT_future is a preprocessor guard. Do not pass it to API calls. +#define XR_EXT_future 1 +XR_DEFINE_OPAQUE_64(XrFutureEXT) +#define XR_EXT_future_SPEC_VERSION 1 +#define XR_EXT_FUTURE_EXTENSION_NAME "XR_EXT_future" +#define XR_NULL_FUTURE_EXT 0 + +typedef enum XrFutureStateEXT { + XR_FUTURE_STATE_PENDING_EXT = 1, + XR_FUTURE_STATE_READY_EXT = 2, + XR_FUTURE_STATE_MAX_ENUM_EXT = 0x7FFFFFFF +} XrFutureStateEXT; +typedef struct XrFutureCancelInfoEXT { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrFutureEXT future; +} XrFutureCancelInfoEXT; + +typedef struct XrFuturePollInfoEXT { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrFutureEXT future; +} XrFuturePollInfoEXT; + +typedef struct XR_MAY_ALIAS XrFutureCompletionBaseHeaderEXT { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; +} XrFutureCompletionBaseHeaderEXT; + +typedef struct XrFutureCompletionEXT { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; +} XrFutureCompletionEXT; + +typedef struct XrFuturePollResultEXT { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrFutureStateEXT state; +} XrFuturePollResultEXT; + +typedef XrResult (XRAPI_PTR *PFN_xrPollFutureEXT)(XrInstance instance, const XrFuturePollInfoEXT* pollInfo, XrFuturePollResultEXT* pollResult); +typedef XrResult (XRAPI_PTR *PFN_xrCancelFutureEXT)(XrInstance instance, const XrFutureCancelInfoEXT* cancelInfo); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrPollFutureEXT( + XrInstance instance, + const XrFuturePollInfoEXT* pollInfo, + XrFuturePollResultEXT* pollResult); + +XRAPI_ATTR XrResult XRAPI_CALL xrCancelFutureEXT( + XrInstance instance, + const XrFutureCancelInfoEXT* cancelInfo); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_EXT_user_presence is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_user_presence 1 #define XR_EXT_user_presence_SPEC_VERSION 1 #define XR_EXT_USER_PRESENCE_EXTENSION_NAME "XR_EXT_user_presence" @@ -6924,6 +7390,7 @@ typedef struct XrSystemUserPresencePropertiesEXT { +// XR_ML_user_calibration is a preprocessor guard. Do not pass it to API calls. #define XR_ML_user_calibration 1 #define XR_ML_user_calibration_SPEC_VERSION 1 #define XR_ML_USER_CALIBRATION_EXTENSION_NAME "XR_ML_user_calibration" @@ -6973,10 +7440,17 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnableUserCalibrationEventsML( #endif /* !XR_NO_PROTOTYPES */ +// XR_YVR_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_YVR_controller_interaction 1 #define XR_YVR_controller_interaction_SPEC_VERSION 1 #define XR_YVR_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_YVR_controller_interaction" + +// XR_EXT_composition_layer_inverted_alpha is a preprocessor guard. Do not pass it to API calls. +#define XR_EXT_composition_layer_inverted_alpha 1 +#define XR_EXT_composition_layer_inverted_alpha_SPEC_VERSION 1 +#define XR_EXT_COMPOSITION_LAYER_INVERTED_ALPHA_EXTENSION_NAME "XR_EXT_composition_layer_inverted_alpha" + #ifdef __cplusplus } #endif diff --git a/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h b/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h index 619b226760..19c18cc2a3 100644 --- a/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h +++ b/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h @@ -20,6 +20,7 @@ extern "C" { +// XR_LOADER_VERSION_1_0 is a preprocessor guard. Do not pass it to API calls. #define XR_LOADER_VERSION_1_0 1 #define XR_CURRENT_LOADER_API_LAYER_VERSION 1 diff --git a/thirdparty/openxr/include/openxr/openxr_platform.h b/thirdparty/openxr/include/openxr/openxr_platform.h index 1f47428676..cbe1008906 100644 --- a/thirdparty/openxr/include/openxr/openxr_platform.h +++ b/thirdparty/openxr/include/openxr/openxr_platform.h @@ -21,6 +21,7 @@ extern "C" { #ifdef XR_USE_PLATFORM_ANDROID +// XR_KHR_android_thread_settings is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_android_thread_settings 1 #define XR_KHR_android_thread_settings_SPEC_VERSION 6 #define XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME "XR_KHR_android_thread_settings" @@ -46,6 +47,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetAndroidApplicationThreadKHR( #ifdef XR_USE_PLATFORM_ANDROID +// XR_KHR_android_surface_swapchain is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_android_surface_swapchain 1 #define XR_KHR_android_surface_swapchain_SPEC_VERSION 4 #define XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME "XR_KHR_android_surface_swapchain" @@ -64,6 +66,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchainAndroidSurfaceKHR( #ifdef XR_USE_PLATFORM_ANDROID +// XR_KHR_android_create_instance is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_android_create_instance 1 #define XR_KHR_android_create_instance_SPEC_VERSION 3 #define XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME "XR_KHR_android_create_instance" @@ -79,6 +82,7 @@ typedef struct XrInstanceCreateInfoAndroidKHR { #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_KHR_vulkan_swapchain_format_list is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_vulkan_swapchain_format_list 1 #define XR_KHR_vulkan_swapchain_format_list_SPEC_VERSION 4 #define XR_KHR_VULKAN_SWAPCHAIN_FORMAT_LIST_EXTENSION_NAME "XR_KHR_vulkan_swapchain_format_list" @@ -93,6 +97,7 @@ typedef struct XrVulkanSwapchainFormatListCreateInfoKHR { #ifdef XR_USE_GRAPHICS_API_OPENGL +// XR_KHR_opengl_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_opengl_enable 1 #define XR_KHR_opengl_enable_SPEC_VERSION 10 #define XR_KHR_OPENGL_ENABLE_EXTENSION_NAME "XR_KHR_opengl_enable" @@ -169,6 +174,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLGraphicsRequirementsKHR( #ifdef XR_USE_GRAPHICS_API_OPENGL_ES +// XR_KHR_opengl_es_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_opengl_es_enable 1 #define XR_KHR_opengl_es_enable_SPEC_VERSION 8 #define XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME "XR_KHR_opengl_es_enable" @@ -210,6 +216,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLESGraphicsRequirementsKHR( #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_KHR_vulkan_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_vulkan_enable 1 #define XR_KHR_vulkan_enable_SPEC_VERSION 8 #define XR_KHR_VULKAN_ENABLE_EXTENSION_NAME "XR_KHR_vulkan_enable" @@ -274,6 +281,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirementsKHR( #ifdef XR_USE_GRAPHICS_API_D3D11 +// XR_KHR_D3D11_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_D3D11_enable 1 #define XR_KHR_D3D11_enable_SPEC_VERSION 9 #define XR_KHR_D3D11_ENABLE_EXTENSION_NAME "XR_KHR_D3D11_enable" @@ -311,6 +319,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR( #ifdef XR_USE_GRAPHICS_API_D3D12 +// XR_KHR_D3D12_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_D3D12_enable 1 #define XR_KHR_D3D12_enable_SPEC_VERSION 9 #define XR_KHR_D3D12_ENABLE_EXTENSION_NAME "XR_KHR_D3D12_enable" @@ -349,6 +358,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR( #ifdef XR_USE_PLATFORM_WIN32 +// XR_KHR_win32_convert_performance_counter_time is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_win32_convert_performance_counter_time 1 #define XR_KHR_win32_convert_performance_counter_time_SPEC_VERSION 1 #define XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME_EXTENSION_NAME "XR_KHR_win32_convert_performance_counter_time" @@ -372,6 +382,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToWin32PerformanceCounterKHR( #ifdef XR_USE_TIMESPEC +// XR_KHR_convert_timespec_time is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_convert_timespec_time 1 #define XR_KHR_convert_timespec_time_SPEC_VERSION 1 #define XR_KHR_CONVERT_TIMESPEC_TIME_EXTENSION_NAME "XR_KHR_convert_timespec_time" @@ -395,6 +406,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToTimespecTimeKHR( #ifdef XR_USE_PLATFORM_ANDROID +// XR_KHR_loader_init_android is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_loader_init_android 1 #define XR_KHR_loader_init_android_SPEC_VERSION 1 #define XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME "XR_KHR_loader_init_android" @@ -409,6 +421,7 @@ typedef struct XrLoaderInitInfoAndroidKHR { #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_KHR_vulkan_enable2 is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_vulkan_enable2 1 #define XR_KHR_vulkan_enable2_SPEC_VERSION 2 #define XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME "XR_KHR_vulkan_enable2" @@ -488,6 +501,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirements2KHR( #ifdef XR_USE_PLATFORM_EGL +// XR_MNDX_egl_enable is a preprocessor guard. Do not pass it to API calls. #define XR_MNDX_egl_enable 1 #define XR_MNDX_egl_enable_SPEC_VERSION 2 #define XR_MNDX_EGL_ENABLE_EXTENSION_NAME "XR_MNDX_egl_enable" @@ -506,6 +520,7 @@ typedef struct XrGraphicsBindingEGLMNDX { #ifdef XR_USE_PLATFORM_WIN32 +// XR_MSFT_perception_anchor_interop is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_perception_anchor_interop 1 #define XR_MSFT_perception_anchor_interop_SPEC_VERSION 1 #define XR_MSFT_PERCEPTION_ANCHOR_INTEROP_EXTENSION_NAME "XR_MSFT_perception_anchor_interop" @@ -529,6 +544,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrTryGetPerceptionAnchorFromSpatialAnchorMSFT( #ifdef XR_USE_PLATFORM_WIN32 +// XR_MSFT_holographic_window_attachment is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_holographic_window_attachment 1 #define XR_MSFT_holographic_window_attachment_SPEC_VERSION 1 #define XR_MSFT_HOLOGRAPHIC_WINDOW_ATTACHMENT_EXTENSION_NAME "XR_MSFT_holographic_window_attachment" @@ -546,6 +562,7 @@ typedef struct XrHolographicWindowAttachmentMSFT { #ifdef XR_USE_PLATFORM_ANDROID +// XR_FB_android_surface_swapchain_create is a preprocessor guard. Do not pass it to API calls. #define XR_FB_android_surface_swapchain_create 1 #define XR_FB_android_surface_swapchain_create_SPEC_VERSION 1 #define XR_FB_ANDROID_SURFACE_SWAPCHAIN_CREATE_EXTENSION_NAME "XR_FB_android_surface_swapchain_create" @@ -568,6 +585,7 @@ typedef struct XrAndroidSurfaceSwapchainCreateInfoFB { #ifdef XR_USE_PLATFORM_ML +// XR_ML_compat is a preprocessor guard. Do not pass it to API calls. #define XR_ML_compat 1 #define XR_ML_compat_SPEC_VERSION 1 #define XR_ML_COMPAT_EXTENSION_NAME "XR_ML_compat" @@ -592,6 +610,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpaceFromCoordinateFrameUIDML( #ifdef XR_USE_PLATFORM_WIN32 +// XR_OCULUS_audio_device_guid is a preprocessor guard. Do not pass it to API calls. #define XR_OCULUS_audio_device_guid 1 #define XR_OCULUS_audio_device_guid_SPEC_VERSION 1 #define XR_OCULUS_AUDIO_DEVICE_GUID_EXTENSION_NAME "XR_OCULUS_audio_device_guid" @@ -614,6 +633,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioInputDeviceGuidOculus( #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_FB_foveation_vulkan is a preprocessor guard. Do not pass it to API calls. #define XR_FB_foveation_vulkan 1 #define XR_FB_foveation_vulkan_SPEC_VERSION 1 #define XR_FB_FOVEATION_VULKAN_EXTENSION_NAME "XR_FB_foveation_vulkan" @@ -630,6 +650,7 @@ typedef struct XrSwapchainImageFoveationVulkanFB { #ifdef XR_USE_PLATFORM_ANDROID +// XR_FB_swapchain_update_state_android_surface is a preprocessor guard. Do not pass it to API calls. #define XR_FB_swapchain_update_state_android_surface 1 #define XR_FB_swapchain_update_state_android_surface_SPEC_VERSION 1 #define XR_FB_SWAPCHAIN_UPDATE_STATE_ANDROID_SURFACE_EXTENSION_NAME "XR_FB_swapchain_update_state_android_surface" @@ -646,6 +667,7 @@ typedef struct XrSwapchainStateAndroidSurfaceDimensionsFB { #ifdef XR_USE_GRAPHICS_API_OPENGL_ES +// XR_FB_swapchain_update_state_opengl_es is a preprocessor guard. Do not pass it to API calls. #define XR_FB_swapchain_update_state_opengl_es 1 #define XR_FB_swapchain_update_state_opengl_es_SPEC_VERSION 1 #define XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME "XR_FB_swapchain_update_state_opengl_es" @@ -670,6 +692,7 @@ typedef struct XrSwapchainStateSamplerOpenGLESFB { #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_FB_swapchain_update_state_vulkan is a preprocessor guard. Do not pass it to API calls. #define XR_FB_swapchain_update_state_vulkan 1 #define XR_FB_swapchain_update_state_vulkan_SPEC_VERSION 1 #define XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME "XR_FB_swapchain_update_state_vulkan" @@ -695,6 +718,7 @@ typedef struct XrSwapchainStateSamplerVulkanFB { #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_META_vulkan_swapchain_create_info is a preprocessor guard. Do not pass it to API calls. #define XR_META_vulkan_swapchain_create_info 1 #define XR_META_vulkan_swapchain_create_info_SPEC_VERSION 1 #define XR_META_VULKAN_SWAPCHAIN_CREATE_INFO_EXTENSION_NAME "XR_META_vulkan_swapchain_create_info" diff --git a/thirdparty/openxr/include/openxr/openxr_reflection.h b/thirdparty/openxr/include/openxr/openxr_reflection.h index b449c7099b..b9d45689a7 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection.h @@ -86,6 +86,8 @@ XR_ENUM_STR(XrResult); _(XR_ERROR_LOCALIZED_NAME_INVALID, -49) \ _(XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING, -50) \ _(XR_ERROR_RUNTIME_UNAVAILABLE, -51) \ + _(XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED, -1000710001) \ + _(XR_ERROR_PERMISSION_INSUFFICIENT, -1000710000) \ _(XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR, -1000003000) \ _(XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR, -1000003001) \ _(XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT, -1000039001) \ @@ -135,10 +137,13 @@ XR_ENUM_STR(XrResult); _(XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB, -1000169003) \ _(XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB, -1000169004) \ _(XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META, -1000266000) \ + _(XR_ENVIRONMENT_DEPTH_NOT_AVAILABLE_META, 1000291000) \ _(XR_ERROR_HINT_ALREADY_SET_QCOM, -1000306000) \ _(XR_ERROR_NOT_AN_ANCHOR_HTC, -1000319000) \ _(XR_ERROR_SPACE_NOT_LOCATABLE_EXT, -1000429000) \ _(XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT, -1000429001) \ + _(XR_ERROR_FUTURE_PENDING_EXT, -1000469001) \ + _(XR_ERROR_FUTURE_INVALID_EXT, -1000469002) \ _(XR_RESULT_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrStructureType(_) \ @@ -192,6 +197,9 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_ACTIONS_SYNC_INFO, 61) \ _(XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO, 62) \ _(XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO, 63) \ + _(XR_TYPE_SPACES_LOCATE_INFO, 1000471000) \ + _(XR_TYPE_SPACE_LOCATIONS, 1000471001) \ + _(XR_TYPE_SPACE_VELOCITIES, 1000471002) \ _(XR_TYPE_COMPOSITION_LAYER_CUBE_KHR, 1000006000) \ _(XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR, 1000008000) \ _(XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR, 1000010000) \ @@ -457,6 +465,14 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB, 1000287014) \ _(XR_TYPE_FACE_EXPRESSION_INFO2_FB, 1000287015) \ _(XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB, 1000287016) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META, 1000291000) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META, 1000291001) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META, 1000291002) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META, 1000291003) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META, 1000291004) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META, 1000291005) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META, 1000291006) \ + _(XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META, 1000291007) \ _(XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC, 1000317001) \ _(XR_TYPE_PASSTHROUGH_COLOR_HTC, 1000317002) \ _(XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC, 1000317003) \ @@ -478,6 +494,10 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_PLANE_DETECTOR_LOCATION_EXT, 1000429005) \ _(XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT, 1000429006) \ _(XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT, 1000429007) \ + _(XR_TYPE_FUTURE_CANCEL_INFO_EXT, 1000469000) \ + _(XR_TYPE_FUTURE_POLL_INFO_EXT, 1000469001) \ + _(XR_TYPE_FUTURE_COMPLETION_EXT, 1000469002) \ + _(XR_TYPE_FUTURE_POLL_RESULT_EXT, 1000469003) \ _(XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT, 1000470000) \ _(XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT, 1000470001) \ _(XR_STRUCTURE_TYPE_MAX_ENUM, 0x7FFFFFFF) @@ -490,7 +510,7 @@ XR_ENUM_STR(XrResult); #define XR_LIST_ENUM_XrViewConfigurationType(_) \ _(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO, 1) \ _(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 2) \ - _(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO, 1000037000) \ + _(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET, 1000037000) \ _(XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT, 1000054000) \ _(XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM, 0x7FFFFFFF) @@ -504,10 +524,10 @@ XR_ENUM_STR(XrResult); _(XR_REFERENCE_SPACE_TYPE_VIEW, 1) \ _(XR_REFERENCE_SPACE_TYPE_LOCAL, 2) \ _(XR_REFERENCE_SPACE_TYPE_STAGE, 3) \ + _(XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR, 1000426000) \ _(XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT, 1000038000) \ _(XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO, 1000121000) \ _(XR_REFERENCE_SPACE_TYPE_LOCALIZATION_MAP_ML, 1000139000) \ - _(XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT, 1000426000) \ _(XR_REFERENCE_SPACE_TYPE_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrActionType(_) \ @@ -566,6 +586,8 @@ XR_ENUM_STR(XrResult); _(XR_OBJECT_TYPE_SPACE_USER_FB, 1000241000) \ _(XR_OBJECT_TYPE_PASSTHROUGH_COLOR_LUT_META, 1000266000) \ _(XR_OBJECT_TYPE_FACE_TRACKER2_FB, 1000287012) \ + _(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META, 1000291000) \ + _(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META, 1000291001) \ _(XR_OBJECT_TYPE_PASSTHROUGH_HTC, 1000317000) \ _(XR_OBJECT_TYPE_PLANE_DETECTOR_EXT, 1000429000) \ _(XR_OBJECT_TYPE_MAX_ENUM, 0x7FFFFFFF) @@ -1377,6 +1399,11 @@ XR_ENUM_STR(XrResult); _(XR_PLANE_DETECTION_STATE_FATAL_EXT, 4) \ _(XR_PLANE_DETECTION_STATE_MAX_ENUM_EXT, 0x7FFFFFFF) +#define XR_LIST_ENUM_XrFutureStateEXT(_) \ + _(XR_FUTURE_STATE_PENDING_EXT, 1) \ + _(XR_FUTURE_STATE_READY_EXT, 2) \ + _(XR_FUTURE_STATE_MAX_ENUM_EXT, 0x7FFFFFFF) + #define XR_LIST_ENUM_XrHeadsetFitStatusML(_) \ _(XR_HEADSET_FIT_STATUS_UNKNOWN_ML, 0) \ _(XR_HEADSET_FIT_STATUS_NOT_WORN_ML, 1) \ @@ -1424,6 +1451,7 @@ XR_ENUM_STR(XrResult); _(XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT, 0x00000001) \ _(XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT, 0x00000002) \ _(XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT, 0x00000004) \ + _(XR_COMPOSITION_LAYER_INVERTED_ALPHA_BIT_EXT, 0x00000008) \ #define XR_LIST_BITS_XrViewStateFlags(_) \ _(XR_VIEW_STATE_ORIENTATION_VALID_BIT, 0x00000001) \ @@ -1573,6 +1601,10 @@ XR_ENUM_STR(XrResult); _(XR_PERFORMANCE_METRICS_COUNTER_UINT_VALUE_VALID_BIT_META, 0x00000002) \ _(XR_PERFORMANCE_METRICS_COUNTER_FLOAT_VALUE_VALID_BIT_META, 0x00000004) \ +#define XR_LIST_BITS_XrEnvironmentDepthProviderCreateFlagsMETA(_) + +#define XR_LIST_BITS_XrEnvironmentDepthSwapchainCreateFlagsMETA(_) + #define XR_LIST_BITS_XrFoveationDynamicFlagsHTC(_) \ _(XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_BIT_HTC, 0x00000001) \ _(XR_FOVEATION_DYNAMIC_CLEAR_FOV_ENABLED_BIT_HTC, 0x00000002) \ @@ -2152,6 +2184,73 @@ XR_ENUM_STR(XrResult); _(getInstanceProcAddr) \ _(createApiLayerInstance) \ +/// Calls your macro with the name of each member of XrColor3f, in order. +#define XR_LIST_STRUCT_XrColor3f(_) \ + _(r) \ + _(g) \ + _(b) \ + +/// Calls your macro with the name of each member of XrExtent3Df, in order. +#define XR_LIST_STRUCT_XrExtent3Df(_) \ + _(width) \ + _(height) \ + _(depth) \ + +/// Calls your macro with the name of each member of XrSpheref, in order. +#define XR_LIST_STRUCT_XrSpheref(_) \ + _(center) \ + _(radius) \ + +/// Calls your macro with the name of each member of XrBoxf, in order. +#define XR_LIST_STRUCT_XrBoxf(_) \ + _(center) \ + _(extents) \ + +/// Calls your macro with the name of each member of XrFrustumf, in order. +#define XR_LIST_STRUCT_XrFrustumf(_) \ + _(pose) \ + _(fov) \ + _(nearZ) \ + _(farZ) \ + +/// Calls your macro with the name of each member of XrUuid, in order. +#define XR_LIST_STRUCT_XrUuid(_) \ + _(data) \ + +/// Calls your macro with the name of each member of XrSpacesLocateInfo, in order. +#define XR_LIST_STRUCT_XrSpacesLocateInfo(_) \ + _(type) \ + _(next) \ + _(baseSpace) \ + _(time) \ + _(spaceCount) \ + _(spaces) \ + +/// Calls your macro with the name of each member of XrSpaceLocationData, in order. +#define XR_LIST_STRUCT_XrSpaceLocationData(_) \ + _(locationFlags) \ + _(pose) \ + +/// Calls your macro with the name of each member of XrSpaceLocations, in order. +#define XR_LIST_STRUCT_XrSpaceLocations(_) \ + _(type) \ + _(next) \ + _(locationCount) \ + _(locations) \ + +/// Calls your macro with the name of each member of XrSpaceVelocityData, in order. +#define XR_LIST_STRUCT_XrSpaceVelocityData(_) \ + _(velocityFlags) \ + _(linearVelocity) \ + _(angularVelocity) \ + +/// Calls your macro with the name of each member of XrSpaceVelocities, in order. +#define XR_LIST_STRUCT_XrSpaceVelocities(_) \ + _(type) \ + _(next) \ + _(velocityCount) \ + _(velocities) \ + /// Calls your macro with the name of each member of XrCompositionLayerCubeKHR, in order. #define XR_LIST_STRUCT_XrCompositionLayerCubeKHR(_) \ _(type) \ @@ -3231,10 +3330,6 @@ XR_ENUM_STR(XrResult); _(enabled) \ _(changePending) \ -/// Calls your macro with the name of each member of XrUuidEXT, in order. -#define XR_LIST_STRUCT_XrUuidEXT(_) \ - _(data) \ - /// Calls your macro with the name of each member of XrEventDataSpatialAnchorCreateCompleteFB, in order. #define XR_LIST_STRUCT_XrEventDataSpatialAnchorCreateCompleteFB(_) \ _(type) \ @@ -3857,12 +3952,6 @@ XR_ENUM_STR(XrResult); _(amplitudeCount) \ _(amplitudes) \ -/// Calls your macro with the name of each member of XrExtent3DfFB, in order. -#define XR_LIST_STRUCT_XrExtent3DfFB(_) \ - _(width) \ - _(height) \ - _(depth) \ - /// Calls your macro with the name of each member of XrOffset3DfFB, in order. #define XR_LIST_STRUCT_XrOffset3DfFB(_) \ _(x) \ @@ -4348,6 +4437,61 @@ XR_ENUM_STR(XrResult); _(dataSource) \ _(time) \ +/// Calls your macro with the name of each member of XrEnvironmentDepthProviderCreateInfoMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthProviderCreateInfoMETA(_) \ + _(type) \ + _(next) \ + _(createFlags) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthSwapchainCreateInfoMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthSwapchainCreateInfoMETA(_) \ + _(type) \ + _(next) \ + _(createFlags) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthSwapchainStateMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthSwapchainStateMETA(_) \ + _(type) \ + _(next) \ + _(width) \ + _(height) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthImageAcquireInfoMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthImageAcquireInfoMETA(_) \ + _(type) \ + _(next) \ + _(space) \ + _(displayTime) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthImageViewMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthImageViewMETA(_) \ + _(type) \ + _(next) \ + _(fov) \ + _(pose) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthImageMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthImageMETA(_) \ + _(type) \ + _(next) \ + _(swapchainIndex) \ + _(nearZ) \ + _(farZ) \ + _(views) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthHandRemovalSetInfoMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthHandRemovalSetInfoMETA(_) \ + _(type) \ + _(next) \ + _(enabled) \ + +/// Calls your macro with the name of each member of XrSystemEnvironmentDepthPropertiesMETA, in order. +#define XR_LIST_STRUCT_XrSystemEnvironmentDepthPropertiesMETA(_) \ + _(type) \ + _(next) \ + _(supportsEnvironmentDepth) \ + _(supportsHandRemoval) \ + /// Calls your macro with the name of each member of XrPassthroughCreateInfoHTC, in order. #define XR_LIST_STRUCT_XrPassthroughCreateInfoHTC(_) \ _(type) \ @@ -4483,12 +4627,6 @@ XR_ENUM_STR(XrResult); _(next) \ _(flags) \ -/// Calls your macro with the name of each member of XrExtent3DfEXT, in order. -#define XR_LIST_STRUCT_XrExtent3DfEXT(_) \ - _(width) \ - _(height) \ - _(depth) \ - /// Calls your macro with the name of each member of XrPlaneDetectorBeginInfoEXT, in order. #define XR_LIST_STRUCT_XrPlaneDetectorBeginInfoEXT(_) \ _(type) \ @@ -4539,6 +4677,36 @@ XR_ENUM_STR(XrResult); _(vertexCountOutput) \ _(vertices) \ +/// Calls your macro with the name of each member of XrFutureCancelInfoEXT, in order. +#define XR_LIST_STRUCT_XrFutureCancelInfoEXT(_) \ + _(type) \ + _(next) \ + _(future) \ + +/// Calls your macro with the name of each member of XrFuturePollInfoEXT, in order. +#define XR_LIST_STRUCT_XrFuturePollInfoEXT(_) \ + _(type) \ + _(next) \ + _(future) \ + +/// Calls your macro with the name of each member of XrFutureCompletionBaseHeaderEXT, in order. +#define XR_LIST_STRUCT_XrFutureCompletionBaseHeaderEXT(_) \ + _(type) \ + _(next) \ + _(futureResult) \ + +/// Calls your macro with the name of each member of XrFutureCompletionEXT, in order. +#define XR_LIST_STRUCT_XrFutureCompletionEXT(_) \ + _(type) \ + _(next) \ + _(futureResult) \ + +/// Calls your macro with the name of each member of XrFuturePollResultEXT, in order. +#define XR_LIST_STRUCT_XrFuturePollResultEXT(_) \ + _(type) \ + _(next) \ + _(state) \ + /// Calls your macro with the name of each member of XrEventDataUserPresenceChangedEXT, in order. #define XR_LIST_STRUCT_XrEventDataUserPresenceChangedEXT(_) \ _(type) \ @@ -4644,6 +4812,9 @@ XR_ENUM_STR(XrResult); _(XrEventDataReferenceSpaceChangePending, XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING) \ _(XrEventDataInteractionProfileChanged, XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) \ _(XrHapticVibration, XR_TYPE_HAPTIC_VIBRATION) \ + _(XrSpacesLocateInfo, XR_TYPE_SPACES_LOCATE_INFO) \ + _(XrSpaceLocations, XR_TYPE_SPACE_LOCATIONS) \ + _(XrSpaceVelocities, XR_TYPE_SPACE_VELOCITIES) \ _(XrCompositionLayerCubeKHR, XR_TYPE_COMPOSITION_LAYER_CUBE_KHR) \ _(XrCompositionLayerDepthInfoKHR, XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR) \ _(XrCompositionLayerCylinderKHR, XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) \ @@ -4873,6 +5044,14 @@ XR_ENUM_STR(XrResult); _(XrFaceTrackerCreateInfo2FB, XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB) \ _(XrFaceExpressionInfo2FB, XR_TYPE_FACE_EXPRESSION_INFO2_FB) \ _(XrFaceExpressionWeights2FB, XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB) \ + _(XrEnvironmentDepthProviderCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META) \ + _(XrEnvironmentDepthSwapchainCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META) \ + _(XrEnvironmentDepthSwapchainStateMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META) \ + _(XrEnvironmentDepthImageAcquireInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META) \ + _(XrEnvironmentDepthImageViewMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META) \ + _(XrEnvironmentDepthImageMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META) \ + _(XrEnvironmentDepthHandRemovalSetInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META) \ + _(XrSystemEnvironmentDepthPropertiesMETA, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META) \ _(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \ _(XrPassthroughColorHTC, XR_TYPE_PASSTHROUGH_COLOR_HTC) \ _(XrPassthroughMeshTransformInfoHTC, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC) \ @@ -4894,6 +5073,10 @@ XR_ENUM_STR(XrResult); _(XrPlaneDetectorLocationEXT, XR_TYPE_PLANE_DETECTOR_LOCATION_EXT) \ _(XrPlaneDetectorLocationsEXT, XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT) \ _(XrPlaneDetectorPolygonBufferEXT, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT) \ + _(XrFutureCancelInfoEXT, XR_TYPE_FUTURE_CANCEL_INFO_EXT) \ + _(XrFuturePollInfoEXT, XR_TYPE_FUTURE_POLL_INFO_EXT) \ + _(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ + _(XrFuturePollResultEXT, XR_TYPE_FUTURE_POLL_RESULT_EXT) \ _(XrEventDataUserPresenceChangedEXT, XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT) \ _(XrSystemUserPresencePropertiesEXT, XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT) \ _(XrEventDataHeadsetFitChangedML, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML) \ @@ -5200,6 +5383,7 @@ XR_ENUM_STR(XrResult); _(XR_META_automatic_layer_filter, 272) \ _(XR_META_touch_controller_plus, 280) \ _(XR_FB_face_tracking2, 288) \ + _(XR_META_environment_depth, 292) \ _(XR_EXT_uuid, 300) \ _(XR_EXT_hand_interaction, 303) \ _(XR_QCOM_tracking_optimization_settings, 307) \ @@ -5213,9 +5397,13 @@ XR_ENUM_STR(XrResult); _(XR_EXT_hand_tracking_data_source, 429) \ _(XR_EXT_plane_detection, 430) \ _(XR_OPPO_controller_interaction, 454) \ + _(XR_EXT_future, 470) \ _(XR_EXT_user_presence, 471) \ + _(XR_KHR_locate_spaces, 472) \ _(XR_ML_user_calibration, 473) \ _(XR_YVR_controller_interaction, 498) \ + _(XR_EXT_composition_layer_inverted_alpha, 555) \ + _(XR_KHR_maintenance1, 711) \ @@ -5291,6 +5479,14 @@ XR_ENUM_STR(XrResult); _(NegotiateLoaderApiLayerInterface, LOADER_VERSION_1_0) \ +/// For every function defined by XR_VERSION_1_1 in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_VERSION_1_1(_) \ + _(LocateSpaces, VERSION_1_1) \ + + /// For every function defined by XR_KHR_android_thread_settings in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, @@ -5973,6 +6169,23 @@ XR_ENUM_STR(XrResult); _(GetFaceExpressionWeights2FB, FB_face_tracking2) \ +/// For every function defined by XR_META_environment_depth in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_META_environment_depth(_) \ + _(CreateEnvironmentDepthProviderMETA, META_environment_depth) \ + _(DestroyEnvironmentDepthProviderMETA, META_environment_depth) \ + _(StartEnvironmentDepthProviderMETA, META_environment_depth) \ + _(StopEnvironmentDepthProviderMETA, META_environment_depth) \ + _(CreateEnvironmentDepthSwapchainMETA, META_environment_depth) \ + _(DestroyEnvironmentDepthSwapchainMETA, META_environment_depth) \ + _(EnumerateEnvironmentDepthSwapchainImagesMETA, META_environment_depth) \ + _(GetEnvironmentDepthSwapchainStateMETA, META_environment_depth) \ + _(AcquireEnvironmentDepthImageMETA, META_environment_depth) \ + _(SetEnvironmentDepthHandRemovalMETA, META_environment_depth) \ + + /// For every function defined by XR_QCOM_tracking_optimization_settings in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, @@ -6028,6 +6241,15 @@ XR_ENUM_STR(XrResult); _(GetPlanePolygonBufferEXT, EXT_plane_detection) \ +/// For every function defined by XR_EXT_future in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_EXT_future(_) \ + _(PollFutureEXT, EXT_future) \ + _(CancelFutureEXT, EXT_future) \ + + /// For every function defined by XR_ML_user_calibration in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h index 4241682e0b..50466abc1b 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h @@ -32,6 +32,7 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a _avail(XrCompositionLayerCylinderKHR, XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) \ _avail(XrCompositionLayerEquirectKHR, XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR) \ _avail(XrCompositionLayerEquirect2KHR, XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR) \ + _avail(XrCompositionLayerPassthroughFB, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB) \ _avail(XrCompositionLayerPassthroughHTC, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC) \ @@ -264,5 +265,18 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrFutureCompletionBaseHeaderEXT +#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT(_avail, _unavail) \ + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT_CORE(_avail, _unavail) \ + + +// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT() +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT_CORE(_avail, _unavail) \ + _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ + + + + + #endif diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h index f973539cbb..e87e55b560 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h @@ -92,6 +92,9 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrEventDataReferenceSpaceChangePending, XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING) \ _avail(XrEventDataInteractionProfileChanged, XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) \ _avail(XrHapticVibration, XR_TYPE_HAPTIC_VIBRATION) \ + _avail(XrSpacesLocateInfo, XR_TYPE_SPACES_LOCATE_INFO) \ + _avail(XrSpaceLocations, XR_TYPE_SPACE_LOCATIONS) \ + _avail(XrSpaceVelocities, XR_TYPE_SPACE_VELOCITIES) \ _avail(XrCompositionLayerCubeKHR, XR_TYPE_COMPOSITION_LAYER_CUBE_KHR) \ _avail(XrCompositionLayerDepthInfoKHR, XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR) \ _avail(XrCompositionLayerCylinderKHR, XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) \ @@ -321,6 +324,14 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrFaceTrackerCreateInfo2FB, XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB) \ _avail(XrFaceExpressionInfo2FB, XR_TYPE_FACE_EXPRESSION_INFO2_FB) \ _avail(XrFaceExpressionWeights2FB, XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB) \ + _avail(XrEnvironmentDepthProviderCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META) \ + _avail(XrEnvironmentDepthSwapchainCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META) \ + _avail(XrEnvironmentDepthSwapchainStateMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META) \ + _avail(XrEnvironmentDepthImageAcquireInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META) \ + _avail(XrEnvironmentDepthImageViewMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META) \ + _avail(XrEnvironmentDepthImageMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META) \ + _avail(XrEnvironmentDepthHandRemovalSetInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META) \ + _avail(XrSystemEnvironmentDepthPropertiesMETA, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META) \ _avail(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \ _avail(XrPassthroughColorHTC, XR_TYPE_PASSTHROUGH_COLOR_HTC) \ _avail(XrPassthroughMeshTransformInfoHTC, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC) \ @@ -342,6 +353,10 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrPlaneDetectorLocationEXT, XR_TYPE_PLANE_DETECTOR_LOCATION_EXT) \ _avail(XrPlaneDetectorLocationsEXT, XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT) \ _avail(XrPlaneDetectorPolygonBufferEXT, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT) \ + _avail(XrFutureCancelInfoEXT, XR_TYPE_FUTURE_CANCEL_INFO_EXT) \ + _avail(XrFuturePollInfoEXT, XR_TYPE_FUTURE_POLL_INFO_EXT) \ + _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ + _avail(XrFuturePollResultEXT, XR_TYPE_FUTURE_POLL_RESULT_EXT) \ _avail(XrEventDataUserPresenceChangedEXT, XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT) \ _avail(XrSystemUserPresencePropertiesEXT, XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT) \ _avail(XrEventDataHeadsetFitChangedML, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML) \ diff --git a/thirdparty/openxr/src/common/platform_utils.hpp b/thirdparty/openxr/src/common/platform_utils.hpp index 35369a1477..d047c17d9d 100644 --- a/thirdparty/openxr/src/common/platform_utils.hpp +++ b/thirdparty/openxr/src/common/platform_utils.hpp @@ -47,7 +47,7 @@ #define XR_ARCH_ABI "aarch64" #elif (defined(__ARM_ARCH) && __ARM_ARCH >= 7 && (defined(__ARM_PCS_VFP) || defined(__ANDROID__))) || defined(_M_ARM) #define XR_ARCH_ABI "armv7a-vfp" -#elif defined(__ARM_ARCH_5TE__) +#elif defined(__ARM_ARCH_5TE__) || (defined(__ARM_ARCH) && __ARM_ARCH > 5) #define XR_ARCH_ABI "armv5te" #elif defined(__mips64) #define XR_ARCH_ABI "mips64" @@ -91,8 +91,6 @@ namespace detail { static inline char* ImplGetEnv(const char* name) { return getenv(name); } -static inline int ImplSetEnv(const char* name, const char* value, int overwrite) { return setenv(name, value, overwrite); } - static inline char* ImplGetSecureEnv(const char* name) { #ifdef HAVE_SECURE_GETENV return secure_getenv(name); @@ -162,12 +160,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* name) { static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; } -static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { - const int shouldOverwrite = 1; - int result = detail::ImplSetEnv(name, value, shouldOverwrite); - return (result == 0); -} - #elif defined(XR_OS_APPLE) static inline std::string PlatformUtilsGetEnv(const char* name) { @@ -188,12 +180,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* name) { static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; } -static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { - const int shouldOverwrite = 1; - int result = detail::ImplSetEnv(name, value, shouldOverwrite); - return (result == 0); -} - static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { return detail::ImplTryRuntimeFilename("/usr/local/share/openxr/", major_version, file_name); } @@ -337,19 +323,10 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* name) { return envValue; } -// Sets an environment variable via UTF8 strings. -// The name is case-sensitive. -// Overwrites the variable if it already exists. -// Returns true if it could be set. -static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { - const std::wstring wname = utf8_to_wide(name); - const std::wstring wvalue = utf8_to_wide(value); - BOOL result = ::SetEnvironmentVariableW(wname.c_str(), wvalue.c_str()); - return (result != 0); -} - #elif defined(XR_OS_ANDROID) +#include <sys/system_properties.h> + static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { // Stub func return false; @@ -365,11 +342,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) { return {}; } -static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) { - // Stub func - return false; -} - // Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { // Prefix for the runtime JSON file name @@ -385,6 +357,37 @@ static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std: return false; } + +// Android system properties are sufficiently different from environment variables that we are not re-using +// PlatformUtilsGetEnv for this purpose +static inline std::string PlatformUtilsGetAndroidSystemProperty(const char* name) { + std::string result; + const prop_info* pi = __system_property_find(name); + if (pi == nullptr) { + return {}; + } + +#if __ANDROID_API__ >= 26 + // use callback to retrieve > 92 character sys prop values, if available + __system_property_read_callback( + pi, + [](void* cookie, const char*, const char* value, uint32_t) { + auto property_value = reinterpret_cast<std::string*>(cookie); + *property_value = value; + }, + reinterpret_cast<void*>(&result)); +#endif // __ANDROID_API__ >= 26 + // fallback to __system_property_get if no value retrieved via callback + if (result.empty()) { + char value[PROP_VALUE_MAX] = {}; + if (__system_property_get(name, value) != 0) { + result = value; + } + } + + return result; +} + #else // Not Linux, Apple, nor Windows static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { @@ -402,11 +405,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) { return {}; } -static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) { - // Stub func - return false; -} - static inline bool PlatformGetGlobalRuntimeFileName(uint16_t /* major_version */, std::string const& /* file_name */) { // Stub func return false; diff --git a/thirdparty/ufbx/ufbx.c b/thirdparty/ufbx/ufbx.c index 6d584369a7..2d13c78bd8 100644 --- a/thirdparty/ufbx/ufbx.c +++ b/thirdparty/ufbx/ufbx.c @@ -570,7 +570,7 @@ ufbx_static_assert(sizeof_f64, sizeof(double) == 8); // -- Version -#define UFBX_SOURCE_VERSION ufbx_pack_version(0, 14, 0) +#define UFBX_SOURCE_VERSION ufbx_pack_version(0, 14, 3) ufbx_abi_data_def const uint32_t ufbx_source_version = UFBX_SOURCE_VERSION; ufbx_static_assert(source_header_version, UFBX_SOURCE_VERSION/1000u == UFBX_HEADER_VERSION/1000u); @@ -7257,8 +7257,8 @@ typedef enum { } ufbxi_parse_state; typedef enum { - UFBXI_ARRAY_FLAG_RESULT = 0x1, // < Alloacte the array from the result buffer - UFBXI_ARRAY_FLAG_TMP_BUF = 0x2, // < Alloacte the array from the result buffer + UFBXI_ARRAY_FLAG_RESULT = 0x1, // < Allocate the array from the result buffer + UFBXI_ARRAY_FLAG_TMP_BUF = 0x2, // < Allocate the array from the result buffer UFBXI_ARRAY_FLAG_PAD_BEGIN = 0x4, // < Pad the begin of the array with 4 zero elements to guard from invalid -1 index accesses UFBXI_ARRAY_FLAG_ACCURATE_F32 = 0x8, // < Must be parsed as bit-accurate 32-bit floats } ufbxi_array_flags; @@ -21772,6 +21772,10 @@ ufbxi_noinline static ufbx_quat ufbxi_get_rotation(const ufbx_props *props, ufbx ufbxi_mul_rotate_quat(&t, node->adjust_pre_rotation); } + if (node->adjust_mirror_axis) { + ufbxi_mirror_rotation(&t.rotation, node->adjust_mirror_axis); + } + return t.rotation; } @@ -24710,7 +24714,7 @@ static ufbxi_noinline const ufbx_prop *ufbxi_next_prop_slow(ufbxi_prop_iter *ite const ufbx_prop_override *over = iter->over; if (prop == iter->prop_end && over == iter->over_end) return NULL; - // We can use `UINT32_MAX` as a termianting key (aka prefix) as prop names must + // We can use `UINT32_MAX` as a terminating key (aka prefix) as prop names must // be valid UTF-8 and the byte sequence "\xff\xff\xff\xff" is not valid. uint32_t prop_key = prop != iter->prop_end ? prop->_internal_key : UINT32_MAX; uint32_t over_key = over != iter->over_end ? over->_internal_key : UINT32_MAX; diff --git a/thirdparty/ufbx/ufbx.h b/thirdparty/ufbx/ufbx.h index 072569068a..8d856edaad 100644 --- a/thirdparty/ufbx/ufbx.h +++ b/thirdparty/ufbx/ufbx.h @@ -266,7 +266,7 @@ struct ufbx_converter { }; // `ufbx_source_version` contains the version of the corresponding source file. // HINT: The version can be compared numerically to the result of `ufbx_pack_version()`, // for example `#if UFBX_VERSION >= ufbx_pack_version(0, 12, 0)`. -#define UFBX_HEADER_VERSION ufbx_pack_version(0, 14, 0) +#define UFBX_HEADER_VERSION ufbx_pack_version(0, 14, 3) #define UFBX_VERSION UFBX_HEADER_VERSION // -- Basic types @@ -2673,7 +2673,7 @@ UFBX_ENUM_TYPE(ufbx_texture_type, UFBX_TEXTURE_TYPE, UFBX_TEXTURE_SHADER); // Blend modes to combine layered textures with, compatible with common blend // mode definitions in many art programs. Simpler blend modes have equations -// specified below where `src` is the layer to compososite over `dst`. +// specified below where `src` is the layer to composite over `dst`. // See eg. https://www.w3.org/TR/2013/WD-compositing-1-20131010/#blendingseparable typedef enum ufbx_blend_mode UFBX_ENUM_REPR { UFBX_BLEND_TRANSLUCENT, // < `src` effects result alpha @@ -5286,6 +5286,8 @@ typedef enum ufbx_transform_flags UFBX_FLAG_REPR { } ufbx_transform_flags; // Evaluate the animated transform of a node given a time. +// The returned transform is the local transform of the node (ie. relative to the parent), +// comparable to `ufbx_node.local_transform`. ufbx_abi ufbx_transform ufbx_evaluate_transform(const ufbx_anim *anim, const ufbx_node *node, double time); ufbx_abi ufbx_transform ufbx_evaluate_transform_flags(const ufbx_anim *anim, const ufbx_node *node, double time, uint32_t flags); |