diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 728acb8da7d..eed5b661b84 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -15,3 +15,39 @@ # Style: clang-format: Disable AllowShortIfStatementsOnASingleLine e956e80c1fa1cc8aefcb1533e5acf5cf3c8ffdd9 + +# One Copyright Update to rule them all +d95794ec8a7c362b06a9cf080e2554ef77adb667 + +# Update copyright statements to 2022 +fe52458154c64fb1b741df4f7bd10106395f7cbd + +# Update copyright statements to 2021 +b5334d14f7a471f94bcbd64d5bae2ad853d0b7f1 + +# Update copyright statements to 2020 +a7f49ac9a107820a62677ee3fb49d38982a25165 + +# Update copyright statements to 2019 +b16c309f82c77d606472c3c721a1857e323a09e7 + +# Update copyright statements to 2018 +b50a9114b105dafafdda8248a38653bca314a6f3 + +# Welcome in 2017, dear changelog reader! +c7bc44d5ad9aae4902280012f7654e2318cd910e + +# Update copyright to 2016 in headers +5be9ff7b6715a661e85f99b108f96340de7ef435 + +# Updated copyright year in all headers +fdaa2920eb21fff3320a17e9239e04dfadecdb00 + +# Add missing copyright headers and fix formatting +e4213e66b2dd8f5a87d8cf5015ac83ba3143279d + +# Use HTTPS URL for Godot's website in the headers +bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 + +# Add "Godot Engine contributors" copyright line +df61dc4b2bd54a5a40c515493c76f5a458e5b541 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 031582bc637..e1ea85e147a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -139,10 +139,9 @@ doc_classes/* @godotengine/documentation /platform/android/ @godotengine/android /platform/ios/ @godotengine/ios -/platform/javascript/ @godotengine/html5 /platform/linuxbsd/ @godotengine/linux-bsd /platform/macos/ @godotengine/macos -/platform/uwp/ @godotengine/uwp +/platform/web/ @godotengine/web /platform/windows/ @godotengine/windows # Scene diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index 048e7eaf517..36bc04cac35 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -150,7 +150,7 @@ jobs: run: | ${{ matrix.bin }} --version ${{ matrix.bin }} --help - ${{ matrix.bin }} --test --headless + ${{ matrix.bin }} --headless --test --force-colors # Check class reference - name: Check for class reference updates diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 7d96a3b6dfe..97d4c4c9f7b 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -72,4 +72,4 @@ jobs: run: | ${{ matrix.bin }} --version ${{ matrix.bin }} --help - ${{ matrix.bin }} --test + ${{ matrix.bin }} --test --force-colors diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml index 57f1822f558..754e918d7ae 100644 --- a/.github/workflows/web_builds.yml +++ b/.github/workflows/web_builds.yml @@ -7,7 +7,7 @@ env: # Used for the cache key. Add version suffix to force clean build. GODOT_BASE_BRANCH: '4.1' SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no - EM_VERSION: 3.1.45 + EM_VERSION: 3.1.39 EM_CACHE_FOLDER: "emsdk-cache" concurrency: diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index b11e10ca080..f5882def4ce 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -75,4 +75,4 @@ jobs: run: | ${{ matrix.bin }} --version ${{ matrix.bin }} --help - ${{ matrix.bin }} --test + ${{ matrix.bin }} --test --force-colors diff --git a/.gitignore b/.gitignore index 060f5696b87..61ea171b8b8 100644 --- a/.gitignore +++ b/.gitignore @@ -132,23 +132,9 @@ cppcheck-cppcheck-build-dir/ *.pydevproject *.launch -# Gcov and Lcov code coverage -*.gcno +# GCOV code coverage *.gcda -*.gcov.html -*.func.html -*.func-sort-c.html -*index-sort-f.html -*index-sort-l.html -*index.html -godot.info -amber.png -emerald.png -glass.png -ruby.png -snow.png -updown.png -gcov.css +*.gcno # Geany *.geany diff --git a/core/input/input.cpp b/core/input/input.cpp index 0e691aaec7f..b15a347e65e 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -1060,7 +1060,8 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) { return; } - JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, p_value); + JoyAxisRange range; + JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, p_value, range); if (map.type == TYPE_BUTTON) { bool pressed = map.value > 0.5; @@ -1100,7 +1101,7 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) { if (map.type == TYPE_AXIS) { JoyAxis axis = JoyAxis(map.index); float value = map.value; - if (axis == JoyAxis::TRIGGER_LEFT || axis == JoyAxis::TRIGGER_RIGHT) { + if (range == FULL_AXIS && (axis == JoyAxis::TRIGGER_LEFT || axis == JoyAxis::TRIGGER_RIGHT)) { // Convert to a value between 0.0f and 1.0f. value = 0.5f + value / 2.0f; } @@ -1206,7 +1207,7 @@ Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, return event; } -Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value) { +Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value, JoyAxisRange &r_range) { JoyEvent event; for (int i = 0; i < mapping.bindings.size(); i++) { @@ -1252,6 +1253,7 @@ Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, J case TYPE_AXIS: event.index = (int)binding.output.axis.axis; event.value = value; + r_range = binding.output.axis.range; if (binding.output.axis.range != binding.input.axis.range) { switch (binding.output.axis.range) { case POSITIVE_HALF_AXIS: diff --git a/core/input/input.h b/core/input/input.h index ec16871b728..92cf921030a 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -219,7 +219,7 @@ private: Vector map_db; JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button); - JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value); + JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value, JoyAxisRange &r_range); void _get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat, JoyEvent r_events[(size_t)HatDir::MAX]); JoyButton _get_output_button(String output); JoyAxis _get_output_axis(String output); diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 6ca7b0f831c..bbf3719ce39 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -1619,8 +1619,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo encode_uint32(datalen, buf); buf += 4; const uint8_t *r = data.ptr(); - memcpy(buf, &r[0], datalen * datasize); - buf += datalen * datasize; + if (r) { + memcpy(buf, &r[0], datalen * datasize); + buf += datalen * datasize; + } } r_len += 4 + datalen * datasize; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 1fe662b1fad..b9bbfc5e661 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -917,7 +917,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem } // Fallback to p_path if new_path does not exist. - if (!FileAccess::exists(new_path)) { + if (!FileAccess::exists(new_path + ".import") && !FileAccess::exists(new_path)) { WARN_PRINT(vformat("Translation remap '%s' does not exist. Falling back to '%s'.", new_path, p_path)); new_path = p_path; } diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h index 2dbb7e468e4..f8e8c4d7e90 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -81,35 +81,27 @@ template class CallableCustomMethodPointer : public CallableCustomMethodPointerBase { struct Data { T *instance; -#ifdef DEBUG_ENABLED uint64_t object_id; -#endif void (T::*method)(P...); } data; public: virtual ObjectID get_object() const { -#ifdef DEBUG_ENABLED if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { return ObjectID(); } -#endif return data.instance->get_instance_id(); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); -#endif + ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error); } CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; -#ifdef DEBUG_ENABLED data.object_id = p_instance->get_instance_id(); -#endif data.method = p_method; _setup((uint32_t *)&data, sizeof(Data)); } @@ -135,36 +127,28 @@ template class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase { struct Data { T *instance; -#ifdef DEBUG_ENABLED uint64_t object_id; -#endif R(T::*method) (P...); } data; public: virtual ObjectID get_object() const { -#ifdef DEBUG_ENABLED if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { return ObjectID(); } -#endif return data.instance->get_instance_id(); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); -#endif + ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); } CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; -#ifdef DEBUG_ENABLED data.object_id = p_instance->get_instance_id(); -#endif data.method = p_method; _setup((uint32_t *)&data, sizeof(Data)); } @@ -190,36 +174,28 @@ template class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase { struct Data { T *instance; -#ifdef DEBUG_ENABLED uint64_t object_id; -#endif R(T::*method) (P...) const; } data; public: virtual ObjectID get_object() const override { -#ifdef DEBUG_ENABLED if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { return ObjectID(); } -#endif return data.instance->get_instance_id(); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); -#endif + ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); } CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; -#ifdef DEBUG_ENABLED data.object_id = p_instance->get_instance_id(); -#endif data.method = p_method; _setup((uint32_t *)&data, sizeof(Data)); } diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp index 18ba5d5b300..b03456c6254 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -35,6 +35,8 @@ #include "core/object/class_db.h" #include "core/object/script_language.h" +#include + #ifdef DEV_ENABLED // Includes sanity checks to ensure that a queue set as a thread singleton override // is only ever called from the thread it was set for. @@ -93,7 +95,7 @@ Error CallQueue::push_callablep(const Callable &p_callable, const Variant **p_ar if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) { if (pages_used == max_pages) { - ERR_PRINT("Failed method: " + p_callable + ". Message queue out of memory. " + error_text); + fprintf(stderr, "Failed method: %s. Message queue out of memory. %s\n", String(p_callable).utf8().get_data(), error_text.utf8().get_data()); statistics(); UNLOCK_MUTEX; return ERR_OUT_OF_MEMORY; @@ -144,7 +146,7 @@ Error CallQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant if (ObjectDB::get_instance(p_id)) { type = ObjectDB::get_instance(p_id)->get_class(); } - ERR_PRINT("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id) + ". Message queue out of memory. " + error_text); + fprintf(stderr, "Failed set: %s: %s target ID: %s. Message queue out of memory. %s\n", type.utf8().get_data(), String(p_prop).utf8().get_data(), itos(p_id).utf8().get_data(), error_text.utf8().get_data()); statistics(); UNLOCK_MUTEX; @@ -181,7 +183,7 @@ Error CallQueue::push_notification(ObjectID p_id, int p_notification) { if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) { if (pages_used == max_pages) { - ERR_PRINT("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id) + ". Message queue out of memory. " + error_text); + fprintf(stderr, "Failed notification: %s target ID: %s. Message queue out of memory. %s\n", itos(p_notification).utf8().get_data(), itos(p_id).utf8().get_data(), error_text.utf8().get_data()); statistics(); UNLOCK_MUTEX; return ERR_OUT_OF_MEMORY; diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 02380c92bb0..a443ed308d6 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -520,11 +520,11 @@ String TranslationServer::get_country_name(const String &p_country) const { void TranslationServer::set_locale(const String &p_locale) { locale = standardize_locale(p_locale); + ResourceLoader::reload_translation_remaps(); + if (OS::get_singleton()->get_main_loop()) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); } - - ResourceLoader::reload_translation_remaps(); } String TranslationServer::get_locale() const { @@ -816,10 +816,11 @@ bool TranslationServer::is_pseudolocalization_enabled() const { void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) { pseudolocalization_enabled = p_enabled; + ResourceLoader::reload_translation_remaps(); + if (OS::get_singleton()->get_main_loop()) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); } - ResourceLoader::reload_translation_remaps(); } void TranslationServer::set_editor_pseudolocalization(bool p_enabled) { @@ -836,10 +837,11 @@ void TranslationServer::reload_pseudolocalization() { pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix"); pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders"); + ResourceLoader::reload_translation_remaps(); + if (OS::get_singleton()->get_main_loop()) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); } - ResourceLoader::reload_translation_remaps(); } StringName TranslationServer::pseudolocalize(const StringName &p_message) const { diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 9ec8a406b17..1811e3ce1ff 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -305,7 +305,11 @@ void String::copy_from(const char *p_cstr) { char32_t *dst = this->ptrw(); for (size_t i = 0; i <= len; i++) { +#if CHAR_MIN == 0 + uint8_t c = p_cstr[i]; +#else uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]); +#endif if (c == 0 && i < len) { print_unicode_error("NUL character", true); dst[i] = _replacement_char; @@ -338,7 +342,11 @@ void String::copy_from(const char *p_cstr, const int p_clip_to) { char32_t *dst = this->ptrw(); for (int i = 0; i < len; i++) { +#if CHAR_MIN == 0 + uint8_t c = p_cstr[i]; +#else uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]); +#endif if (c == 0) { print_unicode_error("NUL character", true); dst[i] = _replacement_char; @@ -544,7 +552,11 @@ String &String::operator+=(const char *p_str) { char32_t *dst = ptrw() + lhs_len; for (size_t i = 0; i <= rhs_len; i++) { +#if CHAR_MIN == 0 + uint8_t c = p_str[i]; +#else uint8_t c = p_str[i] >= 0 ? p_str[i] : uint8_t(256 + p_str[i]); +#endif if (c == 0 && i < rhs_len) { print_unicode_error("NUL character", true); dst[i] = _replacement_char; @@ -1814,7 +1826,11 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) { int skip = 0; uint8_t c_start = 0; while (ptrtmp != ptrtmp_limit && *ptrtmp) { +#if CHAR_MIN == 0 + uint8_t c = *ptrtmp; +#else uint8_t c = *ptrtmp >= 0 ? *ptrtmp : uint8_t(256 + *ptrtmp); +#endif if (skip == 0) { if (p_skip_cr && c == '\r') { @@ -1882,7 +1898,11 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) { int skip = 0; uint32_t unichar = 0; while (cstr_size) { +#if CHAR_MIN == 0 + uint8_t c = *p_utf8; +#else uint8_t c = *p_utf8 >= 0 ? *p_utf8 : uint8_t(256 + *p_utf8); +#endif if (skip == 0) { if (p_skip_cr && c == '\r') { @@ -3945,24 +3965,22 @@ bool String::is_absolute_path() const { } } -static _FORCE_INLINE_ bool _is_valid_identifier_bit(int p_index, char32_t p_char) { - if (p_index == 0 && is_digit(p_char)) { - return false; // No start with number plz. - } - return is_ascii_identifier_char(p_char); -} - String String::validate_identifier() const { if (is_empty()) { return "_"; // Empty string is not a valid identifier; } - String result = *this; + String result; + if (is_digit(operator[](0))) { + result = "_" + *this; + } else { + result = *this; + } + int len = result.length(); char32_t *buffer = result.ptrw(); - for (int i = 0; i < len; i++) { - if (!_is_valid_identifier_bit(i, buffer[i])) { + if (!is_ascii_identifier_char(buffer[i])) { buffer[i] = '_'; } } @@ -3977,10 +3995,14 @@ bool String::is_valid_identifier() const { return false; } + if (is_digit(operator[](0))) { + return false; + } + const char32_t *str = &operator[](0); for (int i = 0; i < len; i++) { - if (!_is_valid_identifier_bit(i, str[i])) { + if (!is_ascii_identifier_char(str[i])) { return false; } } diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index e33822fedfb..c37d46956df 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -90,6 +90,10 @@ private: } _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { + if (unlikely(p_elements == 0)) { + *out = 0; + return true; + } #if defined(__GNUC__) size_t o; size_t p; @@ -101,13 +105,12 @@ private: if (__builtin_add_overflow(o, static_cast(32), &p)) { return false; // No longer allocated here. } - return true; #else // Speed is more important than correctness here, do the operations unchecked // and hope for the best. *out = _get_alloc_size(p_elements); - return true; #endif + return *out; } void _unref(void *p_data); diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index 2a212f3dcb1..05960292f5f 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -310,7 +310,7 @@ struct HashMapHasherDefault { static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(p_uchar); } static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); } static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); } - static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.ptr()); } + static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.get_data()); } static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); } static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); } static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); } diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index 3e94f3d0bdd..bb354320b3f 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -257,7 +257,7 @@ - Adds a new track that is a copy of the given track from [param to_animation]. + Adds a new track to [param to_animation] that is a copy of the given track from this animation. diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index c04de121491..7ea13301b2f 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -498,22 +498,22 @@ Represents the size of the [enum TextureParam] enum. - The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized. + The texture filter reads from the nearest pixel only. This makes the texture look pixelated from up close, and grainy from a distance (due to mipmaps not being sampled). - The texture filter blends between the nearest 4 pixels. Use this when you want to avoid a pixelated style, but do not want mipmaps. + The texture filter blends between the nearest 4 pixels. This makes the texture look smooth from up close, and grainy from a distance (due to mipmaps not being sampled). - The texture filter reads from the nearest pixel in the nearest mipmap. The fastest way to read from textures with mipmaps. + The texture filter reads from the nearest pixel and blends between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look pixelated from up close, and smooth from a distance. - The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps. Use this for most cases as mipmaps are important to smooth out pixels that are far from the camera. + The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look smooth from up close, and smooth from a distance. - The texture filter reads from the nearest pixel, but selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + The texture filter reads from the nearest pixel and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look pixelated from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. - The texture filter blends between the nearest 4 pixels and selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. This is the slowest of the filtering options, but results in the highest quality texturing. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + The texture filter blends between the nearest 4 pixels and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look smooth from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. Represents the size of the [enum TextureFilter] enum. diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index 73d859eacd1..87588ae1089 100644 --- a/doc/classes/CPUParticles2D.xml +++ b/doc/classes/CPUParticles2D.xml @@ -290,7 +290,7 @@ Particles are drawn in the order emitted. - Particles are drawn in order of remaining lifetime. + Particles are drawn in order of remaining lifetime. In other words, the particle with the highest lifetime is drawn at the front. Use with [method set_param_min], [method set_param_max], and [method set_param_curve] to set initial velocity properties. diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml index 85ede2dde6e..fd06bd473d3 100644 --- a/doc/classes/CPUParticles3D.xml +++ b/doc/classes/CPUParticles3D.xml @@ -315,7 +315,7 @@ Particles are drawn in the order emitted. - Particles are drawn in order of remaining lifetime. + Particles are drawn in order of remaining lifetime. In other words, the particle with the highest lifetime is drawn at the front. Particles are drawn in order of depth. diff --git a/doc/classes/CameraAttributesPhysical.xml b/doc/classes/CameraAttributesPhysical.xml index 585e8578131..31c8b12c213 100644 --- a/doc/classes/CameraAttributesPhysical.xml +++ b/doc/classes/CameraAttributesPhysical.xml @@ -32,7 +32,7 @@ Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. - Time for shutter to open and close, measured in seconds. A higher value will let in more light leading to a brighter image, while a lower amount will let in less light leading to a darker image. + Time for shutter to open and close, evaluated as [code]1 / shutter_speed[/code] seconds. A higher value will allow less light (leading to a darker image), while a lower value will allow more light (leading to a brighter image). Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index ba280ab0fa5..834693ae05c 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -660,24 +660,26 @@ The [CanvasItem] will inherit the filter from its parent. - The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering. Useful for pixel art. + The texture filter reads from the nearest pixel only. This makes the texture look pixelated from up close, and grainy from a distance (due to mipmaps not being sampled). - The texture filter blends between the nearest four pixels. Use this for most cases where you want to avoid a pixelated style. + The texture filter blends between the nearest 4 pixels. This makes the texture look smooth from up close, and grainy from a distance (due to mipmaps not being sampled). - The texture filter reads from the nearest pixel in the nearest mipmap. This is the fastest way to read from textures with mipmaps. + The texture filter reads from the nearest pixel and blends between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look pixelated from up close, and smooth from a distance. + Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. - The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps. Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. + The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look smooth from up close, and smooth from a distance. + Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. - The texture filter reads from the nearest pixel, but selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. - [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] is usually more appropriate. + The texture filter reads from the nearest pixel and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look pixelated from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] is usually more appropriate in this case. - The texture filter blends between the nearest 4 pixels and selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. This is the slowest of the filtering options, but results in the highest quality texturing. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. - [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] is usually more appropriate. + The texture filter blends between the nearest 4 pixels and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look smooth from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] is usually more appropriate in this case. Represents the size of the [enum TextureFilter] enum. diff --git a/doc/classes/ConcavePolygonShape2D.xml b/doc/classes/ConcavePolygonShape2D.xml index 817c2891d96..f6076b2dacd 100644 --- a/doc/classes/ConcavePolygonShape2D.xml +++ b/doc/classes/ConcavePolygonShape2D.xml @@ -5,7 +5,7 @@ A 2D polyline shape, intended for use in physics. Used internally in [CollisionPolygon2D] when it's in [code]BUILD_SEGMENTS[/code] mode. - Being just a collection of interconnected line segments, [ConcavePolygonShape2D] is the most freely configurable single 2D shape. It can be used to form polygons of any nature, or even shapes that don't enclose an area. However, [ConvexPolygonShape2D] is [i]hollow[/i] even if the interconnected line segments do enclose an area, which often makes it unsuitable for physics or detection. + Being just a collection of interconnected line segments, [ConcavePolygonShape2D] is the most freely configurable single 2D shape. It can be used to form polygons of any nature, or even shapes that don't enclose an area. However, [ConcavePolygonShape2D] is [i]hollow[/i] even if the interconnected line segments do enclose an area, which often makes it unsuitable for physics or detection. [b]Note:[/b] When used for collision, [ConcavePolygonShape2D] is intended to work with static [CollisionShape2D] nodes like [StaticBody2D] and will likely not behave well for [CharacterBody2D]s or [RigidBody2D]s in a mode other than Static. [b]Warning:[/b] Physics bodies that are small have a chance to clip through this shape when moving fast. This happens because on one frame, the physics body may be on the "outside" of the shape, and on the next frame it may be "inside" it. [ConcavePolygonShape2D] is hollow, so it won't detect a collision. [b]Performance:[/b] Due to its complexity, [ConcavePolygonShape2D] is the slowest 2D collision shape to check collisions against. Its use should generally be limited to level geometry. If the polyline is closed, [CollisionPolygon2D]'s [code]BUILD_SOLIDS[/code] mode can be used, which decomposes the polygon into convex ones; see [ConvexPolygonShape2D]'s documentation for instructions. diff --git a/doc/classes/ConcavePolygonShape3D.xml b/doc/classes/ConcavePolygonShape3D.xml index a9ac34c04ab..b3944e9f465 100644 --- a/doc/classes/ConcavePolygonShape3D.xml +++ b/doc/classes/ConcavePolygonShape3D.xml @@ -5,7 +5,7 @@ A 3D trimesh shape, intended for use in physics. Usually used to provide a shape for a [CollisionShape3D]. - Being just a collection of interconnected triangles, [ConcavePolygonShape3D] is the most freely configurable single 3D shape. It can be used to form polyhedra of any nature, or even shapes that don't enclose a volume. However, [ConvexPolygonShape3D] is [i]hollow[/i] even if the interconnected triangles do enclose a volume, which often makes it unsuitable for physics or detection. + Being just a collection of interconnected triangles, [ConcavePolygonShape3D] is the most freely configurable single 3D shape. It can be used to form polyhedra of any nature, or even shapes that don't enclose a volume. However, [ConcavePolygonShape3D] is [i]hollow[/i] even if the interconnected triangles do enclose a volume, which often makes it unsuitable for physics or detection. [b]Note:[/b] When used for collision, [ConcavePolygonShape3D] is intended to work with static [CollisionShape3D] nodes like [StaticBody3D] and will likely not behave well for [CharacterBody3D]s or [RigidBody3D]s in a mode other than Static. [b]Warning:[/b] Physics bodies that are small have a chance to clip through this shape when moving fast. This happens because on one frame, the physics body may be on the "outside" of the shape, and on the next frame it may be "inside" it. [ConcavePolygonShape3D] is hollow, so it won't detect a collision. [b]Performance:[/b] Due to its complexity, [ConcavePolygonShape3D] is the slowest 3D collision shape to check collisions against. Its use should generally be limited to level geometry. For convex geometry, [ConvexPolygonShape3D] should be used. For dynamic physics bodies that need concave collision, several [ConvexPolygonShape3D]s can be used to represent its collision by using convex decomposition; see [ConvexPolygonShape3D]'s documentation for instructions. diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 5f4e4989c21..c03637f1983 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -1161,10 +1161,12 @@ [b]Note:[/b] As an optimization, this notification won't be sent from changes that occur while this node is outside of the scene tree. Instead, all of the theme item updates can be applied at once when the node enters the scene tree. - Sent when this node is inside a [ScrollContainer] which has begun being scrolled. + Sent when this node is inside a [ScrollContainer] which has begun being scrolled when dragging the scrollable area [i]with a touch event[/i]. This notification is [i]not[/i] sent when scrolling by dragging the scrollbar, scrolling with the mouse wheel or scrolling with keyboard/gamepad events. + [b]Note:[/b] This signal is only emitted on Android or iOS, or on desktop/web platforms when [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is enabled. - Sent when this node is inside a [ScrollContainer] which has stopped being scrolled. + Sent when this node is inside a [ScrollContainer] which has stopped being scrolled when dragging the scrollable area [i]with a touch event[/i]. This notification is [i]not[/i] sent when scrolling by dragging the scrollbar, scrolling with the mouse wheel or scrolling with keyboard/gamepad events. + [b]Note:[/b] This signal is only emitted on Android or iOS, or on desktop/web platforms when [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is enabled. Sent when control layout direction is changed. diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 515a54b27d4..1fc0ab51a60 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -294,6 +294,42 @@ Adds entries from [param dictionary] to this dictionary. By default, duplicate keys are not copied over, unless [param overwrite] is [code]true[/code]. + [codeblocks] + [gdscript] + var dict = { "item": "sword", "quantity": 2 } + var other_dict = { "quantity": 15, "color": "silver" } + + # Overwriting of existing keys is disabled by default. + dict.merge(other_dict) + print(dict) # { "item": "sword", "quantity": 2, "color": "silver" } + + # With overwriting of existing keys enabled. + dict.merge(other_dict, true) + print(dict) # { "item": "sword", "quantity": 15, "color": "silver" } + [/gdscript] + [csharp] + var dict = new Godot.Collections.Dictionary + { + ["item"] = "sword", + ["quantity"] = 2, + }; + + var otherDict = new Godot.Collections.Dictionary + { + ["quantity"] = 15, + ["color"] = "silver", + }; + + // Overwriting of existing keys is disabled by default. + dict.Merge(otherDict); + GD.Print(dict); // { "item": "sword", "quantity": 2, "color": "silver" } + + // With overwriting of existing keys enabled. + dict.Merge(otherDict, true); + GD.Print(dict); // { "item": "sword", "quantity": 15, "color": "silver" } + [/csharp] + [/codeblocks] + [b]Note:[/b] [method merge] is [i]not[/i] recursive. Nested dictionaries are considered as keys that can be overwritten or not depending on the value of [param overwrite], but they will never be merged together. diff --git a/doc/classes/EditorSpinSlider.xml b/doc/classes/EditorSpinSlider.xml index 045a1796dd3..3dc3af00012 100644 --- a/doc/classes/EditorSpinSlider.xml +++ b/doc/classes/EditorSpinSlider.xml @@ -5,6 +5,7 @@ This [Control] node is used in the editor's Inspector dock to allow editing of numeric values. Can be used with [EditorInspectorPlugin] to recreate the same behavior. + If [member step] is [code]1[/code], the [EditorSpinSlider] will display up/down arrows, similar to [SpinBox]. If the [member step] is not [code]1[/code], a slider will be displayed instead. @@ -14,7 +15,7 @@ - If [code]true[/code], the slider is hidden. + If [code]true[/code], the slider and up/down arrows are hidden. The text that displays to the left of the value. diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml index d59ece4ab6d..bf3d51f2198 100644 --- a/doc/classes/GPUParticles2D.xml +++ b/doc/classes/GPUParticles2D.xml @@ -113,9 +113,10 @@ Particles are drawn in the order emitted. - Particles are drawn in order of remaining lifetime. + Particles are drawn in order of remaining lifetime. In other words, the particle with the highest lifetime is drawn at the front. + Particles are drawn in reverse order of remaining lifetime. In other words, the particle with the lowest lifetime is drawn at the front. Particle starts at the specified position. diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index c86a2227c6d..cd1e32b98b0 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -137,9 +137,10 @@ Particles are drawn in the order emitted. - Particles are drawn in order of remaining lifetime. + Particles are drawn in order of remaining lifetime. In other words, the particle with the highest lifetime is drawn at the front. + Particles are drawn in reverse order of remaining lifetime. In other words, the particle with the lowest lifetime is drawn at the front. Particles are drawn in order of depth. diff --git a/doc/classes/Material.xml b/doc/classes/Material.xml index 8b566ffa208..07a913c5cda 100644 --- a/doc/classes/Material.xml +++ b/doc/classes/Material.xml @@ -1,10 +1,11 @@ - Abstract base [Resource] for coloring and shading geometry. + Virtual base class for applying visual properties to an object, such as color and roughness. - Material is a base [Resource] used for coloring and shading geometry. All materials inherit from it and almost all [VisualInstance3D] derived nodes carry a Material. A few flags and parameters are shared between all material types and are configured here. + [Material] is a base resource used for coloring and shading geometry. All materials inherit from it and almost all [VisualInstance3D] derived nodes carry a [Material]. A few flags and parameters are shared between all material types and are configured here. + Importantly, you can inherit from [Material] to create your own custom material type in script or in GDExtension. https://godotengine.org/asset-library/asset/123 @@ -14,21 +15,25 @@ + Only exposed for the purpose of overriding. You cannot call this function directly. Used internally to determine if [member next_pass] should be shown in the editor or not. + Only exposed for the purpose of overriding. You cannot call this function directly. Used internally to determine if [member render_priority] should be shown in the editor or not. + Only exposed for the purpose of overriding. You cannot call this function directly. Used internally by various editor tools. + Only exposed for the purpose of overriding. You cannot call this function directly. Used internally by various editor tools. Used to access the RID of the [Material]'s [Shader]. @@ -40,18 +45,20 @@ + Only available when running in the editor. Opens a popup that visualizes the generated shader code, including all variants and internal shader code. Sets the [Material] to be used for the next pass. This renders the object again using a different material. + [b]Note:[/b] [member next_pass] materials are not necessarily drawn immediately after the source [Material]. Draw order is determined by material properties, [member render_priority], and distance to camera. [b]Note:[/b] This only applies to [StandardMaterial3D]s and [ShaderMaterial]s with type "Spatial". - Sets the render priority for transparent objects in 3D scenes. Higher priority objects will be sorted in front of lower priority objects. + Sets the render priority for objects in 3D scenes. Higher priority objects will be sorted in front of lower priority objects. In other words, all objects with [member render_priority] [code]1[/code] will render before all objects with [member render_priority] [code]0[/code]). [b]Note:[/b] This only applies to [StandardMaterial3D]s and [ShaderMaterial]s with type "Spatial". - [b]Note:[/b] This only applies to sorting of transparent objects. This will not impact how transparent objects are sorted relative to opaque objects. This is because opaque objects are not sorted, while transparent objects are sorted from back to front (subject to priority). + [b]Note:[/b] This will not impact how transparent objects are sorted relative to opaque objects or how dynamic meshes will be sorted relative to other opaque meshes. This is because all transparent objects are drawn after all opaque objects and all dynamic opaque meshes are drawn before other opaque meshes. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index aaf889a09ad..b8b6a75a59a 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -445,7 +445,7 @@ - Returns the [SceneTree] that contains this node. + Returns the [SceneTree] that contains this node. Returns [code]null[/code] and prints an error if this node is not inside the scene tree. See also [method is_inside_tree]. diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 2b91236f564..b9a1c17d2ef 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -172,7 +172,7 @@ - Adds an item that will act as a submenu of the parent [PopupMenu] node when clicked. The [param submenu] argument is the name of the child [PopupMenu] node that will be shown when the item is clicked. + Adds an item that will act as a submenu of the parent [PopupMenu] node when clicked. The [param submenu] argument must be the name of an existing [PopupMenu] that has been added as a child to this node. This submenu will be shown when the item is clicked, hovered for long enough, or activated using the [code]ui_select[/code] or [code]ui_right[/code] input actions. An [param id] can optionally be provided. If no [param id] is provided, one will be created from the index. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 912d026ea9f..977c0c0979f 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -324,7 +324,8 @@ Changes to this setting will only be applied upon restarting the application. - Forces a delay between frames in the main loop (in milliseconds). This may be useful if you plan to disable vertical synchronization. + Forces a [i]constant[/i] delay between frames in the main loop (in milliseconds). In most situations, [member application/run/max_fps] should be preferred as an FPS limiter as it's more precise. + This setting can be overridden using the [code]--frame-delay <ms;>[/code] command line argument. If [code]true[/code], enables low-processor usage mode. This setting only works on desktop platforms. The screen is not redrawn if nothing changes visually. This is meant for writing applications and editors, but is pretty useless (and can hurt performance) in most games. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index b6f44e3ba7c..f2224abc8e9 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -4416,9 +4416,10 @@ Draw particles in the order that they appear in the particles array. - Sort particles based on their lifetime. + Sort particles based on their lifetime. In other words, the particle with the highest lifetime is drawn at the front. + Sort particles based on the inverse of their lifetime. In other words, the particle with the lowest lifetime is drawn at the front. Sort particles based on their distance to the camera. @@ -5032,22 +5033,26 @@ Uses the default filter mode for this [Viewport]. - The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized. + The texture filter reads from the nearest pixel only. This makes the texture look pixelated from up close, and grainy from a distance (due to mipmaps not being sampled). - The texture filter blends between the nearest 4 pixels. Use this when you want to avoid a pixelated style, but do not want mipmaps. + The texture filter blends between the nearest 4 pixels. This makes the texture look smooth from up close, and grainy from a distance (due to mipmaps not being sampled). - The texture filter reads from the nearest pixel in the nearest mipmap. The fastest way to read from textures with mipmaps. + The texture filter reads from the nearest pixel and blends between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look pixelated from up close, and smooth from a distance. + Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. - The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps. + The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look smooth from up close, and smooth from a distance. + Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. - The texture filter reads from the nearest pixel, but selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. + The texture filter reads from the nearest pixel and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look pixelated from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] is usually more appropriate in this case. - The texture filter blends between the nearest 4 pixels and selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. This is the slowest of the filtering options, but results in the highest quality texturing. + The texture filter blends between the nearest 4 pixels and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look smooth from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] is usually more appropriate in this case. Max value for [enum CanvasItemTextureFilter] enum. diff --git a/doc/classes/VideoStreamPlayer.xml b/doc/classes/VideoStreamPlayer.xml index 3181be1d49a..cdd7362abab 100644 --- a/doc/classes/VideoStreamPlayer.xml +++ b/doc/classes/VideoStreamPlayer.xml @@ -6,7 +6,6 @@ A control used for playback of [VideoStream] resources. Supported video formats are [url=https://www.theora.org/]Ogg Theora[/url] ([code].ogv[/code], [VideoStreamTheora]) and any format exposed via a GDExtension plugin. - [b]Note:[/b] Due to a bug, VideoStreamPlayer does not support localization remapping yet. [b]Warning:[/b] On Web, video playback [i]will[/i] perform poorly due to missing architecture-specific assembly optimizations. diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index e28ea285656..1f299ff8763 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -543,16 +543,18 @@ - The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized. + The texture filter reads from the nearest pixel only. This makes the texture look pixelated from up close, and grainy from a distance (due to mipmaps not being sampled). - The texture filter blends between the nearest 4 pixels. Use this when you want to avoid a pixelated style, but do not want mipmaps. + The texture filter blends between the nearest 4 pixels. This makes the texture look smooth from up close, and grainy from a distance (due to mipmaps not being sampled). - The texture filter reads from the nearest pixel in the nearest mipmap. The fastest way to read from textures with mipmaps. + The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look smooth from up close, and smooth from a distance. + Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. - The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps. + The texture filter reads from the nearest pixel and blends between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look pixelated from up close, and smooth from a distance. + Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. Max value for [enum DefaultCanvasItemTextureFilter] enum. diff --git a/doc/classes/VisualShaderNodeTextureParameter.xml b/doc/classes/VisualShaderNodeTextureParameter.xml index 412c1b4745c..159557374ed 100644 --- a/doc/classes/VisualShaderNodeTextureParameter.xml +++ b/doc/classes/VisualShaderNodeTextureParameter.xml @@ -57,24 +57,26 @@ Sample the texture using the filter determined by the node this shader is attached to. - The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized. + The texture filter reads from the nearest pixel only. This makes the texture look pixelated from up close, and grainy from a distance (due to mipmaps not being sampled). - The texture filter blends between the nearest four pixels. Use this for most cases where you want to avoid a pixelated style. + The texture filter blends between the nearest 4 pixels. This makes the texture look smooth from up close, and grainy from a distance (due to mipmaps not being sampled). - The texture filter reads from the nearest pixel in the nearest mipmap. This is the fastest way to read from textures with mipmaps. + The texture filter reads from the nearest pixel and blends between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look pixelated from up close, and smooth from a distance. + Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. - The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps. Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. + The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look smooth from up close, and smooth from a distance. + Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. - The texture filter reads from the nearest pixel, but selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. - [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_LINEAR_MIPMAP] is usually more appropriate. + The texture filter reads from the nearest pixel and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look pixelated from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_NEAREST_MIPMAP] is usually more appropriate in this case. - The texture filter blends between the nearest 4 pixels and selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. This is the slowest of the filtering options, but results in the highest quality texturing. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. - [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_LINEAR_MIPMAP] is usually more appropriate. + The texture filter blends between the nearest 4 pixels and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look smooth from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_LINEAR_MIPMAP] is usually more appropriate in this case. Represents the size of the [enum TextureFilter] enum. diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index b0b6cf4917a..e78d13c462d 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -660,6 +660,11 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou state.current_tex = RID(); for (uint32_t i = 0; i <= state.current_batch_index; i++) { + // Skipping when there is no instances. + if (state.canvas_instance_batches[i].instance_count == 0) { + continue; + } + //setup clip if (current_clip != state.canvas_instance_batches[i].clip) { current_clip = state.canvas_instance_batches[i].clip; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 71cc5b061d8..1ad130d23a0 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1734,7 +1734,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ RenderDataGLES3 render_data; { render_data.render_buffers = rb; - render_data.transparent_bg = rb.is_valid() ? rb->is_transparent : false; + render_data.transparent_bg = rb.is_valid() ? rt->is_transparent : false; // Our first camera is used by default render_data.cam_transform = p_camera_data->main_transform; render_data.inv_cam_transform = render_data.cam_transform.affine_inverse(); @@ -1980,6 +1980,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ } if (!keep_color) { + clear_color.a = render_data.transparent_bg ? 0.0f : 1.0f; glClearBufferfv(GL_COLOR, 0, clear_color.components); } RENDER_TIMESTAMP("Render Opaque Pass"); diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index 19bf57df945..b01abda0be3 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -38,8 +38,6 @@ RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { } void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { - GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); - //internal_size.x = p_internal_size.x; // ignore for now //internal_size.y = p_internal_size.y; width = p_target_size.x; @@ -54,10 +52,6 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte view_count = p_view_count; free_render_buffer_data(); - - GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target); - - is_transparent = rt->is_transparent; } void RenderSceneBuffersGLES3::free_render_buffer_data() { diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h index d07a0812f64..e3b784380c9 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.h +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -58,8 +58,6 @@ public: //bool use_debanding = false; uint32_t view_count = 1; - bool is_transparent = false; - RID render_target; //built-in textures used for ping pong image processing and blurring diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 305be687064..256a5c8ef5c 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -59,8 +59,6 @@ static const GLenum _cube_side_enum[6] = { TextureStorage::TextureStorage() { singleton = this; - system_fbo = 0; - { //create default textures { // White Textures @@ -709,18 +707,20 @@ void TextureStorage::texture_free(RID p_texture) { memdelete(t->canvas_texture); } - if (t->tex_id != 0) { - if (!t->is_external) { - GLES3::Utilities::get_singleton()->texture_free_data(t->tex_id); + bool must_free_data = false; + if (t->is_proxy) { + if (t->proxy_to.is_valid()) { + Texture *proxy_to = texture_owner.get_or_null(t->proxy_to); + if (proxy_to) { + proxy_to->proxies.erase(p_texture); + } } - t->tex_id = 0; + } else { + must_free_data = t->tex_id != 0 && !t->is_external; } - - if (t->is_proxy && t->proxy_to.is_valid()) { - Texture *proxy_to = texture_owner.get_or_null(t->proxy_to); - if (proxy_to) { - proxy_to->proxies.erase(p_texture); - } + if (must_free_data) { + GLES3::Utilities::get_singleton()->texture_free_data(t->tex_id); + t->tex_id = 0; } texture_atlas_remove_texture(p_texture); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 24a6d89118d..369330dd16c 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -2694,14 +2694,21 @@ void AnimationTrackEdit::gui_input(const Ref &p_event) { AnimationPlayer *ap = ape->get_player(); if (ap) { NodePath npath = animation->track_get_path(track); - Node *nd = ap->get_node(ap->get_root())->get_node(NodePath(npath.get_concatenated_names())); - StringName prop = npath.get_concatenated_subnames(); - PropertyInfo prop_info; - ClassDB::get_property_info(nd->get_class(), prop, &prop_info); - bool is_angle = prop_info.type == Variant::FLOAT && prop_info.hint_string.find("radians") != -1; - if (is_angle) { - menu->add_icon_item(get_theme_icon(SNAME("InterpLinearAngle"), SNAME("EditorIcons")), TTR("Linear Angle"), MENU_INTERPOLATION_LINEAR_ANGLE); - menu->add_icon_item(get_theme_icon(SNAME("InterpCubicAngle"), SNAME("EditorIcons")), TTR("Cubic Angle"), MENU_INTERPOLATION_CUBIC_ANGLE); + Node *a_ap_root_node = ap->get_node(ap->get_root()); + Node *nd = nullptr; + // We must test that we have a valid a_ap_root_node before trying to access its content to init the nd Node. + if (a_ap_root_node) { + nd = a_ap_root_node->get_node(NodePath(npath.get_concatenated_names())); + } + if (nd) { + StringName prop = npath.get_concatenated_subnames(); + PropertyInfo prop_info; + ClassDB::get_property_info(nd->get_class(), prop, &prop_info); + bool is_angle = prop_info.type == Variant::FLOAT && prop_info.hint_string.find("radians") != -1; + if (is_angle) { + menu->add_icon_item(get_theme_icon(SNAME("InterpLinearAngle"), SNAME("EditorIcons")), TTR("Linear Angle"), MENU_INTERPOLATION_LINEAR_ANGLE); + menu->add_icon_item(get_theme_icon(SNAME("InterpCubicAngle"), SNAME("EditorIcons")), TTR("Cubic Angle"), MENU_INTERPOLATION_CUBIC_ANGLE); + } } } } diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 4c3d23aa631..a776e528e0a 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -367,6 +367,9 @@ void FindReplaceBar::_update_results_count() { int col_pos = 0; + bool searched_start_is_symbol = is_symbol(searched[0]); + bool searched_end_is_symbol = is_symbol(searched[searched.length() - 1]); + while (true) { col_pos = is_case_sensitive() ? line_text.find(searched, col_pos) : line_text.findn(searched, col_pos); @@ -375,11 +378,11 @@ void FindReplaceBar::_update_results_count() { } if (is_whole_words()) { - if (col_pos > 0 && !is_symbol(line_text[col_pos - 1])) { + if (!searched_start_is_symbol && col_pos > 0 && !is_symbol(line_text[col_pos - 1])) { col_pos += searched.length(); continue; } - if (col_pos + searched.length() < line_text.length() && !is_symbol(line_text[col_pos + searched.length()])) { + if (!searched_end_is_symbol && col_pos + searched.length() < line_text.length() && !is_symbol(line_text[col_pos + searched.length()])) { col_pos += searched.length(); continue; } diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index 7a1dc2e33b0..7cfaf2eaeb6 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -338,8 +338,7 @@ void EditorAutoloadSettings::_autoload_button_pressed(Object *p_item, int p_colu undo_redo->add_do_property(ProjectSettings::get_singleton(), name, Variant()); undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, GLOBAL_GET(name)); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_persisting", name, true); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", order); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", name, order); undo_redo->add_do_method(this, "update_autoload"); undo_redo->add_undo_method(this, "update_autoload"); @@ -795,8 +794,7 @@ void EditorAutoloadSettings::autoload_remove(const String &p_name) { undo_redo->add_do_property(ProjectSettings::get_singleton(), name, Variant()); undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, GLOBAL_GET(name)); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_persisting", name, true); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", order); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", name, order); undo_redo->add_do_method(this, "update_autoload"); undo_redo->add_undo_method(this, "update_autoload"); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 17ae863a6f6..75a56ba2668 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -854,12 +854,12 @@ void EditorNode::_on_plugin_ready(Object *p_script, const String &p_activate_nam if (scr.is_null()) { return; } - if (p_activate_name.length()) { - set_addon_plugin_enabled(p_activate_name, true); - } project_settings_editor->update_plugins(); project_settings_editor->hide(); push_item(scr.operator->()); + if (p_activate_name.length()) { + set_addon_plugin_enabled(p_activate_name, true); + } } void EditorNode::_remove_plugin_from_enabled(const String &p_name) { @@ -6690,7 +6690,7 @@ static void _execute_thread(void *p_ud) { eta->done.set(); } -int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List &p_arguments, bool p_close_on_ok, bool p_close_on_errors) { +int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List &p_arguments, bool p_close_on_ok, bool p_close_on_errors, String *r_output) { if (execute_output_dialog) { execute_output_dialog->set_title(p_title); execute_output_dialog->get_ok_button()->set_disabled(true); @@ -6736,6 +6736,9 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p execute_output_dialog->get_ok_button()->set_disabled(false); } + if (r_output) { + *r_output = eta.output; + } return eta.exitcode; } @@ -7443,10 +7446,6 @@ EditorNode::EditorNode() { project_menu->add_item(TTR("Customize Engine Build Configuration..."), TOOLS_BUILD_PROFILE_MANAGER); project_menu->add_separator(); - plugin_config_dialog = memnew(PluginConfigDialog); - plugin_config_dialog->connect("plugin_ready", callable_mp(this, &EditorNode::_on_plugin_ready)); - gui_base->add_child(plugin_config_dialog); - tool_menu = memnew(PopupMenu); tool_menu->set_name("Tools"); tool_menu->connect("index_pressed", callable_mp(this, &EditorNode::_tool_menu_option)); diff --git a/editor/editor_node.h b/editor/editor_node.h index 7c7cd838e8f..ba2144e2bdf 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -357,8 +357,6 @@ private: Timer *screenshot_timer = nullptr; - PluginConfigDialog *plugin_config_dialog = nullptr; - RichTextLabel *load_errors = nullptr; AcceptDialog *load_error_dialog = nullptr; @@ -934,7 +932,7 @@ public: bool has_scenes_in_session(); - int execute_and_show_output(const String &p_title, const String &p_path, const List &p_arguments, bool p_close_on_ok = true, bool p_close_on_errors = false); + int execute_and_show_output(const String &p_title, const String &p_path, const List &p_arguments, bool p_close_on_ok = true, bool p_close_on_errors = false, String *r_output = nullptr); EditorNode(); ~EditorNode(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 77d6ec9ab22..bb4e4283e24 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -3664,7 +3664,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::VECTOR2I: { EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide)); EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true); - editor->setup(hint.min, hint.max, 1, true, p_hint == PROPERTY_HINT_LINK, hint.suffix); + editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix); return editor; } break; @@ -3691,7 +3691,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::VECTOR3I: { EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide)); EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true); - editor->setup(hint.min, hint.max, 1, true, p_hint == PROPERTY_HINT_LINK, hint.suffix); + editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix); return editor; } break; @@ -3705,7 +3705,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::VECTOR4I: { EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i); EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true); - editor->setup(hint.min, hint.max, 1, true, p_hint == PROPERTY_HINT_LINK, hint.suffix); + editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix); return editor; } break; diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 2ff53dd9f12..934fc05ea4b 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1631,7 +1631,7 @@ Ref create_editor_theme(const Ref p_theme) { if (increase_scrollbar_touch_area) { theme->set_stylebox("scroll", "HScrollBar", make_line_stylebox(separator_color, 50)); } else { - theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, -5, 1, -5, 1)); } theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); @@ -1649,7 +1649,7 @@ Ref create_editor_theme(const Ref p_theme) { if (increase_scrollbar_touch_area) { theme->set_stylebox("scroll", "VScrollBar", make_line_stylebox(separator_color, 50, 1, 1, true)); } else { - theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, -5, 1, -5)); } theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 8ea30ab53fb..00c2a8426d3 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -1421,7 +1421,7 @@ void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_ nullptr, 0, 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions - 0); + 1 << 11); // Bit 11 is the language encoding flag. When set, filename and comment fields must be encoded using UTF-8. String target = da->read_link(f); zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size()); @@ -1465,7 +1465,7 @@ void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_ nullptr, 0, 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions - 0); + 1 << 11); // Bit 11 is the language encoding flag. When set, filename and comment fields must be encoded using UTF-8. Ref fa = FileAccess::open(dir.path_join(f), FileAccess::READ); if (fa.is_null()) { diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 7c7762e0fd7..38d28cddeaf 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -242,7 +242,7 @@ void ProjectExportDialog::_edit_preset(int p_index) { export_filter->select(current->get_export_filter()); include_filters->set_text(current->get_include_filter()); - include_label->set_text(current->get_export_filter() == EditorExportPreset::EXCLUDE_SELECTED_RESOURCES ? TTR("Resources to exclude:") : TTR("Resources to export:")); + include_label->set_text(_get_resource_export_header(current->get_export_filter())); exclude_filters->set_text(current->get_exclude_filter()); server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED); @@ -703,13 +703,24 @@ void ProjectExportDialog::_export_type_changed(int p_which) { if (filter_type == EditorExportPreset::EXPORT_CUSTOMIZED && current->get_customized_files_count() == 0) { current->set_file_export_mode("res://", EditorExportPreset::MODE_FILE_STRIP); } - include_label->set_text(current->get_export_filter() == EditorExportPreset::EXCLUDE_SELECTED_RESOURCES ? TTR("Resources to exclude:") : TTR("Resources to export:")); + include_label->set_text(_get_resource_export_header(current->get_export_filter())); updating = true; _fill_resource_tree(); updating = false; } +String ProjectExportDialog::_get_resource_export_header(EditorExportPreset::ExportFilter p_filter) const { + switch (p_filter) { + case EditorExportPreset::EXCLUDE_SELECTED_RESOURCES: + return TTR("Resources to exclude:"); + case EditorExportPreset::EXPORT_CUSTOMIZED: + return TTR("Resources to override export behavior:"); + default: + return TTR("Resources to export:"); + } +} + void ProjectExportDialog::_filter_changed(const String &p_filter) { if (updating) { return; diff --git a/editor/export/project_export.h b/editor/export/project_export.h index e53e3934324..98985092fb8 100644 --- a/editor/export/project_export.h +++ b/editor/export/project_export.h @@ -115,6 +115,7 @@ private: void _export_type_changed(int p_which); void _filter_changed(const String &p_filter); + String _get_resource_export_header(EditorExportPreset::ExportFilter p_filter) const; void _fill_resource_tree(); void _setup_item_for_file_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_mode); bool _fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref ¤t, EditorExportPreset::ExportFilter p_export_filter); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 49f073f2451..2a7454a7a25 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -1507,6 +1507,10 @@ void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) { int base = 1; String name = base_name; while (state_machine->has_node(name)) { + if (name == prev_name) { + name_edit_popup->hide(); // The old name wins, the name doesn't change, just hide the popup. + return; + } base++; name = base_name + " " + itos(base); } diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp index 3d94dd13a72..411d7100ec3 100644 --- a/editor/plugins/bone_map_editor_plugin.cpp +++ b/editor/plugins/bone_map_editor_plugin.cpp @@ -533,6 +533,11 @@ void BoneMapper::_clear_mapping_current_group() { } #ifdef MODULE_REGEX_ENABLED +bool BoneMapper::is_match_with_bone_name(String p_bone_name, String p_word) { + RegEx re = RegEx(p_word); + return !re.search(p_bone_name.to_lower()).is_null(); +} + int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector p_picklist, BoneSegregation p_segregation, int p_parent, int p_child, int p_children_count) { // There may be multiple candidates hit by existing the subsidiary bone. // The one with the shortest name is probably the original. @@ -540,7 +545,6 @@ int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector p_pic String shortest = ""; for (int word_idx = 0; word_idx < p_picklist.size(); word_idx++) { - RegEx re = RegEx(p_picklist[word_idx]); if (p_child == -1) { Vector bones_to_process = p_parent == -1 ? p_skeleton->get_parentless_bones() : p_skeleton->get_bone_children(p_parent); while (bones_to_process.size() > 0) { @@ -559,7 +563,7 @@ int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector p_pic } String bn = skeleton->get_bone_name(idx); - if (!re.search(bn.to_lower()).is_null() && guess_bone_segregation(bn) == p_segregation) { + if (is_match_with_bone_name(bn, p_picklist[word_idx]) && guess_bone_segregation(bn) == p_segregation) { hit_list.push_back(bn); } } @@ -584,7 +588,7 @@ int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector p_pic } String bn = skeleton->get_bone_name(idx); - if (!re.search(bn.to_lower()).is_null() && guess_bone_segregation(bn) == p_segregation) { + if (is_match_with_bone_name(bn, p_picklist[word_idx]) && guess_bone_segregation(bn) == p_segregation) { hit_list.push_back(bn); } idx = skeleton->get_bone_parent(idx); @@ -654,6 +658,7 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { picklist.push_back("pelvis"); picklist.push_back("waist"); picklist.push_back("torso"); + picklist.push_back("spine"); int hips = search_bone_by_name(skeleton, picklist); if (hips == -1) { WARN_PRINT("Auto Mapping couldn't guess Hips. Abort auto mapping."); @@ -704,70 +709,7 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { bone_idx = -1; search_path.clear(); - // 3. Guess Neck - picklist.push_back("neck"); - picklist.push_back("head"); // For no neck model. - picklist.push_back("face"); // Same above. - int neck = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, hips); - picklist.clear(); - - // 4. Guess Head - picklist.push_back("head"); - picklist.push_back("face"); - int head = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck); - if (head == -1) { - search_path = skeleton->get_bone_children(neck); - if (search_path.size() == 1) { - head = search_path[0]; // Maybe only one child of the Neck is Head. - } - } - if (head == -1) { - if (neck != -1) { - head = neck; // The head animation should have more movement. - neck = -1; - p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head)); - } else { - WARN_PRINT("Auto Mapping couldn't guess Neck or Head."); // Continued for guessing on the other bones. But abort when guessing spines step. - } - } else { - p_bone_map->_set_skeleton_bone_name("Neck", skeleton->get_bone_name(neck)); - p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head)); - } - picklist.clear(); - search_path.clear(); - - int neck_or_head = neck != -1 ? neck : (head != -1 ? head : -1); - if (neck_or_head != -1) { - // 4-1. Guess Eyes - picklist.push_back("eye(?!.*(brow|lash|lid))"); - bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, neck_or_head); - if (bone_idx == -1) { - WARN_PRINT("Auto Mapping couldn't guess LeftEye."); - } else { - p_bone_map->_set_skeleton_bone_name("LeftEye", skeleton->get_bone_name(bone_idx)); - } - - bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, neck_or_head); - if (bone_idx == -1) { - WARN_PRINT("Auto Mapping couldn't guess RightEye."); - } else { - p_bone_map->_set_skeleton_bone_name("RightEye", skeleton->get_bone_name(bone_idx)); - } - picklist.clear(); - - // 4-2. Guess Jaw - picklist.push_back("jaw"); - bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck_or_head); - if (bone_idx == -1) { - WARN_PRINT("Auto Mapping couldn't guess Jaw."); - } else { - p_bone_map->_set_skeleton_bone_name("Jaw", skeleton->get_bone_name(bone_idx)); - } - bone_idx = -1; - picklist.clear(); - } - - // 5. Guess Foots + // 3. Guess Foots picklist.push_back("foot"); picklist.push_back("ankle"); int left_foot = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips); @@ -784,7 +726,7 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { } picklist.clear(); - // 5-1. Guess LowerLegs + // 3-1. Guess LowerLegs picklist.push_back("(low|under).*leg"); picklist.push_back("knee"); picklist.push_back("shin"); @@ -810,7 +752,7 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { } picklist.clear(); - // 5-2. Guess UpperLegs + // 3-2. Guess UpperLegs picklist.push_back("up.*leg"); picklist.push_back("thigh"); picklist.push_back("leg"); @@ -834,7 +776,7 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { bone_idx = -1; picklist.clear(); - // 5-3. Guess Toes + // 3-3. Guess Toes picklist.push_back("toe"); picklist.push_back("ball"); if (left_foot != -1) { @@ -871,7 +813,7 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { bone_idx = -1; picklist.clear(); - // 6. Guess Hands + // 4. Guess Hands picklist.push_back("hand"); picklist.push_back("wrist"); picklist.push_back("palm"); @@ -916,7 +858,7 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { bone_idx = -1; picklist.clear(); - // 6-1. Guess Finger + // 4-1. Guess Finger bool named_finger_is_found = false; LocalVector fingers; fingers.push_back("thumb|pollex"); @@ -1106,7 +1048,7 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { } } - // 7. Guess Arms + // 5. Guess Arms picklist.push_back("shoulder"); picklist.push_back("clavicle"); picklist.push_back("collar"); @@ -1124,7 +1066,7 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { } picklist.clear(); - // 7-1. Guess LowerArms + // 5-1. Guess LowerArms picklist.push_back("(low|fore).*arm"); picklist.push_back("elbow"); picklist.push_back("arm"); @@ -1148,7 +1090,7 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { } picklist.clear(); - // 7-2. Guess UpperArms + // 5-2. Guess UpperArms picklist.push_back("up.*arm"); picklist.push_back("arm"); if (left_shoulder != -1 && left_lower_arm != -1) { @@ -1171,6 +1113,99 @@ void BoneMapper::auto_mapping_process(Ref &p_bone_map) { bone_idx = -1; picklist.clear(); + // 6. Guess Neck + picklist.push_back("neck"); + picklist.push_back("head"); // For no neck model. + picklist.push_back("face"); // Same above. + int neck = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, hips); + picklist.clear(); + if (neck == -1) { + // If it can't expect by name, search child spine of where the right and left shoulders (or hands) cross. + int ls_idx = left_shoulder != -1 ? left_shoulder : (left_hand_or_palm != -1 ? left_hand_or_palm : -1); + int rs_idx = right_shoulder != -1 ? right_shoulder : (right_hand_or_palm != -1 ? right_hand_or_palm : -1); + if (ls_idx != -1 && rs_idx != -1) { + bool detect = false; + while (ls_idx != hips && ls_idx >= 0 && rs_idx != hips && rs_idx >= 0) { + ls_idx = skeleton->get_bone_parent(ls_idx); + rs_idx = skeleton->get_bone_parent(rs_idx); + if (ls_idx == rs_idx) { + detect = true; + break; + } + } + if (detect) { + Vector children = skeleton->get_bone_children(ls_idx); + children.erase(ls_idx); + children.erase(rs_idx); + String word = "spine"; // It would be better to limit the search with "spine" because it could be mistaken with breast, wing and etc... + for (int i = 0; children.size(); i++) { + bone_idx = children[i]; + if (is_match_with_bone_name(skeleton->get_bone_name(bone_idx), word)) { + neck = bone_idx; + break; + }; + } + bone_idx = -1; + } + } + } + + // 7. Guess Head + picklist.push_back("head"); + picklist.push_back("face"); + int head = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck); + if (head == -1) { + search_path = skeleton->get_bone_children(neck); + if (search_path.size() == 1) { + head = search_path[0]; // Maybe only one child of the Neck is Head. + } + } + if (head == -1) { + if (neck != -1) { + head = neck; // The head animation should have more movement. + neck = -1; + p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head)); + } else { + WARN_PRINT("Auto Mapping couldn't guess Neck or Head."); // Continued for guessing on the other bones. But abort when guessing spines step. + } + } else { + p_bone_map->_set_skeleton_bone_name("Neck", skeleton->get_bone_name(neck)); + p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head)); + } + picklist.clear(); + search_path.clear(); + + int neck_or_head = neck != -1 ? neck : (head != -1 ? head : -1); + if (neck_or_head != -1) { + // 7-1. Guess Eyes + picklist.push_back("eye(?!.*(brow|lash|lid))"); + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, neck_or_head); + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess LeftEye."); + } else { + p_bone_map->_set_skeleton_bone_name("LeftEye", skeleton->get_bone_name(bone_idx)); + } + + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, neck_or_head); + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess RightEye."); + } else { + p_bone_map->_set_skeleton_bone_name("RightEye", skeleton->get_bone_name(bone_idx)); + } + picklist.clear(); + + // 7-2. Guess Jaw + picklist.push_back("jaw"); + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck_or_head); + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess Jaw."); + } else { + p_bone_map->_set_skeleton_bone_name("Jaw", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + picklist.clear(); + } + // 8. Guess UpperChest or Chest if (neck_or_head == -1) { return; // Abort. diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h index 7974d241a2c..9ff32707c75 100644 --- a/editor/plugins/bone_map_editor_plugin.h +++ b/editor/plugins/bone_map_editor_plugin.h @@ -179,6 +179,7 @@ class BoneMapper : public VBoxContainer { BONE_SEGREGATION_LEFT, BONE_SEGREGATION_RIGHT }; + bool is_match_with_bone_name(String p_bone_name, String p_word); int search_bone_by_name(Skeleton3D *p_skeleton, Vector p_picklist, BoneSegregation p_segregation = BONE_SEGREGATION_NONE, int p_parent = -1, int p_child = -1, int p_children_count = -1); BoneSegregation guess_bone_segregation(String p_bone_name); void auto_mapping_process(Ref &p_bone_map); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index d2b3efcd653..38ab1df1492 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -456,6 +456,7 @@ void Polygon2DEditor::_uv_mode(int p_mode) { for (int i = 0; i < UV_MODE_MAX; i++) { uv_button[i]->set_pressed(p_mode == i); } + uv_edit_draw->queue_redraw(); } void Polygon2DEditor::_uv_input(const Ref &p_input) { @@ -980,9 +981,36 @@ void Polygon2DEditor::_uv_draw() { mtx.columns[2] = -uv_draw_ofs; mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom)); - RS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(), mtx); - uv_edit_draw->draw_texture(base_tex, Point2()); - RS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(), Transform2D()); + // Draw texture as a background if editing uvs or no uv mapping exist. + if (uv_edit_mode[0]->is_pressed() || uv_mode == UV_MODE_CREATE || node->get_polygon().is_empty() || node->get_uv().size() != node->get_polygon().size()) { + Transform2D texture_transform = Transform2D(node->get_texture_rotation(), node->get_texture_offset()); + texture_transform.scale(node->get_texture_scale()); + texture_transform.affine_invert(); + RS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(), mtx * texture_transform); + uv_edit_draw->draw_texture(base_tex, Point2()); + RS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(), Transform2D()); + preview_polygon->hide(); + } else { + preview_polygon->set_transform(mtx); + // Keep in sync with newly added Polygon2D properties (when relevant). + preview_polygon->set_texture(node->get_texture()); + preview_polygon->set_texture_offset(node->get_texture_offset()); + preview_polygon->set_texture_rotation(node->get_texture_rotation()); + preview_polygon->set_texture_scale(node->get_texture_scale()); + preview_polygon->set_texture_filter(node->get_texture_filter_in_tree()); + preview_polygon->set_texture_repeat(node->get_texture_repeat_in_tree()); + preview_polygon->set_polygon(node->get_polygon()); + preview_polygon->set_uv(node->get_uv()); + preview_polygon->set_invert(node->get_invert()); + preview_polygon->set_invert_border(node->get_invert_border()); + preview_polygon->set_internal_vertex_count(node->get_internal_vertex_count()); + if (uv_mode == UV_MODE_ADD_POLYGON) { + preview_polygon->set_polygons(Array()); + } else { + preview_polygon->set_polygons(node->get_polygons()); + } + preview_polygon->show(); + } if (snap_show_grid) { Color grid_color = Color(1.0, 1.0, 1.0, 0.15); @@ -1347,10 +1375,19 @@ Polygon2DEditor::Polygon2DEditor() { HSplitContainer *uv_main_hsc = memnew(HSplitContainer); uv_main_vb->add_child(uv_main_hsc); uv_main_hsc->set_v_size_flags(SIZE_EXPAND_FILL); - uv_edit_draw = memnew(Panel); - uv_main_hsc->add_child(uv_edit_draw); - uv_edit_draw->set_h_size_flags(SIZE_EXPAND_FILL); - uv_edit_draw->set_custom_minimum_size(Size2(200, 200) * EDSCALE); + + uv_edit_background = memnew(Panel); + uv_main_hsc->add_child(uv_edit_background); + uv_edit_background->set_h_size_flags(SIZE_EXPAND_FILL); + uv_edit_background->set_custom_minimum_size(Size2(200, 200) * EDSCALE); + uv_edit_background->set_clip_contents(true); + + preview_polygon = memnew(Polygon2D); + uv_edit_background->add_child(preview_polygon); + + uv_edit_draw = memnew(Control); + uv_edit_background->add_child(uv_edit_draw); + uv_edit_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); Control *space = memnew(Control); uv_mode_hb->add_child(space); @@ -1491,8 +1528,6 @@ Polygon2DEditor::Polygon2DEditor() { error = memnew(AcceptDialog); add_child(error); - - uv_edit_draw->set_clip_contents(true); } Polygon2DEditorPlugin::Polygon2DEditorPlugin() : diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index 3547c031207..8c52984b593 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -82,7 +82,9 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { Button *uv_button[UV_MODE_MAX]; Button *b_snap_enable = nullptr; Button *b_snap_grid = nullptr; - Panel *uv_edit_draw = nullptr; + Panel *uv_edit_background = nullptr; + Polygon2D *preview_polygon = nullptr; + Control *uv_edit_draw = nullptr; HSlider *uv_zoom = nullptr; SpinBox *uv_zoom_value = nullptr; HScrollBar *uv_hscroll = nullptr; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 35cda31bb6b..4b379807b67 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -421,7 +421,11 @@ ScriptEditor *ScriptEditor::script_editor = nullptr; String ScriptEditor::_get_debug_tooltip(const String &p_text, Node *_se) { String val = EditorDebuggerNode::get_singleton()->get_var_value(p_text); + const int display_limit = 300; if (!val.is_empty()) { + if (val.size() > display_limit) { + val = val.left(display_limit) + " [...] truncated!"; + } return p_text + ": " + val; } else { return String(); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 563398e5126..420ea087fa4 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -948,13 +948,16 @@ void SpriteFramesEditor::_animation_name_edited() { String name = new_name; int counter = 0; while (frames->has_animation(name)) { + if (name == String(edited_anim)) { + edited->set_text(0, name); // The name didn't change, just updated the column text to name. + return; + } counter++; name = new_name + "_" + itos(counter); } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Rename Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); - _rename_node_animation(undo_redo, false, edited_anim, "", ""); undo_redo->add_do_method(frames.ptr(), "rename_animation", edited_anim, name); undo_redo->add_undo_method(frames.ptr(), "rename_animation", name, edited_anim); _rename_node_animation(undo_redo, false, edited_anim, name, name); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index f2e650a604f..4ed650e47a8 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -1099,7 +1099,7 @@ TextureRegionEditor::TextureRegionEditor() { snap_mode_button->add_item(TTR("Pixel Snap"), 1); snap_mode_button->add_item(TTR("Grid Snap"), 2); snap_mode_button->add_item(TTR("Auto Slice"), 3); - snap_mode_button->select(0); + snap_mode_button->select(snap_mode); snap_mode_button->connect("item_selected", callable_mp(this, &TextureRegionEditor::_set_snap_mode)); hb_grid = memnew(HBoxContainer); diff --git a/editor/renames_map_3_to_4.cpp b/editor/renames_map_3_to_4.cpp index 1908e877e7e..952b07b870b 100644 --- a/editor/renames_map_3_to_4.cpp +++ b/editor/renames_map_3_to_4.cpp @@ -1451,7 +1451,7 @@ const char *RenamesMap3To4::shaders_renames[][2] = { { "NORMALMAP_DEPTH", "NORMAL_MAP_DEPTH" }, { "TRANSMISSION", "BACKLIGHT" }, { "WORLD_MATRIX", "MODEL_MATRIX" }, - { "depth_draw_alpha_prepass", "depth_draw_opaque" }, + { "depth_draw_alpha_prepass", "depth_prepass_alpha" }, { "hint_albedo", "source_color" }, { "hint_aniso", "hint_anisotropy" }, { "hint_black", "hint_default_black" }, diff --git a/main/main.cpp b/main/main.cpp index 9bd390d1afa..ad289b940b5 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1962,6 +1962,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (frame_delay == 0) { frame_delay = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), 0); + if (Engine::get_singleton()->is_editor_hint()) { + frame_delay = 0; + } } OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false)); diff --git a/methods.py b/methods.py index d68316f0f01..95d38b94e51 100644 --- a/methods.py +++ b/methods.py @@ -907,9 +907,16 @@ def generate_vs_project(env, original_args, project_name="godot"): defines=mono_defines, ) - env["MSVSBUILDCOM"] = module_configs.build_commandline("scons") - env["MSVSREBUILDCOM"] = module_configs.build_commandline("scons vsproj=yes") - env["MSVSCLEANCOM"] = module_configs.build_commandline("scons --clean") + scons_cmd = "scons" + + path_to_venv = os.getenv("VIRTUAL_ENV") + path_to_scons_exe = Path(str(path_to_venv)) / "Scripts" / "scons.exe" + if path_to_venv and path_to_scons_exe.exists(): + scons_cmd = str(path_to_scons_exe) + + env["MSVSBUILDCOM"] = module_configs.build_commandline(scons_cmd) + env["MSVSREBUILDCOM"] = module_configs.build_commandline(f"{scons_cmd} vsproj=yes") + env["MSVSCLEANCOM"] = module_configs.build_commandline(f"{scons_cmd} --clean") if not env.get("MSVS"): env["MSVS"]["PROJECTSUFFIX"] = ".vcxproj" env["MSVS"]["SOLUTIONSUFFIX"] = ".sln" diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 66ce28893ca..814f2184b02 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -573,6 +573,11 @@ void CSGShape3D::_notification(int p_what) { // Update this node's parent only if its own visibility has changed, not the visibility of parent nodes parent_shape->_make_dirty(); } + if (is_visible()) { + _update_debug_collision_shape(); + } else { + _clear_debug_collision_shape(); + } last_visible = is_visible(); } break; diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 622a155d374..38888fa5e96 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -190,6 +190,9 @@ namespace GodotTools case ExternalEditorId.CustomEditor: { string file = ProjectSettings.GlobalizePath(script.ResourcePath); + string project = ProjectSettings.GlobalizePath("res://"); + // Since ProjectSettings.GlobalizePath replaces only "res:/", leaving a trailing slash, it is removed here. + project = project[..^1]; var execCommand = _editorSettings.GetSetting(Settings.CustomExecPath).As(); var execArgs = _editorSettings.GetSetting(Settings.CustomExecPathArgs).As(); var args = new List(); @@ -226,6 +229,7 @@ namespace GodotTools hasFileFlag = true; } + arg = arg.ReplaceN("{project}", project); arg = arg.ReplaceN("{file}", file); args.Add(arg); diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index ea306e80ebb..3ff9127fc29 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -1116,6 +1116,7 @@ void NavMap::_update_rvo_obstacles_tree_2d() { rvo_2d_vertices.reserve(_obstacle_vertices.size()); uint32_t _obstacle_avoidance_layers = obstacle->get_avoidance_layers(); + real_t _obstacle_height = obstacle->get_height(); for (const Vector3 &_obstacle_vertex : _obstacle_vertices) { rvo_2d_vertices.push_back(RVO2D::Vector2(_obstacle_vertex.x + _obstacle_position.x, _obstacle_vertex.z + _obstacle_position.z)); @@ -1126,6 +1127,9 @@ void NavMap::_update_rvo_obstacles_tree_2d() { for (size_t i = 0; i < rvo_2d_vertices.size(); i++) { RVO2D::Obstacle2D *rvo_2d_obstacle = new RVO2D::Obstacle2D(); rvo_2d_obstacle->point_ = rvo_2d_vertices[i]; + rvo_2d_obstacle->height_ = _obstacle_height; + rvo_2d_obstacle->elevation_ = _obstacle_position.y; + rvo_2d_obstacle->avoidance_layers_ = _obstacle_avoidance_layers; if (i != 0) { diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 53faa65e97b..036fdcf1198 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -28,6 +28,11 @@ elif env["platform"] == "linuxbsd": env_openxr.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"]) elif env["platform"] == "windows": env_openxr.AppendUnique(CPPDEFINES=["XR_OS_WINDOWS", "NOMINMAX", "XR_USE_PLATFORM_WIN32"]) +elif env["platform"] == "macos": + env_openxr.AppendUnique(CPPDEFINES=["XR_OS_APPLE"]) + + # There does not seem to be a XR_USE_PLATFORM_XYZ for Apple + # may need to check and set: # - XR_USE_TIMESPEC @@ -96,7 +101,7 @@ if env["platform"] == "android": env_openxr.add_source_files(module_obj, "extensions/openxr_android_extension.cpp") if env["vulkan"]: env_openxr.add_source_files(module_obj, "extensions/openxr_vulkan_extension.cpp") -if env["opengl3"]: +if env["opengl3"] and env["platform"] != "macos": env_openxr.add_source_files(module_obj, "extensions/openxr_opengl_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_palm_pose_extension.cpp") diff --git a/modules/openxr/config.py b/modules/openxr/config.py index 7d0653ba7e7..92aaf06fcea 100644 --- a/modules/openxr/config.py +++ b/modules/openxr/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - if platform in ("linuxbsd", "windows", "android"): + if platform in ("linuxbsd", "windows", "android", "macos"): return env["openxr"] and not env["disable_3d"] else: # not supported on these platforms diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 458ee007876..eb9e31cff7a 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -47,7 +47,7 @@ #ifdef VULKAN_ENABLED #define XR_USE_GRAPHICS_API_VULKAN #endif -#ifdef GLES3_ENABLED +#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED) #ifdef ANDROID_ENABLED #define XR_USE_GRAPHICS_API_OPENGL_ES #include @@ -72,7 +72,7 @@ #include "extensions/openxr_vulkan_extension.h" #endif -#ifdef GLES3_ENABLED +#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED) #include "extensions/openxr_opengl_extension.h" #endif @@ -1306,7 +1306,7 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) { ERR_FAIL_V(false); #endif } else if (p_rendering_driver == "opengl3") { -#ifdef GLES3_ENABLED +#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED) graphics_extension = memnew(OpenXROpenGLExtension); register_extension_wrapper(graphics_extension); #else diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index fcd717cfeca..f3473aaad95 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -327,9 +327,9 @@ void AudioStreamPlaybackOggVorbis::seek(double p_time) { int64_t samples_to_burn = samples_in_page - (granule_pos - desired_sample); if (samples_to_burn > samples_in_page) { - WARN_PRINT("Burning more samples than we have in this page. Check seek algorithm."); + WARN_PRINT_ONCE("Burning more samples than we have in this page. Check seek algorithm."); } else if (samples_to_burn < 0) { - WARN_PRINT("Burning negative samples doesn't make sense. Check seek algorithm."); + WARN_PRINT_ONCE("Burning negative samples doesn't make sense. Check seek algorithm."); } // Seek again, this time we'll burn a specific number of samples instead of all of them. diff --git a/modules/zip/zip_packer.cpp b/modules/zip/zip_packer.cpp index c8b4fb4e77f..ae9f5a0df98 100644 --- a/modules/zip/zip_packer.cpp +++ b/modules/zip/zip_packer.cpp @@ -72,7 +72,24 @@ Error ZIPPacker::start_file(String p_path) { zipfi.internal_fa = 0; zipfi.external_fa = 0; - int err = zipOpenNewFileInZip(zf, p_path.utf8().get_data(), &zipfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION); + int err = zipOpenNewFileInZip4(zf, + p_path.utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION, + 0, + -MAX_WBITS, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, + nullptr, + 0, + 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions. + 1 << 11); // Bit 11 is the language encoding flag. When set, filename and comment fields must be encoded using UTF-8. return err == ZIP_OK ? OK : FAILED; } diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index c94119e13d5..233a168a00d 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1531,18 +1531,32 @@ void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_n String EditorExportPlatformAndroid::load_splash_refs(Ref &splash_image, Ref &splash_bg_color_image) { bool scale_splash = GLOBAL_GET("application/boot_splash/fullsize"); bool apply_filter = GLOBAL_GET("application/boot_splash/use_filter"); + bool show_splash_image = GLOBAL_GET("application/boot_splash/show_image"); String project_splash_path = GLOBAL_GET("application/boot_splash/image"); - if (!project_splash_path.is_empty()) { - splash_image.instantiate(); - print_verbose("Loading splash image: " + project_splash_path); - const Error err = ImageLoader::load_image(project_splash_path, splash_image); - if (err) { - if (OS::get_singleton()->is_stdout_verbose()) { - print_error("- unable to load splash image from " + project_splash_path + " (" + itos(err) + ")"); + // Setup the splash bg color. + bool bg_color_valid = false; + Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid); + if (!bg_color_valid) { + bg_color = boot_splash_bg_color; + } + + if (show_splash_image) { + if (!project_splash_path.is_empty()) { + splash_image.instantiate(); + print_verbose("Loading splash image: " + project_splash_path); + const Error err = ImageLoader::load_image(project_splash_path, splash_image); + if (err) { + if (OS::get_singleton()->is_stdout_verbose()) { + print_error("- unable to load splash image from " + project_splash_path + " (" + itos(err) + ")"); + } + splash_image.unref(); } - splash_image.unref(); } + } else { + splash_image.instantiate(); + splash_image->initialize_data(1, 1, false, Image::FORMAT_RGBA8); + splash_image->set_pixel(0, 0, bg_color); } if (splash_image.is_null()) { @@ -1566,13 +1580,6 @@ String EditorExportPlatformAndroid::load_splash_refs(Ref &splash_image, R splash_image->resize(width, height); } - // Setup the splash bg color - bool bg_color_valid; - Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid); - if (!bg_color_valid) { - bg_color = boot_splash_bg_color; - } - print_verbose("Creating splash background color image."); splash_bg_color_image.instantiate(); splash_bg_color_image->initialize_data(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); @@ -1699,7 +1706,6 @@ void EditorExportPlatformAndroid::get_preset_features(const Ref abis = get_enabled_abis(p_preset); for (int i = 0; i < abis.size(); ++i) { r_features->push_back(abis[i].arch); - r_features->push_back(abis[i].abi); } } @@ -2933,10 +2939,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Refexecute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline); + String build_project_output; + int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline, true, false, &build_project_output); if (result != 0) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Building of Android project failed, check output for the error. Alternatively visit docs.godotengine.org for Android build documentation.")); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Building of Android project failed, check output for the error:") + "\n\n" + build_project_output); return ERR_CANT_CREATE; + } else { + print_verbose(build_project_output); } List copy_args; @@ -2963,10 +2972,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Refexecute_and_show_output(TTR("Moving output"), build_command, copy_args); + String copy_binary_output; + int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args, true, false, ©_binary_output); if (copy_result != 0) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to copy and rename export file, check gradle project directory for outputs.")); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to copy and rename export file:") + "\n\n" + copy_binary_output); return ERR_CANT_CREATE; + } else { + print_verbose(copy_binary_output); } print_verbose("Successfully completed Android gradle build."); diff --git a/platform/android/java/editor/src/.gitignore b/platform/android/java/editor/src/.gitignore new file mode 100644 index 00000000000..c081ec3425d --- /dev/null +++ b/platform/android/java/editor/src/.gitignore @@ -0,0 +1 @@ +!/debug diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml index c7b2c8ad678..2bb6b43503f 100644 --- a/platform/android/java/editor/src/main/AndroidManifest.xml +++ b/platform/android/java/editor/src/main/AndroidManifest.xml @@ -35,7 +35,7 @@ @@ -53,7 +53,7 @@ android:name=".GodotEditor" android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode" android:process=":GodotEditor" - android:launchMode="singleInstance" + android:launchMode="singleTask" android:screenOrientation="userLandscape" android:exported="false"> MultiMeshInstance3D::get_multimesh() const { return multimesh; } +Array MultiMeshInstance3D::get_meshes() const { + if (multimesh.is_null() || multimesh->get_mesh().is_null() || multimesh->get_transform_format() != MultiMesh::TransformFormat::TRANSFORM_3D) { + return Array(); + } + + int count = multimesh->get_visible_instance_count(); + if (count == -1) { + count = multimesh->get_instance_count(); + } + + Ref mesh = multimesh->get_mesh(); + + Array results; + for (int i = 0; i < count; i++) { + results.push_back(multimesh->get_instance_transform(i)); + results.push_back(mesh); + } + return results; +} + AABB MultiMeshInstance3D::get_aabb() const { if (multimesh.is_null()) { return AABB(); diff --git a/scene/3d/multimesh_instance_3d.h b/scene/3d/multimesh_instance_3d.h index cd18281b913..404f31d1e37 100644 --- a/scene/3d/multimesh_instance_3d.h +++ b/scene/3d/multimesh_instance_3d.h @@ -47,6 +47,8 @@ public: void set_multimesh(const Ref &p_multimesh); Ref get_multimesh() const; + Array get_meshes() const; + virtual AABB get_aabb() const override; MultiMeshInstance3D(); diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index e533f08861b..2e34f6aad02 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -68,8 +68,9 @@ Color ReflectionProbe::get_ambient_color() const { } void ReflectionProbe::set_max_distance(float p_distance) { - max_distance = p_distance; - RS::get_singleton()->reflection_probe_set_max_distance(probe, p_distance); + max_distance = CLAMP(p_distance, 0.0, 262'144.0); + // Reflection rendering breaks if distance exceeds 262,144 units (due to floating-point precision with the near plane being 0.01). + RS::get_singleton()->reflection_probe_set_max_distance(probe, max_distance); } float ReflectionProbe::get_max_distance() const { diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 43906b25865..8250083a9fc 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -314,9 +314,21 @@ Ref VoxelGI::get_camera_attributes() const { return camera_attributes; } +static bool is_node_voxel_bakeable(Node3D *p_node) { + if (!p_node->is_visible_in_tree()) { + return false; + } + + GeometryInstance3D *geometry = Object::cast_to(p_node); + if (geometry != nullptr && geometry->get_gi_mode() != GeometryInstance3D::GI_MODE_STATIC) { + return false; + } + return true; +} + void VoxelGI::_find_meshes(Node *p_at_node, List &plot_meshes) { MeshInstance3D *mi = Object::cast_to(p_at_node); - if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) { + if (mi && is_node_voxel_bakeable(mi)) { Ref mesh = mi->get_mesh(); if (mesh.is_valid()) { AABB aabb = mesh->get_aabb(); @@ -338,8 +350,15 @@ void VoxelGI::_find_meshes(Node *p_at_node, List &plot_meshes) { Node3D *s = Object::cast_to(p_at_node); if (s) { - if (s->is_visible_in_tree()) { - Array meshes = p_at_node->call("get_meshes"); + if (is_node_voxel_bakeable(s)) { + Array meshes; + MultiMeshInstance3D *multi_mesh = Object::cast_to(p_at_node); + if (multi_mesh) { + meshes = multi_mesh->get_meshes(); + } else { + meshes = p_at_node->call("get_meshes"); + } + for (int i = 0; i < meshes.size(); i += 2) { Transform3D mxf = meshes[i]; Ref mesh = meshes[i + 1]; diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index a84dc1e73e1..b71ad6aa2d5 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -383,6 +383,8 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref p_material } void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref &p_mesh, const Vector> &p_materials, const Ref &p_override_material) { + ERR_FAIL_COND_MSG(!p_xform.is_finite(), "Invalid mesh bake transform."); + for (int i = 0; i < p_mesh->get_surface_count(); i++) { if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { continue; //only triangles diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 0f1dfaa4e00..ea5e0aa9748 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1443,11 +1443,11 @@ void ItemList::_check_shape_changed() { } } - for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) { - items.write[j].rect_cache.size.y = max_h; - } - if (all_fit) { + for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) { + items.write[j].rect_cache.size.y = max_h; + } + float page = MAX(0, size.height - theme_cache.panel_style->get_minimum_size().height); float max = MAX(page, ofs.y + max_h); if (auto_height) { diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index e28af630ce5..67d77d48dbd 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -1812,6 +1812,7 @@ void PopupMenu::scroll_to_item(int p_idx) { } bool PopupMenu::activate_item_by_event(const Ref &p_event, bool p_for_global_only) { + ERR_FAIL_COND_V(p_event.is_null(), false); Key code = Key::NONE; Ref k = p_event; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index e65c3050df9..b9939c77399 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1659,7 +1659,7 @@ void RichTextLabel::_scroll_changed(double) { return; } - if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page())) { + if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - Math::round(vscroll->get_page()))) { scroll_following = true; } else { scroll_following = false; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 3b2013f7ecf..7bb99db5f1a 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2939,15 +2939,17 @@ void TextEdit::_update_placeholder() { return; // Not in tree? } + const String placeholder_translated = atr(placeholder_text); + // Placeholder is generally smaller then text documents, and updates less so this should be fast enough for now. placeholder_data_buf->clear(); placeholder_data_buf->set_width(text.get_width()); placeholder_data_buf->set_break_flags(text.get_brk_flags()); placeholder_data_buf->set_direction((TextServer::Direction)text_direction); placeholder_data_buf->set_preserve_control(draw_control_chars); - placeholder_data_buf->add_string(placeholder_text, theme_cache.font, theme_cache.font_size, language); + placeholder_data_buf->add_string(placeholder_translated, theme_cache.font, theme_cache.font_size, language); - placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_text); + placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_translated); if (placeholder_bidi_override.is_empty()) { TS->shaped_text_set_bidi_override(placeholder_data_buf->get_rid(), placeholder_bidi_override); } @@ -2972,7 +2974,7 @@ void TextEdit::_update_placeholder() { placeholder_wraped_rows.clear(); for (int i = 0; i <= wrap_amount; i++) { Vector2i line_range = placeholder_data_buf->get_line_range(i); - placeholder_wraped_rows.push_back(placeholder_text.substr(line_range.x, line_range.y - line_range.x)); + placeholder_wraped_rows.push_back(placeholder_translated.substr(line_range.x, line_range.y - line_range.x)); } } @@ -4131,6 +4133,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro int line = p_from_line; int pos = -1; + bool key_start_is_symbol = is_symbol(p_key[0]); + bool key_end_is_symbol = is_symbol(p_key[p_key.length() - 1]); + for (int i = 0; i < text.size() + 1; i++) { if (line < 0) { line = text.size() - 1; @@ -4194,9 +4199,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) { // Validate for whole words. - if (pos > 0 && !is_symbol(text_line[pos - 1])) { + if (!key_start_is_symbol && pos > 0 && !is_symbol(text_line[pos - 1])) { is_match = false; - } else if (pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) { + } else if (!key_end_is_symbol && pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) { is_match = false; } } @@ -6990,6 +6995,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc p_from_column = 0; } + bool key_start_is_symbol = is_symbol(p_key[0]); + bool key_end_is_symbol = is_symbol(p_key[p_key.length() - 1]); + while (col == -1 && p_from_column <= p_search.length()) { if (p_search_flags & SEARCH_MATCH_CASE) { col = p_search.find(p_key, p_from_column); @@ -7001,9 +7009,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) { p_from_column = col; - if (col > 0 && !is_symbol(p_search[col - 1])) { + if (!key_start_is_symbol && col > 0 && !is_symbol(p_search[col - 1])) { col = -1; - } else if ((col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) { + } else if (!key_end_is_symbol && (col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) { col = -1; } } diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index 19a54e01c4c..54fd83f9528 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -224,6 +224,12 @@ bool VideoStreamPlayer::has_expand() const { void VideoStreamPlayer::set_stream(const Ref &p_stream) { stop(); + // Make sure to handle stream changes seamlessly, e.g. when done via + // translation remapping. + if (stream.is_valid()) { + stream->disconnect("changed", callable_mp(this, &VideoStreamPlayer::set_stream)); + } + AudioServer::get_singleton()->lock(); mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); stream = p_stream; @@ -235,6 +241,10 @@ void VideoStreamPlayer::set_stream(const Ref &p_stream) { } AudioServer::get_singleton()->unlock(); + if (stream.is_valid()) { + stream->connect("changed", callable_mp(this, &VideoStreamPlayer::set_stream).bind(stream)); + } + if (!playback.is_null()) { playback->set_paused(paused); texture = playback->get_texture(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index fb8053744c0..70b20df295b 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2727,9 +2727,15 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const { copytarget = p_copy->get_node(ptarget); } - if (copy && copytarget) { - const Callable copy_callable = Callable(copytarget, E.callable.get_method()); + if (copy && copytarget && E.callable.get_method() != StringName()) { + Callable copy_callable = Callable(copytarget, E.callable.get_method()); if (!copy->is_connected(E.signal.get_name(), copy_callable)) { + int arg_count = E.callable.get_bound_arguments_count(); + if (arg_count > 0) { + copy_callable = copy_callable.bindv(E.callable.get_bound_arguments()); + } else if (arg_count < 0) { + copy_callable = copy_callable.unbind(-arg_count); + } copy->connect(E.signal.get_name(), copy_callable, E.flags); } } diff --git a/scene/resources/bone_map.cpp b/scene/resources/bone_map.cpp index 759d189bfa6..5363b8ec79c 100644 --- a/scene/resources/bone_map.cpp +++ b/scene/resources/bone_map.cpp @@ -37,7 +37,7 @@ bool BoneMap::_set(const StringName &p_path, const Variant &p_value) { set_skeleton_bone_name(which, p_value); return true; } - return true; + return false; } bool BoneMap::_get(const StringName &p_path, Variant &r_ret) const { @@ -47,7 +47,7 @@ bool BoneMap::_get(const StringName &p_path, Variant &r_ret) const { r_ret = get_skeleton_bone_name(which); return true; } - return true; + return false; } void BoneMap::_get_property_list(List *p_list) const { diff --git a/scene/resources/fog_material.cpp b/scene/resources/fog_material.cpp index aabaa54505b..5e4f1970ee8 100644 --- a/scene/resources/fog_material.cpp +++ b/scene/resources/fog_material.cpp @@ -82,7 +82,7 @@ float FogMaterial::get_edge_fade() const { void FogMaterial::set_density_texture(const Ref &p_texture) { density_texture = p_texture; - RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant(); RS::get_singleton()->material_set_param(_get_material(), "density_texture", tex_rid); } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index d07d9913a6b..67dbf229acf 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -2973,14 +2973,19 @@ void SystemFont::_update_base_font() { continue; } - // If it's a font collection check all faces to match requested style. + // If it's a font collection check all faces to match requested style and name. int best_score = 0; for (int i = 0; i < file->get_face_count(); i++) { + int score = 0; file->set_face_index(0, i); + const String n = file->get_font_name(); + if (n.to_upper() == E.to_upper()) { + score += 80; + } BitField style = file->get_font_style(); int font_weight = file->get_font_weight(); int font_stretch = file->get_font_stretch(); - int score = (20 - Math::abs(font_weight - weight) / 50); + score += (20 - Math::abs(font_weight - weight) / 50); score += (20 - Math::abs(font_stretch - stretch) / 10); if (bool(style & TextServer::FONT_ITALIC) == italic) { score += 30; @@ -2999,7 +3004,7 @@ void SystemFont::_update_base_font() { file->set_face_index(0, face_indeces[0]); // If it's a variable font, apply weight, stretch and italic coordinates to match requested style. - if (best_score != 50) { + if (best_score != 150) { Dictionary ftr = file->get_supported_variation_list(); if (ftr.has(TS->name_to_tag("width"))) { ftr_stretch = stretch; diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 092ba84aa5d..723ce8aa537 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -971,7 +971,7 @@ Vector> ImporterMesh::convex_decompose(const Refvalue; } else { - index = ++vertex_count; + index = vertex_count++; vertex_map[vertex] = index; vertex_w[index] = vertex; } diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 8ebfd887700..2763498d89f 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -1914,7 +1914,7 @@ void BaseMaterial3D::set_texture(TextureParam p_param, const Ref &p_t ERR_FAIL_INDEX(p_param, TEXTURE_MAX); textures[p_param] = p_texture; - RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + Variant rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant(); RS::get_singleton()->material_set_param(_get_material(), shader_names->texture_names[p_param], rid); if (p_texture.is_valid() && p_param == TEXTURE_ALBEDO) { @@ -3147,7 +3147,7 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value) { "flags_use_shadow_to_opacity", "shadow_to_opacity" }, { "flags_no_depth_test", "no_depth_test" }, { "flags_use_point_size", "use_point_size" }, - { "flags_fixed_size", "fixed_Size" }, + { "flags_fixed_size", "fixed_size" }, { "flags_albedo_tex_force_srgb", "albedo_texture_force_srgb" }, { "flags_do_not_receive_shadows", "disable_receive_shadows" }, { "flags_disable_ambient_light", "disable_ambient_light" }, diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 80927570273..c82363ba7c9 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -1684,7 +1684,7 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const { return true; } - return true; + return false; } void ArrayMesh::reset_state() { diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 359c4765a2e..9dcb7cecae4 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -89,7 +89,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } int nc = nodes.size(); - ERR_FAIL_COND_V(nc == 0, nullptr); + ERR_FAIL_COND_V_MSG(nc == 0, nullptr, vformat("Failed to instantiate scene state of \"%s\", node count is 0. Make sure the PackedScene resource is valid.", path)); const StringName *snames = nullptr; int sname_count = names.size(); @@ -170,7 +170,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { Ref sdata = props[n.instance & FLAG_MASK]; ERR_FAIL_COND_V(!sdata.is_valid(), nullptr); node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); - ERR_FAIL_NULL_V(node, nullptr); + ERR_FAIL_NULL_V_MSG(node, nullptr, vformat("Failed to load scene dependency: \"%s\". Make sure the required scene is valid.", sdata->get_path())); } } else if (n.type == TYPE_INSTANTIATED) { diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 476ef67a57e..d6a78cb43fc 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -1118,7 +1118,7 @@ void ParticleProcessMaterial::set_param_texture(Parameter p_param, const Refget_rid() : RID(); + Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant(); switch (p_param) { case PARAM_INITIAL_LINEAR_VELOCITY: { @@ -1201,7 +1201,7 @@ Color ParticleProcessMaterial::get_color() const { void ParticleProcessMaterial::set_color_ramp(const Ref &p_texture) { color_ramp = p_texture; - RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant(); RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, tex_rid); _queue_shader_change(); notify_property_list_changed(); @@ -1213,7 +1213,7 @@ Ref ParticleProcessMaterial::get_color_ramp() const { void ParticleProcessMaterial::set_color_initial_ramp(const Ref &p_texture) { color_initial_ramp = p_texture; - RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant(); RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_initial_ramp, tex_rid); _queue_shader_change(); notify_property_list_changed(); @@ -1256,19 +1256,19 @@ void ParticleProcessMaterial::set_emission_box_extents(Vector3 p_extents) { void ParticleProcessMaterial::set_emission_point_texture(const Ref &p_points) { emission_point_texture = p_points; - RID tex_rid = p_points.is_valid() ? p_points->get_rid() : RID(); + Variant tex_rid = p_points.is_valid() ? Variant(p_points->get_rid()) : Variant(); RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, tex_rid); } void ParticleProcessMaterial::set_emission_normal_texture(const Ref &p_normals) { emission_normal_texture = p_normals; - RID tex_rid = p_normals.is_valid() ? p_normals->get_rid() : RID(); + Variant tex_rid = p_normals.is_valid() ? Variant(p_normals->get_rid()) : Variant(); RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, tex_rid); } void ParticleProcessMaterial::set_emission_color_texture(const Ref &p_colors) { emission_color_texture = p_colors; - RID tex_rid = p_colors.is_valid() ? p_colors->get_rid() : RID(); + Variant tex_rid = p_colors.is_valid() ? Variant(p_colors->get_rid()) : Variant(); RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, tex_rid); _queue_shader_change(); } diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 204987cac9b..f84aba6c8f4 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -1301,7 +1301,11 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const { points.push_back(Vector3(-x, z, 0.0) + center_offset); } normals.push_back(normal); - ADD_TANGENT(1.0, 0.0, 0.0, 1.0); + if (orientation == FACE_X) { + ADD_TANGENT(0.0, 0.0, -1.0, 1.0); + } else { + ADD_TANGENT(1.0, 0.0, 0.0, 1.0); + } uvs.push_back(Vector2(1.0 - u, 1.0 - v)); /* 1.0 - uv to match orientation with Quad */ point++; diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp index 1d4a980a12b..1ad8d0eccc0 100644 --- a/scene/resources/skeleton_modification_2d_ccdik.cpp +++ b/scene/resources/skeleton_modification_2d_ccdik.cpp @@ -60,21 +60,23 @@ bool SkeletonModification2DCCDIK::_set(const StringName &p_path, const Variant & } else if (what == "constraint_in_localspace") { set_ccdik_joint_constraint_in_localspace(which, p_value); } - #ifdef TOOLS_ENABLED - if (what.begins_with("editor_draw_gizmo")) { + else if (what.begins_with("editor_draw_gizmo")) { set_ccdik_joint_editor_draw_gizmo(which, p_value); } #endif // TOOLS_ENABLED - - return true; + else { + return false; + } } - #ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { + else if (path.begins_with("editor/draw_gizmo")) { set_editor_draw_gizmo(p_value); } #endif // TOOLS_ENABLED + else { + return false; + } return true; } @@ -104,21 +106,23 @@ bool SkeletonModification2DCCDIK::_get(const StringName &p_path, Variant &r_ret) } else if (what == "constraint_in_localspace") { r_ret = get_ccdik_joint_constraint_in_localspace(which); } - #ifdef TOOLS_ENABLED - if (what.begins_with("editor_draw_gizmo")) { + else if (what.begins_with("editor_draw_gizmo")) { r_ret = get_ccdik_joint_editor_draw_gizmo(which); } #endif // TOOLS_ENABLED - - return true; + else { + return false; + } } - #ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { + else if (path.begins_with("editor/draw_gizmo")) { r_ret = get_editor_draw_gizmo(); } #endif // TOOLS_ENABLED + else { + return false; + } return true; } diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp index 419a9a79893..dd1c4a91d56 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.cpp +++ b/scene/resources/skeleton_modification_2d_fabrik.cpp @@ -51,7 +51,11 @@ bool SkeletonModification2DFABRIK::_set(const StringName &p_path, const Variant set_fabrik_joint_magnet_position(which, p_value); } else if (what == "use_target_rotation") { set_fabrik_joint_use_target_rotation(which, p_value); + } else { + return false; } + } else { + return false; } return true; @@ -73,8 +77,11 @@ bool SkeletonModification2DFABRIK::_get(const StringName &p_path, Variant &r_ret r_ret = get_fabrik_joint_magnet_position(which); } else if (what == "use_target_rotation") { r_ret = get_fabrik_joint_use_target_rotation(which); + } else { + return false; } - return true; + } else { + return false; } return true; } diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp index 65217dabd05..2ace9577e42 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.cpp +++ b/scene/resources/skeleton_modification_2d_jiggle.cpp @@ -57,14 +57,15 @@ bool SkeletonModification2DJiggle::_set(const StringName &p_path, const Variant set_jiggle_joint_use_gravity(which, p_value); } else if (what == "gravity") { set_jiggle_joint_gravity(which, p_value); + } else { + return false; } - return true; + } else if (path == "use_colliders") { + set_use_colliders(p_value); + } else if (path == "collision_mask") { + set_collision_mask(p_value); } else { - if (path == "use_colliders") { - set_use_colliders(p_value); - } else if (path == "collision_mask") { - set_collision_mask(p_value); - } + return false; } return true; } @@ -93,14 +94,15 @@ bool SkeletonModification2DJiggle::_get(const StringName &p_path, Variant &r_ret r_ret = get_jiggle_joint_use_gravity(which); } else if (what == "gravity") { r_ret = get_jiggle_joint_gravity(which); + } else { + return false; } - return true; + } else if (path == "use_colliders") { + r_ret = get_use_colliders(); + } else if (path == "collision_mask") { + r_ret = get_collision_mask(); } else { - if (path == "use_colliders") { - r_ret = get_use_colliders(); - } else if (path == "collision_mask") { - r_ret = get_collision_mask(); - } + return false; } return true; } diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp index 3576164a034..8f6f6bc4ae4 100644 --- a/scene/resources/skeleton_modification_2d_lookat.cpp +++ b/scene/resources/skeleton_modification_2d_lookat.cpp @@ -51,12 +51,14 @@ bool SkeletonModification2DLookAt::_set(const StringName &p_path, const Variant } else if (path.begins_with("additional_rotation")) { set_additional_rotation(Math::deg_to_rad(float(p_value))); } - #ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { + else if (path.begins_with("editor/draw_gizmo")) { set_editor_draw_gizmo(p_value); } #endif // TOOLS_ENABLED + else { + return false; + } return true; } @@ -77,12 +79,14 @@ bool SkeletonModification2DLookAt::_get(const StringName &p_path, Variant &r_ret } else if (path.begins_with("additional_rotation")) { r_ret = Math::rad_to_deg(get_additional_rotation()); } - #ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { + else if (path.begins_with("editor/draw_gizmo")) { r_ret = get_editor_draw_gizmo(); } #endif // TOOLS_ENABLED + else { + return false; + } return true; } diff --git a/scene/resources/skeleton_modification_2d_physicalbones.cpp b/scene/resources/skeleton_modification_2d_physicalbones.cpp index 520b2ce4831..85481cc20f0 100644 --- a/scene/resources/skeleton_modification_2d_physicalbones.cpp +++ b/scene/resources/skeleton_modification_2d_physicalbones.cpp @@ -55,10 +55,10 @@ bool SkeletonModification2DPhysicalBones::_set(const StringName &p_path, const V if (what == "nodepath") { set_physical_bone_node(which, p_value); + return true; } - return true; } - return true; + return false; } bool SkeletonModification2DPhysicalBones::_get(const StringName &p_path, Variant &r_ret) const { @@ -79,10 +79,10 @@ bool SkeletonModification2DPhysicalBones::_get(const StringName &p_path, Variant if (what == "nodepath") { r_ret = get_physical_bone_node(which); + return true; } - return true; } - return true; + return false; } void SkeletonModification2DPhysicalBones::_get_property_list(List *p_list) const { diff --git a/scene/resources/skeleton_modification_2d_stackholder.cpp b/scene/resources/skeleton_modification_2d_stackholder.cpp index 34d31bac8ab..6d4cd290f1b 100644 --- a/scene/resources/skeleton_modification_2d_stackholder.cpp +++ b/scene/resources/skeleton_modification_2d_stackholder.cpp @@ -37,12 +37,14 @@ bool SkeletonModification2DStackHolder::_set(const StringName &p_path, const Var if (path == "held_modification_stack") { set_held_modification_stack(p_value); } - #ifdef TOOLS_ENABLED - if (path == "editor/draw_gizmo") { + else if (path == "editor/draw_gizmo") { set_editor_draw_gizmo(p_value); } #endif // TOOLS_ENABLED + else { + return false; + } return true; } @@ -53,12 +55,14 @@ bool SkeletonModification2DStackHolder::_get(const StringName &p_path, Variant & if (path == "held_modification_stack") { r_ret = get_held_modification_stack(); } - #ifdef TOOLS_ENABLED - if (path == "editor/draw_gizmo") { + else if (path == "editor/draw_gizmo") { r_ret = get_editor_draw_gizmo(); } #endif // TOOLS_ENABLED + else { + return false; + } return true; } diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp index 1e8eb9842ca..d0b56252987 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp @@ -47,14 +47,16 @@ bool SkeletonModification2DTwoBoneIK::_set(const StringName &p_path, const Varia } else if (path == "joint_two_bone2d_node") { set_joint_two_bone2d_node(p_value); } - #ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { + else if (path.begins_with("editor/draw_gizmo")) { set_editor_draw_gizmo(p_value); } else if (path.begins_with("editor/draw_min_max")) { set_editor_draw_min_max(p_value); } #endif // TOOLS_ENABLED + else { + return false; + } return true; } @@ -71,14 +73,16 @@ bool SkeletonModification2DTwoBoneIK::_get(const StringName &p_path, Variant &r_ } else if (path == "joint_two_bone2d_node") { r_ret = get_joint_two_bone2d_node(); } - #ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { + else if (path.begins_with("editor/draw_gizmo")) { r_ret = get_editor_draw_gizmo(); } else if (path.begins_with("editor/draw_min_max")) { r_ret = get_editor_draw_min_max(); } #endif // TOOLS_ENABLED + else { + return false; + } return true; } diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp index 71ddbc0898c..5703185374e 100644 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -49,7 +49,7 @@ bool SkeletonModificationStack2D::_set(const StringName &p_path, const Variant & set_modification(mod_idx, p_value); return true; } - return true; + return false; } bool SkeletonModificationStack2D::_get(const StringName &p_path, Variant &r_ret) const { @@ -60,7 +60,7 @@ bool SkeletonModificationStack2D::_get(const StringName &p_path, Variant &r_ret) r_ret = get_modification(mod_idx); return true; } - return true; + return false; } void SkeletonModificationStack2D::setup() { diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp index dc8545426f5..3b152d11cee 100644 --- a/scene/resources/video_stream.cpp +++ b/scene/resources/video_stream.cpp @@ -172,6 +172,7 @@ Ref VideoStream::instantiate_playback() { void VideoStream::set_file(const String &p_file) { file = p_file; + emit_changed(); } String VideoStream::get_file() { 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 45309d4fd28..16f265ae6e2 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -776,7 +776,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color bool draw_sky = false; bool draw_sky_fog_only = false; // We invert luminance_multiplier for sky so that we can combine it with exposure value. - float sky_energy_multiplier = 1.0 / _render_buffers_get_luminance_multiplier(); + float inverse_luminance_multiplier = 1.0 / _render_buffers_get_luminance_multiplier(); + float sky_energy_multiplier = inverse_luminance_multiplier; Color clear_color = p_default_bg_color; bool keep_color = false; @@ -921,13 +922,19 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color { // regular forward for now Vector c; - c.push_back(clear_color.srgb_to_linear()); // our render buffer - if (rb_data.is_valid()) { - if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { - c.push_back(clear_color.srgb_to_linear()); // our resolve buffer + { + Color cc = clear_color.srgb_to_linear() * inverse_luminance_multiplier; + if (rb_data.is_valid()) { + cc.a = 0; // For transparent viewport backgrounds. } - if (using_subpass_post_process) { - c.push_back(Color()); // our 2D buffer we're copying into + c.push_back(cc); // Our render buffer. + if (rb_data.is_valid()) { + if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + c.push_back(clear_color.srgb_to_linear() * inverse_luminance_multiplier); // Our resolve buffer. + } + if (using_subpass_post_process) { + c.push_back(Color()); // Our 2D buffer we're copying into. + } } } diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index 870da3c321c..b1da6ffbd18 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -307,15 +307,16 @@ RendererCompositorRD::RendererCompositorRD() { uint64_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE); if (rendering_method == "mobile" || textures_per_stage < 48) { - scene = memnew(RendererSceneRenderImplementation::RenderForwardMobile()); if (rendering_method == "forward_plus") { WARN_PRINT_ONCE("Platform supports less than 48 textures per stage which is less than required by the Clustered renderer. Defaulting to Mobile renderer."); } + scene = memnew(RendererSceneRenderImplementation::RenderForwardMobile()); } else if (rendering_method == "forward_plus") { - // default to our high end renderer scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered()); } else { - ERR_FAIL_MSG("Cannot instantiate RenderingDevice-based renderer with renderer type " + rendering_method); + // Fall back to our high end renderer. + ERR_PRINT(vformat("Cannot instantiate RenderingDevice-based renderer with renderer type '%s'. Defaulting to Forward+ renderer.", rendering_method)); + scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered()); } scene->init(); diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 1515ed32f6b..ae15c0acc55 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -418,13 +418,13 @@ static String _get_global_shader_uniform_from_type_and_index(const String &p_buf return "(" + p_buffer + "[" + p_index + "].xyzw)"; } case ShaderLanguage::TYPE_MAT2: { - return "mat2(" + p_buffer + "[" + p_index + "].xy," + p_buffer + "[" + p_index + "+1].xy)"; + return "mat2(" + p_buffer + "[" + p_index + "].xy," + p_buffer + "[" + p_index + "+1u].xy)"; } case ShaderLanguage::TYPE_MAT3: { - return "mat3(" + p_buffer + "[" + p_index + "].xyz," + p_buffer + "[" + p_index + "+1].xyz," + p_buffer + "[" + p_index + "+2].xyz)"; + return "mat3(" + p_buffer + "[" + p_index + "].xyz," + p_buffer + "[" + p_index + "+1u].xyz," + p_buffer + "[" + p_index + "+2u].xyz)"; } case ShaderLanguage::TYPE_MAT4: { - return "mat4(" + p_buffer + "[" + p_index + "].xyzw," + p_buffer + "[" + p_index + "+1].xyzw," + p_buffer + "[" + p_index + "+2].xyzw," + p_buffer + "[" + p_index + "+3].xyzw)"; + return "mat4(" + p_buffer + "[" + p_index + "].xyzw," + p_buffer + "[" + p_index + "+1u].xyzw," + p_buffer + "[" + p_index + "+2u].xyzw," + p_buffer + "[" + p_index + "+3u].xyzw)"; } default: { ERR_FAIL_V("void"); diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp index bab8e9ae4b1..aae42855c4e 100644 --- a/servers/xr_server.cpp +++ b/servers/xr_server.cpp @@ -198,9 +198,7 @@ void XRServer::remove_interface(const Ref &p_interface) { }; ERR_FAIL_COND_MSG(idx == -1, "Interface not found."); - - print_verbose("XR: Removed interface" + p_interface->get_name()); - + print_verbose("XR: Removed interface \"" + p_interface->get_name() + "\""); emit_signal(SNAME("interface_removed"), p_interface->get_name()); interfaces.remove_at(idx); }; diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index a716b13022f..e2156602990 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -1711,7 +1711,7 @@ TEST_CASE("[String] validate_identifier") { CHECK(empty_string.validate_identifier() == "_"); String numeric_only = "12345"; - CHECK(numeric_only.validate_identifier() == "_2345"); + CHECK(numeric_only.validate_identifier() == "_12345"); String name_with_spaces = "Name with spaces"; CHECK(name_with_spaces.validate_identifier() == "Name_with_spaces"); diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 8cfb1893709..3fcb6cc83b9 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -3221,7 +3221,7 @@ TEST_CASE("[SceneTree][TextEdit] search") { TextEdit *text_edit = memnew(TextEdit); SceneTree::get_singleton()->get_root()->add_child(text_edit); - text_edit->set_text("hay needle, hay\nHAY NEEDLE, HAY"); + text_edit->set_text("hay needle, hay\nHAY NEEDLE, HAY\nwordword.word.word"); int length = text_edit->get_line(1).length(); CHECK(text_edit->search("test", 0, 0, 0) == Point2i(-1, -1)); @@ -3253,6 +3253,11 @@ TEST_CASE("[SceneTree][TextEdit] search") { CHECK(text_edit->search("need", TextEdit::SEARCH_WHOLE_WORDS | TextEdit::SEARCH_MATCH_CASE, 0, 0) == Point2i(-1, -1)); CHECK(text_edit->search("need", TextEdit::SEARCH_WHOLE_WORDS | TextEdit::SEARCH_MATCH_CASE | TextEdit::SEARCH_BACKWARDS, 0, 0) == Point2i(-1, -1)); + CHECK(text_edit->search("word", TextEdit::SEARCH_WHOLE_WORDS, 2, 0) == Point2i(9, 2)); + CHECK(text_edit->search("word", TextEdit::SEARCH_WHOLE_WORDS, 2, 10) == Point2i(14, 2)); + CHECK(text_edit->search(".word", TextEdit::SEARCH_WHOLE_WORDS, 2, 0) == Point2i(8, 2)); + CHECK(text_edit->search("word.", TextEdit::SEARCH_WHOLE_WORDS, 2, 0) == Point2i(9, 2)); + ERR_PRINT_OFF; CHECK(text_edit->search("", 0, 0, 0) == Point2i(-1, -1)); CHECK(text_edit->search("needle", 0, -1, 0) == Point2i(-1, -1)); diff --git a/thirdparty/README.md b/thirdparty/README.md index 0d7ebfbdd9e..a751452d0d4 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -507,7 +507,7 @@ Collection of single-file libraries used in Godot components. * License: MIT - `r128.h` * Upstream: https://github.com/fahickman/r128 - * Version: 1.4.4 (cf2e88fc3e7d7dfe99189686f914874cd0bda15e, 2020) + * Version: git (6fc177671c47640d5bb69af10cf4ee91050015a1, 2023) * License: Public Domain or Unlicense - `smaz.{c,h}` * Upstream: https://github.com/antirez/smaz diff --git a/thirdparty/misc/r128.h b/thirdparty/misc/r128.h index a345cc47baf..ff05569ea78 100644 --- a/thirdparty/misc/r128.h +++ b/thirdparty/misc/r128.h @@ -1,5 +1,5 @@ /* -r128.h: 128-bit (64.64) signed fixed-point arithmetic. Version 1.4.4 +r128.h: 128-bit (64.64) signed fixed-point arithmetic. Version 1.6.0 COMPILATION ----------- @@ -127,8 +127,10 @@ extern double r128ToFloat(const R128 *v); // Copy extern void r128Copy(R128 *dst, const R128 *src); -// Negate -extern void r128Neg(R128 *dst, const R128 *src); +// Sign manipulation +extern void r128Neg(R128 *dst, const R128 *v); // -v +extern void r128Abs(R128* dst, const R128* v); // abs(v) +extern void r128Nabs(R128* dst, const R128* v); // -abs(v) // Bitwise operations extern void r128Not(R128 *dst, const R128 *src); // ~a @@ -155,6 +157,7 @@ extern void r128Min(R128 *dst, const R128 *a, const R128 *b); extern void r128Max(R128 *dst, const R128 *a, const R128 *b); extern void r128Floor(R128 *dst, const R128 *v); extern void r128Ceil(R128 *dst, const R128 *v); +extern void r128Round(R128 *dst, const R128 *v); // round to nearest, rounding halfway values away from zero extern int r128IsNeg(const R128 *v); // quick check for < 0 // String conversion @@ -1413,15 +1416,15 @@ void r128FromString(R128 *dst, const char *s, char **endptr) } } - for (--s; s >= exp; --s) { + for (const char *c = s - 1; c >= exp; --c) { R128_U64 digit, unused; - if ('0' <= *s && *s <= '9') { - digit = *s - '0'; - } else if ('a' <= *s && *s <= 'f') { - digit = *s - 'a' + 10; + if ('0' <= *c && *c <= '9') { + digit = *c - '0'; + } else if ('a' <= *c && *c <= 'f') { + digit = *c - 'a' + 10; } else { - digit = *s - 'A' + 10; + digit = *c - 'A' + 10; } lo = r128__udiv128(lo, digit, base, &unused); @@ -1441,7 +1444,11 @@ void r128FromString(R128 *dst, const char *s, char **endptr) R128_S64 r128ToInt(const R128 *v) { R128_ASSERT(v != NULL); - return (R128_S64)v->hi; + if ((R128_S64)v->hi < 0) { + return (R128_S64)v->hi + (v->lo != 0); + } else { + return (R128_S64)v->hi; + } } double r128ToFloat(const R128 *v) @@ -1546,12 +1553,40 @@ void r128Copy(R128 *dst, const R128 *src) R128_DEBUG_SET(dst); } -void r128Neg(R128 *dst, const R128 *src) +void r128Neg(R128 *dst, const R128 *v) { - r128__neg(dst, src); + r128__neg(dst, v); R128_DEBUG_SET(dst); } +void r128Abs(R128* dst, const R128* v) +{ + R128 sign, inv; + + R128_ASSERT(dst != NULL); + R128_ASSERT(v != NULL); + + sign.lo = sign.hi = (R128_U64)(((R128_S64)v->hi) >> 63); + inv.lo = v->lo ^ sign.lo; + inv.hi = v->hi ^ sign.hi; + + r128Sub(dst, &inv, &sign); +} + +void r128Nabs(R128* dst, const R128* v) +{ + R128 sign, inv; + + R128_ASSERT(dst != NULL); + R128_ASSERT(v != NULL); + + sign.lo = sign.hi = (R128_U64)(((R128_S64)v->hi) >> 63); + inv.lo = v->lo ^ sign.lo; + inv.hi = v->hi ^ sign.hi; + + r128Sub(dst, &sign, &inv); +} + void r128Not(R128 *dst, const R128 *src) { R128_ASSERT(dst != NULL); @@ -1643,7 +1678,7 @@ void r128Shl(R128 *dst, const R128 *src, int amount) r[1] = r[0] << (amount - 64); r[0] = 0; } else if (amount) { -# ifdef _M_X64 +# if defined(_M_X64) && !defined(R128_STDC_ONLY) r[1] = __shiftleft128(r[0], r[1], (char) amount); # else r[1] = (r[1] << amount) | (r[0] >> (64 - amount)); @@ -1705,7 +1740,7 @@ void r128Shr(R128 *dst, const R128 *src, int amount) r[2] = r[3] >> (amount - 64); r[3] = 0; } else if (amount) { -#ifdef _M_X64 +#if defined(_M_X64) && !defined(R128_STDC_ONLY) r[2] = __shiftright128(r[2], r[3], (char) amount); #else r[2] = (r[2] >> amount) | (r[3] << (64 - amount)); @@ -2097,11 +2132,7 @@ void r128Floor(R128 *dst, const R128 *v) R128_ASSERT(dst != NULL); R128_ASSERT(v != NULL); - if ((R128_S64)v->hi < 0) { - dst->hi = v->hi - (v->lo != 0); - } else { - dst->hi = v->hi; - } + dst->hi = v->hi; dst->lo = 0; R128_DEBUG_SET(dst); } @@ -2111,11 +2142,17 @@ void r128Ceil(R128 *dst, const R128 *v) R128_ASSERT(dst != NULL); R128_ASSERT(v != NULL); - if ((R128_S64)v->hi > 0) { - dst->hi = v->hi + (v->lo != 0); - } else { - dst->hi = v->hi; - } + dst->hi = v->hi + (v->lo != 0); + dst->lo = 0; + R128_DEBUG_SET(dst); +} + +void r128Round(R128* dst, const R128* v) +{ + R128_ASSERT(dst != NULL); + R128_ASSERT(v != NULL); + + dst->hi = v->hi + (v->lo >= R128_LIT_U64(0x8000000000000000) + (R128_U64)((R128_S64)v->hi < 0)); dst->lo = 0; R128_DEBUG_SET(dst); }