diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e4bd18cac..0811255cec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy` - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.4 + rev: v0.12.0 hooks: - id: ruff args: [--fix] diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index d7e96540f2..4a0606b32f 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -430,7 +430,7 @@ License: CC0-1.0 Files: thirdparty/miniupnpc/* Comment: MiniUPnP Project -Copyright: 2005-2024, Thomas Bernard +Copyright: 2005-2025, Thomas Bernard License: BSD-3-clause Files: thirdparty/minizip/* diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 1a00e63c5b..5db4a36ffd 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -424,7 +424,7 @@ void ProjectSettings::_get_property_list(List *p_list) const { _THREAD_SAFE_METHOD_ RBSet<_VCSort> vclist; - HashMap> setting_overrides; + HashMap> setting_overrides; for (const KeyValue &E : props) { const VariantContainer *v = &E.value; @@ -467,10 +467,11 @@ void ProjectSettings::_get_property_list(List *p_list) const { } int dot = vc.name.rfind_char('.'); - if (dot != -1 && !custom_prop_info.has(vc.name)) { + if (dot != -1) { StringName n = vc.name.substr(0, dot); - if (props.has(n)) { // Property is an override. - setting_overrides[n].append(vc); + if (props.has(n)) { + // Property is an override. + setting_overrides[n].push_back(vc); } else { vclist.insert(vc); } @@ -508,6 +509,12 @@ void ProjectSettings::_get_property_list(List *p_list) const { pi.name = over.name; pi.usage = over.flags; p_list->push_back(pi); + } else if (custom_prop_info.has(base.name)) { + // Fallback to base property info. + PropertyInfo pi = custom_prop_info[base.name]; + pi.name = over.name; + pi.usage = over.flags; + p_list->push_back(pi); } else { p_list->push_back(PropertyInfo(over.type, over.name, PROPERTY_HINT_NONE, "", over.flags)); } @@ -827,7 +834,7 @@ Error ProjectSettings::_load_settings_binary(const String &p_path) { for (uint32_t i = 0; i < count; i++) { uint32_t slen = f->get_32(); CharString cs; - cs.resize(slen + 1); + cs.resize_uninitialized(slen + 1); cs[slen] = 0; f->get_buffer((uint8_t *)cs.ptr(), slen); String key = String::utf8(cs.ptr(), slen); diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 849ca49025..0c0951d1e6 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -682,6 +682,7 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ONESHOT); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GROUP_ENABLE); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INPUT_NAME); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_FILE_PATH); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE); diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp index f6dbdcd0d5..f54516fb31 100644 --- a/core/debugger/remote_debugger_peer.cpp +++ b/core/debugger/remote_debugger_peer.cpp @@ -46,8 +46,10 @@ bool RemoteDebuggerPeerTCP::has_message() { Array RemoteDebuggerPeerTCP::get_message() { MutexLock lock(mutex); - ERR_FAIL_COND_V(!has_message(), Array()); - Array out = in_queue.front()->get(); + List::Element *E = in_queue.front(); + ERR_FAIL_NULL_V_MSG(E, Array(), "No remote debugger messages in queue."); + + Array out = E->get(); in_queue.pop_front(); return out; } @@ -98,11 +100,13 @@ void RemoteDebuggerPeerTCP::_write_out() { while (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED && tcp_client->wait(NetSocket::POLL_TYPE_OUT) == OK) { uint8_t *buf = out_buf.ptrw(); if (out_left <= 0) { - if (out_queue.is_empty()) { - break; // Nothing left to send - } mutex.lock(); - Variant var = out_queue.front()->get(); + List::Element *E = out_queue.front(); + if (!E) { + mutex.unlock(); + break; + } + Variant var = E->get(); out_queue.pop_front(); mutex.unlock(); int size = 0; @@ -165,7 +169,8 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po const int tries = 6; const int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 }; - tcp_client->connect_to_host(ip, port); + Error err = tcp_client->connect_to_host(ip, port); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Remote Debugger: Unable to connect to host '%s:%d'.", p_host, port)); for (int i = 0; i < tries; i++) { tcp_client->poll(); diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 1a20ce3094..988446e4cd 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -1060,7 +1060,7 @@ static void gdextension_string_operator_plus_eq_c32str(GDExtensionStringPtr p_se static GDExtensionInt gdextension_string_resize(GDExtensionStringPtr p_self, GDExtensionInt p_length) { String *self = (String *)p_self; - return (*self).resize(p_length); + return (*self).resize_uninitialized(p_length); } static void gdextension_string_name_new_with_latin1_chars(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents, GDExtensionBool p_is_static) { diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 7484d2b74b..b098cb25fb 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -830,8 +830,12 @@ typedef void (*GDExtensionMainLoopShutdownCallback)(); typedef void (*GDExtensionMainLoopFrameCallback)(); typedef struct { + // Will be called after Godot is started and is fully initialized. GDExtensionMainLoopStartupCallback startup_func; + // Will be called before Godot is shutdown when it is still fully initialized. GDExtensionMainLoopShutdownCallback shutdown_func; + // Will be called for each process frame. This will run after all `_process()` methods on Node, and before `ScriptServer::frame()`. + // This is intended to be the equivalent of `ScriptLanguage::frame()` for GDExtension language bindings that don't use the script API. GDExtensionMainLoopFrameCallback frame_func; } GDExtensionMainLoopCallbacks; diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp index c23fcdb305..3ee44e0441 100644 --- a/core/extension/gdextension_manager.cpp +++ b/core/extension/gdextension_manager.cpp @@ -79,13 +79,6 @@ GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(co emit_signal("extension_unloading", p_extension); #endif - if (level >= 0) { // Already initialized up to some level. - // Deinitialize down from current level. - for (int32_t i = level; i >= GDExtension::INITIALIZATION_LEVEL_CORE; i--) { - p_extension->deinitialize_library(GDExtension::InitializationLevel(i)); - } - } - if (!shutdown_callback_called) { // Extension is unloading before the shutdown callback has been called, // which means the engine hasn't shutdown yet but we want to make sure @@ -95,6 +88,13 @@ GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(co } } + if (level >= 0) { // Already initialized up to some level. + // Deinitialize down from current level. + for (int32_t i = level; i >= GDExtension::INITIALIZATION_LEVEL_CORE; i--) { + p_extension->deinitialize_library(GDExtension::InitializationLevel(i)); + } + } + for (const KeyValue &kv : p_extension->class_icon_paths) { gdextension_class_icon_paths.erase(kv.key); } diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index b507046c5c..0ac8c8cc9d 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -410,7 +410,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = { { "ui_filedialog_show_hidden", TTRC("Show Hidden") }, { "ui_swap_input_direction ", TTRC("Swap Input Direction") }, { "ui_unicode_start", TTRC("Start Unicode Character Input") }, - { "ui_colorpicker_delete_preset", TTRC("Toggle License Notices") }, + { "ui_colorpicker_delete_preset", TTRC("ColorPicker: Delete Preset") }, { "ui_accessibility_drag_and_drop", TTRC("Accessibility: Keyboard Drag and Drop") }, { "", ""} /* clang-format on */ diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index a841d6fb9c..ff6faeb7e2 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -260,7 +260,12 @@ String FileAccess::fix_path(const String &p_path) const { case ACCESS_RESOURCES: { if (ProjectSettings::get_singleton()) { if (r_path.begins_with("uid://")) { - r_path = ResourceUID::uid_to_path(r_path); + ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(r_path); + if (ResourceUID::get_singleton()->has_id(uid)) { + r_path = ResourceUID::get_singleton()->get_id_path(uid); + } else { + r_path.clear(); + } } if (r_path.begins_with("res://")) { @@ -771,7 +776,7 @@ bool FileAccess::store_pascal_string(const String &p_string) { String FileAccess::get_pascal_string() { uint32_t sl = get_32(); CharString cs; - cs.resize(sl + 1); + cs.resize_uninitialized(sl + 1); get_buffer((uint8_t *)cs.ptr(), sl); cs[sl] = 0; diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index e79e40aaad..860893fa85 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -308,7 +308,7 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, for (int i = 0; i < file_count; i++) { uint32_t sl = f->get_32(); CharString cs; - cs.resize(sl + 1); + cs.resize_uninitialized(sl + 1); f->get_buffer((uint8_t *)cs.ptr(), sl); cs[sl] = 0; diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 3dc3efc3b8..f203188c29 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -261,7 +261,7 @@ PackedStringArray IP::_get_local_addresses() const { List ip_addresses; get_local_addresses(&ip_addresses); for (const IPAddress &E : ip_addresses) { - addresses.push_back(E); + addresses.push_back(String(E)); } return addresses; diff --git a/core/io/ip_address.h b/core/io/ip_address.h index 03587ac111..e36556080d 100644 --- a/core/io/ip_address.h +++ b/core/io/ip_address.h @@ -91,7 +91,7 @@ public: const uint8_t *get_ipv6() const; void set_ipv6(const uint8_t *p_buf); - operator String() const; + explicit operator String() const; IPAddress(const String &p_string); IPAddress(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d, bool is_v6 = false); IPAddress() { clear(); } diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index dba85f8bb1..4dccda9555 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -70,7 +70,7 @@ Error PacketPeerUDP::leave_multicast_group(IPAddress p_multi_address, const Stri } String PacketPeerUDP::_get_packet_ip() const { - return get_packet_address(); + return String(get_packet_address()); } Error PacketPeerUDP::_set_dest_address(const String &p_address, int p_port) { diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 8976535676..1702dad3fd 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -113,6 +113,12 @@ Error PCKPacker::pck_start(const String &p_pck_path, int p_alignment, const Stri file->store_32(0); // Reserved. } + // Align for first file. + int pad = _get_pad(alignment, file->get_position()); + for (int i = 0; i < pad; i++) { + file->store_8(0); + } + file_base = file->get_position(); file->seek(file_base_ofs); file->store_64(file_base); // Update files base. diff --git a/core/io/plist.cpp b/core/io/plist.cpp index 31b1b287b5..bb7201d949 100644 --- a/core/io/plist.cpp +++ b/core/io/plist.cpp @@ -555,7 +555,7 @@ Ref PList::read_bplist_obj(Ref p_file, uint64_t p_offset_ marker_size = read_bplist_var_size_int(p_file, std::pow(2, ext)); } node->data_type = PL_NODE_TYPE_STRING; - node->data_string.resize(marker_size + 1); + node->data_string.resize_uninitialized(marker_size + 1); p_file->get_buffer(reinterpret_cast(node->data_string.ptrw()), marker_size); } break; case 0x60: { @@ -564,7 +564,7 @@ Ref PList::read_bplist_obj(Ref p_file, uint64_t p_offset_ marker_size = read_bplist_var_size_int(p_file, std::pow(2, ext)); } Char16String cs16; - cs16.resize(marker_size + 1); + cs16.resize_uninitialized(marker_size + 1); for (uint64_t i = 0; i < marker_size; i++) { cs16[i] = BSWAP16(p_file->get_16()); } diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 39969e554b..abd7fadd7e 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -145,7 +145,7 @@ String Resource::generate_scene_unique_id() { static constexpr uint32_t char_count = ('z' - 'a'); static constexpr uint32_t base = char_count + ('9' - '0'); String id; - id.resize(characters + 1); + id.resize_uninitialized(characters + 1); char32_t *ptr = id.ptrw(); for (uint32_t i = 0; i < characters; i++) { uint32_t c = random_num % base; diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 315bdeca1d..be48a07734 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -38,6 +38,7 @@ #include "core/io/missing_resource.h" #include "core/object/script_language.h" #include "core/version.h" +#include "scene/property_utils.h" #include "scene/resources/packed_scene.h" //#define print_bl(m_what) print_line(m_what) @@ -2286,7 +2287,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Refget_class(), F.name); + bool is_script = F.name == CoreStringName(script); + Variant default_value = is_script ? Variant() : PropertyUtils::get_property_default_value(E.ptr(), F.name); if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, p.value, default_value))) { continue; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 5cf611200b..1d6f701c51 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -341,8 +341,8 @@ Ref ResourceLoader::_load(const String &p_path, const String &p_origin } } #endif - ERR_FAIL_COND_V_MSG(found, Ref(), - vformat("Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once.", p_path)); + + ERR_FAIL_COND_V_MSG(found, Ref(), vformat("Failed loading resource: %s.", p_path)); #ifdef TOOLS_ENABLED Ref file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); @@ -350,14 +350,14 @@ Ref ResourceLoader::_load(const String &p_path, const String &p_origin if (r_error) { *r_error = ERR_FILE_NOT_FOUND; } - ERR_FAIL_V_MSG(Ref(), vformat("Resource file not found: %s (expected type: %s)", p_path, p_type_hint)); + ERR_FAIL_V_MSG(Ref(), vformat("Resource file not found: %s (expected type: %s)", p_path, !p_type_hint.is_empty() ? p_type_hint : "unknown")); } #endif if (r_error) { *r_error = ERR_FILE_UNRECOGNIZED; } - ERR_FAIL_V_MSG(Ref(), vformat("No loader found for resource: %s (expected type: %s)", p_path, p_type_hint)); + ERR_FAIL_V_MSG(Ref(), vformat("No loader found for resource: %s (expected type: %s)", p_path, !p_type_hint.is_empty() ? p_type_hint : "unknown")); } // This implementation must allow re-entrancy for a task that started awaiting in a deeper stack frame. diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index 7e4a4e6fe4..7de4471a79 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -68,7 +68,7 @@ String ResourceUID::id_to_text(ID p_id) const { // tmp_size + uid:// (6) + 1 for null. String txt; - txt.resize(tmp_size + 7); + txt.resize_uninitialized(tmp_size + 7); char32_t *p = txt.ptrw(); p[0] = 'u'; @@ -275,7 +275,7 @@ Error ResourceUID::load_from_cache(bool p_reset) { int64_t id = f->get_64(); int32_t len = f->get_32(); Cache c; - c.cs.resize(len + 1); + c.cs.resize_uninitialized(len + 1); ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // Out of memory. c.cs[len] = 0; int32_t rl = f->get_buffer((uint8_t *)c.cs.ptrw(), len); @@ -335,7 +335,7 @@ String ResourceUID::get_path_from_cache(Ref &p_cache_file, const Str for (uint32_t i = 0; i < entry_count; i++) { int64_t id = p_cache_file->get_64(); int32_t len = p_cache_file->get_32(); - cs.resize(len + 1); + cs.resize_uninitialized(len + 1); ERR_FAIL_COND_V(cs.size() != len + 1, String()); cs[len] = 0; int32_t rl = p_cache_file->get_buffer((uint8_t *)cs.ptrw(), len); diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index bed2b32252..a1a6f2e1a2 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -483,6 +483,10 @@ Vector AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_ return ret; } + if (!a->enabled) { + return Vector(); + } + Point *begin_point = a; Point *end_point = b; @@ -766,6 +770,10 @@ Vector AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_ return ret; } + if (!a->enabled) { + return Vector(); + } + AStar3D::Point *begin_point = a; AStar3D::Point *end_point = b; diff --git a/core/math/color.cpp b/core/math/color.cpp index ce77ffed72..1c0cf46170 100644 --- a/core/math/color.cpp +++ b/core/math/color.cpp @@ -121,7 +121,7 @@ void _append_hex(float p_val, char32_t *string) { String Color::to_html(bool p_alpha) const { String txt; - txt.resize(p_alpha ? 9 : 7); + txt.resize_uninitialized(p_alpha ? 9 : 7); char32_t *ptr = txt.ptrw(); _append_hex(r, ptr + 0); diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 75dbdd7321..24f74f76c6 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -825,7 +825,7 @@ Expression::ENode *Expression::_parse_expression() { if (!Variant::is_utility_function_vararg(bifunc->func)) { int expected_args = Variant::get_utility_function_argument_count(bifunc->func); if (expected_args != bifunc->arguments.size()) { - _set_error("Builtin func '" + String(bifunc->func) + "' expects " + itos(expected_args) + " arguments."); + _set_error("Builtin func '" + String(bifunc->func) + "' expects " + itos(expected_args) + " argument(s)."); } } diff --git a/core/object/object.h b/core/object/object.h index 633b024855..ed51d2e885 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -97,6 +97,7 @@ enum PropertyHint { PROPERTY_HINT_NO_NODEPATH, /// < this property will not contain a NodePath, regardless of type (Array, Dictionary, List, etc.). Needed for SceneTreeDock. PROPERTY_HINT_GROUP_ENABLE, ///< used to make the property's group checkable. Only use for boolean types. Optional "feature" hint string force hides anything inside when unchecked. PROPERTY_HINT_INPUT_NAME, + PROPERTY_HINT_FILE_PATH, PROPERTY_HINT_MAX, }; diff --git a/core/os/os.cpp b/core/os/os.cpp index e6d1c39a71..79d015359d 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -593,15 +593,20 @@ bool OS::has_feature(const String &p_feature) { } #endif -#ifdef THREADS_ENABLED if (p_feature == "threads") { +#ifdef THREADS_ENABLED return true; - } #else - if (p_feature == "nothreads") { - return true; - } + return false; #endif + } + if (p_feature == "nothreads") { +#ifdef THREADS_ENABLED + return false; +#else + return true; +#endif + } if (_check_internal_feature_support(p_feature)) { return true; diff --git a/core/string/node_path.h b/core/string/node_path.h index 48bf0ecf15..cd6a73eff6 100644 --- a/core/string/node_path.h +++ b/core/string/node_path.h @@ -80,7 +80,7 @@ public: return data->hash_cache; } - operator String() const; + explicit operator String() const; bool is_empty() const; bool operator==(const NodePath &p_path) const; diff --git a/core/string/optimized_translation.cpp b/core/string/optimized_translation.cpp index 986efec724..f9c7fca727 100644 --- a/core/string/optimized_translation.cpp +++ b/core/string/optimized_translation.cpp @@ -84,20 +84,20 @@ void OptimizedTranslation::generate(const Ref &p_from) { if (ps.orig_len != 0) { CharString dst_s; - dst_s.resize(src_s.size()); + dst_s.resize_uninitialized(src_s.size()); int ret = smaz_compress(src_s.get_data(), src_s.size(), dst_s.ptrw(), src_s.size()); if (ret >= src_s.size()) { //if compressed is larger than original, just use original ps.orig_len = src_s.size(); ps.compressed = src_s; } else { - dst_s.resize(ret); + dst_s.resize_uninitialized(ret); //ps.orig_len=; ps.compressed = dst_s; } } else { ps.orig_len = 1; - ps.compressed.resize(1); + ps.compressed.resize_uninitialized(1); ps.compressed[0] = 0; } @@ -258,7 +258,7 @@ StringName OptimizedTranslation::get_message(const StringName &p_src_text, const return String::utf8(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].uncomp_size); } else { CharString uncomp; - uncomp.resize(bucket.elem[idx].uncomp_size + 1); + uncomp.resize_uninitialized(bucket.elem[idx].uncomp_size + 1); smaz_decompress(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].comp_size, uncomp.ptrw(), bucket.elem[idx].uncomp_size); return String::utf8(uncomp.get_data()); } @@ -284,7 +284,7 @@ Vector OptimizedTranslation::get_translated_message_list() const { msgs.push_back(rstr); } else { CharString uncomp; - uncomp.resize(bucket.elem[j].uncomp_size + 1); + uncomp.resize_uninitialized(bucket.elem[j].uncomp_size + 1); smaz_decompress(&sptr[bucket.elem[j].str_offset], bucket.elem[j].comp_size, uncomp.ptrw(), bucket.elem[j].uncomp_size); String rstr = String::utf8(uncomp.get_data()); msgs.push_back(rstr); diff --git a/core/string/string_buffer.h b/core/string/string_buffer.h index 3e0eb3c354..6b75373792 100644 --- a/core/string/string_buffer.h +++ b/core/string/string_buffer.h @@ -125,7 +125,7 @@ StringBuffer &StringBuffer::reserve(int p_ } bool need_copy = string_length > 0 && buffer.is_empty(); - buffer.resize(next_power_of_2((uint32_t)p_size)); + buffer.resize_uninitialized(next_power_of_2((uint32_t)p_size)); if (need_copy) { memcpy(buffer.ptrw(), short_buffer, string_length * sizeof(char32_t)); } @@ -144,7 +144,7 @@ String StringBuffer::as_string() { if (buffer.is_empty()) { return String(short_buffer); } else { - buffer.resize(string_length + 1); + buffer.resize_uninitialized(string_length + 1); return buffer; } } diff --git a/core/string/string_builder.cpp b/core/string/string_builder.cpp index 8f70d022b3..37d878663e 100644 --- a/core/string/string_builder.cpp +++ b/core/string/string_builder.cpp @@ -62,7 +62,7 @@ String StringBuilder::as_string() const { } String string; - string.resize(string_length + 1); + string.resize_uninitialized(string_length + 1); char32_t *buffer = string.ptrw(); int current_position = 0; diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index fb72dde2ff..4b70908103 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -316,99 +316,6 @@ StringName::StringName(const String &p_name, bool p_static) { Table::table[idx] = _data; } -StringName StringName::search(const char *p_name) { - ERR_FAIL_COND_V(!configured, StringName()); - - ERR_FAIL_NULL_V(p_name, StringName()); - if (!p_name[0]) { - return StringName(); - } - - const uint32_t hash = String::hash(p_name); - const uint32_t idx = hash & Table::TABLE_MASK; - - MutexLock lock(Table::mutex); - _Data *_data = Table::table[idx]; - - while (_data) { - // compare hash first - if (_data->hash == hash && _data->name == p_name) { - break; - } - _data = _data->next; - } - - if (_data && _data->refcount.ref()) { -#ifdef DEBUG_ENABLED - if (unlikely(debug_stringname)) { - _data->debug_references++; - } -#endif - - return StringName(_data); - } - - return StringName(); //does not exist -} - -StringName StringName::search(const char32_t *p_name) { - ERR_FAIL_COND_V(!configured, StringName()); - - ERR_FAIL_NULL_V(p_name, StringName()); - if (!p_name[0]) { - return StringName(); - } - - const uint32_t hash = String::hash(p_name); - const uint32_t idx = hash & Table::TABLE_MASK; - - MutexLock lock(Table::mutex); - _Data *_data = Table::table[idx]; - - while (_data) { - // compare hash first - if (_data->hash == hash && _data->name == p_name) { - break; - } - _data = _data->next; - } - - if (_data && _data->refcount.ref()) { - return StringName(_data); - } - - return StringName(); //does not exist -} - -StringName StringName::search(const String &p_name) { - ERR_FAIL_COND_V(p_name.is_empty(), StringName()); - - const uint32_t hash = p_name.hash(); - const uint32_t idx = hash & Table::TABLE_MASK; - - MutexLock lock(Table::mutex); - _Data *_data = Table::table[idx]; - - while (_data) { - // compare hash first - if (_data->hash == hash && _data->name == p_name) { - break; - } - _data = _data->next; - } - - if (_data && _data->refcount.ref()) { -#ifdef DEBUG_ENABLED - if (unlikely(debug_stringname)) { - _data->debug_references++; - } -#endif - return StringName(_data); - } - - return StringName(); //does not exist -} - bool operator==(const String &p_name, const StringName &p_string_name) { return p_string_name.operator==(p_name); } diff --git a/core/string/string_name.h b/core/string/string_name.h index 87745ee471..a0f57dccd9 100644 --- a/core/string/string_name.h +++ b/core/string/string_name.h @@ -136,10 +136,6 @@ public: return String(); } - static StringName search(const char *p_name); - static StringName search(const char32_t *p_name); - static StringName search(const String &p_name); - struct AlphCompare { template _FORCE_INLINE_ bool operator()(const LT &l, const RT &r) const { diff --git a/core/string/translation_domain.cpp b/core/string/translation_domain.cpp index 34a59a7d81..af10d055cc 100644 --- a/core/string/translation_domain.cpp +++ b/core/string/translation_domain.cpp @@ -257,6 +257,20 @@ PackedStringArray TranslationDomain::get_loaded_locales() const { return locales; } +// Translation objects that could potentially be used for the given locale. +HashSet> TranslationDomain::get_potential_translations(const String &p_locale) const { + HashSet> res; + + for (const Ref &E : translations) { + ERR_CONTINUE(E.is_null()); + + if (TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale()) > 0) { + res.insert(E); + } + } + return res; +} + Ref TranslationDomain::get_translation_object(const String &p_locale) const { Ref res; int best_score = 0; diff --git a/core/string/translation_domain.h b/core/string/translation_domain.h index b7bde1b221..ff80dc7103 100644 --- a/core/string/translation_domain.h +++ b/core/string/translation_domain.h @@ -73,6 +73,7 @@ public: StringName get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_context) const; StringName get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const; PackedStringArray get_loaded_locales() const; + HashSet> get_potential_translations(const String &p_locale) const; public: Ref get_translation_object(const String &p_locale) const; diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index ee3f0887de..8bf5850d4d 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -61,34 +61,6 @@ static _FORCE_INLINE_ char32_t lower_case(char32_t c) { return (is_ascii_upper_case(c) ? (c + ('a' - 'A')) : c); } -bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) { - const String &s = p_s; - int beg = CLAMP(p_col, 0, s.length()); - int end = beg; - - if (s[beg] > 32 || beg == s.length()) { - bool symbol = beg < s.length() && is_symbol(s[beg]); - - while (beg > 0 && s[beg - 1] > 32 && (symbol == is_symbol(s[beg - 1]))) { - beg--; - } - while (end < s.length() && s[end + 1] > 32 && (symbol == is_symbol(s[end + 1]))) { - end++; - } - - if (end < s.length()) { - end += 1; - } - - r_beg = beg; - r_end = end; - - return true; - } else { - return false; - } -} - Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path, String &r_fragment) const { // Splits the URL into scheme, host, port, path, fragment. Strip credentials when present. String base = *this; @@ -177,7 +149,7 @@ void String::append_latin1(const Span &p_cstr) { } const int prev_length = length(); - resize(prev_length + p_cstr.size() + 1); // include 0 + resize_uninitialized(prev_length + p_cstr.size() + 1); // include 0 const char *src = p_cstr.ptr(); const char *end = src + p_cstr.size(); @@ -196,7 +168,7 @@ void String::append_utf32(const Span &p_cstr) { } const int prev_length = length(); - resize(prev_length + p_cstr.size() + 1); + resize_uninitialized(prev_length + p_cstr.size() + 1); const char32_t *src = p_cstr.ptr(); const char32_t *end = p_cstr.ptr() + p_cstr.size(); char32_t *dst = ptrw() + prev_length; @@ -225,7 +197,7 @@ void String::append_utf32(const Span &p_cstr) { // p_length <= p_char strlen // p_char is a valid UTF32 string void String::copy_from_unchecked(const char32_t *p_char, const int p_length) { - resize(p_length + 1); // + 1 for \0 + resize_uninitialized(p_length + 1); // + 1 for \0 char32_t *dst = ptrw(); memcpy(dst, p_char, p_length * sizeof(char32_t)); *(dst + p_length) = _null; @@ -1367,7 +1339,7 @@ String String::join(const Vector &parts) const { new_size += 1; String ret; - ret.resize(new_size); + ret.resize_uninitialized(new_size); char32_t *ret_ptrw = ret.ptrw(); const char32_t *this_ptr = ptr(); @@ -1406,7 +1378,7 @@ String String::to_upper() const { } String upper; - upper.resize(size()); + upper.resize_uninitialized(size()); const char32_t *old_ptr = ptr(); char32_t *upper_ptrw = upper.ptrw(); @@ -1425,7 +1397,7 @@ String String::to_lower() const { } String lower; - lower.resize(size()); + lower.resize_uninitialized(size()); const char32_t *old_ptr = ptr(); char32_t *lower_ptrw = lower.ptrw(); @@ -1551,7 +1523,7 @@ String String::num_int64(int64_t p_num, int base, bool capitalize_hex) { chars++; } String s; - s.resize(chars + 1); + s.resize_uninitialized(chars + 1); char32_t *c = s.ptrw(); c[chars] = 0; n = p_num; @@ -1586,7 +1558,7 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) { } while (n); String s; - s.resize(chars + 1); + s.resize_uninitialized(chars + 1); char32_t *c = s.ptrw(); c[chars] = 0; n = p_num; @@ -1677,7 +1649,7 @@ String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) { static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; String ret; - ret.resize(p_len * 2 + 1); + ret.resize_uninitialized(p_len * 2 + 1); char32_t *ret_ptrw = ret.ptrw(); for (int i = 0; i < p_len; i++) { @@ -1708,7 +1680,7 @@ Vector String::hex_decode() const { Vector out; int len = length() / 2; - out.resize(len); + out.resize_uninitialized(len); uint8_t *out_ptrw = out.ptrw(); for (int i = 0; i < len; i++) { char32_t c; @@ -1734,7 +1706,7 @@ CharString String::ascii(bool p_allow_extended) const { } CharString cs; - cs.resize(size()); + cs.resize_uninitialized(size()); char *cs_ptrw = cs.ptrw(); const char32_t *this_ptr = ptr(); @@ -1757,7 +1729,7 @@ Error String::append_ascii(const Span &p_range) { } const int prev_length = length(); - resize(prev_length + p_range.size() + 1); // Include \0 + resize_uninitialized(prev_length + p_range.size() + 1); // Include \0 const char *src = p_range.ptr(); const char *end = src + p_range.size(); @@ -1802,7 +1774,7 @@ Error String::append_utf8(const char *p_utf8, int p_len, bool p_skip_cr) { const int prev_length = length(); // If all utf8 characters maps to ASCII, then the max size will be p_len, and we add +1 for the null termination. - resize(prev_length + p_len + 1); + resize_uninitialized(prev_length + p_len + 1); char32_t *dst = ptrw() + prev_length; Error result = Error::OK; @@ -1948,7 +1920,7 @@ Error String::append_utf8(const char *p_utf8, int p_len, bool p_skip_cr) { } (*dst++) = 0; - resize(prev_length + dst - ptr()); + resize_uninitialized(dst - ptr()); return result; } @@ -1961,7 +1933,7 @@ CharString String::utf8(Vector *r_ch_length_map) const { uint8_t *map_ptr = nullptr; if (r_ch_length_map) { - r_ch_length_map->resize(l); + r_ch_length_map->resize_uninitialized(l); map_ptr = r_ch_length_map->ptrw(); } @@ -1999,7 +1971,7 @@ CharString String::utf8(Vector *r_ch_length_map) const { return utf8s; } - utf8s.resize(fl + 1); + utf8s.resize_uninitialized(fl + 1); uint8_t *cdst = (uint8_t *)utf8s.get_data(); #define APPEND_CHAR(m_c) *(cdst++) = m_c @@ -2126,7 +2098,7 @@ Error String::append_utf16(const char16_t *p_utf16, int p_len, bool p_default_li } const int prev_length = length(); - resize(prev_length + str_size + 1); + resize_uninitialized(prev_length + str_size + 1); char32_t *dst = ptrw() + prev_length; dst[str_size] = 0; @@ -2196,7 +2168,7 @@ Char16String String::utf16() const { return utf16s; } - utf16s.resize(fl + 1); + utf16s.resize_uninitialized(fl + 1); uint16_t *cdst = (uint16_t *)utf16s.get_data(); #define APPEND_CHAR(m_c) *(cdst++) = m_c @@ -2849,7 +2821,7 @@ Vector String::md5_buffer() const { CryptoCore::md5((unsigned char *)cs.ptr(), cs.length(), hash); Vector ret; - ret.resize(16); + ret.resize_uninitialized(16); uint8_t *ret_ptrw = ret.ptrw(); for (int i = 0; i < 16; i++) { ret_ptrw[i] = hash[i]; @@ -2863,7 +2835,7 @@ Vector String::sha1_buffer() const { CryptoCore::sha1((unsigned char *)cs.ptr(), cs.length(), hash); Vector ret; - ret.resize(20); + ret.resize_uninitialized(20); uint8_t *ret_ptrw = ret.ptrw(); for (int i = 0; i < 20; i++) { ret_ptrw[i] = hash[i]; @@ -2878,7 +2850,7 @@ Vector String::sha256_buffer() const { CryptoCore::sha256((unsigned char *)cs.ptr(), cs.length(), hash); Vector ret; - ret.resize(32); + ret.resize_uninitialized(32); uint8_t *ret_ptrw = ret.ptrw(); for (int i = 0; i < 32; i++) { ret_ptrw[i] = hash[i]; @@ -2896,7 +2868,7 @@ String String::insert(int p_at_pos, const String &p_string) const { } String ret; - ret.resize(length() + p_string.length() + 1); + ret.resize_uninitialized(length() + p_string.length() + 1); char32_t *ret_ptrw = ret.ptrw(); const char32_t *this_ptr = ptr(); @@ -2960,7 +2932,7 @@ String String::remove_char(char32_t p_char) const { // If we found at least one occurrence of `char`, create new string, allocating enough space for the current length minus one. String new_string; - new_string.resize(len); + new_string.resize_uninitialized(len); char32_t *new_ptr = new_string.ptrw(); // Copy part of input before `char`. @@ -2980,7 +2952,7 @@ String String::remove_char(char32_t p_char) const { new_ptr[new_size] = _null; // Shrink new string to fit. - new_string.resize(new_size + 1); + new_string.resize_uninitialized(new_size + 1); return new_string; } @@ -3015,7 +2987,7 @@ static String _remove_chars_common(const String &p_this, const T *p_chars, int p // If we found at least one occurrence of `chars`, create new string, allocating enough space for the current length minus one. String new_string; - new_string.resize(len); + new_string.resize_uninitialized(len); char32_t *new_ptr = new_string.ptrw(); // Copy part of input before `char`. @@ -3035,7 +3007,7 @@ static String _remove_chars_common(const String &p_this, const T *p_chars, int p new_ptr[new_size] = 0; // Shrink new string to fit. - new_string.resize(new_size + 1); + new_string.resize_uninitialized(new_size + 1); return new_string; } @@ -3749,7 +3721,7 @@ Vector String::bigrams() const { if (n_pairs <= 0) { return b; } - b.resize(n_pairs); + b.resize_initialized(n_pairs); String *b_ptrw = b.ptrw(); for (int i = 0; i < n_pairs; i++) { b_ptrw[i] = substr(i, 2); @@ -3895,7 +3867,7 @@ static String _replace_common(const String &p_this, const String &p_key, const S const int with_length = p_with.length(); const int old_length = p_this.length(); - new_string.resize(old_length + int(found.size()) * (with_length - key_length) + 1); + new_string.resize_uninitialized(old_length + int(found.size()) * (with_length - key_length) + 1); char32_t *new_ptrw = new_string.ptrw(); const char32_t *old_ptr = p_this.ptr(); @@ -3954,7 +3926,7 @@ static String _replace_common(const String &p_this, char const *p_key, char cons const int with_length = with_string.length(); const int old_length = p_this.length(); - new_string.resize(old_length + int(found.size()) * (with_length - key_length) + 1); + new_string.resize_uninitialized(old_length + int(found.size()) * (with_length - key_length) + 1); char32_t *new_ptrw = new_string.ptrw(); const char32_t *old_ptr = p_this.ptr(); @@ -4000,7 +3972,7 @@ String String::replace_first(const String &p_key, const String &p_with) const { const int with_length = p_with.length(); String new_string; - new_string.resize(old_length + (with_length - key_length) + 1); + new_string.resize_uninitialized(old_length + (with_length - key_length) + 1); char32_t *new_ptrw = new_string.ptrw(); const char32_t *old_ptr = ptr(); @@ -4038,7 +4010,7 @@ String String::replace_first(const char *p_key, const char *p_with) const { const int with_length = strlen(p_with); String new_string; - new_string.resize(old_length + (with_length - key_length) + 1); + new_string.resize_uninitialized(old_length + (with_length - key_length) + 1); char32_t *new_ptrw = new_string.ptrw(); const char32_t *old_ptr = ptr(); @@ -4093,7 +4065,7 @@ String String::replace_char(char32_t p_key, char32_t p_with) const { // If we found at least one occurrence of `key`, create new string. String new_string; - new_string.resize(len + 1); + new_string.resize_uninitialized(len + 1); char32_t *new_ptr = new_string.ptrw(); // Copy part of input before `key`. @@ -4146,7 +4118,7 @@ static String _replace_chars_common(const String &p_this, const T *p_keys, int p // If we found at least one occurrence of `keys`, create new string. String new_string; - new_string.resize(len + 1); + new_string.resize_uninitialized(len + 1); char32_t *new_ptr = new_string.ptrw(); // Copy part of input before `key`. @@ -4198,7 +4170,7 @@ String String::repeat(int p_count) const { int len = length(); String new_string = *this; - new_string.resize(p_count * len + 1); + new_string.resize_uninitialized(p_count * len + 1); char32_t *dst = new_string.ptrw(); int offset = 1; @@ -4218,7 +4190,7 @@ String String::reverse() const { return *this; } String new_string; - new_string.resize(len + 1); + new_string.resize_uninitialized(len + 1); const char32_t *src = ptr(); char32_t *dst = new_string.ptrw(); @@ -4911,7 +4883,7 @@ String String::xml_unescape() const { if (len == 0) { return String(); } - str.resize(len + 1); + str.resize_uninitialized(len + 1); char32_t *str_ptrw = str.ptrw(); _xml_unescape(get_data(), l, str_ptrw); str_ptrw[len] = 0; @@ -5882,7 +5854,7 @@ Vector String::to_ascii_buffer() const { Vector retval; size_t len = charstr.length(); - retval.resize(len); + retval.resize_uninitialized(len); uint8_t *w = retval.ptrw(); memcpy(w, charstr.ptr(), len); @@ -5898,7 +5870,7 @@ Vector String::to_utf8_buffer() const { Vector retval; size_t len = charstr.length(); - retval.resize(len); + retval.resize_uninitialized(len); uint8_t *w = retval.ptrw(); memcpy(w, charstr.ptr(), len); @@ -5914,7 +5886,7 @@ Vector String::to_utf16_buffer() const { Vector retval; size_t len = charstr.length() * sizeof(char16_t); - retval.resize(len); + retval.resize_uninitialized(len); uint8_t *w = retval.ptrw(); memcpy(w, (const void *)charstr.ptr(), len); @@ -5929,7 +5901,7 @@ Vector String::to_utf32_buffer() const { Vector retval; size_t len = s->length() * sizeof(char32_t); - retval.resize(len); + retval.resize_uninitialized(len); uint8_t *w = retval.ptrw(); memcpy(w, (const void *)s->ptr(), len); diff --git a/core/string/ustring.h b/core/string/ustring.h index d2acf1a198..de52f4d8fa 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -188,7 +188,9 @@ public: _FORCE_INLINE_ operator Span() const { return Span(ptr(), length()); } _FORCE_INLINE_ Span span() const { return Span(ptr(), length()); } - _FORCE_INLINE_ Error resize(int p_size) { return _cowdata.template resize(p_size); } + /// Resizes the string. The given size must include the null terminator. + /// New characters are not initialized, and should be set by the caller. + _FORCE_INLINE_ Error resize_uninitialized(int64_t p_size) { return _cowdata.template resize(p_size); } _FORCE_INLINE_ T get(int p_index) const { return _cowdata.get(p_index); } _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); } @@ -223,7 +225,7 @@ public: } _FORCE_INLINE_ CharStringT &operator+=(T p_char) { const int lhs_len = length(); - resize(lhs_len + 2); + resize_uninitialized(lhs_len + 2); T *dst = ptrw(); dst[lhs_len] = p_char; @@ -235,17 +237,17 @@ public: protected: void copy_from(const T *p_cstr) { if (!p_cstr) { - resize(0); + resize_uninitialized(0); return; } size_t len = strlen(p_cstr); if (len == 0) { - resize(0); + resize_uninitialized(0); return; } - Error err = resize(++len); // include terminating null char. + Error err = resize_uninitialized(++len); // include terminating null char. ERR_FAIL_COND_MSG(err != OK, "Failed to copy C-string."); @@ -322,11 +324,14 @@ public: void remove_at(int p_index) { _cowdata.remove_at(p_index); } - _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ void clear() { resize_uninitialized(0); } _FORCE_INLINE_ char32_t get(int p_index) const { return _cowdata.get(p_index); } _FORCE_INLINE_ void set(int p_index, const char32_t &p_elem) { _cowdata.set(p_index, p_elem); } - Error resize(int p_size) { return _cowdata.resize(p_size); } + + /// Resizes the string. The given size must include the null terminator. + /// New characters are not initialized, and should be set by the caller. + Error resize_uninitialized(int64_t p_size) { return _cowdata.resize(p_size); } _FORCE_INLINE_ const char32_t &operator[](int p_index) const { if (unlikely(p_index == _cowdata.size())) { @@ -784,8 +789,6 @@ _FORCE_INLINE_ String ETRN(const String &p_text, const String &p_text_plural, in return p_text_plural; } -bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end); - template _FORCE_INLINE_ Vector sarray(P... p_args) { return Vector({ String(p_args)... }); diff --git a/core/templates/hash_set.h b/core/templates/hash_set.h index 8df77e970d..5eba3357e8 100644 --- a/core/templates/hash_set.h +++ b/core/templates/hash_set.h @@ -439,6 +439,21 @@ public: _init_from(p_other); } + bool operator==(const HashSet &p_other) const { + if (num_elements != p_other.num_elements) { + return false; + } + for (uint32_t i = 0; i < num_elements; i++) { + if (!p_other.has(keys[i])) { + return false; + } + } + return true; + } + bool operator!=(const HashSet &p_other) const { + return !(*this == p_other); + } + HashSet(uint32_t p_initial_capacity) { // Capacity can't be 0. capacity_index = 0; diff --git a/core/templates/span.h b/core/templates/span.h index c7ba4e6ef3..c8126a60e0 100644 --- a/core/templates/span.h +++ b/core/templates/span.h @@ -53,8 +53,17 @@ public: std::is_same>; _FORCE_INLINE_ constexpr Span() = default; - _FORCE_INLINE_ constexpr Span(const T *p_ptr, uint64_t p_len) : - _ptr(p_ptr), _len(p_len) {} + + _FORCE_INLINE_ Span(const T *p_ptr, uint64_t p_len) : + _ptr(p_ptr), _len(p_len) { +#ifdef DEBUG_ENABLED + // TODO In c++20, make this check run only in non-consteval, and make this constructor constexpr. + if (_ptr == nullptr && _len > 0) { + ERR_PRINT("Internal bug, please report: Span was created from nullptr with size > 0. Recovering by using size = 0."); + _len = 0; + } +#endif + } // Allows creating Span directly from C arrays and string literals. template @@ -84,6 +93,11 @@ public: _FORCE_INLINE_ constexpr const T *begin() const { return _ptr; } _FORCE_INLINE_ constexpr const T *end() const { return _ptr + _len; } + template + _FORCE_INLINE_ constexpr Span reinterpret() const { + return Span(reinterpret_cast(_ptr), _len * sizeof(T) / sizeof(T1)); + } + // Algorithms. constexpr int64_t find(const T &p_val, uint64_t p_from = 0) const; constexpr int64_t rfind(const T &p_val, uint64_t p_from) const; diff --git a/core/templates/tuple.h b/core/templates/tuple.h index 6167e2f8dc..5f34d9d996 100644 --- a/core/templates/tuple.h +++ b/core/templates/tuple.h @@ -44,7 +44,7 @@ // recursion. So: float value; int value; etc. // // This works by splitting up the parameter pack for each step in the recursion minus the first. -// so the the first step creates the "T value" from the first template parameter. +// so the first step creates the "T value" from the first template parameter. // any further template arguments end up in "Rest", which we then use to instantiate a new // tuple, but now minus the first argument. To write this all out: // diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index e6d65fad5a..59d7002f23 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -1644,7 +1644,7 @@ String Variant::stringify(int recursion_count) const { case STRING_NAME: return operator StringName(); case NODE_PATH: - return operator NodePath(); + return String(operator NodePath()); case COLOR: return String(operator Color()); case DICTIONARY: { @@ -3503,9 +3503,9 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method, err_text = "Cannot convert argument " + itos(errorarg + 1) + " from [missing argptr, type unknown] to " + Variant::get_type_name(Variant::Type(ce.expected)); } } else if (ce.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { - err_text = "Method expected " + itos(ce.expected) + " arguments, but called with " + itos(p_argcount); + err_text = "Method expected " + itos(ce.expected) + " argument(s), but called with " + itos(p_argcount); } else if (ce.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { - err_text = "Method expected " + itos(ce.expected) + " arguments, but called with " + itos(p_argcount); + err_text = "Method expected " + itos(ce.expected) + " argument(s), but called with " + itos(p_argcount); } else if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) { err_text = "Method not found"; } else if (ce.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) { diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index ea6bb698c5..938dd89184 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -687,7 +687,7 @@ struct _VariantCall { if (p_instance->size() > 0) { const uint8_t *r = p_instance->ptr(); CharString cs; - cs.resize(p_instance->size() + 1); + cs.resize_uninitialized(p_instance->size() + 1); memcpy(cs.ptrw(), r, p_instance->size()); cs[(int)p_instance->size()] = 0; @@ -1014,6 +1014,62 @@ struct _VariantCall { return dest; } + static PackedVector2Array func_PackedByteArray_decode_vector2_array(PackedByteArray *p_instance) { + uint64_t size = p_instance->size(); + PackedVector2Array dest; + if (size == 0) { + return dest; + } + ERR_FAIL_COND_V_MSG(size % sizeof(Vector2), dest, "PackedByteArray size must be a multiple of " + itos(sizeof(Vector2)) + " (size of Vector2) to convert to PackedVector2Array."); + const uint8_t *r = p_instance->ptr(); + dest.resize(size / sizeof(Vector2)); + ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed. + memcpy(dest.ptrw(), r, dest.size() * sizeof(Vector2)); + return dest; + } + + static PackedVector3Array func_PackedByteArray_decode_vector3_array(PackedByteArray *p_instance) { + uint64_t size = p_instance->size(); + PackedVector3Array dest; + if (size == 0) { + return dest; + } + ERR_FAIL_COND_V_MSG(size % sizeof(Vector3), dest, "PackedByteArray size must be a multiple of " + itos(sizeof(Vector3)) + " (size of Vector3) to convert to PackedVector3Array."); + const uint8_t *r = p_instance->ptr(); + dest.resize(size / sizeof(Vector3)); + ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed. + memcpy(dest.ptrw(), r, dest.size() * sizeof(Vector3)); + return dest; + } + + static PackedVector4Array func_PackedByteArray_decode_vector4_array(PackedByteArray *p_instance) { + uint64_t size = p_instance->size(); + PackedVector4Array dest; + if (size == 0) { + return dest; + } + ERR_FAIL_COND_V_MSG(size % sizeof(Vector4), dest, "PackedByteArray size must be a multiple of " + itos(sizeof(Vector4)) + " (size of Vector4) to convert to PackedVector4Array."); + const uint8_t *r = p_instance->ptr(); + dest.resize(size / sizeof(Vector4)); + ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed. + memcpy(dest.ptrw(), r, dest.size() * sizeof(Vector4)); + return dest; + } + + static PackedColorArray func_PackedByteArray_decode_color_array(PackedByteArray *p_instance) { + uint64_t size = p_instance->size(); + PackedColorArray dest; + if (size == 0) { + return dest; + } + ERR_FAIL_COND_V_MSG(size % sizeof(Color), dest, "PackedByteArray size must be a multiple of " + itos(sizeof(Color)) + " (size of Color variant) to convert to PackedColorArray."); + const uint8_t *r = p_instance->ptr(); + dest.resize(size / sizeof(Color)); + ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed. + memcpy(dest.ptrw(), r, dest.size() * sizeof(Color)); + return dest; + } + static void func_PackedByteArray_encode_u8(PackedByteArray *p_instance, int64_t p_offset, int64_t p_value) { uint64_t size = p_instance->size(); ERR_FAIL_COND(p_offset < 0 || p_offset > int64_t(size) - 1); @@ -1898,7 +1954,7 @@ static void _register_variant_builtin_methods_string() { bind_static_method(String, num, sarray("number", "decimals"), varray(-1)); bind_static_method(String, num_int64, sarray("number", "base", "capitalize_hex"), varray(10, false)); bind_static_method(String, num_uint64, sarray("number", "base", "capitalize_hex"), varray(10, false)); - bind_static_method(String, chr, sarray("char"), varray()); + bind_static_method(String, chr, sarray("code"), varray()); bind_static_method(String, humanize_size, sarray("size"), varray()); /* StringName */ @@ -2562,6 +2618,10 @@ static void _register_variant_builtin_methods_array() { bind_function(PackedByteArray, to_int64_array, _VariantCall::func_PackedByteArray_decode_s64_array, sarray(), varray()); bind_function(PackedByteArray, to_float32_array, _VariantCall::func_PackedByteArray_decode_float_array, sarray(), varray()); bind_function(PackedByteArray, to_float64_array, _VariantCall::func_PackedByteArray_decode_double_array, sarray(), varray()); + bind_function(PackedByteArray, to_vector2_array, _VariantCall::func_PackedByteArray_decode_vector2_array, sarray(), varray()); + bind_function(PackedByteArray, to_vector3_array, _VariantCall::func_PackedByteArray_decode_vector3_array, sarray(), varray()); + bind_function(PackedByteArray, to_vector4_array, _VariantCall::func_PackedByteArray_decode_vector4_array, sarray(), varray()); + bind_function(PackedByteArray, to_color_array, _VariantCall::func_PackedByteArray_decode_color_array, sarray(), varray()); bind_functionnc(PackedByteArray, bswap16, _VariantCall::func_PackedByteArray_bswap16, sarray("offset", "count"), varray(0, -1)); bind_functionnc(PackedByteArray, bswap32, _VariantCall::func_PackedByteArray_bswap32, sarray("offset", "count"), varray(0, -1)); diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index e4d5d864df..5292bb3c94 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -823,7 +823,7 @@ struct VariantInternalAccessor { template <> struct VariantInternalAccessor { static _FORCE_INLINE_ IPAddress get(const Variant *v) { return IPAddress(*VariantInternal::get_string(v)); } - static _FORCE_INLINE_ void set(Variant *v, IPAddress p_value) { *VariantInternal::get_string(v) = p_value; } + static _FORCE_INLINE_ void set(Variant *v, IPAddress p_value) { *VariantInternal::get_string(v) = String(p_value); } }; template <> diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 5b07b633c2..31b8e42121 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2893,7 +2893,7 @@ Hints that an integer property is a bitmask using the optionally named avoidance layers. - Hints that a [String] property is a path to a file. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code]. + Hints that a [String] property is a path to a file. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code]. By default the file will be stored as UID whenever available. You can use [ResourceUID] methods to convert it back to path. For storing a raw path, use [constant PROPERTY_HINT_FILE_PATH]. Hints that a [String] property is a path to a directory. Editing it will show a file dialog for picking the path. @@ -3034,7 +3034,7 @@ Hints that a property will be changed on its own after setting, such as [member AudioStreamPlayer.playing] or [member GPUParticles3D.emitting]. - Hints that a boolean property will enable the feature associated with the group that it occurs in. Only works within a group or subgroup. Use the optional hint string [code]"feature"[/code] when the group only has variables that are meaningful when the feature is enabled. + Hints that a boolean property will enable the feature associated with the group that it occurs in. Only works within a group or subgroup. Use the optional hint string [code]"feature"[/code] when the group only has properties that are meaningful when the feature is enabled. [b]Note:[/b] The [code]"feature"[/code] hint string does not modify or reset any values. @@ -3042,7 +3042,10 @@ - If it contains [code]"show_builtin"[/code], built-in input actions are included in the selection. - If it contains [code]"loose_mode"[/code], loose mode is enabled. This allows inserting any action name even if it's not present in the input map. - + + Like [constant PROPERTY_HINT_FILE], but the property is stored as a raw path, not UID. That means the reference will be broken if you move the file. Consider using [constant PROPERTY_HINT_FILE] when possible. + + Represents the size of the [enum PropertyHint] enum. diff --git a/doc/classes/AcceptDialog.xml b/doc/classes/AcceptDialog.xml index a5b589842a..e676b4cd09 100644 --- a/doc/classes/AcceptDialog.xml +++ b/doc/classes/AcceptDialog.xml @@ -15,7 +15,8 @@ - Adds a button with label [param text] and a custom [param action] to the dialog and returns the created button. [param action] will be passed to the [signal custom_action] signal when pressed. + Adds a button with label [param text] and a custom [param action] to the dialog and returns the created button. + If [param action] is not empty, pressing the button will emit the [signal custom_action] signal with the specified action string. If [code]true[/code], [param right] will place the button to the right of any sibling buttons. You can use [method remove_button] method to remove a button created with this method from the dialog. @@ -97,7 +98,7 @@ - Emitted when a custom button is pressed. See [method add_button]. + Emitted when a custom button with an action is pressed. See [method add_button]. diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index c0027f471a..bc83de1bbe 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -687,7 +687,7 @@ [b]Note:[/b] Length is not delimited by the last key, as this one may be before or after the end to ensure correct interpolation and looping. - Determines the behavior of both ends of the animation timeline during animation playback. This is used for correct interpolation of animation cycles, and for hinting the player that it must restart the animation. + Determines the behavior of both ends of the animation timeline during animation playback. This indicates whether and how the animation should be restarted, and is also used to correctly interpolate animation cycles. The animation step value. diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml index fe5274f42a..2d40cfa9ab 100644 --- a/doc/classes/AudioStream.xml +++ b/doc/classes/AudioStream.xml @@ -91,7 +91,7 @@ - Returns the length of the audio stream in seconds. + Returns the length of the audio stream in seconds. If this stream is an [AudioStreamRandomizer], returns the length of the last played stream. If this stream has an indefinite length (such as for [AudioStreamGenerator] and [AudioStreamMicrophone]), returns [code]0.0[/code]. diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index 4e74dfec36..cf6d219198 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -159,6 +159,10 @@ Determines when depth rendering takes place. See also [member transparency]. + + Determines which comparison operator is used when testing depth. See [enum DepthTest]. + [b]Note:[/b] Changing [member depth_test] to a non-default value only has a visible effect when used on a transparent material, or a material that has [member depth_draw_mode] set to [constant DEPTH_DRAW_DISABLED]. + Texture that specifies the color of the detail overlay. [member detail_albedo]'s alpha channel is used as a mask, even when the material is opaque. To use a dedicated texture as a mask, see [member detail_mask]. [b]Note:[/b] [member detail_albedo] is [i]not[/i] modulated by [member albedo_color]. @@ -374,6 +378,24 @@ The method for rendering the specular blob. [b]Note:[/b] [member specular_mode] only applies to the specular blob. It does not affect specular reflections from the sky, screen-space reflections, [VoxelGI], SDFGI or [ReflectionProbe]s. To disable reflections from these sources as well, set [member metallic_specular] to [code]0.0[/code] instead. + + The primary color of the stencil effect. + + + The comparison operator to use for stencil masking operations. See [enum StencilCompare]. + + + The flags dictating how the stencil operation behaves. See [enum StencilFlags]. + + + The stencil effect mode. See [enum StencilMode]. + + + The outline thickness for [constant STENCIL_MODE_OUTLINE]. + + + The stencil reference value (0-255). Typically a power of 2. + If [code]true[/code], subsurface scattering is enabled. Emulates light that penetrates an object's surface, is scattered, and then emerges. Subsurface scattering quality is controlled by [member ProjectSettings.rendering/environment/subsurface_scattering/subsurface_scattering_quality]. @@ -661,6 +683,12 @@ Objects will not write their depth to the depth buffer, even during the depth prepass (if enabled). + + Depth test will discard the pixel if it is behind other pixels. + + + Depth test will discard the pixel if it is in front of other pixels. Useful for stencil effects. + Default cull mode. The back of the object is culled when not visible. Back face triangles will be culled when facing the camera. This results in only the front side of triangles being drawn. For closed-surface meshes, this means that only the exterior of the mesh will be visible. @@ -818,5 +846,49 @@ Smoothly fades the object out based on the object's distance from the camera using a dithering approach. Dithering discards pixels based on a set pattern to smoothly fade without enabling transparency. On certain hardware, this can be faster than [constant DISTANCE_FADE_PIXEL_ALPHA] and [constant DISTANCE_FADE_PIXEL_DITHER]. + + Disables stencil operations. + + + Stencil preset which applies an outline to the object. + [b]Note:[/b] Requires a [member Material.next_pass] material which will be automatically applied. Any manual changes made to [member Material.next_pass] will be lost when the stencil properties are modified or the scene is reloaded. To safely apply a [member Material.next_pass] material on a material that uses stencil presets, use [member GeometryInstance3D.material_overlay] instead. + + + Stencil preset which shows a silhouette of the object behind walls. + [b]Note:[/b] Requires a [member Material.next_pass] material which will be automatically applied. Any manual changes made to [member Material.next_pass] will be lost when the stencil properties are modified or the scene is reloaded. To safely apply a [member Material.next_pass] material on a material that uses stencil presets, use [member GeometryInstance3D.material_overlay] instead. + + + Enables stencil operations without a preset. + + + The material will only be rendered where it passes a stencil comparison with existing stencil buffer values. See [enum StencilCompare]. + + + The material will write the reference value to the stencil buffer where it passes the depth test. + + + The material will write the reference value to the stencil buffer where it fails the depth test. + + + Always passes the stencil test. + + + Passes the stencil test when the reference value is less than the existing stencil value. + + + Passes the stencil test when the reference value is equal to the existing stencil value. + + + Passes the stencil test when the reference value is less than or equal to the existing stencil value. + + + Passes the stencil test when the reference value is greater than the existing stencil value. + + + Passes the stencil test when the reference value is not equal to the existing stencil value. + + + Passes the stencil test when the reference value is greater than or equal to the existing stencil value. + diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index 20819a8056..c6b39c9941 100644 --- a/doc/classes/CPUParticles2D.xml +++ b/doc/classes/CPUParticles2D.xml @@ -91,7 +91,7 @@ - Enables or disables the given flag. + Enables or disables the given particle flag. diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index 64a1ab6b69..0971bf0522 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -128,7 +128,8 @@ - Sets the camera projection to orthogonal mode (see [constant PROJECTION_ORTHOGONAL]), by specifying a [param size], and the [param z_near] and [param z_far] clip planes in world space units. (As a hint, 2D games often use this projection, with values specified in pixels.) + Sets the camera projection to orthogonal mode (see [constant PROJECTION_ORTHOGONAL]), by specifying a [param size], and the [param z_near] and [param z_far] clip planes in world space units. + As a hint, 3D games that look 2D often use this projection, with [param size] specified in pixels. diff --git a/doc/classes/CameraFeed.xml b/doc/classes/CameraFeed.xml index 4ec59b6d91..407fbb4e62 100644 --- a/doc/classes/CameraFeed.xml +++ b/doc/classes/CameraFeed.xml @@ -67,10 +67,10 @@ - Sets the feed format parameters for the given index in the [member formats] array. Returns [code]true[/code] on success. By default YUYV encoded stream is transformed to FEED_RGB. YUYV encoded stream output format can be changed with [param parameters].output value: - [code]separate[/code] will result in FEED_YCBCR_SEP - [code]grayscale[/code] will result in desaturated FEED_RGB - [code]copy[/code] will result in FEED_YCBCR + Sets the feed format parameters for the given [param index] in the [member formats] array. Returns [code]true[/code] on success. By default, the YUYV encoded stream is transformed to [constant FEED_RGB]. The YUYV encoded stream output format can be changed by setting [param parameters]'s [code]output[/code] entry to one of the following: + - [code]"separate"[/code] will result in [constant FEED_YCBCR_SEP]; + - [code]"grayscale"[/code] will result in desaturated [constant FEED_RGB]; + - [code]"copy"[/code] will result in [constant FEED_YCBCR]. diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 5efea9c3d5..4e9b29cf1f 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -173,7 +173,7 @@ - Draws a textured rectangle region of the multi-channel signed distance field texture at a given position, optionally modulated by a color. See [member FontFile.multichannel_signed_distance_field] for more information and caveats about MSDF font rendering. + Draws a textured rectangle region of the multichannel signed distance field texture at a given position, optionally modulated by a color. See [member FontFile.multichannel_signed_distance_field] for more information and caveats about MSDF font rendering. If [param outline] is positive, each alpha channel value of pixel in region is set to maximum value of true distance in the [param outline] radius. Value of the [param pixel_range] should the same that was used during distance field texture generation. @@ -644,7 +644,8 @@ The filtering mode used to render this [CanvasItem]'s texture(s). - The repeating mode used to render this [CanvasItem]'s texture(s). + The repeating mode used to render this [CanvasItem]'s texture(s). It affects what happens when the texture is sampled outside its extents, for example by setting a [member Sprite2D.region_rect] that is larger than the texture or assigning [Polygon2D] UV points outside the texture. + [b]Note:[/b] [TextureRect] is not affected by [member texture_repeat], as it uses its own texture repeating implementation. If [code]true[/code], this [CanvasItem] will [i]not[/i] inherit its transform from parent [CanvasItem]s. Its draw order will also be changed to make it draw on top of other [CanvasItem]s that do not have [member top_level] set to [code]true[/code]. The [CanvasItem] will effectively act as if it was placed as a child of a bare [Node]. @@ -754,7 +755,7 @@ The [CanvasItem] will inherit the filter from its parent. - The texture does not repeat. + The texture does not repeat. Sampling the texture outside its extents will result in "stretching" of the edge pixels. You can avoid this by ensuring a 1-pixel fully transparent border on each side of the texture. The texture repeats when exceeding the texture's size. diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index b867bbbc50..87fef6d4eb 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -37,7 +37,7 @@ Redot calls this method to test if [param data] from a control's [method _get_drag_data] can be dropped at [param at_position]. [param at_position] is local to this control. This method should only be used to test the data. Process the data in [method _drop_data]. - [b]Note:[/b] If drag was initiated by keyboard shortcut or [method accessibility_drag], [param at_position] is set to [code]Vector2(INFINITY, INFINITY)[/code] and the currently selected item/text position should be used as drop position. + [b]Note:[/b] If the drag was initiated by a keyboard shortcut or [method accessibility_drag], [param at_position] is set to [constant Vector2.INF], and the currently selected item/text position should be used as the drop position. [codeblocks] [gdscript] func _can_drop_data(position, data): @@ -62,7 +62,7 @@ Redot calls this method to pass you the [param data] from a control's [method _get_drag_data] result. Redot first calls [method _can_drop_data] to test if [param data] is allowed to drop at [param at_position] where [param at_position] is local to this control. - [b]Note:[/b] If drag was initiated by keyboard shortcut or [method accessibility_drag], [param at_position] is set to [code]Vector2(INFINITY, INFINITY)[/code] and the currently selected item/text position should be used as drop position. + [b]Note:[/b] If the drag was initiated by a keyboard shortcut or [method accessibility_drag], [param at_position] is set to [constant Vector2.INF], and the currently selected item/text position should be used as the drop position. [codeblocks] [gdscript] func _can_drop_data(position, data): @@ -98,7 +98,7 @@ Redot calls this method to get data that can be dragged and dropped onto controls that expect drop data. Returns [code]null[/code] if there is no data to drag. Controls that want to receive drop data should implement [method _can_drop_data] and [method _drop_data]. [param at_position] is local to this control. Drag may be forced with [method force_drag]. A preview that will follow the mouse that should represent the data can be set with [method set_drag_preview]. A good time to set the preview is in this method. - [b]Note:[/b] If drag was initiated by keyboard shortcut or [method accessibility_drag], [param at_position] is set to [code]Vector2(INFINITY, INFINITY)[/code] and the currently selected item/text position should be used as drop position. + [b]Note:[/b] If the drag was initiated by a keyboard shortcut or [method accessibility_drag], [param at_position] is set to [constant Vector2.INF], and the currently selected item/text position should be used as the drag position. [codeblocks] [gdscript] func _get_drag_data(position): @@ -1002,7 +1002,7 @@ Anchors the top edge of the node to the origin, the center or the end of its parent control. It changes how the top offset updates when the node moves or changes size. You can use one of the [enum Anchor] constants for convenience. - + Toggles if any text should automatically change to its translated version depending on the current locale. diff --git a/doc/classes/DampedSpringJoint2D.xml b/doc/classes/DampedSpringJoint2D.xml index 4bb80c84e5..c1ddebdca7 100644 --- a/doc/classes/DampedSpringJoint2D.xml +++ b/doc/classes/DampedSpringJoint2D.xml @@ -4,7 +4,7 @@ A physics joint that connects two 2D physics bodies with a spring-like force. - A physics joint that connects two 2D physics bodies with a spring-like force. This resembles a spring that always wants to stretch to a given length. + A physics joint that connects two 2D physics bodies with a spring-like force. This behaves like a spring that always wants to stretch to a given length. diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 6c9ac18bf2..60aea0adc7 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -2045,7 +2045,7 @@ Stops synthesis in progress and removes all utterances from the queue. - [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11/Linux), macOS, and Windows. + [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11/Wayland), macOS, and Windows. @@ -2543,7 +2543,7 @@ Display server supports [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url], which is commonly used for inputting Chinese/Japanese/Korean text. This is handled by the operating system, rather than by Redot. [b]Windows, macOS, Linux (X11)[/b] - Display server supports windows can use per-pixel transparency to make windows behind them partially or fully visible. [b]Windows, macOS, Linux (X11/Wayland)[/b] + Display server supports windows can use per-pixel transparency to make windows behind them partially or fully visible. [b]Windows, macOS, Linux (X11/Wayland), Android[/b] Display server supports querying the operating system's display scale factor. This allows automatically detecting the hiDPI display [i]reliably[/i], instead of guessing based on the screen resolution and the display's reported DPI (which might be unreliable due to broken monitor EDID). [b]Windows, Linux (Wayland), macOS[/b] @@ -2856,13 +2856,13 @@ Set scroll offset action, callback argument is set to [Vector2] with the scroll offset. - Set value action action, callback argument is set to [String] or number with the new value. + Set value action, callback argument is set to [String] or number with the new value. Show context menu action, callback argument is not set. - Custom action, callback argument is set to the integer action id. + Custom action, callback argument is set to the integer action ID. Indicates that updates to the live region should not be presented. @@ -3054,6 +3054,7 @@ A single window full screen mode. This mode has less overhead, but only one window can be open on a given screen at a time (opening a child window or application switching will trigger a full screen transition). Full screen window covers the entire display area of a screen and has no border or decorations. The display's video mode is not changed. + [b]Note:[/b] This mode might not work with screen recording software. [b]On Android:[/b] This enables immersive mode. [b]On Windows:[/b] Depending on video driver, full screen transition might cause screens to go black for a moment. [b]On macOS:[/b] A new desktop is used to display the running project. Exclusive full screen mode prevents Dock and Menu from showing up when the mouse pointer is hovering the edge of the screen. @@ -3073,7 +3074,7 @@ The window background can be transparent. [b]Note:[/b] This flag has no effect if [method is_window_transparency_available] returns [code]false[/code]. - [b]Note:[/b] Transparency support is implemented on Linux (X11/Wayland), macOS, and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities. + [b]Note:[/b] Transparency support is implemented on Android, Linux (X11/Wayland), macOS, and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities. The window can't be focused. No-focus window will ignore all input, except mouse clicks. @@ -3097,7 +3098,7 @@ Window is excluded from screenshots taken by [method screen_get_image], [method screen_get_image_rect], and [method screen_get_pixel]. [b]Note:[/b] This flag is implemented on macOS and Windows. - [b]Note:[/b] Setting this flag will [b]NOT[/b] prevent other apps from capturing an image. It should not be used as a security measure. + [b]Note:[/b] Setting this flag will prevent standard screenshot methods from capturing a window image, but does [b]NOT[/b] guarantee that other apps won't be able to capture an image. It should not be used as a DRM or security measure. Signals the window manager that this window is supposed to be an implementation-defined "popup" (usually a floating, borderless, untileable and immovable child window). diff --git a/doc/classes/EditorContextMenuPlugin.xml b/doc/classes/EditorContextMenuPlugin.xml index 8c79221a93..07d7a8e51a 100644 --- a/doc/classes/EditorContextMenuPlugin.xml +++ b/doc/classes/EditorContextMenuPlugin.xml @@ -89,7 +89,14 @@ Context menu of Script editor's script tabs. [method _popup_menu] will be called with the path to the currently edited script, while option callback will receive reference to that script. - The "Create..." submenu of FileSystem dock's context menu, or the "New" section of the main context menu when empty space is clicked. [method _popup_menu] and option callback will be called with the path of the currently selected folder, wrapped in a list. When clicking the empty space, the list of paths for popup method will be empty. + The "Create..." submenu of FileSystem dock's context menu, or the "New" section of the main context menu when empty space is clicked. [method _popup_menu] and option callback will be called with the path of the currently selected folder. When clicking the empty space, the list of paths for popup method will be empty. + [codeblock] + func _popup_menu(paths): + if paths.is_empty(): + add_context_menu_item("New Image File...", create_image) + else: + add_context_menu_item("Image File...", create_image) + [/codeblock] Context menu of Script editor's code editor. [method _popup_menu] will be called with the path to the [CodeEdit] node. You can fetch it using this code: diff --git a/doc/classes/EditorExportPlatform.xml b/doc/classes/EditorExportPlatform.xml index bd62974faf..305f325e39 100644 --- a/doc/classes/EditorExportPlatform.xml +++ b/doc/classes/EditorExportPlatform.xml @@ -260,19 +260,19 @@ Message type for error messages that must be addressed and fail the export. - Flag is set if remotely debugged project is expected to use remote file system. If set, [method gen_export_flags] will add [code]--remote-fs[/code] and [code]--remote-fs-password[/code] (if password is set in the editor settings) command line arguments to the list. + Flag is set if the remotely debugged project is expected to use the remote file system. If set, [method gen_export_flags] will append [code]--remote-fs[/code] and [code]--remote-fs-password[/code] (if [member EditorSettings.filesystem/file_server/password] is defined) command line arguments to the returned list. - Flag is set if remote debug is enabled. If set, [method gen_export_flags] will add [code]--remote-debug[/code] and [code]--breakpoints[/code] (if breakpoints are selected in the script editor or added by the plugin) command line arguments to the list. + Flag is set if remote debug is enabled. If set, [method gen_export_flags] will append [code]--remote-debug[/code] and [code]--breakpoints[/code] (if breakpoints are selected in the script editor or added by the plugin) command line arguments to the returned list. Flag is set if remotely debugged project is running on the localhost. If set, [method gen_export_flags] will use [code]localhost[/code] instead of [member EditorSettings.network/debug/remote_host] as remote debugger host. - Flag is set if "Visible Collision Shapes" remote debug option is enabled. If set, [method gen_export_flags] will add [code]--debug-collisions[/code] command line arguments to the list. + Flag is set if the "Visible Collision Shapes" remote debug option is enabled. If set, [method gen_export_flags] will append the [code]--debug-collisions[/code] command line argument to the returned list. - Flag is set if Visible Navigation" remote debug option is enabled. If set, [method gen_export_flags] will add [code]--debug-navigation[/code] command line arguments to the list. + Flag is set if the "Visible Navigation" remote debug option is enabled. If set, [method gen_export_flags] will append the [code]--debug-navigation[/code] command line argument to the returned list. diff --git a/doc/classes/EditorExportPlatformExtension.xml b/doc/classes/EditorExportPlatformExtension.xml index 98051094d9..5ed3571161 100644 --- a/doc/classes/EditorExportPlatformExtension.xml +++ b/doc/classes/EditorExportPlatformExtension.xml @@ -139,7 +139,7 @@ - Returns platform logo displayed in the export dialog, logo should be 32x32 adjusted to the current editor scale, see [method EditorInterface.get_editor_scale]. + Returns the platform logo displayed in the export dialog. The logo should be 32×32 pixels, adjusted for the current editor scale (see [method EditorInterface.get_editor_scale]). @@ -152,7 +152,7 @@ - Returns one-click deploy menu item icon for the specified [param device], icon should be 16x16 adjusted to the current editor scale, see [method EditorInterface.get_editor_scale]. + Returns the item icon for the specified [param device] in the one-click deploy menu. The icon should be 16×16 pixels, adjusted for the current editor scale (see [method EditorInterface.get_editor_scale]). @@ -172,7 +172,7 @@ - Returns number one-click deploy devices (or other one-click option displayed in the menu). + Returns the number of devices (or other options) available in the one-click deploy menu. @@ -203,7 +203,7 @@ - Returns icon of the one-click deploy menu button, icon should be 16x16 adjusted to the current editor scale, see [method EditorInterface.get_editor_scale]. + Returns the icon of the one-click deploy menu button. The icon should be 16×16 pixels, adjusted for the current editor scale (see [method EditorInterface.get_editor_scale]). diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index 849bb55634..f8440a12a8 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -230,7 +230,7 @@ - Return [code]true[/code], if the result of [method _get_export_options] has changed and the export options of preset corresponding to [param platform] should be updated. + Return [code]true[/code] if the result of [method _get_export_options] has changed and the export options of the preset corresponding to [param platform] should be updated. diff --git a/doc/classes/EditorExportPreset.xml b/doc/classes/EditorExportPreset.xml index a2d08f45c9..bcfe3a5ac7 100644 --- a/doc/classes/EditorExportPreset.xml +++ b/doc/classes/EditorExportPreset.xml @@ -4,7 +4,7 @@ Export preset configuration. - Export preset configuration. Instances of [EditorExportPreset] by editor UI and intended to be used a read-only configuration passed to the [EditorExportPlatform] methods when exporting the project. + Represents the configuration of an export preset, as created by the editor's export dialog. An [EditorExportPreset] instance is intended to be used a read-only configuration passed to the [EditorExportPlatform] methods when exporting the project. @@ -12,25 +12,25 @@ - Returns [code]true[/code] if "Advanced" toggle is enabled in the export dialog. + Returns [code]true[/code] if the "Advanced" toggle is enabled in the export dialog. - Returns string with a comma separated list of custom features. + Returns a comma-separated list of custom features added to this preset, as a string. See [url=$DOCS_URL/tutorials/export/feature_tags.html]Feature tags[/url] in the documentation for more information. - Returns [Dictionary] of files selected in the "Resources" tab of the export dialog. Dictionary keys are file names and values are export mode - [code]"strip"[/code], [code]"keep"[/code], or [code]"remove"[/code]. See also [method get_file_export_mode]. + Returns a dictionary of files selected in the "Resources" tab of the export dialog. The dictionary's keys are file paths, and its values are the corresponding export modes: [code]"strip"[/code], [code]"keep"[/code], or [code]"remove"[/code]. See also [method get_file_export_mode]. - Returns number of files selected in the "Resources" tab of the export dialog. + Returns the number of files selected in the "Resources" tab of the export dialog. @@ -118,7 +118,7 @@ - Returns export preset name. + Returns this export preset's name. @@ -131,7 +131,7 @@ - Returns script export mode. + Returns the export mode used by GDScript files. [code]0[/code] for "Text", [code]1[/code] for "Binary tokens", and [code]2[/code] for "Compressed binary tokens (smaller files)". @@ -154,19 +154,19 @@ - Returns [code]true[/code] if specified file is exported. + Returns [code]true[/code] if the file at the specified [param path] will be exported. - Returns [code]true[/code] if dedicated server export mode is selected in the export dialog. + Returns [code]true[/code] if the dedicated server export mode is selected in the export dialog. - Returns [code]true[/code] if "Runnable" toggle is enabled in the export dialog. + Returns [code]true[/code] if the "Runnable" toggle is enabled in the export dialog. diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml index 03cf36fde4..bd15baa071 100644 --- a/doc/classes/EditorFileDialog.xml +++ b/doc/classes/EditorFileDialog.xml @@ -15,7 +15,7 @@ - Adds a comma-delimited file name [param filter] option to the [EditorFileDialog] with an optional [param description], which restricts what files can be picked. + Adds a comma-separated file name [param filter] option to the [EditorFileDialog] with an optional [param description], which restricts what files can be picked. A [param filter] should be of the form [code]"filename.extension"[/code], where filename and extension can be [code]*[/code] to match any string. Filters starting with [code].[/code] (i.e. empty filenames) are not allowed. For example, a [param filter] of [code]"*.tscn, *.scn"[/code] and a [param description] of [code]"Scenes"[/code] results in filter text "Scenes (*.tscn, *.scn)". diff --git a/doc/classes/EditorScenePostImportPlugin.xml b/doc/classes/EditorScenePostImportPlugin.xml index ddcbb757ad..d901d7c54b 100644 --- a/doc/classes/EditorScenePostImportPlugin.xml +++ b/doc/classes/EditorScenePostImportPlugin.xml @@ -63,15 +63,15 @@ - Post process the scene. This function is called after the final scene has been configured. + Post-process the scene. This function is called after the final scene has been configured. - Pre Process the scene. This function is called right after the scene format loader loaded the scene and no changes have been made. - Pre process may be used to adjust internal import options in the [code]"nodes"[/code], [code]"meshes"[/code], [code]"animations"[/code] or [code]"materials"[/code] keys inside [code]get_option_value("_subresources")[/code]. + Pre-process the scene. This function is called right after the scene format loader loaded the scene and no changes have been made. + Pre-process may be used to adjust internal import options in the [code]"nodes"[/code], [code]"meshes"[/code], [code]"animations"[/code] or [code]"materials"[/code] keys inside [code]get_option_value("_subresources")[/code]. diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index edb8f12388..63b44852ad 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -518,6 +518,9 @@ The shape of [Skeleton3D] bone gizmos in the 3D editor. [b]Wire[/b] is a thin line, while [b]Octahedron[/b] is a set of lines that represent a thicker hollow line pointing in a specific direction (similar to most 3D animation software). + + Size of probe gizmos displayed when editing [LightmapGI] and [LightmapProbe] nodes. Setting this to [code]0.0[/code] will hide the probe spheres of [LightmapGI] and wireframes of [LightmapProbe] nodes, but will keep the wireframes linking probes from [LightmapGI] and billboard icons from [LightmapProbe] intact. + Size of the disk gizmo displayed when editing [Path3D]'s tilt handles. @@ -784,13 +787,15 @@ If set to [code]Adaptive[/code], the dialog opens in list view or grid view depending on the requested type. If set to [code]Last Used[/code], the display mode will always open the way you last used it. - If [code]true[/code], fuzzy matching of search tokens is allowed. + If [code]true[/code], together with exact matches of a filename, the dialog includes approximate matches. + This is useful for finding the correct files even when there are typos in the search query; for example, searching "nprmal" will find "normal". Additionally, it allows you to write shorter search queries; for example, searching "nml" will also find "normal". + See also [member filesystem/quick_open_dialog/max_fuzzy_misses]. If [code]true[/code], results will include files located in the [code]addons[/code] folder. - The number of allowed missed query characters in a match, if fuzzy matching is enabled. For example, with the default value of 2, [code]foobar[/code] would match [code]foobur[/code] and [code]foob[/code] but not [code]foo[/code]. + The number of missed query characters allowed in a match when fuzzy matching is enabled. For example, with the default value of [code]2[/code], [code]"normal"[/code] would match [code]"narmal"[/code] and [code]"norma"[/code] but not [code]"nor"[/code]. Maximum number of matches to show in dialog. @@ -812,6 +817,13 @@ Input accumulation can be disabled to get slightly more precise/reactive input at the cost of increased CPU usage. [b]Note:[/b] Input accumulation is [i]enabled[/i] by default. + + Editor accessibility support mode: + - [b]Auto[/b] ([code]0[/code]): Accessibility support is enabled, but updates to the accessibility information are processed only if an assistive app (such as a screen reader or a Braille display) is active (default). + - [b]Always Active[/b] ([code]1[/code]): Accessibility support is enabled, and updates to the accessibility information are always processed, regardless of the status of assistive apps. + - [b]Disabled[/b] ([code]2[/code]): Accessibility support is fully disabled. + [b]Note:[/b] Accessibility debugging tools, such as Accessibility Insights for Windows, Accessibility Inspector (macOS), or AT-SPI Browser (Linux/BSD) do not count as assistive apps. To test your project with these tools, use [b]Always Active[/b]. + How to position the Cancel and OK buttons in the editor's [AcceptDialog]s. Different platforms have different standard behaviors for this, which can be overridden using this setting. This is useful if you use Redot both on Windows and macOS/Linux and your Redot muscle memory is stronger than your OS specific one. - [b]Auto[/b] follows the platform convention: OK first on Windows, KDE, and LXQt, Cancel first on macOS and other Linux desktop environments. diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml index a016ba794a..cbb328a55f 100644 --- a/doc/classes/FileDialog.xml +++ b/doc/classes/FileDialog.xml @@ -14,7 +14,7 @@ - Adds a comma-delimited file name [param filter] option to the [FileDialog] with an optional [param description], which restricts what files can be picked. + Adds a comma-separated file name [param filter] option to the [FileDialog] with an optional [param description], which restricts what files can be picked. A [param filter] should be of the form [code]"filename.extension"[/code], where filename and extension can be [code]*[/code] to match any string. Filters starting with [code].[/code] (i.e. empty filenames) are not allowed. For example, a [param filter] of [code]"*.png, *.jpg"[/code] and a [param description] of [code]"Images"[/code] results in filter text "Images (*.png, *.jpg)". @@ -96,6 +96,21 @@ [b]Note:[/b] This method does nothing on native file dialogs. + + + + + Returns [code]true[/code] if the provided [param flag] is enabled. + + + + + + + + Toggles the specified customization [param flag], allowing to customize features available in this [FileDialog]. See [enum Customization] for options. + + @@ -140,9 +155,18 @@ Display mode of the dialog's file list. + + If [code]true[/code], shows the toggle favorite button and favorite list on the left side of the dialog. + + + If [code]true[/code], shows the toggle file filter button. + The dialog's open or save mode, which affects the selection behavior. + + If [code]true[/code], shows the file sorting options button. + The filter for file names (case-insensitive). When set to a non-empty string, only files that contains the substring will be shown. [member filename_filter] can be edited by the user with the filter button at the top of the file dialog. See also [member filters], which should be used to restrict the file types that can be selected instead of [member filename_filter] which is meant to be set by the user. @@ -151,12 +175,24 @@ The available file type filters. Each filter string in the array should be formatted like this: [code]*.png,*.jpg,*.jpeg;Image Files;image/png,image/jpeg[/code]. The description text of the filter is optional and can be omitted. Both file extensions and MIME type should be always set. [b]Note:[/b] Embedded file dialog and Windows file dialog support only file extensions, while Android, Linux, and macOS file dialogs also support MIME types. + + If [code]true[/code], shows the button for creating new directories (when using [constant FILE_MODE_OPEN_DIR], [constant FILE_MODE_OPEN_ANY], or [constant FILE_MODE_SAVE_FILE]). + + + If [code]true[/code], shows the toggle hidden files button. + + + If [code]true[/code], shows the layout switch buttons (list/thumbnails). + If [code]true[/code], changing the [member file_mode] property will set the window title accordingly (e.g. setting [member file_mode] to [constant FILE_MODE_OPEN_FILE] will change the window title to "Open a File"). The number of additional [OptionButton]s and [CheckBox]es in the dialog. + + If [code]true[/code], shows the recent directories list on the left side of the dialog. + If non-empty, the given sub-folder will be "root" of this [FileDialog], i.e. user won't be able to go to its parent directory. [b]Note:[/b] This property is ignored by native file dialogs. @@ -232,6 +268,34 @@ The dialog displays files as a list of filenames. + + Toggles visibility of the favorite button, and the favorite list on the left side of the dialog. + Equivalent to [member hidden_files_toggle_enabled]. + + + If enabled, shows the button for creating new directories (when using [constant FILE_MODE_OPEN_DIR], [constant FILE_MODE_OPEN_ANY], or [constant FILE_MODE_SAVE_FILE]). + Equivalent to [member folder_creation_enabled]. + + + If enabled, shows the toggle file filter button. + Equivalent to [member file_filter_toggle_enabled]. + + + If enabled, shows the file sorting options button. + Equivalent to [member file_sort_options_enabled]. + + + If enabled, shows the toggle favorite button and favorite list on the left side of the dialog. + Equivalent to [member favorites_enabled]. + + + If enabled, shows the recent directories list on the left side of the dialog. + Equivalent to [member recent_list_enabled]. + + + If enabled, shows the layout switch buttons (list/thumbnails). + Equivalent to [member layout_toggle_enabled]. + diff --git a/doc/classes/FoldableContainer.xml b/doc/classes/FoldableContainer.xml index 7b1509fa72..ae200d8c5a 100644 --- a/doc/classes/FoldableContainer.xml +++ b/doc/classes/FoldableContainer.xml @@ -57,10 +57,10 @@ The container's title text. - Title's horizontal text alignment as defined in the [enum HorizontalAlignment] enum. + Title's horizontal text alignment. - Title's position as defined in the [enum TitlePosition] enum. + Title's position. Title text writing direction. diff --git a/doc/classes/InputEvent.xml b/doc/classes/InputEvent.xml index e81064517a..044909ce0a 100644 --- a/doc/classes/InputEvent.xml +++ b/doc/classes/InputEvent.xml @@ -89,9 +89,9 @@ - Returns [code]true[/code] if the specified [param event] matches this event. Only valid for action events i.e key ([InputEventKey]), button ([InputEventMouseButton] or [InputEventJoypadButton]), axis [InputEventJoypadMotion] or action ([InputEventAction]) events. - If [param exact_match] is [code]false[/code], it ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. - [b]Note:[/b] Only considers the event configuration (such as the keyboard key or joypad axis), not state information like [method is_pressed], [method is_released], [method is_echo], or [method is_canceled]. + Returns [code]true[/code] if the specified [param event] matches this event. Only valid for action events, which include key ([InputEventKey]), button ([InputEventMouseButton] or [InputEventJoypadButton]), axis [InputEventJoypadMotion], and action ([InputEventAction]) events. + If [param exact_match] is [code]false[/code], the check ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. + [b]Note:[/b] This method only considers the event configuration (such as the keyboard key or the joypad axis), not state information like [method is_pressed], [method is_released], [method is_echo], or [method is_canceled]. diff --git a/doc/classes/InstancePlaceholder.xml b/doc/classes/InstancePlaceholder.xml index abd1428bac..b390d5d9bd 100644 --- a/doc/classes/InstancePlaceholder.xml +++ b/doc/classes/InstancePlaceholder.xml @@ -5,7 +5,7 @@ Turning on the option [b]Load As Placeholder[/b] for an instantiated scene in the editor causes it to be replaced by an [InstancePlaceholder] when running the game, this will not replace the node in the editor. This makes it possible to delay actually loading the scene until calling [method create_instance]. This is useful to avoid loading large scenes all at once by loading parts of it selectively. - The [InstancePlaceholder] does not have a transform. This causes any child nodes to be positioned relatively to the [Viewport] from point (0,0), rather than their parent as displayed in the editor. Replacing the placeholder with a scene with a transform will transform children relatively to their parent again. + [b]Note:[/b] Like [Node], [InstancePlaceholder] does not have a transform. This causes any child nodes to be positioned relatively to the [Viewport] origin, rather than their parent as displayed in the editor. Replacing the placeholder with a scene with a transform will transform children relatively to their parent again. diff --git a/doc/classes/LabelSettings.xml b/doc/classes/LabelSettings.xml index 03d75066d6..620a2563fb 100644 --- a/doc/classes/LabelSettings.xml +++ b/doc/classes/LabelSettings.xml @@ -164,7 +164,7 @@ The number of stacked outlines. - Returns the stacked shadow count. + The number of stacked shadows. diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index f086a05abc..64ca40ae86 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -318,7 +318,7 @@ Maximum number of characters that can be entered inside the [LineEdit]. If [code]0[/code], there is no limit. When a limit is defined, characters that would exceed [member max_length] are truncated. This happens both for existing [member text] contents when setting the max length, or for new text inserted in the [LineEdit], including pasting. - If any input text is truncated, the [signal text_change_rejected] signal is emitted with the truncated substring as parameter: + If any input text is truncated, the [signal text_change_rejected] signal is emitted with the truncated substring as a parameter: [codeblocks] [gdscript] text = "Hello world" @@ -327,7 +327,7 @@ max_length = 10 text += " goodbye" # `text` becomes "Hello good". - # `text_change_rejected` is emitted with "bye" as parameter. + # `text_change_rejected` is emitted with "bye" as a parameter. [/gdscript] [csharp] Text = "Hello world"; @@ -336,7 +336,7 @@ MaxLength = 10; Text += " goodbye"; // `Text` becomes "Hello good". - // `text_change_rejected` is emitted with "bye" as parameter. + // `text_change_rejected` is emitted with "bye" as a parameter. [/csharp] [/codeblocks] diff --git a/doc/classes/LookAtModifier3D.xml b/doc/classes/LookAtModifier3D.xml index cf0e53c36c..22b24097ee 100644 --- a/doc/classes/LookAtModifier3D.xml +++ b/doc/classes/LookAtModifier3D.xml @@ -120,9 +120,9 @@ The transition type of the time-based interpolation. See also [enum Tween.TransitionType]. - If [code]true[/code], limits the degree of rotation. This helps prevent the character's neck from rotating 360 degrees. + If [code]true[/code], limits the amount of rotation. For example, this helps to prevent a character's neck from rotating 360 degrees. [b]Note:[/b] As with [AnimationTree] blending, interpolation is provided that favors [method Skeleton3D.get_bone_rest]. This means that interpolation does not select the shortest path in some cases. - [b]Note:[/b] Some [member transition_type] may exceed the limitations (e.g. `Back`, `Elastic`, and `Spring`). If interpolation occurs while overshooting the limitations, the result might possibly not respect the bone rest. + [b]Note:[/b] Some values for [member transition_type] (such as [constant Tween.TRANS_BACK], [constant Tween.TRANS_ELASTIC], and [constant Tween.TRANS_SPRING]) may exceed the limitations. If interpolation occurs while overshooting the limitations, the result might not respect the bone rest. If [code]true[/code], provides rotation by two axes. diff --git a/doc/classes/MeshDataTool.xml b/doc/classes/MeshDataTool.xml index 9d0a632623..370e5502f6 100644 --- a/doc/classes/MeshDataTool.xml +++ b/doc/classes/MeshDataTool.xml @@ -100,8 +100,8 @@ - Returns index of specified vertex connected to given edge. - Vertex argument can only be 0 or 1 because edges are comprised of two vertices. + Returns the index of the specified [param vertex] connected to the edge at index [param idx]. + [param vertex] can only be [code]0[/code] or [code]1[/code], as edges are composed of two vertices. @@ -115,8 +115,8 @@ - Returns specified edge associated with given face. - Edge argument must be either 0, 1, or 2 because a face only has three edges. + Returns the edge associated with the face at index [param idx]. + [param edge] argument must be either [code]0[/code], [code]1[/code], or [code]2[/code] because a face only has three edges. diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml index a53a363694..83472b1cc9 100644 --- a/doc/classes/NavigationAgent2D.xml +++ b/doc/classes/NavigationAgent2D.xml @@ -75,6 +75,12 @@ Returns the next position in global coordinates that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent. The use of this function once every physics frame is required to update the internal path logic of the NavigationAgent. + + + + Returns the length of the currently calculated path. The returned value is [code]0.0[/code], if the path is still calculating or no calculation has been requested yet. + + @@ -191,6 +197,19 @@ The path postprocessing applied to the raw path corridor found by the [member pathfinding_algorithm]. + + The maximum allowed length of the returned path in world units. A path will be clipped when going over this length. + + + The maximum allowed radius in world units that the returned path can be from the path start. The path will be clipped when going over this radius. Compared to [member path_return_max_length], this allows the agent to go that much further, if they need to walk around a corner. + [b]Note:[/b] This will perform a sphere clip considering only the actual navigation mesh path points with the first path position being the sphere's center. + + + The maximum distance a searched polygon can be away from the start polygon before the pathfinding cancels the search for a path to the (possibly unreachable or very far away) target position polygon. In this case the pathfinding resets and builds a path from the start polygon to the polygon that was found closest to the target position so far. A value of [code]0[/code] or below counts as unlimited. In case of unlimited the pathfinding will search all polygons connected with the start polygon until either the target position polygon is found or all available polygon search options are exhausted. + + + The maximum number of polygons that are searched before the pathfinding cancels the search for a path to the (possibly unreachable or very far away) target position polygon. In this case the pathfinding resets and builds a path from the start polygon to the polygon that was found closest to the target position so far. A value of [code]0[/code] or below counts as unlimited. In case of unlimited the pathfinding will search all polygons connected with the start polygon until either the target position polygon is found or all available polygon search options are exhausted. + The pathfinding algorithm used in the path query. diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml index 5f9f4991d1..3eaecc1308 100644 --- a/doc/classes/NavigationAgent3D.xml +++ b/doc/classes/NavigationAgent3D.xml @@ -75,6 +75,12 @@ Returns the next position in global coordinates that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent. The use of this function once every physics frame is required to update the internal path logic of the NavigationAgent. + + + + Returns the length of the currently calculated path. The returned value is [code]0.0[/code], if the path is still calculating or no calculation has been requested yet. + + @@ -197,6 +203,19 @@ The path postprocessing applied to the raw path corridor found by the [member pathfinding_algorithm]. + + The maximum allowed length of the returned path in world units. A path will be clipped when going over this length. + + + The maximum allowed radius in world units that the returned path can be from the path start. The path will be clipped when going over this radius. Compared to [member path_return_max_length], this allows the agent to go that much further, if they need to walk around a corner. + [b]Note:[/b] This will perform a sphere clip considering only the actual navigation mesh path points with the first path position being the sphere's center. + + + The maximum distance a searched polygon can be away from the start polygon before the pathfinding cancels the search for a path to the (possibly unreachable or very far away) target position polygon. In this case the pathfinding resets and builds a path from the start polygon to the polygon that was found closest to the target position so far. A value of [code]0[/code] or below counts as unlimited. In case of unlimited the pathfinding will search all polygons connected with the start polygon until either the target position polygon is found or all available polygon search options are exhausted. + + + The maximum number of polygons that are searched before the pathfinding cancels the search for a path to the (possibly unreachable or very far away) target position polygon. In this case the pathfinding resets and builds a path from the start polygon to the polygon that was found closest to the target position so far. A value of [code]0[/code] or below counts as unlimited. In case of unlimited the pathfinding will search all polygons connected with the start polygon until either the target position polygon is found or all available polygon search options are exhausted. + The pathfinding algorithm used in the path query. diff --git a/doc/classes/NavigationPathQueryParameters2D.xml b/doc/classes/NavigationPathQueryParameters2D.xml index 29a6d835cd..15b94f0d80 100644 --- a/doc/classes/NavigationPathQueryParameters2D.xml +++ b/doc/classes/NavigationPathQueryParameters2D.xml @@ -30,6 +30,19 @@ The path postprocessing applied to the raw path corridor found by the [member pathfinding_algorithm]. + + The maximum allowed length of the returned path in world units. A path will be clipped when going over this length. A value of [code]0[/code] or below counts as disabled. + + + The maximum allowed radius in world units that the returned path can be from the path start. The path will be clipped when going over this radius. A value of [code]0[/code] or below counts as disabled. + [b]Note:[/b] This will perform a circle shaped clip operation on the path with the first path position being the circle's center position. + + + The maximum distance a searched polygon can be away from the start polygon before the pathfinding cancels the search for a path to the (possibly unreachable or very far away) target position polygon. In this case the pathfinding resets and builds a path from the start polygon to the polygon that was found closest to the target position so far. A value of [code]0[/code] or below counts as unlimited. In case of unlimited the pathfinding will search all polygons connected with the start polygon until either the target position polygon is found or all available polygon search options are exhausted. + + + The maximum number of polygons that are searched before the pathfinding cancels the search for a path to the (possibly unreachable or very far away) target position polygon. In this case the pathfinding resets and builds a path from the start polygon to the polygon that was found closest to the target position so far. A value of [code]0[/code] or below counts as unlimited. In case of unlimited the pathfinding will search all polygons connected with the start polygon until either the target position polygon is found or all available polygon search options are exhausted. + The pathfinding algorithm used in the path query. diff --git a/doc/classes/NavigationPathQueryParameters3D.xml b/doc/classes/NavigationPathQueryParameters3D.xml index a9b4794886..b5fef6e4cb 100644 --- a/doc/classes/NavigationPathQueryParameters3D.xml +++ b/doc/classes/NavigationPathQueryParameters3D.xml @@ -30,6 +30,19 @@ The path postprocessing applied to the raw path corridor found by the [member pathfinding_algorithm]. + + The maximum allowed length of the returned path in world units. A path will be clipped when going over this length. A value of [code]0[/code] or below counts as disabled. + + + The maximum allowed radius in world units that the returned path can be from the path start. The path will be clipped when going over this radius. A value of [code]0[/code] or below counts as disabled. + [b]Note:[/b] This will perform a sphere shaped clip operation on the path with the first path position being the sphere's center position. + + + The maximum distance a searched polygon can be away from the start polygon before the pathfinding cancels the search for a path to the (possibly unreachable or very far away) target position polygon. In this case the pathfinding resets and builds a path from the start polygon to the polygon that was found closest to the target position so far. A value of [code]0[/code] or below counts as unlimited. In case of unlimited the pathfinding will search all polygons connected with the start polygon until either the target position polygon is found or all available polygon search options are exhausted. + + + The maximum number of polygons that are searched before the pathfinding cancels the search for a path to the (possibly unreachable or very far away) target position polygon. In this case the pathfinding resets and builds a path from the start polygon to the polygon that was found closest to the target position so far. A value of [code]0[/code] or below counts as unlimited. In case of unlimited the pathfinding will search all polygons connected with the start polygon until either the target position polygon is found or all available polygon search options are exhausted. + The pathfinding algorithm used in the path query. diff --git a/doc/classes/NavigationPathQueryResult2D.xml b/doc/classes/NavigationPathQueryResult2D.xml index e1ef2ee7de..94d6b6e3e0 100644 --- a/doc/classes/NavigationPathQueryResult2D.xml +++ b/doc/classes/NavigationPathQueryResult2D.xml @@ -21,6 +21,9 @@ The resulting path array from the navigation query. All path array positions are in global coordinates. Without customized query parameters this is the same path as returned by [method NavigationServer2D.map_get_path]. + + Returns the length of the path. + The [code]ObjectID[/code]s of the [Object]s which manage the regions and links each point of the path goes through. diff --git a/doc/classes/NavigationPathQueryResult3D.xml b/doc/classes/NavigationPathQueryResult3D.xml index 1cda3e64d5..84a9f7d1d6 100644 --- a/doc/classes/NavigationPathQueryResult3D.xml +++ b/doc/classes/NavigationPathQueryResult3D.xml @@ -21,6 +21,9 @@ The resulting path array from the navigation query. All path array positions are in global coordinates. Without customized query parameters this is the same path as returned by [method NavigationServer3D.map_get_path]. + + Returns the length of the path. + The [code]ObjectID[/code]s of the [Object]s which manage the regions and links each point of the path goes through. diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml index 91ebb03271..9eac989d19 100644 --- a/doc/classes/NavigationPolygon.xml +++ b/doc/classes/NavigationPolygon.xml @@ -90,7 +90,7 @@ - Returns the [NavigationMesh] resulting from this navigation polygon. This navigation mesh can be used to update the navigation mesh of a region with the [method NavigationServer3D.region_set_navigation_mesh] API directly (as 2D uses the 3D server behind the scene). + Returns the [NavigationMesh] resulting from this navigation polygon. This navigation mesh can be used to update the navigation mesh of a region with the [method NavigationServer3D.region_set_navigation_mesh] API directly. diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index 0b6d21a58c..aa176bdcaa 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -215,7 +215,7 @@ - If [param paused] is [code]true[/code] the specified [param agent] will not be processed, e.g. calculate avoidance velocities or receive avoidance callbacks. + If [param paused] is [code]true[/code] the specified [param agent] will not be processed. For example, it will not calculate avoidance velocities or receive avoidance callbacks. @@ -737,7 +737,7 @@ - If [param paused] is [code]true[/code] the specified [param obstacle] will not be processed, e.g. affect avoidance velocities. + If [param paused] is [code]true[/code] the specified [param obstacle] will not be processed. For example, it will no longer affect avoidance velocities. @@ -905,6 +905,13 @@ Returns the travel cost of this [param region]. + + + + + Returns [code]true[/code] if the [param region] uses an async synchronization process that runs on a background thread. + + @@ -986,6 +993,14 @@ Sets the [param travel_cost] for this [param region]. + + + + + + If [param enabled] is [code]true[/code] the [param region] uses an async synchronization process that runs on a background thread. + + diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index ec917df139..2c9ffdb1a0 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -237,7 +237,7 @@ - If [param paused] is [code]true[/code] the specified [param agent] will not be processed, e.g. calculate avoidance velocities or receive avoidance callbacks. + If [param paused] is [code]true[/code] the specified [param agent] will not be processed. For example, it will not calculate avoidance velocities or receive avoidance callbacks. @@ -855,7 +855,7 @@ - If [param paused] is [code]true[/code] the specified [param obstacle] will not be processed, e.g. affect avoidance velocities. + If [param paused] is [code]true[/code] the specified [param obstacle] will not be processed. For example, it will no longer affect avoidance velocities. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index fc5545a80e..5558aef846 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -250,6 +250,12 @@ This function ensures that the calling of this function will succeed, no matter whether it's being done from a thread or not. If called from a thread that is not allowed to call the function, the call will become deferred. Otherwise, the call will go through directly. + + + + Returns [code]true[/code] if this node can automatically translate messages depending on the current locale. See [member auto_translate_mode], [method atr], and [method atr_n]. + + diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index d425ffb1bb..dd8316b903 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -264,6 +264,7 @@ [/csharp] [/codeblocks] [b]Note:[/b] The base [Object] defines a few notifications ([constant NOTIFICATION_POSTINITIALIZE] and [constant NOTIFICATION_PREDELETE]). Inheriting classes such as [Node] define a lot more notifications, which are also received by this method. + [b]Note:[/b] Unlike other virtual methods, this method is called automatically for every script that overrides it. This means that the base implementation should not be called via [code]super[/code] in GDScript or its equivalents in other languages. diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml index 3d2ba8313a..fdfa9eb06c 100644 --- a/doc/classes/PackedByteArray.xml +++ b/doc/classes/PackedByteArray.xml @@ -497,6 +497,13 @@ Sorts the elements of the array in ascending order. + + + + Returns a copy of the data converted to a [PackedColorArray], where each block of 16 bytes has been converted to a [Color] variant. + [b]Note:[/b] The size of the input array must be a multiple of 16 (size of four 32-bit float variables). The size of the new array will be [code]byte_array.size() / 16[/code]. If the original data can't be converted to [Color] variants, the resulting data is undefined. + + @@ -529,6 +536,27 @@ If the original data can't be converted to signed 64-bit integers, the resulting data is undefined. + + + + Returns a copy of the data converted to a [PackedVector2Array], where each block of 8 bytes or 16 bytes (32-bit or 64-bit) has been converted to a [Vector2] variant. + [b]Note:[/b] The size of the input array must be a multiple of 8 or 16 (depending on the build settings, see [Vector2] for more details). The size of the new array will be [code]byte_array.size() / (8 or 16)[/code]. If the original data can't be converted to [Vector2] variants, the resulting data is undefined. + + + + + + Returns a copy of the data converted to a [PackedVector3Array], where each block of 12 or 24 bytes (32-bit or 64-bit) has been converted to a [Vector3] variant. + [b]Note:[/b] The size of the input array must be a multiple of 12 or 24 (depending on the build settings, see [Vector3] for more details). The size of the new array will be [code]byte_array.size() / (12 or 24)[/code]. If the original data can't be converted to [Vector3] variants, the resulting data is undefined. + + + + + + Returns a copy of the data converted to a [PackedVector4Array], where each block of 16 or 32 bytes (32-bit or 64-bit) has been converted to a [Vector4] variant. + [b]Note:[/b] The size of the input array must be a multiple of 16 or 32 (depending on the build settings, see [Vector4] for more details). The size of the new array will be [code]byte_array.size() / (16 or 32)[/code]. If the original data can't be converted to [Vector4] variants, the resulting data is undefined. + + diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml index a5018b47dd..86bd29ea25 100644 --- a/doc/classes/PhysicsServer2D.xml +++ b/doc/classes/PhysicsServer2D.xml @@ -1124,7 +1124,7 @@ Constant to set/get a body's inertia. The default value of this parameter is [code]0.0[/code]. If the body's inertia is set to a value [code]<= 0.0[/code], then the inertia will be recalculated based on the body's shapes, mass, and center of mass. - Constant to set/get a body's center of mass position in the body's local coordinate system. The default value of this parameter is [code]Vector2(0,0)[/code]. If this parameter is never set explicitly, then it is recalculated based on the body's shapes when setting the parameter [constant BODY_PARAM_MASS] or when calling [method body_set_space]. + Constant to set/get a body's center of mass position in the body's local coordinate system. The default value of this parameter is [code]Vector2(0, 0)[/code]. If this parameter is never set explicitly, then it is recalculated based on the body's shapes when setting the parameter [constant BODY_PARAM_MASS] or when calling [method body_set_space]. Constant to set/get a body's gravity multiplier. The default value of this parameter is [code]1.0[/code]. diff --git a/doc/classes/PhysicsServer2DExtension.xml b/doc/classes/PhysicsServer2DExtension.xml index 02124f4881..77fee1df9a 100644 --- a/doc/classes/PhysicsServer2DExtension.xml +++ b/doc/classes/PhysicsServer2DExtension.xml @@ -1092,7 +1092,7 @@ - Called every physics step to process the physics simulation. [param step] is the time elapsed since the last physics step, in seconds. It is usually the same as [method Node.get_physics_process_delta_time]. + Called every physics step to process the physics simulation. [param step] is the time elapsed since the last physics step, in seconds. It is usually the same as the value returned by [method Node.get_physics_process_delta_time]. Overridable version of [PhysicsServer2D]'s internal [code skip-lint]step[/code] method. diff --git a/doc/classes/PointMesh.xml b/doc/classes/PointMesh.xml index 201e44af19..87ca4777f3 100644 --- a/doc/classes/PointMesh.xml +++ b/doc/classes/PointMesh.xml @@ -1,12 +1,12 @@ - Mesh with a single Point primitive. + Mesh with a single point primitive. - The PointMesh is made from a single point. Instead of relying on triangles, points are rendered as a single rectangle on the screen with a constant size. They are intended to be used with Particle systems, but can be used as a cheap way to render constant size billboarded sprites (for example in a point cloud). - PointMeshes, must be used with a material that has a point size. Point size can be accessed in a shader with [code]POINT_SIZE[/code], or in a [BaseMaterial3D] by setting [member BaseMaterial3D.use_point_size] and the variable [member BaseMaterial3D.point_size]. - When using PointMeshes, properties that normally alter vertices will be ignored, including billboard mode, grow, and cull face. + A [PointMesh] is a primitive mesh composed of a single point. Instead of relying on triangles, points are rendered as a single rectangle on the screen with a constant size. They are intended to be used with particle systems, but can also be used as a cheap way to render billboarded sprites (for example in a point cloud). + In order to be displayed, point meshes must be used with a material that has a point size. The point size can be accessed in a shader with the [code]POINT_SIZE[/code] built-in, or in a [BaseMaterial3D] by setting the [member BaseMaterial3D.use_point_size] and [member BaseMaterial3D.point_size] properties. + [b]Note:[/b] When using point meshes, properties that normally affect vertices will be ignored, including [member BaseMaterial3D.billboard_mode], [member BaseMaterial3D.grow], and [member BaseMaterial3D.cull_mode]. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 29f820afce..f7e2998907 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -261,10 +261,10 @@ Accessibility support mode: - - [b]Auto[/b] ([code]0[/code]): accessibility support is enabled, but accessibility information updates are processed only if an assistive app (e.g. screen reader or Braille display) is active (default). - - [b]Always Active[/b] ([code]1[/code]): accessibility support is enabled, and accessibility information updates are processed regardless of current assistive apps' status. - - [b]Disabled[/b] ([code]2[/code]): accessibility support is fully disabled. - [b]Note:[/b] Accessibility debugging tools, such as Accessibility Insights for Windows, macOS Accessibility Inspector, or AT-SPI Browser do not count as assistive apps. To test your app with these tools, use [code]1[/code]. + - [b]Auto[/b] ([code]0[/code]): Accessibility support is enabled, but updates to the accessibility information are processed only if an assistive app (such as a screen reader or a Braille display) is active (default). + - [b]Always Active[/b] ([code]1[/code]): Accessibility support is enabled, and updates to the accessibility information are always processed, regardless of the status of assistive apps. + - [b]Disabled[/b] ([code]2[/code]): Accessibility support is fully disabled. + [b]Note:[/b] Accessibility debugging tools, such as Accessibility Insights for Windows, Accessibility Inspector (macOS), or AT-SPI Browser (Linux/BSD) do not count as assistive apps. To test your project with these tools, use [b]Always Active[/b]. The number of accessibility information updates per second. @@ -1646,6 +1646,14 @@ [b]Note:[/b] "ICU / HarfBuzz / Graphite" text server data includes dictionaries for Burmese, Chinese, Japanese, Khmer, Lao and Thai as well as Unicode Standard Annex #29 and Unicode Standard Annex #14 word and line breaking rules. Data is about 4 MB large. [b]Note:[/b] [TextServerFallback] does not use additional data. + + Default strictness of line-breaking rules. Can be overridden by adding [code]@lb={auto,loose,normal,strict}[/code] to the language code. + - [b]Auto[/b] ([code]0[/code]) - strictness is based on the length of the line. + - [b]Loose[/b] ([code]1[/code]) - the least restrictive set of line-breaking rules. Typically used for short lines. + - [b]Normal[/b] ([code]2[/code]) - the most common set of line-breaking rules. + - [b]Strict[/b] ([code]3[/code]) - the most stringent set of line-breaking rules. + See [url=https://www.w3.org/TR/css-text-3/#line-break-property]Line Breaking Strictness: the line-break property[/url] for more info. + If non-empty, this locale will be used instead of the automatically detected system locale. [b]Note:[/b] This setting also applies to the exported project. To only affect testing within the editor, override this setting with an [code]editor[/code] [url=$DOCS_URL/tutorials/export/feature_tags.html]feature tag[/url] for localization testing purposes. @@ -2696,7 +2704,7 @@ Maximum number of uniform sets that will be cached by the 2D renderer when batching draw calls. - [b]Note:[/b] A project that uses a large number of unique sprite textures per frame may benefit from increasing this value. + [b]Note:[/b] Increasing this value can improve performance if the project renders many unique sprite textures every frame. Controls how much of the original viewport size should be covered by the 2D signed distance field. This SDF can be sampled in [CanvasItem] shaders and is used for [GPUParticles2D] collision. Higher values allow portions of occluders located outside the viewport to still be taken into account in the generated signed distance field, at the cost of performance. If you notice particles falling through [LightOccluder2D]s as the occluders leave the viewport, increase this setting. @@ -2900,15 +2908,15 @@ - [code]opengl3_angle[/code], OpenGL ES 3.0 using the ANGLE compatibility layer over native Direct3D 11 drivers. If [member rendering/gl_compatibility/fallback_to_angle] is enabled, this is used as a fallback if OpenGL 3.3 is not supported. By default, ANGLE is used as the default driver for some devices listed in [member rendering/gl_compatibility/force_angle_on_devices]. - If [code]true[/code], the compatibility renderer will fall back to ANGLE if native OpenGL is not supported or the device is listed in [member rendering/gl_compatibility/force_angle_on_devices]. + If [code]true[/code], the Compatibility renderer will fall back to ANGLE if native OpenGL is not supported or the device is listed in [member rendering/gl_compatibility/force_angle_on_devices]. [b]Note:[/b] This setting is implemented only on Windows. - If [code]true[/code], the compatibility renderer will fall back to OpenGLES if desktop OpenGL is not supported. + If [code]true[/code], the Compatibility renderer will fall back to OpenGLES if desktop OpenGL is not supported. [b]Note:[/b] This setting is implemented only on Linux/X11. - If [code]true[/code], the compatibility renderer will fall back to native OpenGL if ANGLE is not supported, or ANGLE dynamic libraries aren't found. + If [code]true[/code], the Compatibility renderer will fall back to native OpenGL if ANGLE is not supported, or ANGLE dynamic libraries aren't found. [b]Note:[/b] This setting is implemented on macOS and Windows. @@ -3024,19 +3032,19 @@ Use 16 bits for the omni/spot shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices. - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + The subdivision amount of the first quadrant on the shadow atlas. See the [url=$DOCS_URL/tutorials/tutorials/3d/lights_and_shadows.html#shadow-atlas]documentation[/url] for more information. - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + The subdivision amount of the second quadrant on the shadow atlas. See the [url=$DOCS_URL/tutorials/tutorials/3d/lights_and_shadows.html#shadow-atlas]documentation[/url] for more information. - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + The subdivision amount of the third quadrant on the shadow atlas. See the [url=$DOCS_URL/tutorials/tutorials/3d/lights_and_shadows.html#shadow-atlas]documentation[/url] for more information. - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + The subdivision amount of the fourth quadrant on the shadow atlas. See the [url=$DOCS_URL/tutorials/tutorials/3d/lights_and_shadows.html#shadow-atlas]documentation[/url] for more information. - Size for shadow atlas (used for OmniLights and SpotLights). See documentation. + The size of the shadow atlas used for [OmniLight3D] and [SpotLight3D] nodes. See the [url=$DOCS_URL/tutorials/tutorials/3d/lights_and_shadows.html#shadow-atlas]documentation[/url] for more information. Lower-end override for [member rendering/lights_and_shadows/positional_shadow/atlas_size] on mobile devices, due to performance concerns or driver support. diff --git a/doc/classes/ReferenceRect.xml b/doc/classes/ReferenceRect.xml index 18e2fd5e95..257bdcad2c 100644 --- a/doc/classes/ReferenceRect.xml +++ b/doc/classes/ReferenceRect.xml @@ -1,10 +1,10 @@ - A rectangle hint for designing UIs. + A rectangular box for designing UIs. - A rectangle box that displays only a colored border around its rectangle. It is used to visualize the extents of a [Control]. + A rectangular box that displays only a colored border around its rectangle (see [method Control.get_rect]). It can be used to visualize the extents of a [Control] node, for testing purposes. diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 9d1847444c..22d5143045 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -1147,23 +1147,23 @@ Specific device object based on a physical device. - - Vulkan: Vulkan device driver resource ([code]VkDevice[/code]). ([code]rid[/code] argument doesn't apply.) + - Vulkan: Vulkan device driver resource ([code]VkDevice[/code]) ([code]rid[/code] parameter is ignored). Physical device the specific logical device is based on. - - Vulkan: [code]VkDevice[/code]. ([code]rid[/code] argument doesn't apply.) + - Vulkan: [code]VkDevice[/code] ([code]rid[/code] parameter is ignored). Top-most graphics API entry object. - - Vulkan: [code]VkInstance[/code]. ([code]rid[/code] argument doesn't apply.) + - Vulkan: [code]VkInstance[/code] ([code]rid[/code] parameter is ignored). The main graphics-compute command queue. - - Vulkan: [code]VkQueue[/code]. ([code]rid[/code] argument doesn't apply.) + - Vulkan: [code]VkQueue[/code] ([code]rid[/code] parameter is ignored). The specific family the main queue belongs to. - - Vulkan: the queue family index, an [code]uint32_t[/code]. ([code]rid[/code] argument doesn't apply.) + - Vulkan: The queue family index, a [code]uint32_t[/code] ([code]rid[/code] parameter is ignored). - Vulkan: [code]VkImage[/code]. diff --git a/doc/classes/ResourceFormatLoader.xml b/doc/classes/ResourceFormatLoader.xml index 45c3a1d606..775fd8e3f1 100644 --- a/doc/classes/ResourceFormatLoader.xml +++ b/doc/classes/ResourceFormatLoader.xml @@ -28,8 +28,20 @@ - If implemented, gets the dependencies of a given resource. If [param add_types] is [code]true[/code], paths should be appended [code]::TypeName[/code], where [code]TypeName[/code] is the class name of the dependency. - [b]Note:[/b] Custom resource types defined by scripts aren't known by the [ClassDB], so you might just return [code]"Resource"[/code] for them. + Should return the dependencies for the resource at the given [param path]. Each dependency is a string composed of one to three sections separated by [code]::[/code], with trailing empty sections omitted: + - The first section should contain the UID if the resource has one. Otherwise, it should contain the file path. + - The second section should contain the class name of the dependency if [param add_types] is [code]true[/code]. Otherwise, it should be empty. + - The third section should contain the fallback path if the resource has a UID. Otherwise, it should be empty. + [codeblock] + func _get_dependencies(path, add_types): + return [ + "uid://fqgvuwrkuixh::Script::res://script.gd", + "uid://fqgvuwrkuixh::::res://script.gd", + "res://script.gd::Script", + "res://script.gd", + ] + [/codeblock] + [b]Note:[/b] Custom resource types defined by scripts aren't known by the [ClassDB], so [code]"Resource"[/code] can be used for the class name. diff --git a/doc/classes/ResourceImporterImageFont.xml b/doc/classes/ResourceImporterImageFont.xml index 663c906001..fcc95c2060 100644 --- a/doc/classes/ResourceImporterImageFont.xml +++ b/doc/classes/ResourceImporterImageFont.xml @@ -21,7 +21,7 @@ The character ranges to import from the font image. This is an array that maps each position on the image (in tile coordinates, not pixels). The font atlas is traversed from left to right and top to bottom. Characters can be specified with decimal numbers (127), hexadecimal numbers ([code]0x007f[/code], or [code]U+007f[/code]) or between single quotes ([code]'~'[/code]). Ranges can be specified with a hyphen between characters. For example, [code]0-127[/code] represents the full ASCII range. It can also be written as [code]0x0000-0x007f[/code] (or [code]U+0000-U+007f[/code]). As another example, [code]' '-'~'[/code] is equivalent to [code]32-127[/code] and represents the range of printable (visible) ASCII characters. For any range, the character advance and offset can be customized by appending three space-separated integer values (additional advance, x offset, y offset) to the end. For example [code]'a'-'b' 4 5 2[/code] sets the advance to [code]char_width + 4[/code] and offset to [code]Vector2(5, 2)[/code] for both `a` and `b` characters. - Make sure [member character_ranges] doesn't exceed the number of [member columns] * [member rows] defined. Otherwise, the font will fail to import. + [b]Note:[/b] The overall number of characters must not exceed the number of [member columns] multiplied by [member rows]. Otherwise, the font will fail to import. Number of columns in the font image. See also [member rows]. diff --git a/doc/classes/ResourceImporterScene.xml b/doc/classes/ResourceImporterScene.xml index 2a694c7a52..99df890eb4 100644 --- a/doc/classes/ResourceImporterScene.xml +++ b/doc/classes/ResourceImporterScene.xml @@ -1,7 +1,7 @@ - Imports a glTF, FBX, Collada or Blender 3D scene. + Imports a glTF, FBX, COLLADA, or Blender 3D scene. See also [ResourceImporterOBJ], which is used for OBJ models that can be imported as an independent [Mesh] or a scene. @@ -80,6 +80,9 @@ The uniform scale to use for the scene root. The default value of [code]1.0[/code] will not perform any rescaling. See [member nodes/apply_root_scale] for details of how this scale is applied. + + If set to a valid script, attaches the script to the root node of the imported scene. If the type of the root node is not compatible with the script, the root node will be replaced with a type that is compatible with the script. This setting can also be used on other non-mesh nodes in the scene to attach scripts to them. + Override for the root node type. If empty, the root node will use what the scene specifies, or [Node3D] if the scene does not specify a root type. Using a node type that inherits from [Node3D] is recommended. Otherwise, you'll lose the ability to position the node directly in the 3D editor. diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml index 122d64a4ad..ab58d261ed 100644 --- a/doc/classes/ResourceLoader.xml +++ b/doc/classes/ResourceLoader.xml @@ -45,11 +45,14 @@ Returns the dependencies for the resource at the given [param path]. - [b]Note:[/b] The dependencies are returned with slices separated by [code]::[/code]. You can use [method String.get_slice] to get their components. + Each dependency is a string that can be divided into sections by [code]::[/code]. There can be either one section or three sections, with the second section always being empty. When there is one section, it contains the file path. When there are three sections, the first section contains the UID and the third section contains the fallback path. [codeblock] for dependency in ResourceLoader.get_dependencies(path): - print(dependency.get_slice("::", 0)) # Prints the UID. - print(dependency.get_slice("::", 2)) # Prints the path. + if dependency.contains("::"): + print(dependency.get_slice("::", 0)) # Prints the UID. + print(dependency.get_slice("::", 2)) # Prints the fallback path. + else: + print(dependency) # Prints the path. [/codeblock] diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 5a9723d89c..c79880a5ce 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -17,6 +17,20 @@ https://godotengine.org/asset-library/asset/2789 + + + + + + + + + + Adds a horizontal rule that can be used to separate content. + If [param width_in_percent] is set, [param width] values are percentages of the control width instead of pixels. + If [param height_in_percent] is set, [param height] values are percentages of the control width instead of pixels. + + @@ -28,15 +42,17 @@ - - + + + Adds an image's opening and closing tags to the tag stack, optionally providing a [param width] and [param height] to resize the image, a [param color] to tint the image and a [param region] to only use parts of the image. If [param width] or [param height] is set to 0, the image size will be adjusted in order to keep the original aspect ratio. If [param width] and [param height] are not set, but [param region] is, the region's rect will be used. [param key] is an optional identifier, that can be used to modify the image via [method update_image]. If [param pad] is set, and the image is smaller than the size specified by [param width] and [param height], the image padding is added to match the size instead of upscaling. - If [param size_in_percent] is set, [param width] and [param height] values are percentages of the control width instead of pixels. + If [param width_in_percent] is set, [param width] values are percentages of the control width instead of pixels. + If [param height_in_percent] is set, [param height] values are percentages of the control width instead of pixels. [param alt_text] is used as the image description for assistive apps. @@ -652,7 +668,8 @@ - + + Updates the existing images with the key [param key]. Only properties specified by [param mask] bits are updated. See [method add_image]. @@ -939,6 +956,9 @@ The default text font size. + + The horizontal rule texture. + The background used when the [RichTextLabel] is focused. The [theme_item focus] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons. diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml index 4b84231db4..b8b42dc282 100644 --- a/doc/classes/RigidBody2D.xml +++ b/doc/classes/RigidBody2D.xml @@ -7,8 +7,9 @@ [RigidBody2D] implements full 2D physics. It cannot be controlled directly, instead, you must apply forces to it (gravity, impulses, etc.), and the physics simulation will calculate the resulting movement, rotation, react to collisions, and affect other physics bodies in its path. The body's behavior can be adjusted via [member lock_rotation], [member freeze], and [member freeze_mode]. By changing various properties of the object, such as [member mass], you can control how the physics simulation acts on it. A rigid body will always maintain its shape and size, even when forces are applied to it. It is useful for objects that can be interacted with in an environment, such as a tree that can be knocked over or a stack of crates that can be pushed around. + If you need to directly affect the body, prefer [method _integrate_forces] as it allows you to directly access the physics state. If you need to override the default physics behavior, you can write a custom force integration function. See [member custom_integrator]. - [b]Note:[/b] Changing the 2D transform or [member linear_velocity] of a [RigidBody2D] very often may lead to some unpredictable behaviors. If you need to directly affect the body, prefer [method _integrate_forces] as it allows you to directly access the physics state. + [b]Note:[/b] Changing the 2D transform or [member linear_velocity] of a [RigidBody2D] very often may lead to some unpredictable behaviors. This also happens when a [RigidBody2D] is the descendant of a constantly moving node, like another [RigidBody2D], as that will cause its global transform to be set whenever its ancestor moves. https://godotengine.org/asset-library/asset/2725 diff --git a/doc/classes/RigidBody3D.xml b/doc/classes/RigidBody3D.xml index 0fe2a28eb0..24e46158f4 100644 --- a/doc/classes/RigidBody3D.xml +++ b/doc/classes/RigidBody3D.xml @@ -7,8 +7,9 @@ [RigidBody3D] implements full 3D physics. It cannot be controlled directly, instead, you must apply forces to it (gravity, impulses, etc.), and the physics simulation will calculate the resulting movement, rotation, react to collisions, and affect other physics bodies in its path. The body's behavior can be adjusted via [member lock_rotation], [member freeze], and [member freeze_mode]. By changing various properties of the object, such as [member mass], you can control how the physics simulation acts on it. A rigid body will always maintain its shape and size, even when forces are applied to it. It is useful for objects that can be interacted with in an environment, such as a tree that can be knocked over or a stack of crates that can be pushed around. + If you need to directly affect the body, prefer [method _integrate_forces] as it allows you to directly access the physics state. If you need to override the default physics behavior, you can write a custom force integration function. See [member custom_integrator]. - [b]Note:[/b] Changing the 3D transform or [member linear_velocity] of a [RigidBody3D] very often may lead to some unpredictable behaviors. If you need to directly affect the body, prefer [method _integrate_forces] as it allows you to directly access the physics state. + [b]Note:[/b] Changing the 3D transform or [member linear_velocity] of a [RigidBody3D] very often may lead to some unpredictable behaviors. This also happens when a [RigidBody3D] is the descendant of a constantly moving node, like another [RigidBody3D], as that will cause its global transform to be set whenever its ancestor moves. $DOCS_URL/tutorials/physics/physics_introduction.html diff --git a/doc/classes/Shortcut.xml b/doc/classes/Shortcut.xml index b63fa81314..3aa77f6f98 100644 --- a/doc/classes/Shortcut.xml +++ b/doc/classes/Shortcut.xml @@ -16,8 +16,8 @@ var key_event = InputEventKey.new() key_event.keycode = KEY_S key_event.ctrl_pressed = true - key_event.command_or_control_autoremap = true # Swaps ctrl for Command on Mac. - save_shortcut.set_events([key_event]) + key_event.command_or_control_autoremap = true # Swaps Ctrl for Command on Mac. + save_shortcut.events = [key_event] func _input(event): if save_shortcut.matches_event(event) and event.is_pressed() and not event.is_echo(): @@ -25,39 +25,35 @@ get_viewport().set_input_as_handled() [/gdscript] [csharp] - public partial class YourScriptName : Godot.Node + using Godot; + + public partial class MyNode : Node + { + private readonly Shortcut _saveShortcut = new Shortcut(); + + public override void _Ready() { - private Godot.Shortcut saveShortcut; - - public override void _Ready() + InputEventKey keyEvent = new InputEventKey { - // Enable input processing explicitly (optional for Node, but included for clarity) - SetProcessInput(true); + Keycode = Key.S, + CtrlPressed = true, + CommandOrControlAutoremap = true, // Swaps Ctrl for Command on Mac. + }; - saveShortcut = new Godot.Shortcut(); + _saveShortcut.Events = [keyEvent]; + } - Godot.InputEventKey keyEvent = new Godot.InputEventKey - { - Keycode = Godot.Key.S, - CtrlPressed = true, - CommandOrControlAutoremap = true - }; - - Godot.Collections.Array<Godot.InputEvent> events = new Godot.Collections.Array<Godot.InputEvent> { keyEvent }; - saveShortcut.SetEvents(events); - } - - public override void _Input(Godot.InputEvent @event) + public override void _Input(InputEvent @event) + { + if (@event is InputEventKey keyEvent && + _saveShortcut.MatchesEvent(@event) && + keyEvent.Pressed && !keyEvent.Echo) { - if (@event is Godot.InputEventKey keyEvent && - saveShortcut.MatchesEvent(@event) && - keyEvent.Pressed && !keyEvent.Echo) - { - Godot.GD.Print("Save shortcut pressed!"); - GetViewport().SetInputAsHandled(); - } + GD.Print("Save shortcut pressed!"); + GetViewport().SetInputAsHandled(); } } + } [/csharp] [/codeblocks] diff --git a/doc/classes/SkeletonModification2DFABRIK.xml b/doc/classes/SkeletonModification2DFABRIK.xml index 16784dbf4d..e4aee899e1 100644 --- a/doc/classes/SkeletonModification2DFABRIK.xml +++ b/doc/classes/SkeletonModification2DFABRIK.xml @@ -6,7 +6,7 @@ This [SkeletonModification2D] uses an algorithm called Forward And Backward Reaching Inverse Kinematics, or FABRIK, to rotate a bone chain so that it reaches a target. FABRIK works by knowing the positions and lengths of a series of bones, typically called a "bone chain". It first starts by running a forward pass, which places the final bone at the target's position. Then all other bones are moved towards the tip bone, so they stay at the defined bone length away. Then a backwards pass is performed, where the root/first bone in the FABRIK chain is placed back at the origin. Then all other bones are moved so they stay at the defined bone length away. This positions the bone chain so that it reaches the target when possible, but all of the bones stay the correct length away from each other. - Because of how FABRIK works, it often gives more natural results than those seen in [SkeletonModification2DCCDIK]. FABRIK also supports angle constraints, which are fully taken into account when solving. + Because of how FABRIK works, it often gives more natural results than those seen in [SkeletonModification2DCCDIK]. [b]Note:[/b] The FABRIK modifier has [code]fabrik_joints[/code], which are the data objects that hold the data for each joint in the FABRIK chain. This is different from [Bone2D] nodes! FABRIK joints hold the data needed for each [Bone2D] in the bone chain used by FABRIK. To help control how the FABRIK joints move, a magnet vector can be passed, which can nudge the bones in a certain direction prior to solving, giving a level of control over the final result. diff --git a/doc/classes/Slider.xml b/doc/classes/Slider.xml index b1e78e8987..1545c612d0 100644 --- a/doc/classes/Slider.xml +++ b/doc/classes/Slider.xml @@ -23,6 +23,9 @@ If [code]true[/code], the slider will display ticks for minimum and maximum values. + + Sets the position of the ticks. See [enum TickPosition] for details. + @@ -37,6 +40,20 @@ + + + Places the ticks at the bottom of the [HSlider], or right of the [VSlider]. + + + Places the ticks at the top of the [HSlider], or left of the [VSlider]. + + + Places the ticks at the both sides of the slider. + + + Places the ticks at the center of the slider. + + Boolean constant. If [code]1[/code], the grabber texture size will be ignored and it will fit within slider's bounds based only on its center position. @@ -44,6 +61,9 @@ Vertical or horizontal offset of the grabber. + + Vertical or horizontal offset of the ticks. The offset is reversed for top or left ticks. + The texture for the grabber (the draggable element). diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 692289c44c..0aa2a10164 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -118,13 +118,14 @@ - + - Returns a single Unicode character from the decimal [param char]. You may use [url=https://unicodelookup.com/]unicodelookup.com[/url] or [url=https://www.unicode.org/charts/]unicode.org[/url] as points of reference. + Returns a single Unicode character from the integer [param code]. You may use [url=https://unicodelookup.com/]unicodelookup.com[/url] or [url=https://www.unicode.org/charts/]unicode.org[/url] as points of reference. [codeblock] print(String.chr(65)) # Prints "A" print(String.chr(129302)) # Prints "🤖" (robot face emoji) [/codeblock] + See also [method unicode_at], [method @GDScript.char], and [method @GDScript.ord]. @@ -797,7 +798,7 @@ - Replaces all occurrences of the Unicode character with code [param key] with the Unicode character with code [param with]. Faster version of [method replace] when the key is only one character long. To get a single character use [code]"X".unicode_at(0)[/code] (note that some strings, like compound letters and emoji, can be made up of multiple unicode codepoints, and will not work with this method, use [method length] to make sure). + Replaces all occurrences of the Unicode character with code [param key] with the Unicode character with code [param with]. Faster version of [method replace] when the key is only one character long. To get a single character use [code]"X".unicode_at(0)[/code] (note that some strings, like compound letters and emoji, can be composed of multiple unicode codepoints, and will not work with this method, use [method length] to make sure). @@ -1149,6 +1150,7 @@ Returns the character code at position [param at]. + See also [method chr], [method @GDScript.char], and [method @GDScript.ord]. diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index 6ee2fd83c6..d01f83635b 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -705,7 +705,7 @@ - Replaces all occurrences of the Unicode character with code [param key] with the Unicode character with code [param with]. Faster version of [method replace] when the key is only one character long. To get a single character use [code]"X".unicode_at(0)[/code] (note that some strings, like compound letters and emoji, can be made up of multiple unicode codepoints, and will not work with this method, use [method length] to make sure). + Replaces all occurrences of the Unicode character with code [param key] with the Unicode character with code [param with]. Faster version of [method replace] when the key is only one character long. To get a single character use [code]"X".unicode_at(0)[/code] (note that some strings, like compound letters and emoji, can be composed of multiple unicode codepoints, and will not work with this method, use [method length] to make sure). @@ -1057,6 +1057,7 @@ Returns the character code at position [param at]. + See also [method String.chr], [method @GDScript.char], and [method @GDScript.ord]. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 9488c87c5f..64b1cb510e 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -2194,10 +2194,10 @@ Glyph horizontal position is rounded to one quarter of the pixel size, each glyph is rasterized up to four times. - Maximum font size which will use one half of the pixel subpixel positioning in [constant SUBPIXEL_POSITIONING_AUTO] mode. + Maximum font size which will use "one half of the pixel" subpixel positioning in [constant SUBPIXEL_POSITIONING_AUTO] mode. - Maximum font size which will use one quarter of the pixel subpixel positioning in [constant SUBPIXEL_POSITIONING_AUTO] mode. + Maximum font size which will use "one quarter of the pixel" subpixel positioning in [constant SUBPIXEL_POSITIONING_AUTO] mode. TextServer supports simple text layouts. @@ -2275,7 +2275,7 @@ Font is italic or oblique. - Font have fixed-width characters. + Font has fixed-width characters (also known as monospace). Use default Unicode BiDi algorithm. diff --git a/doc/classes/TileMapPattern.xml b/doc/classes/TileMapPattern.xml index 1733ecf592..f3fce24809 100644 --- a/doc/classes/TileMapPattern.xml +++ b/doc/classes/TileMapPattern.xml @@ -5,7 +5,7 @@ This resource holds a set of cells to help bulk manipulations of [TileMap]. - A pattern always start at the [code](0,0)[/code] coordinates and cannot have cells with negative coordinates. + A pattern always starts at the [code](0, 0)[/code] coordinates and cannot have cells with negative coordinates. diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index c7e5e51d0b..ac56d5c433 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -18,9 +18,9 @@ - + - Adds a button with [Texture2D] [param button] to the end of the cell at column [param column]. The [param id] is used to identify the button in the according [signal Tree.button_clicked] signal and can be different from the buttons index. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately before this method. Optionally, the button can be [param disabled] and have a [param tooltip_text]. [param alt_text] is used as the button description for assistive apps. + Adds a button with [Texture2D] [param button] to the end of the cell at column [param column]. The [param id] is used to identify the button in the according [signal Tree.button_clicked] signal and can be different from the buttons index. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately before this method. Optionally, the button can be [param disabled] and have a [param tooltip_text]. [param description] is used as the button description for assistive apps. @@ -80,13 +80,6 @@ Removes the button at index [param button_index] in column [param column]. - - - - - Returns the given column's alternative text. - - @@ -210,6 +203,13 @@ Returns custom font size used to draw text in the column [param column]. + + + + + Returns the given column's description for assistive apps. + + @@ -514,14 +514,6 @@ Selects the given [param column]. - - - - - - Sets the given column's alternative (description) text for assistive apps. - - @@ -548,15 +540,6 @@ Sets the given column's button [Texture2D] at index [param button_index] to [param button]. - - - - - - - Sets the given column's button alternative text (description) at index [param button_index] for assistive apps. - - @@ -566,6 +549,15 @@ Sets the given column's button color at index [param button_index] to [param color]. + + + + + + + Sets the given column's button description at index [param button_index] for assistive apps. + + @@ -667,6 +659,14 @@ Sets custom font size used to draw text in the given [param column]. + + + + + + Sets the given column's description for assistive apps. + + diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 3e67b5a28b..682957c0ce 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -66,7 +66,7 @@ tween.TweenProperty(sprite, "position", Vector2.Zero, 1.0f); [/csharp] [/codeblocks] - In the example above, all children of a node are moved one after another to position (0, 0). + In the example above, all children of a node are moved one after another to position [code](0, 0)[/code]. You should avoid using more than one [Tween] per object's property. If two or more tweens animate one property at the same time, the last one created will take priority and assign the final value. If you want to interrupt and restart an animation, consider assigning the [Tween] to a variable: [codeblocks] [gdscript] diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml index 83c4ddee4c..c5651c1e6c 100644 --- a/doc/classes/UndoRedo.xml +++ b/doc/classes/UndoRedo.xml @@ -22,7 +22,7 @@ undo_redo.create_action("Move the node") undo_redo.add_do_method(do_something) undo_redo.add_undo_method(undo_something) - undo_redo.add_do_property(node, "position", Vector2(100,100)) + undo_redo.add_do_property(node, "position", Vector2(100, 100)) undo_redo.add_undo_property(node, "position", node.position) undo_redo.commit_action() [/gdscript] diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml index c8230d94e4..c9e1849e94 100644 --- a/doc/classes/VisualShader.xml +++ b/doc/classes/VisualShader.xml @@ -186,11 +186,6 @@ - - - The offset vector of the whole graph. - - A vertex shader, operating on vertices. diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 2fb7ea3988..3cfb17473d 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -587,7 +587,7 @@ If [code]true[/code], the window will be on top of all other windows. Does not work if [member transient] is enabled. - + Toggles if any text should automatically change to its translated version depending on the current locale. @@ -614,7 +614,7 @@ If [code]true[/code], the [Window] is excluded from screenshots taken by [method DisplayServer.screen_get_image], [method DisplayServer.screen_get_image_rect], and [method DisplayServer.screen_get_pixel]. [b]Note:[/b] This property is implemented on macOS and Windows. - [b]Note:[/b] Enabling this setting does [b]NOT[/b] prevent other apps from capturing an image. It should not be used as a security measure. + [b]Note:[/b] Enabling this setting will prevent standard screenshot methods from capturing a window image, but does [b]NOT[/b] guarantee that other apps won't be able to capture an image. It should not be used as a DRM or security measure. If [code]true[/code], the [Window] will be in exclusive mode. Exclusive windows are always on top of their parent and will block all input going to the parent [Window]. @@ -854,17 +854,18 @@ Full screen mode with full multi-window support. Full screen window covers the entire display area of a screen and has no decorations. The display's video mode is not changed. [b]On Android:[/b] This enables immersive mode. - [b]On Windows:[/b] Multi-window full-screen mode has a 1px border of the [member ProjectSettings.rendering/environment/defaults/default_clear_color] color. [b]On macOS:[/b] A new desktop is used to display the running project. [b]Note:[/b] Regardless of the platform, enabling full screen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling full screen mode. A single window full screen mode. This mode has less overhead, but only one window can be open on a given screen at a time (opening a child window or application switching will trigger a full screen transition). Full screen window covers the entire display area of a screen and has no border or decorations. The display's video mode is not changed. + [b]Note:[/b] This mode might not work with screen recording software. [b]On Android:[/b] This enables immersive mode. [b]On Windows:[/b] Depending on video driver, full screen transition might cause screens to go black for a moment. [b]On macOS:[/b] A new desktop is used to display the running project. Exclusive full screen mode prevents Dock and Menu from showing up when the mouse pointer is hovering the edge of the screen. [b]On Linux (X11):[/b] Exclusive full screen mode bypasses compositor. + [b]On Linux (Wayland):[/b] Equivalent to [constant MODE_FULLSCREEN]. [b]Note:[/b] Regardless of the platform, enabling full screen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling full screen mode. @@ -904,7 +905,7 @@ Windows is excluded from screenshots taken by [method DisplayServer.screen_get_image], [method DisplayServer.screen_get_image_rect], and [method DisplayServer.screen_get_pixel]. [b]Note:[/b] This flag is implemented on macOS and Windows. - [b]Note:[/b] Setting this flag will [b]NOT[/b] prevent other apps from capturing an image, it should not be used as a security measure. + [b]Note:[/b] Setting this flag will prevent standard screenshot methods from capturing a window image, but does [b]NOT[/b] guarantee that other apps won't be able to capture an image. It should not be used as a DRM or security measure. Signals the window manager that this window is supposed to be an implementation-defined "popup" (usually a floating, borderless, untileable and immovable child window). diff --git a/doc/classes/XRAnchor3D.xml b/doc/classes/XRAnchor3D.xml index cdfe1da90e..4b5e90c51e 100644 --- a/doc/classes/XRAnchor3D.xml +++ b/doc/classes/XRAnchor3D.xml @@ -5,7 +5,7 @@ The [XRAnchor3D] point is an [XRNode3D] that maps a real world location identified by the AR platform to a position within the game world. For example, as long as plane detection in ARKit is on, ARKit will identify and update the position of planes (tables, floors, etc.) and create anchors for them. - This node is mapped to one of the anchors through its unique ID. When you receive a signal that a new anchor is available, you should add this node to your scene for that anchor. You can predefine nodes and set the ID; the nodes will simply remain on 0,0,0 until a plane is recognized. + This node is mapped to one of the anchors through its unique ID. When you receive a signal that a new anchor is available, you should add this node to your scene for that anchor. You can predefine nodes and set the ID; the nodes will simply remain on [code](0, 0, 0)[/code] until a plane is recognized. Keep in mind that, as long as plane detection is enabled, the size, placing and orientation of an anchor will be updated as the detection logic learns more about the real world out there especially if only part of the surface is in view. diff --git a/doc/classes/XRBodyTracker.xml b/doc/classes/XRBodyTracker.xml index f9735dd097..4ef54b7538 100644 --- a/doc/classes/XRBodyTracker.xml +++ b/doc/classes/XRBodyTracker.xml @@ -304,20 +304,20 @@ Right wrist twist joint. - - Left ankle twist joint. + + Left foot twist joint. - - Left ankle joint. + + Left heel joint. Left middle foot joint. - - Right ankle twist joint. + + Right foot twist joint. - - Right ankle joint. + + Right heel joint. Right middle foot joint. diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml index 247d2ab938..0cebc04cae 100644 --- a/doc/classes/XRServer.xml +++ b/doc/classes/XRServer.xml @@ -30,7 +30,7 @@ This is an important function to understand correctly. AR and VR platforms all handle positioning slightly differently. - For platforms that do not offer spatial tracking, our origin point (0, 0, 0) is the location of our HMD, but you have little control over the direction the player is facing in the real world. + For platforms that do not offer spatial tracking, our origin point [code](0, 0, 0)[/code] is the location of our HMD, but you have little control over the direction the player is facing in the real world. For platforms that do offer spatial tracking, our origin point depends very much on the system. For OpenVR, our origin point is usually the center of the tracking space, on the ground. For other platforms, it's often the location of the tracking camera. This method allows you to center your tracker on the location of the HMD. It will take the current location of the HMD and use that to adjust all your tracking data; in essence, realigning the real world to your player's current position in the game world. For this method to produce usable results, tracking information must be available. This often takes a few frames after starting your game. diff --git a/drivers/apple/os_log_logger.cpp b/drivers/apple/os_log_logger.cpp new file mode 100644 index 0000000000..3613ab3e59 --- /dev/null +++ b/drivers/apple/os_log_logger.cpp @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* os_log_logger.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* REDOT ENGINE */ +/* https://redotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2024-present Redot Engine contributors */ +/* (see REDOT_AUTHORS.md) */ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "os_log_logger.h" + +#include "core/string/print_string.h" + +#include // For malloc/free + +OsLogLogger::OsLogLogger(const char *p_subsystem) { + const char *subsystem = p_subsystem; + if (!subsystem) { + subsystem = "org.redotengine.godot"; + os_log_info(OS_LOG_DEFAULT, "Missing subsystem for os_log logging; using %{public}s", subsystem); + } + + log = os_log_create(subsystem, "engine"); + error_log = os_log_create(subsystem, error_type_string(ErrorType::ERR_ERROR)); + warning_log = os_log_create(subsystem, error_type_string(ErrorType::ERR_WARNING)); + script_log = os_log_create(subsystem, error_type_string(ErrorType::ERR_SCRIPT)); + shader_log = os_log_create(subsystem, error_type_string(ErrorType::ERR_SHADER)); +} + +void OsLogLogger::logv(const char *p_format, va_list p_list, bool p_err) { + constexpr int static_buf_size = 1024; + char static_buf[static_buf_size] = { '\0' }; + char *buf = static_buf; + va_list list_copy; + va_copy(list_copy, p_list); + int len = vsnprintf(buf, static_buf_size, p_format, p_list); + if (len >= static_buf_size) { + buf = (char *)Memory::alloc_static(len + 1); + vsnprintf(buf, len + 1, p_format, list_copy); + } + va_end(list_copy); + + // Choose appropriate log type based on error flag. + os_log_type_t log_type = p_err ? OS_LOG_TYPE_ERROR : OS_LOG_TYPE_INFO; + os_log_with_type(log, log_type, "%{public}s", buf); + + if (len >= static_buf_size) { + Memory::free_static(buf); + } +} + +void OsLogLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector> &p_script_backtraces) { + os_log_t selected_log; + switch (p_type) { + case ERR_WARNING: + selected_log = warning_log; + break; + case ERR_SCRIPT: + selected_log = script_log; + break; + case ERR_SHADER: + selected_log = shader_log; + break; + case ERR_ERROR: + default: + selected_log = error_log; + break; + } + const char *err_details; + if (p_rationale && *p_rationale) { + err_details = p_rationale; + } else { + err_details = p_code; + } + + // Choose log level based on error type. + os_log_type_t log_type; + switch (p_type) { + case ERR_WARNING: + log_type = OS_LOG_TYPE_DEFAULT; + break; + case ERR_ERROR: + case ERR_SCRIPT: + case ERR_SHADER: + default: + log_type = OS_LOG_TYPE_ERROR; + break; + } + + // Append script backtraces, if any. + String back_trace; + for (const Ref &backtrace : p_script_backtraces) { + if (backtrace.is_valid() && !backtrace->is_empty()) { + back_trace += "\n"; + back_trace += backtrace->format(strlen(error_type_indent(p_type))); + } + } + + if (back_trace.is_empty()) { + os_log_with_type(selected_log, log_type, "%{public}s:%d:%{public}s(): %{public}s %{public}s", p_file, p_line, p_function, err_details, p_code); + } else { + os_log_with_type(selected_log, log_type, "%{public}s:%d:%{public}s(): %{public}s %{public}s%{public}s", p_file, p_line, p_function, err_details, p_code, back_trace.utf8().ptr()); + } +} diff --git a/drivers/apple_embedded/terminal_logger_apple_embedded.mm b/drivers/apple/os_log_logger.h similarity index 70% rename from drivers/apple_embedded/terminal_logger_apple_embedded.mm rename to drivers/apple/os_log_logger.h index 6af5dbdb1a..7d687b4922 100644 --- a/drivers/apple_embedded/terminal_logger_apple_embedded.mm +++ b/drivers/apple/os_log_logger.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* terminal_logger_apple_embedded.mm */ +/* os_log_logger.h */ /**************************************************************************/ /* This file is part of: */ /* REDOT ENGINE */ @@ -30,33 +30,28 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#import "terminal_logger_apple_embedded.h" +#pragma once -#ifdef APPLE_EMBEDDED_ENABLED +#include "core/io/logger.h" -#import +#include -void TerminalLoggerAppleEmbedded::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector> &p_script_backtraces) { - if (!should_log(true)) { - return; - } +/** + * @brief Apple unified logging system integration for Godot Engine. + */ +class OsLogLogger : public Logger { + os_log_t log; + os_log_t error_log; + os_log_t warning_log; + os_log_t script_log; + os_log_t shader_log; - const char *err_details; - if (p_rationale && p_rationale[0]) { - err_details = p_rationale; - } else { - err_details = p_code; - } +public: + void logv(const char *p_format, va_list p_list, bool p_err) override _PRINTF_FORMAT_ATTRIBUTE_2_0; + void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR, const Vector> &p_script_backtraces = {}) override; - os_log_error(OS_LOG_DEFAULT, - "%{public}s: %{public}s\nat: %{public}s (%{public}s:%i)", - error_type_string(p_type), err_details, p_function, p_file, p_line); - - for (const Ref &backtrace : p_script_backtraces) { - if (!backtrace->is_empty()) { - os_log_error(OS_LOG_DEFAULT, "%{public}s", backtrace->format().utf8().get_data()); - } - } -} - -#endif // APPLE_EMBEDDED_ENABLED + /** + * @brief Constructs an OsLogLogger with the specified subsystem identifier, which is normally the bundle identifier. + */ + OsLogLogger(const char *p_subsystem); +}; diff --git a/drivers/apple_embedded/os_apple_embedded.h b/drivers/apple_embedded/os_apple_embedded.h index 42e32cb4bd..8de23b1e10 100644 --- a/drivers/apple_embedded/os_apple_embedded.h +++ b/drivers/apple_embedded/os_apple_embedded.h @@ -85,6 +85,8 @@ private: void deinitialize_modules(); + mutable String remote_fs_dir; + public: static OS_AppleEmbedded *get_singleton(); @@ -117,6 +119,7 @@ public: virtual String get_cache_path() const override; virtual String get_temp_path() const override; + virtual String get_resource_dir() const override; virtual String get_locale() const override; @@ -127,6 +130,8 @@ public: virtual bool _check_internal_feature_support(const String &p_feature) override; + virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) override; + void on_focus_out(); void on_focus_in(); diff --git a/drivers/apple_embedded/os_apple_embedded.mm b/drivers/apple_embedded/os_apple_embedded.mm index 11030a8107..a2a819f8ad 100644 --- a/drivers/apple_embedded/os_apple_embedded.mm +++ b/drivers/apple_embedded/os_apple_embedded.mm @@ -37,14 +37,12 @@ #import "app_delegate_service.h" #import "display_server_apple_embedded.h" #import "godot_view_apple_embedded.h" -#import "terminal_logger_apple_embedded.h" #import "view_controller.h" #include "core/config/project_settings.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" -#include "core/io/file_access_pack.h" -#include "drivers/unix/syslog_logger.h" +#import "drivers/apple/os_log_logger.h" #include "main/main.h" #import @@ -144,7 +142,7 @@ OS_AppleEmbedded::OS_AppleEmbedded() { main_loop = nullptr; Vector loggers; - loggers.push_back(memnew(TerminalLoggerAppleEmbedded)); + loggers.push_back(memnew(OsLogLogger(NSBundle.mainBundle.bundleIdentifier.UTF8String))); _set_logger(memnew(CompositeLogger(loggers))); AudioDriverManager::add_driver(&audio_driver); @@ -385,6 +383,18 @@ String OS_AppleEmbedded::get_temp_path() const { return ret; } +String OS_AppleEmbedded::get_resource_dir() const { +#ifdef TOOLS_ENABLED + return OS_Unix::get_resource_dir(); +#else + if (remote_fs_dir.is_empty()) { + return OS_Unix::get_resource_dir(); + } else { + return remote_fs_dir; + } +#endif +} + String OS_AppleEmbedded::get_locale() const { NSString *preferredLanguage = [NSLocale preferredLanguages].firstObject; @@ -643,6 +653,15 @@ bool OS_AppleEmbedded::_check_internal_feature_support(const String &p_feature) return false; } +Error OS_AppleEmbedded::setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) { + r_project_path = OS::get_user_data_dir(); + Error err = OS_Unix::setup_remote_filesystem(p_server_host, p_port, p_password, r_project_path); + if (err == OK) { + remote_fs_dir = r_project_path; + } + return err; +} + void OS_AppleEmbedded::on_focus_out() { if (is_focused) { is_focused = false; diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 37827cf008..7c9ce5c75b 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -2134,21 +2134,21 @@ void RasterizerCanvasGLES3::occluder_polygon_set_shape(RID p_occluder, const Vec glGenBuffers(1, &oc->sdf_vertex_buffer); glBindBuffer(GL_ARRAY_BUFFER, oc->sdf_vertex_buffer); - GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, oc->sdf_vertex_buffer, oc->sdf_point_count * 2 * sizeof(float), p_points.to_byte_array().ptr(), GL_STATIC_DRAW, "Occluder polygon SDF vertex buffer"); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, oc->sdf_vertex_buffer, oc->sdf_point_count * 2 * sizeof(float), p_points.ptr(), GL_STATIC_DRAW, "Occluder polygon SDF vertex buffer"); glEnableVertexAttribArray(RS::ARRAY_VERTEX); glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr); glGenBuffers(1, &oc->sdf_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oc->sdf_index_buffer); - GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ELEMENT_ARRAY_BUFFER, oc->sdf_index_buffer, oc->sdf_index_count * sizeof(uint32_t), sdf_indices.to_byte_array().ptr(), GL_STATIC_DRAW, "Occluder polygon SDF index buffer"); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ELEMENT_ARRAY_BUFFER, oc->sdf_index_buffer, oc->sdf_index_count * sizeof(uint32_t), sdf_indices.ptr(), GL_STATIC_DRAW, "Occluder polygon SDF index buffer"); glBindVertexArray(0); } else { glBindBuffer(GL_ARRAY_BUFFER, oc->sdf_vertex_buffer); - glBufferData(GL_ARRAY_BUFFER, p_points.size() * 2 * sizeof(float), p_points.to_byte_array().ptr(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, p_points.size() * 2 * sizeof(float), p_points.ptr(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oc->sdf_index_buffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sdf_indices.size() * sizeof(uint32_t), sdf_indices.to_byte_array().ptr(), GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sdf_indices.size() * sizeof(uint32_t), sdf_indices.ptr(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 21560af162..63045dc2f5 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -140,6 +140,10 @@ void RasterizerGLES3::clear_depth(float p_depth) { #endif // GLES_API_ENABLED } +void RasterizerGLES3::clear_stencil(int32_t p_stencil) { + glClearStencil(p_stencil); +} + #ifdef CAN_DEBUG static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) { // These are ultimately annoying, so removing for now. diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index f8fe0f24f2..1767bc23d0 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -118,6 +118,7 @@ public: static bool is_gles_over_gl() { return gles_over_gl; } static void clear_depth(float p_depth); + static void clear_stencil(int32_t p_stencil); static void make_current(bool p_gles_over_gl) { gles_over_gl = p_gles_over_gl; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 1bc19f9c8c..c6f3c226c0 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -34,6 +34,7 @@ #include "drivers/gles3/effects/copy_effects.h" #include "drivers/gles3/effects/feed_effects.h" +#include "drivers/gles3/storage/material_storage.h" #include "rasterizer_gles3.h" #include "storage/config.h" #include "storage/mesh_storage.h" @@ -225,10 +226,14 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry flags |= GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS; } - if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED) { + if (p_material->shader_data->stencil_enabled) { + flags |= GeometryInstanceSurface::FLAG_USES_STENCIL; + } + + if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test != GLES3::SceneShaderData::DEPTH_TEST_ENABLED) { //material is only meant for alpha pass flags |= GeometryInstanceSurface::FLAG_PASS_ALPHA; - if (p_material->shader_data->uses_depth_prepass_alpha && !(p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED)) { + if (p_material->shader_data->uses_depth_prepass_alpha && !(p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test != GLES3::SceneShaderData::DEPTH_TEST_ENABLED)) { flags |= GeometryInstanceSurface::FLAG_PASS_DEPTH; flags |= GeometryInstanceSurface::FLAG_PASS_SHADOW; } @@ -238,6 +243,17 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry flags |= GeometryInstanceSurface::FLAG_PASS_SHADOW; } + if (p_material->shader_data->stencil_enabled) { + if (p_material->shader_data->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_READ) { + // Stencil materials which read from the stencil buffer must be in the alpha pass. + // This is critical to preserve compatibility once we'll have the compositor. + if (!(flags & GeometryInstanceSurface::FLAG_PASS_ALPHA)) { + String shader_path = p_material->shader_data->path.is_empty() ? "" : "(" + p_material->shader_data->path + ")"; + ERR_PRINT_ED(vformat("Attempting to use a shader %s that reads stencil but is not in the alpha queue. Ensure the material uses alpha blending or has depth_draw disabled or depth_test disabled.", shader_path)); + } + } + } + GLES3::SceneMaterialData *material_shadow = nullptr; void *surface_shadow = nullptr; if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_world_coordinates && !p_material->shader_data->wireframe) { @@ -1235,6 +1251,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const scene_state.used_screen_texture = false; scene_state.used_normal_texture = false; scene_state.used_depth_texture = false; + scene_state.used_opaque_stencil = false; } Plane near_plane; @@ -1428,6 +1445,9 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const if (surf->flags & GeometryInstanceSurface::FLAG_USES_DEPTH_TEXTURE) { scene_state.used_depth_texture = true; } + if ((surf->flags & GeometryInstanceSurface::FLAG_USES_STENCIL) && !force_alpha && (surf->flags & (GeometryInstanceSurface::FLAG_PASS_DEPTH | GeometryInstanceSurface::FLAG_PASS_OPAQUE))) { + scene_state.used_opaque_stencil = true; + } } else if (p_pass_mode == PASS_MODE_SHADOW) { if (surf->flags & GeometryInstanceSurface::FLAG_PASS_SHADOW) { @@ -2186,7 +2206,7 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas, scene_state.reset_gl_state(); scene_state.enable_gl_depth_test(true); scene_state.enable_gl_depth_draw(true); - glDepthFunc(GL_GREATER); + scene_state.set_gl_depth_func(GL_GREATER); glColorMask(0, 0, 0, 0); glDrawBuffers(0, nullptr); @@ -2491,6 +2511,9 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ // Do depth prepass if it's explicitly enabled bool use_depth_prepass = config->use_depth_prepass; + // Forcibly enable depth prepass if opaque stencil writes are used. + use_depth_prepass = use_depth_prepass || scene_state.used_opaque_stencil; + // Don't do depth prepass we are rendering overdraw use_depth_prepass = use_depth_prepass && get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_OVERDRAW; @@ -2505,12 +2528,15 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ scene_state.enable_gl_depth_test(true); scene_state.enable_gl_depth_draw(true); scene_state.enable_gl_blend(false); - glDepthFunc(GL_GEQUAL); + scene_state.set_gl_depth_func(GL_GEQUAL); scene_state.enable_gl_scissor_test(false); + scene_state.enable_gl_stencil_test(false); + scene_state.set_gl_stencil_write_mask(255); glColorMask(0, 0, 0, 0); RasterizerGLES3::clear_depth(0.0); - glClear(GL_DEPTH_BUFFER_BIT); + RasterizerGLES3::clear_stencil(0); + glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Some desktop GL implementations fall apart when using Multiview with GL_NONE. GLuint db = p_camera_data->view_count > 1 ? GL_COLOR_ATTACHMENT0 : GL_NONE; glDrawBuffers(1, &db); @@ -2543,16 +2569,19 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ scene_state.enable_gl_scissor_test(false); scene_state.enable_gl_depth_test(true); scene_state.enable_gl_depth_draw(true); - glDepthFunc(GL_GEQUAL); + scene_state.set_gl_depth_func(GL_GEQUAL); { GLuint db = GL_COLOR_ATTACHMENT0; glDrawBuffers(1, &db); } + scene_state.enable_gl_stencil_test(false); + if (!fb_cleared) { RasterizerGLES3::clear_depth(0.0); - glClear(GL_DEPTH_BUFFER_BIT); + RasterizerGLES3::clear_stencil(0); + glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } // Need to clear framebuffer unless: @@ -2632,11 +2661,13 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ _render_list_template(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); scene_state.enable_gl_depth_draw(false); + scene_state.enable_gl_stencil_test(false); if (draw_sky || draw_sky_fog_only) { RENDER_TIMESTAMP("Render Sky"); scene_state.enable_gl_depth_test(true); + scene_state.set_gl_depth_func(GL_GEQUAL); scene_state.enable_gl_blend(false); scene_state.set_gl_cull_mode(RS::CULL_MODE_BACK); @@ -2689,7 +2720,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ if (scene_state.used_depth_texture) { glBlitFramebuffer(0, 0, size.x, size.y, 0, 0, size.x, size.y, - GL_DEPTH_BUFFER_BIT, GL_NEAREST); + GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7); glBindTexture(GL_TEXTURE_2D, backbuffer_depth); } @@ -2707,6 +2738,8 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ _render_list_template(&render_list_params_alpha, &render_data, 0, render_list[RENDER_LIST_ALPHA].elements.size(), true); + scene_state.enable_gl_stencil_test(false); + if (!flip_y) { // Restore the default winding order. glFrontFace(GL_CCW); @@ -2836,7 +2869,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend // Copy depth buffer glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_rt); - glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); } glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); @@ -2865,10 +2898,10 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend for (uint32_t v = 0; v < view_count; v++) { glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v); - glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, read_depth, 0, v); glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); - glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); - glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, write_depth, 0, v); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); } } @@ -2910,10 +2943,10 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); for (uint32_t v = 0; v < view_count; v++) { - glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); - glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, read_depth, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, write_depth, 0, v); - glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); } } @@ -3018,7 +3051,13 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } if constexpr (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { - scene_state.enable_gl_depth_test(shader->depth_test == GLES3::SceneShaderData::DEPTH_TEST_ENABLED); + scene_state.enable_gl_depth_test(shader->depth_test != GLES3::SceneShaderData::DEPTH_TEST_DISABLED); + } + + if (shader->depth_test == GLES3::SceneShaderData::DEPTH_TEST_ENABLED_INVERTED) { + scene_state.set_gl_depth_func(GL_LESS); + } else { + scene_state.set_gl_depth_func(GL_GEQUAL); } if constexpr (p_pass_mode != PASS_MODE_SHADOW) { @@ -3046,6 +3085,47 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } } + // Stencil. + if (shader->stencil_enabled) { + static const GLenum stencil_compare_table[GLES3::SceneShaderData::STENCIL_COMPARE_MAX] = { + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS, + }; + + GLenum stencil_compare = stencil_compare_table[shader->stencil_compare]; + GLuint stencil_compare_mask = 0; + GLuint stencil_write_mask = 0; + GLenum stencil_op_dpfail = GL_KEEP; + GLenum stencil_op_dppass = GL_KEEP; + + if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_READ) { + stencil_compare_mask = 255; + } + + if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_WRITE) { + stencil_op_dppass = GL_REPLACE; + stencil_write_mask = 255; + } + + if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_WRITE_DEPTH_FAIL) { + stencil_op_dpfail = GL_REPLACE; + stencil_write_mask = 255; + } + + scene_state.enable_gl_stencil_test(true); + scene_state.set_gl_stencil_func(stencil_compare, shader->stencil_reference, stencil_compare_mask); + scene_state.set_gl_stencil_write_mask(stencil_write_mask); + scene_state.set_gl_stencil_op(GL_KEEP, stencil_op_dpfail, stencil_op_dppass); + } else { + scene_state.enable_gl_stencil_test(false); + scene_state.set_gl_stencil_write_mask(255); + } + if constexpr (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { if (!uses_additive_lighting && pass == 1) { // Don't render additive passes if not using additive lighting. @@ -3715,7 +3795,7 @@ void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, scene_state.reset_gl_state(); scene_state.enable_gl_depth_test(true); scene_state.enable_gl_depth_draw(true); - glDepthFunc(GL_GREATER); + scene_state.set_gl_depth_func(GL_GREATER); glDrawBuffers(0, nullptr); @@ -3761,7 +3841,7 @@ void RasterizerSceneGLES3::_render_uv2(const PagedArray draw_buffers; draw_buffers.push_back(GL_COLOR_ATTACHMENT0); @@ -3854,7 +3934,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref &p_paramet E->value = global_textures_pass; } - if (v->override.get_type() == Variant::RID && ((RID)v->override).is_valid()) { - textures.push_back(v->override); - } else if (v->value.get_type() == Variant::RID && ((RID)v->value).is_valid()) { - textures.push_back(v->value); + RID override_rid = v->override; + if (override_rid.is_valid()) { + textures.push_back(override_rid); + } else { + RID value_rid = v->value; + if (value_rid.is_valid()) { + textures.push_back(value_rid); + } } } @@ -2947,11 +2951,18 @@ void SceneShaderData::set_code(const String &p_code) { // Actual enums set further down after compilation. int blend_modei = BLEND_MODE_MIX; - int depth_testi = DEPTH_TEST_ENABLED; + int depth_test_disabledi = 0; + int depth_test_invertedi = 0; int alpha_antialiasing_modei = ALPHA_ANTIALIASING_OFF; int cull_modei = RS::CULL_MODE_BACK; int depth_drawi = DEPTH_DRAW_OPAQUE; + int stencil_readi = 0; + int stencil_writei = 0; + int stencil_write_depth_faili = 0; + int stencil_comparei = STENCIL_COMPARE_ALWAYS; + int stencil_referencei = -1; + ShaderCompiler::IdentifierActions actions; actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX; actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT; @@ -2970,7 +2981,8 @@ void SceneShaderData::set_code(const String &p_code) { actions.render_mode_values["depth_draw_opaque"] = Pair(&depth_drawi, DEPTH_DRAW_OPAQUE); actions.render_mode_values["depth_draw_always"] = Pair(&depth_drawi, DEPTH_DRAW_ALWAYS); - actions.render_mode_values["depth_test_disabled"] = Pair(&depth_testi, DEPTH_TEST_DISABLED); + actions.render_mode_values["depth_test_disabled"] = Pair(&depth_test_disabledi, 1); + actions.render_mode_values["depth_test_inverted"] = Pair(&depth_test_invertedi, 1); actions.render_mode_values["cull_disabled"] = Pair(&cull_modei, RS::CULL_MODE_DISABLED); actions.render_mode_values["cull_front"] = Pair(&cull_modei, RS::CULL_MODE_FRONT); @@ -3020,6 +3032,20 @@ void SceneShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["BONE_INDICES"] = &uses_bones; actions.usage_flag_pointers["BONE_WEIGHTS"] = &uses_weights; + actions.stencil_mode_values["read"] = Pair(&stencil_readi, STENCIL_FLAG_READ); + actions.stencil_mode_values["write"] = Pair(&stencil_writei, STENCIL_FLAG_WRITE); + actions.stencil_mode_values["write_depth_fail"] = Pair(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL); + + actions.stencil_mode_values["compare_less"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS); + actions.stencil_mode_values["compare_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_EQUAL); + actions.stencil_mode_values["compare_less_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL); + actions.stencil_mode_values["compare_greater"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER); + actions.stencil_mode_values["compare_not_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL); + actions.stencil_mode_values["compare_greater_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL); + actions.stencil_mode_values["compare_always"] = Pair(&stencil_comparei, STENCIL_COMPARE_ALWAYS); + + actions.stencil_reference = &stencil_referencei; + actions.uniforms = &uniforms; Error err = MaterialStorage::get_singleton()->shaders.compiler_scene.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code); @@ -3032,7 +3058,13 @@ void SceneShaderData::set_code(const String &p_code) { blend_mode = BlendMode(blend_modei); alpha_antialiasing_mode = AlphaAntiAliasing(alpha_antialiasing_modei); depth_draw = DepthDraw(depth_drawi); - depth_test = DepthTest(depth_testi); + if (depth_test_disabledi) { + depth_test = DEPTH_TEST_DISABLED; + } else if (depth_test_invertedi) { + depth_test = DEPTH_TEST_ENABLED_INVERTED; + } else { + depth_test = DEPTH_TEST_ENABLED; + } cull_mode = RS::CullMode(cull_modei); vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL; // We can always read vertices and normals. @@ -3054,6 +3086,11 @@ void SceneShaderData::set_code(const String &p_code) { uses_vertex_time = gen_code.uses_vertex_time; uses_fragment_time = gen_code.uses_fragment_time; + stencil_enabled = stencil_referencei != -1; + stencil_flags = stencil_readi | stencil_writei | stencil_write_depth_faili; + stencil_compare = StencilCompare(stencil_comparei); + stencil_reference = stencil_referencei; + #ifdef DEBUG_ENABLED if (uses_particle_trails) { WARN_PRINT_ONCE_ED("Particle trails are only available when using the Forward+ or Mobile renderers."); @@ -3120,7 +3157,7 @@ bool SceneShaderData::casts_shadows() const { bool has_base_alpha = (uses_alpha && !uses_alpha_clip) || has_read_screen_alpha; bool has_alpha = has_base_alpha || uses_blend_alpha; - return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED)); + return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test != DEPTH_TEST_ENABLED)); } RS::ShaderNativeSourceCode SceneShaderData::get_native_source_code() const { diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index d7813dec27..e835674d85 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -259,7 +259,25 @@ struct SceneShaderData : public ShaderData { enum DepthTest { DEPTH_TEST_DISABLED, - DEPTH_TEST_ENABLED + DEPTH_TEST_ENABLED, + DEPTH_TEST_ENABLED_INVERTED, + }; + + enum StencilCompare { + STENCIL_COMPARE_LESS, + STENCIL_COMPARE_EQUAL, + STENCIL_COMPARE_LESS_OR_EQUAL, + STENCIL_COMPARE_GREATER, + STENCIL_COMPARE_NOT_EQUAL, + STENCIL_COMPARE_GREATER_OR_EQUAL, + STENCIL_COMPARE_ALWAYS, + STENCIL_COMPARE_MAX // not an actual operator, just the amount of operators + }; + + enum StencilFlags { + STENCIL_FLAG_READ = 1, + STENCIL_FLAG_WRITE = 2, + STENCIL_FLAG_WRITE_DEPTH_FAIL = 4, }; enum AlphaAntiAliasing { @@ -287,6 +305,11 @@ struct SceneShaderData : public ShaderData { DepthTest depth_test; RS::CullMode cull_mode; + StencilCompare stencil_compare; + uint32_t stencil_flags; + int32_t stencil_reference; + bool stencil_enabled; + bool uses_point_size; bool uses_alpha; bool uses_alpha_clip; diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index c56545c168..bb14b5e7bf 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -65,14 +65,14 @@ void RenderSceneBuffersGLES3::_rt_attach_textures(GLuint p_color, GLuint p_depth if (p_samples > 1) { #if defined(ANDROID_ENABLED) || defined(WEB_ENABLED) glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, p_samples, 0, p_view_count); - glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count); + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count); #else ERR_PRINT_ONCE("Multiview MSAA isn't supported on this platform."); #endif } else { #ifndef IOS_ENABLED glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, 0, p_view_count); - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, 0, p_view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, p_depth, 0, 0, p_view_count); #else ERR_PRINT_ONCE("Multiview isn't supported on this platform."); #endif @@ -81,13 +81,13 @@ void RenderSceneBuffersGLES3::_rt_attach_textures(GLuint p_color, GLuint p_depth if (p_samples > 1) { #ifdef ANDROID_ENABLED glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0, p_samples); - glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples); + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples); #else ERR_PRINT_ONCE("MSAA via EXT_multisampled_render_to_texture isn't supported on this platform."); #endif } else { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0); } } } @@ -198,7 +198,8 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { ERR_FAIL_COND(view_count == 0); bool use_internal_buffer = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF || apply_color_adjustments_in_post; - uint32_t depth_format_size = 3; + GLenum depth_format = GL_DEPTH24_STENCIL8; + uint32_t depth_format_size = 4; bool use_multiview = view_count > 1; if ((!use_internal_buffer || internal3d.color != 0) && (msaa3d.mode == RS::VIEWPORT_MSAA_DISABLED || msaa3d.color != 0)) { @@ -232,9 +233,9 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { glBindTexture(texture_target, internal3d.depth); if (use_multiview) { - glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage3D(texture_target, 0, depth_format, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); } else { - glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage2D(texture_target, 0, depth_format, internal_size.x, internal_size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); } glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -252,13 +253,13 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { #ifndef IOS_ENABLED if (use_multiview) { glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, internal3d.color, 0, 0, view_count); - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, internal3d.depth, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, internal3d.depth, 0, 0, view_count); } else { #else { #endif glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, internal3d.color, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, internal3d.depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texture_target, internal3d.depth, 0); } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -301,15 +302,15 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { glGenRenderbuffers(1, &msaa3d.depth); glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.depth); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y); - GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * 3 * msaa3d.samples, "MSAA 3D depth render buffer"); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, depth_format, internal_size.x, internal_size.y); + GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size * msaa3d.samples, "MSAA 3D depth render buffer"); // Create our MSAA 3D FBO. glGenFramebuffers(1, &msaa3d.fbo); glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaa3d.color); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaa3d.depth); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msaa3d.depth); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -343,9 +344,9 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.depth); #ifdef ANDROID_ENABLED - glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); + glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, depth_format, internal_size.x, internal_size.y, view_count, GL_TRUE); #else - glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); + glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, depth_format, internal_size.x, internal_size.y, view_count, GL_TRUE); #endif GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size * msaa3d.samples, "MSAA 3D depth texture"); @@ -355,7 +356,7 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, msaa3d.color, 0, 0, view_count); - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, msaa3d.depth, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, msaa3d.depth, 0, 0, view_count); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -476,7 +477,8 @@ void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_de bool use_multiview = view_count > 1 && GLES3::Config::get_singleton()->multiview_supported; GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; - uint32_t depth_format_size = 3; + GLenum depth_format = GL_DEPTH24_STENCIL8; + uint32_t depth_format_size = 4; if (backbuffer3d.color == 0 && p_need_color) { glGenTextures(1, &backbuffer3d.color); @@ -511,9 +513,9 @@ void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_de glBindTexture(texture_target, backbuffer3d.depth); if (use_multiview) { - glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage3D(texture_target, 0, depth_format, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT, nullptr); } else { - glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage2D(texture_target, 0, depth_format, internal_size.x, internal_size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT, nullptr); } glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -525,12 +527,12 @@ void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_de #ifndef IOS_ENABLED if (use_multiview) { - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, backbuffer3d.depth, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, backbuffer3d.depth, 0, 0, view_count); } else { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, backbuffer3d.depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texture_target, backbuffer3d.depth, 0); } } diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 3556369ecb..41c9994633 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -2185,14 +2185,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { ERR_FAIL_NULL(texture); rt->depth = texture->tex_id; + rt->depth_has_stencil = rt->overridden.depth_has_stencil; } else { glGenTextures(1, &rt->depth); glBindTexture(texture_target, rt->depth); if (use_multiview) { - glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage3D(texture_target, 0, GL_DEPTH24_STENCIL8, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); } else { - glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage2D(texture_target, 0, GL_DEPTH24_STENCIL8, rt->size.x, rt->size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); } glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -2200,16 +2201,19 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLES3::Utilities::get_singleton()->texture_allocated_data(rt->depth, rt->size.x * rt->size.y * rt->view_count * 3, "Render target depth texture"); + rt->depth_has_stencil = true; + + GLES3::Utilities::get_singleton()->texture_allocated_data(rt->depth, rt->size.x * rt->size.y * rt->view_count * 4, "Render target depth texture"); } + #ifndef IOS_ENABLED if (use_multiview) { - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, rt->depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count); } else { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, rt->depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, rt->depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, texture_target, rt->depth, 0); } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -2356,12 +2360,33 @@ void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_s if (rt->backbuffer_depth == 0 && uses_depth_texture) { glGenTextures(1, &rt->backbuffer_depth); glBindTexture(texture_target, rt->backbuffer_depth); - if (use_multiview) { - glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + + GLint internal_format; + GLenum format; + GLenum type; + GLenum attachment; + int element_size; + + if (rt->depth_has_stencil) { + internal_format = GL_DEPTH24_STENCIL8; + format = GL_DEPTH_STENCIL; + type = GL_UNSIGNED_INT_24_8; + attachment = GL_DEPTH_STENCIL_ATTACHMENT; + element_size = 4; } else { - glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + internal_format = GL_DEPTH_COMPONENT24; + format = GL_DEPTH_COMPONENT; + type = GL_UNSIGNED_INT; + attachment = GL_DEPTH_ATTACHMENT; + element_size = 3; } - GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer_depth, rt->size.x * rt->size.y * rt->view_count * 3, "Render target backbuffer depth texture"); + + if (use_multiview) { + glTexImage3D(texture_target, 0, internal_format, rt->size.x, rt->size.y, rt->view_count, 0, format, type, nullptr); + } else { + glTexImage2D(texture_target, 0, internal_format, rt->size.x, rt->size.y, 0, format, type, nullptr); + } + GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer_depth, rt->size.x * rt->size.y * rt->view_count * element_size, "Render target backbuffer depth texture"); glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -2369,12 +2394,12 @@ void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_s glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #ifndef IOS_ENABLED if (use_multiview) { - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->backbuffer_depth, 0, 0, rt->view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment, rt->backbuffer_depth, 0, 0, rt->view_count); } else { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->backbuffer_depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, rt->backbuffer_depth, 0); } } } @@ -2548,6 +2573,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color rt->overridden.color = p_color_texture; rt->overridden.depth = p_depth_texture; + rt->overridden.depth_has_stencil = p_depth_texture.is_null(); rt->overridden.is_overridden = true; uint32_t hash_key = hash_murmur3_one_64(p_color_texture.get_id()); @@ -2559,6 +2585,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color rt->fbo = cache->get().fbo; rt->color = cache->get().color; rt->depth = cache->get().depth; + rt->depth_has_stencil = cache->get().depth_has_stencil; rt->size = cache->get().size; rt->texture = p_color_texture; return; @@ -2570,6 +2597,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color new_entry.fbo = rt->fbo; new_entry.color = rt->color; new_entry.depth = rt->depth; + new_entry.depth_has_stencil = rt->depth_has_stencil; new_entry.size = rt->size; // Keep track of any textures we had to allocate because they weren't overridden. if (p_color_texture.is_null()) { diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index c075b562ec..df51fc5c91 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -349,6 +349,7 @@ struct RenderTarget { GLuint backbuffer_fbo = 0; GLuint backbuffer = 0; GLuint backbuffer_depth = 0; + bool depth_has_stencil = true; bool hdr = false; // For Compatibility this effects both 2D and 3D rendering! GLuint color_internal_format = GL_RGBA8; @@ -377,6 +378,7 @@ struct RenderTarget { struct RTOverridden { bool is_overridden = false; + bool depth_has_stencil = false; RID color; RID depth; RID velocity; @@ -387,6 +389,7 @@ struct RenderTarget { GLuint depth; Size2i size; Vector allocated_textures; + bool depth_has_stencil; }; RBMap fbo_cache; } overridden; diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm index a2c20e144b..f9a0a19608 100644 --- a/drivers/metal/rendering_device_driver_metal.mm +++ b/drivers/metal/rendering_device_driver_metal.mm @@ -76,8 +76,8 @@ os_log_t LOG_DRIVER; os_log_t LOG_INTERVALS; __attribute__((constructor)) static void InitializeLogging(void) { - LOG_DRIVER = os_log_create("org.godotengine.godot.metal", OS_LOG_CATEGORY_POINTS_OF_INTEREST); - LOG_INTERVALS = os_log_create("org.godotengine.godot.metal", "events"); + LOG_DRIVER = os_log_create("org.redotengine.godot.metal", OS_LOG_CATEGORY_POINTS_OF_INTEREST); + LOG_INTERVALS = os_log_create("org.redotengine.godot.metal", "events"); } /*****************/ diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index 116270dd46..4c78be9a56 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -157,7 +157,7 @@ Error DirAccessWindows::change_dir(String p_dir) { Char16String real_current_dir_name; size_t str_len = GetCurrentDirectoryW(0, nullptr); - real_current_dir_name.resize(str_len + 1); + real_current_dir_name.resize_uninitialized(str_len + 1); GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw()); String prev_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()); @@ -167,7 +167,7 @@ Error DirAccessWindows::change_dir(String p_dir) { String base = _get_root_path(); if (!base.is_empty()) { str_len = GetCurrentDirectoryW(0, nullptr); - real_current_dir_name.resize(str_len + 1); + real_current_dir_name.resize_uninitialized(str_len + 1); GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw()); String new_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/'); if (!new_dir.begins_with(base)) { @@ -177,7 +177,7 @@ Error DirAccessWindows::change_dir(String p_dir) { if (worked) { str_len = GetCurrentDirectoryW(0, nullptr); - real_current_dir_name.resize(str_len + 1); + real_current_dir_name.resize_uninitialized(str_len + 1); GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw()); current_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()); } @@ -449,7 +449,7 @@ String DirAccessWindows::read_link(String p_file) { return f; } Char16String cs; - cs.resize(ret + 1); + cs.resize_uninitialized(ret + 1); GetFinalPathNameByHandleW(hfile, (LPWSTR)cs.ptrw(), ret, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED); CloseHandle(hfile); @@ -477,7 +477,7 @@ DirAccessWindows::DirAccessWindows() { Char16String real_current_dir_name; size_t str_len = GetCurrentDirectoryW(0, nullptr); - real_current_dir_name.resize(str_len + 1); + real_current_dir_name.resize_uninitialized(str_len + 1); GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw()); current_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()); diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 35a1ccb968..396bcf2b6f 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -87,7 +87,7 @@ String FileAccessWindows::fix_path(const String &p_path) const { if (r_path.is_relative_path()) { Char16String current_dir_name; size_t str_len = GetCurrentDirectoryW(0, nullptr); - current_dir_name.resize(str_len + 1); + current_dir_name.resize_uninitialized(str_len + 1); GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw()); r_path = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/').path_join(r_path); } diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 8028fbd37b..5979d43841 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -330,7 +330,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { continue; } - String base_path = animation->track_get_path(i); + String base_path = String(animation->track_get_path(i)); int end = base_path.find_char(':'); if (end != -1) { base_path = base_path.substr(0, end + 1); @@ -407,7 +407,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { int current_track = tracks[i]; - String path = animation->track_get_path(current_track); + String path = String(animation->track_get_path(current_track)); path = path.replace_first(base_path, ""); Color cc = color; @@ -765,7 +765,7 @@ bool AnimationBezierTrackEdit::_is_track_displayed(int p_track_index) { } if (is_filtered) { - String path = animation->track_get_path(p_track_index); + String path = String(animation->track_get_path(p_track_index)); if (root && root->has_node(path)) { Node *node = root->get_node(path); if (!node) { @@ -901,7 +901,7 @@ void AnimationBezierTrackEdit::set_filtered(bool p_filtered) { if (animation.is_null()) { return; } - String base_path = animation->track_get_path(selected_track); + String base_path = String(animation->track_get_path(selected_track)); if (is_filtered) { if (root && root->has_node(base_path)) { Node *node = root->get_node(base_path); @@ -911,7 +911,7 @@ void AnimationBezierTrackEdit::set_filtered(bool p_filtered) { continue; } - base_path = animation->track_get_path(i); + base_path = String(animation->track_get_path(i)); if (root && root->has_node(base_path)) { node = root->get_node(base_path); if (!node) { diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 87576c8cb4..67d02a30b8 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -2193,7 +2193,7 @@ void AnimationTrackEdit::_notification(int p_what) { } else { icon_cache = key_type_icon; - text = anim_path; + text = String(anim_path); } path_cache = text; @@ -2824,7 +2824,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { // Don't overlap track keys if they start at 0. if (path_rect.has_point(p_pos + Size2(type_icon->get_width(), 0))) { - return animation->track_get_path(track); + return String(animation->track_get_path(track)); } if (update_mode_rect.has_point(p_pos)) { @@ -3232,7 +3232,7 @@ void AnimationTrackEdit::gui_input(const Ref &p_event) { path->connect(SceneStringName(text_submitted), callable_mp(this, &AnimationTrackEdit::_path_submitted)); } - path->set_text(animation->track_get_path(track)); + path->set_text(String(animation->track_get_path(track))); const Vector2 theme_ofs = path->get_theme_stylebox(CoreStringName(normal), SNAME("LineEdit"))->get_offset(); moving_selection_attempt = false; @@ -3464,7 +3464,7 @@ Variant AnimationTrackEdit::get_drag_data(const Point2 &p_point) { Dictionary drag_data; drag_data["type"] = "animation_track"; - String base_path = animation->track_get_path(track); + String base_path = String(animation->track_get_path(track)); base_path = base_path.get_slicec(':', 0); // Remove sub-path. drag_data["group"] = base_path; drag_data["index"] = track; @@ -3495,7 +3495,7 @@ bool AnimationTrackEdit::can_drop_data(const Point2 &p_point, const Variant &p_d // Don't allow moving tracks outside their groups. if (get_editor()->is_grouping_tracks()) { - String base_path = animation->track_get_path(track); + String base_path = String(animation->track_get_path(track)); base_path = base_path.get_slicec(':', 0); // Remove sub-path. if (d["group"] != base_path) { return false; @@ -3526,7 +3526,7 @@ void AnimationTrackEdit::drop_data(const Point2 &p_point, const Variant &p_data) // Don't allow moving tracks outside their groups. if (get_editor()->is_grouping_tracks()) { - String base_path = animation->track_get_path(track); + String base_path = String(animation->track_get_path(track)); base_path = base_path.get_slicec(':', 0); // Remove sub-path. if (d["group"] != base_path) { return; @@ -4372,7 +4372,7 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_ } // Let's build a node path. - String path = root->get_path_to(p_node, true); + String path = String(root->get_path_to(p_node, true)); if (!p_sub.is_empty()) { path += ":" + p_sub; } @@ -4412,7 +4412,7 @@ bool AnimationTrackEditor::has_track(Node3D *p_node, const String &p_sub, const } // Let's build a node path. - String path = root->get_path_to(p_node, true); + String path = String(root->get_path_to(p_node, true)); if (!p_sub.is_empty()) { path += ":" + p_sub; } @@ -4425,11 +4425,11 @@ bool AnimationTrackEditor::has_track(Node3D *p_node, const String &p_sub, const } void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant &p_value) { - String path = p_path; + String path = String(p_path); // Animation property is a special case, always creates an animation track. for (int i = 0; i < animation->get_track_count(); i++) { - String np = animation->track_get_path(i); + String np = String(animation->track_get_path(i)); if (path == np && animation->track_get_type(i) == Animation::TYPE_ANIMATION) { // Exists. @@ -4462,7 +4462,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p ERR_FAIL_NULL(root); // Let's build a node path. - String path = root->get_path_to(p_node, true); + String path = String(root->get_path_to(p_node, true)); // Get the value from the subpath. Vector subpath = NodePath(p_property).get_as_property_path().get_subnames(); @@ -4511,14 +4511,14 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p inserted = true; } else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) { Variant actual_value; - String track_path = animation->track_get_path(i); - if (track_path == np) { + String track_path = String(animation->track_get_path(i)); + if (track_path == String(np)) { actual_value = value; // All good. } else { int sep = track_path.rfind_char(':'); if (sep != -1) { String base_path = track_path.substr(0, sep); - if (base_path == np) { + if (base_path == String(np)) { String value_name = track_path.substr(sep + 1); actual_value = value.get(value_name); } else { @@ -5019,7 +5019,7 @@ void AnimationTrackEditor::_update_tracks() { String filter_text = timeline->filter_track->get_text(); if (!filter_text.is_empty()) { - String target = animation->track_get_path(i); + String target = String(animation->track_get_path(i)); if (!target.containsn(filter_text)) { continue; } @@ -5089,7 +5089,7 @@ void AnimationTrackEditor::_update_tracks() { track_edits.push_back(track_edit); if (use_grouping) { - String base_path = animation->track_get_path(i); + String base_path = String(animation->track_get_path(i)); base_path = base_path.get_slicec(':', 0); // Remove sub-path. if (!group_sort.has(base_path)) { @@ -5102,7 +5102,7 @@ void AnimationTrackEditor::_update_tracks() { if (n) { icon = EditorNode::get_singleton()->get_object_icon(n, "Node"); name = n->get_name(); - tooltip = root->get_path_to(n); + tooltip = String(root->get_path_to(n)); } } @@ -6683,7 +6683,6 @@ void AnimationTrackEditor::goto_next_step(bool p_from_mouse_event, bool p_timeli } void AnimationTrackEditor::_edit_menu_pressed(int p_option) { - last_menu_track_opt = p_option; switch (p_option) { case EDIT_COPY_TRACKS: { track_copy_select->clear(); @@ -6713,7 +6712,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { path = NodePath(node->get_path().get_names(), path.get_subnames(), true); // Store full path instead for copying. } else { - text = path; + text = String(path); int sep = text.find_char(':'); if (sep != -1) { text = text.substr(sep + 1); @@ -6843,11 +6842,15 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { undo_redo->commit_action(); } break; - - case EDIT_SCALE_SELECTION: + case EDIT_SCALE_SELECTION: { + scale_dialog->popup_centered(Size2(200, 100) * EDSCALE); + scale->get_line_edit()->grab_focus(); + scale_from_cursor = false; + } break; case EDIT_SCALE_FROM_CURSOR: { scale_dialog->popup_centered(Size2(200, 100) * EDSCALE); scale->get_line_edit()->grab_focus(); + scale_from_cursor = true; } break; case EDIT_SCALE_CONFIRM: { if (selection.is_empty()) { @@ -6870,9 +6873,8 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { } len = to_t - from_t; - if (last_menu_track_opt == EDIT_SCALE_FROM_CURSOR) { + if (scale_from_cursor) { pivot = timeline->get_play_position(); - } else { pivot = from_t; } @@ -6914,7 +6916,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { to_restore.push_back(amr); } -#define NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * Math::abs(s) + from_t +#define NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * Math::abs(s) + pivot // 3 - Move the keys (re insert them). for (RBMap::Element *E = selection.back(); E; E = E->prev()) { float newpos = NEW_POS(E->get().pos); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index afa9caccbf..1d97ad31b1 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -804,7 +804,7 @@ class AnimationTrackEditor : public VBoxContainer { void _edit_menu_about_to_popup(); void _edit_menu_pressed(int p_option); - int last_menu_track_opt = 0; + bool scale_from_cursor = false; void _cleanup_animation(Ref p_animation); diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp index 8feb8829f2..54e5f7c09f 100644 --- a/editor/animation_track_editor_plugins.cpp +++ b/editor/animation_track_editor_plugins.cpp @@ -415,7 +415,7 @@ Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_se animation_name = animations.front()->get(); } else { // Go through other track to find if animation is set - String animation_path = get_animation()->track_get_path(get_track()); + String animation_path = String(get_animation()->track_get_path(get_track())); animation_path = animation_path.replace(":frame", ":animation"); int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track())); float track_time = get_animation()->track_get_key_time(get_track(), p_index); @@ -507,7 +507,7 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in animation_name = animations.front()->get(); } else { // Go through other track to find if animation is set - String animation_path = get_animation()->track_get_path(get_track()); + String animation_path = String(get_animation()->track_get_path(get_track())); animation_path = animation_path.replace(":frame", ":animation"); int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track())); float track_time = get_animation()->track_get_key_time(get_track(), p_index); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index c53e15abb3..b183395ab7 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -41,9 +41,13 @@ #include "editor/plugins/script_editor_plugin.h" #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" +#include "scene/gui/check_box.h" +#include "scene/gui/label.h" #include "scene/gui/line_edit.h" #include "scene/gui/menu_button.h" +#include "scene/gui/rich_text_label.h" #include "scene/gui/separator.h" +#include "scene/main/timer.h" #include "scene/resources/font.h" void GotoLinePopup::popup_find_line(CodeTextEditor *p_text_editor) { @@ -718,10 +722,6 @@ bool FindReplaceBar::is_selection_only() const { return selection_only->is_pressed(); } -void FindReplaceBar::set_error(const String &p_label) { - emit_signal(SNAME("error"), p_label); -} - void FindReplaceBar::set_text_edit(CodeTextEditor *p_text_editor) { if (p_text_editor == base_text_editor) { return; @@ -751,8 +751,6 @@ void FindReplaceBar::set_text_edit(CodeTextEditor *p_text_editor) { void FindReplaceBar::_bind_methods() { ClassDB::bind_method("_search_current", &FindReplaceBar::search_current); - - ADD_SIGNAL(MethodInfo("error")); } FindReplaceBar::FindReplaceBar() { @@ -1189,7 +1187,6 @@ void CodeTextEditor::set_find_replace_bar(FindReplaceBar *p_bar) { find_replace_bar = p_bar; find_replace_bar->set_text_edit(this); - find_replace_bar->connect("error", callable_mp(error, &Label::set_text)); } void CodeTextEditor::remove_find_replace_bar() { @@ -1197,7 +1194,6 @@ void CodeTextEditor::remove_find_replace_bar() { return; } - find_replace_bar->disconnect("error", callable_mp(error, &Label::set_text)); find_replace_bar = nullptr; } @@ -1518,20 +1514,35 @@ Variant CodeTextEditor::get_navigation_state() { } void CodeTextEditor::set_error(const String &p_error) { - // Trim the error message if it is more than 2 lines long. - if (p_error.count("\n") >= 2) { - Vector splits = p_error.split("\n"); - String trimmed_error = String("\n").join(splits.slice(0, 2)); - error->set_text(trimmed_error + "..."); + error->set_text(p_error); + + _update_error_content_height(); + + if (p_error.is_empty()) { + error->set_default_cursor_shape(CURSOR_ARROW); } else { - error->set_text(p_error); + error->set_default_cursor_shape(CURSOR_POINTING_HAND); + } +} + +void CodeTextEditor::_update_error_content_height() { + float margin_height = 0; + const Ref style = error->get_theme_stylebox(CoreStringName(normal)); + if (style.is_valid()) { + margin_height += style->get_content_margin(SIDE_TOP) + style->get_content_margin(SIDE_BOTTOM); } - if (!p_error.is_empty()) { - error->set_default_cursor_shape(CURSOR_POINTING_HAND); - } else { - error->set_default_cursor_shape(CURSOR_ARROW); + const float content_height = margin_height + error->get_content_height(); + + float content_max_height = margin_height; + for (int i = 0; i < 3; i++) { + if (i >= error->get_line_count()) { + break; + } + content_max_height += error->get_line_height(i); } + + error->set_custom_minimum_size(Size2(0, CLAMP(content_height, 0, content_max_height))); } void CodeTextEditor::set_error_pos(int p_line, int p_column) { @@ -1561,28 +1572,36 @@ void CodeTextEditor::goto_error() { void CodeTextEditor::_update_text_editor_theme() { emit_signal(SNAME("load_theme_settings")); - error_button->set_button_icon(get_editor_theme_icon(SNAME("StatusError"))); - warning_button->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); - - Ref status_bar_font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts)); - int status_bar_font_size = get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts)); - - int count = status_bar->get_child_count(); - for (int i = 0; i < count; i++) { - Control *n = Object::cast_to(status_bar->get_child(i)); - if (n) { - n->add_theme_font_override(SceneStringName(font), status_bar_font); - n->add_theme_font_size_override(SceneStringName(font_size), status_bar_font_size); - } - } - + const Ref status_bar_font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts)); + const int status_bar_font_size = get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts)); const Color &error_color = get_theme_color(SNAME("error_color"), EditorStringName(Editor)); const Color &warning_color = get_theme_color(SNAME("warning_color"), EditorStringName(Editor)); + const Ref label_stylebox = get_theme_stylebox(SNAME("normal"), SNAME("Label")); // Empty stylebox. - error->add_theme_color_override(SceneStringName(font_color), error_color); + error->begin_bulk_theme_override(); + error->add_theme_font_override(SNAME("normal_font"), status_bar_font); + error->add_theme_font_size_override(SNAME("normal_font_size"), status_bar_font_size); + error->add_theme_color_override(SNAME("default_color"), error_color); + error->add_theme_style_override(SNAME("normal"), label_stylebox); + error->end_bulk_theme_override(); + + error_button->set_button_icon(get_editor_theme_icon(SNAME("StatusError"))); error_button->add_theme_color_override(SceneStringName(font_color), error_color); + + warning_button->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); warning_button->add_theme_color_override(SceneStringName(font_color), warning_color); + const int child_count = status_bar->get_child_count(); + for (int i = 0; i < child_count; i++) { + Control *child = Object::cast_to(status_bar->get_child(i)); + if (child) { + child->begin_bulk_theme_override(); + child->add_theme_font_override(SceneStringName(font), status_bar_font); + child->add_theme_font_size_override(SceneStringName(font_size), status_bar_font_size); + child->end_bulk_theme_override(); + } + } + _update_font_ligatures(); } @@ -1911,19 +1930,16 @@ CodeTextEditor::CodeTextEditor() { toggle_files_button->hide(); // Error - ScrollContainer *scroll = memnew(ScrollContainer); - scroll->set_h_size_flags(SIZE_EXPAND_FILL); - scroll->set_v_size_flags(SIZE_EXPAND_FILL); - scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); - status_bar->add_child(scroll); - - error = memnew(Label); - error->set_focus_mode(FOCUS_ACCESSIBILITY); + error = memnew(RichTextLabel); + error->set_use_bbcode(true); + error->set_selection_enabled(true); + error->set_context_menu_enabled(true); error->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); - error->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER); - error->set_mouse_filter(MOUSE_FILTER_STOP); - scroll->add_child(error); + error->set_h_size_flags(SIZE_EXPAND_FILL); + error->set_v_size_flags(SIZE_SHRINK_CENTER); error->connect(SceneStringName(gui_input), callable_mp(this, &CodeTextEditor::_error_pressed)); + error->connect(SceneStringName(resized), callable_mp(this, &CodeTextEditor::_update_error_content_height)); + status_bar->add_child(error); // Errors error_button = memnew(Button); diff --git a/editor/code_editor.h b/editor/code_editor.h index 4db506c97a..9cb87548ba 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -33,17 +33,17 @@ #pragma once #include "scene/gui/box_container.h" -#include "scene/gui/button.h" -#include "scene/gui/check_box.h" #include "scene/gui/code_edit.h" #include "scene/gui/dialogs.h" -#include "scene/gui/label.h" -#include "scene/gui/popup.h" -#include "scene/main/timer.h" -class MenuButton; +class Button; +class CheckBox; class CodeTextEditor; +class Label; class LineEdit; +class MenuButton; +class RichTextLabel; +class Timer; class GotoLinePopup : public PopupPanel { GDCLASS(GotoLinePopup, PopupPanel); @@ -140,7 +140,6 @@ public: bool is_case_sensitive() const; bool is_whole_words() const; bool is_selection_only() const; - void set_error(const String &p_label); void set_text_edit(CodeTextEditor *p_text_editor); @@ -185,7 +184,7 @@ class CodeTextEditor : public VBoxContainer { float zoom_factor = 1.0f; - Label *error = nullptr; + RichTextLabel *error = nullptr; int error_line; int error_column; @@ -213,6 +212,8 @@ class CodeTextEditor : public VBoxContainer { void _zoom_out(); void _zoom_to(float p_zoom_factor); + void _update_error_content_height(); + void _error_button_pressed(); void _warning_button_pressed(); void _set_show_errors_panel(bool p_show); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index a8e60fdcae..9f0df5d870 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -292,7 +292,7 @@ List ConnectDialog::_filter_method_list(const List &p_me LocalVector> effective_args; int unbind = get_unbinds(); - effective_args.reserve(p_signal.arguments.size() - unbind); + effective_args.reserve(MAX(p_signal.arguments.size() - unbind, 0)); for (int64_t i = 0; i < p_signal.arguments.size() - unbind; i++) { PropertyInfo pi = p_signal.arguments[i]; effective_args.push_back(Pair(pi.type, pi.class_name)); @@ -933,7 +933,7 @@ ConnectDialog::~ConnectDialog() { Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const { // If it's not a doc tooltip, fallback to the default one. - if (p_text.is_empty() || p_text.contains("::")) { + if (p_text.is_empty() || p_text.contains(" :: ")) { return nullptr; } diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp index 4fc0e36367..aad28abd10 100644 --- a/editor/debugger/editor_debugger_inspector.cpp +++ b/editor/debugger/editor_debugger_inspector.cpp @@ -45,14 +45,17 @@ bool EditorDebuggerRemoteObjects::_set(const StringName &p_name, const Variant & bool EditorDebuggerRemoteObjects::_set_impl(const StringName &p_name, const Variant &p_value, const String &p_field) { String name = p_name; - - if (name.begins_with("Metadata/")) { - name = name.replace_first("Metadata/", "metadata/"); - } if (!prop_values.has(name) || String(name).begins_with("Constants/")) { return false; } + // Change it back to the real name when fetching. + if (name == "Script") { + name = "script"; + } else if (name.begins_with("Metadata/")) { + name = name.replace_first("Metadata/", "metadata/"); + } + Dictionary &values = prop_values[p_name]; Dictionary old_values = values.duplicate(); for (const uint64_t key : values.keys()) { @@ -72,14 +75,17 @@ bool EditorDebuggerRemoteObjects::_set_impl(const StringName &p_name, const Vari bool EditorDebuggerRemoteObjects::_get(const StringName &p_name, Variant &r_ret) const { String name = p_name; - - if (name.begins_with("Metadata/")) { - name = name.replace_first("Metadata/", "metadata/"); - } if (!prop_values.has(name)) { return false; } + // Change it back to the real name when fetching. + if (name == "Script") { + name = "script"; + } else if (name.begins_with("Metadata/")) { + name = name.replace_first("Metadata/", "metadata/"); + } + r_ret = prop_values[p_name][remote_object_ids[0]]; return true; } @@ -87,11 +93,6 @@ bool EditorDebuggerRemoteObjects::_get(const StringName &p_name, Variant &r_ret) void EditorDebuggerRemoteObjects::_get_property_list(List *p_list) const { p_list->clear(); // Sorry, don't want any categories. for (const PropertyInfo &prop : prop_list) { - if (prop.name == "script") { - // Skip the script property, it's always added by the non-virtual method. - continue; - } - p_list->push_back(prop); } } @@ -117,6 +118,8 @@ Variant EditorDebuggerRemoteObjects::get_variant(const StringName &p_name) { void EditorDebuggerRemoteObjects::_bind_methods() { ClassDB::bind_method(D_METHOD("get_title"), &EditorDebuggerRemoteObjects::get_title); + ClassDB::bind_method("_hide_script_from_inspector", &EditorDebuggerRemoteObjects::_hide_script_from_inspector); + ClassDB::bind_method("_hide_metadata_from_inspector", &EditorDebuggerRemoteObjects::_hide_metadata_from_inspector); ADD_SIGNAL(MethodInfo("values_edited", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::DICTIONARY, "values", PROPERTY_HINT_DICTIONARY_TYPE, "uint64_t:Variant"), PropertyInfo(Variant::STRING, "field"))); } @@ -232,10 +235,11 @@ EditorDebuggerRemoteObjects *EditorDebuggerInspector::set_objects(const Array &p for (const SceneDebuggerObject &obj : objects) { for (const SceneDebuggerObject::SceneDebuggerProperty &prop : obj.properties) { PropertyInfo pinfo = prop.first; + // Rename those variables, so they don't conflict with the ones from the resource itself. if (pinfo.name == "script") { - continue; // Added later manually, since this is intercepted before being set (check Variant Object::get()). + pinfo.name = "Script"; } else if (pinfo.name.begins_with("metadata/")) { - pinfo.name = pinfo.name.replace_first("metadata/", "Metadata/"); // Trick to not get actual metadata edited from EditorDebuggerRemoteObjects. + pinfo.name = pinfo.name.replace_first("metadata/", "Metadata/"); } if (!usage.has(pinfo.name)) { @@ -277,33 +281,18 @@ EditorDebuggerRemoteObjects *EditorDebuggerInspector::set_objects(const Array &p const PropertyInfo &pinfo = KV.value.prop.first; Variant var = KV.value.values[remote_objects->remote_object_ids[0]]; - if (pinfo.type == Variant::OBJECT) { - if (var.is_string()) { - String path = var; - if (path.contains("::")) { - // Built-in resource. - String base_path = path.get_slice("::", 0); - Ref dependency = ResourceLoader::load(base_path); - if (dependency.is_valid()) { - remote_dependencies.insert(dependency); - } - } - var = ResourceLoader::load(path); - KV.value.values[remote_objects->remote_object_ids[0]] = var; - - if (pinfo.hint_string == "Script") { - if (remote_objects->get_script() != var) { - remote_objects->set_script(Ref()); - Ref