diff options
176 files changed, 2002 insertions, 717 deletions
diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml index d516c37d16..950e1e51cc 100644 --- a/.github/workflows/android_builds.yml +++ b/.github/workflows/android_builds.yml @@ -14,7 +14,7 @@ concurrency: jobs: build-android: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 name: ${{ matrix.name }} strategy: fail-fast: false diff --git a/.github/workflows/godot_cpp_test.yml b/.github/workflows/godot_cpp_test.yml index e26c109d75..dc82a7cb3c 100644 --- a/.github/workflows/godot_cpp_test.yml +++ b/.github/workflows/godot_cpp_test.yml @@ -15,7 +15,7 @@ concurrency: jobs: godot-cpp-tests: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 name: Build and test Godot CPP steps: - name: Checkout diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index dc3d9f3786..cf653caa3d 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -17,7 +17,8 @@ concurrency: jobs: build-linux: - runs-on: ubuntu-20.04 + # If unspecified, stay one LTS before latest to increase portability of Linux artifacts. + runs-on: ${{ matrix.os || 'ubuntu-22.04' }} name: ${{ matrix.name }} strategy: fail-fast: false @@ -60,6 +61,8 @@ jobs: artifact: false # Test our oldest supported SCons/Python versions on one arbitrary editor build. legacy-scons: true + # Python 3.6 unavailable on 22.04. + os: ubuntu-20.04 - name: Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld) cache-name: linux-editor-thread-sanitizer @@ -100,7 +103,7 @@ jobs: run: | sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EB8B81E14DA65431D7504EA8F63F0F2B90935439 - sudo add-apt-repository "deb https://ppa.launchpadcontent.net/kisak/turtle/ubuntu focal main" + sudo add-apt-repository "deb https://ppa.launchpadcontent.net/kisak/turtle/ubuntu ${{ matrix.os == 'ubuntu-20.04' && 'focal' || 'jammy' }} main" sudo apt-get install -qq mesa-vulkan-drivers # TODO: Figure out somehow how to embed this one. diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml index ff102a06cc..9b326cb43e 100644 --- a/.github/workflows/static_checks.yml +++ b/.github/workflows/static_checks.yml @@ -9,7 +9,7 @@ concurrency: jobs: static-checks: name: Code style, file formatting, and docs - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml index ec57fa2f0d..9ed8475769 100644 --- a/.github/workflows/web_builds.yml +++ b/.github/workflows/web_builds.yml @@ -15,7 +15,7 @@ concurrency: jobs: web-template: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 name: ${{ matrix.name }} strategy: fail-fast: false diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 6ef466bee0..967a7fba75 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -132,6 +132,10 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) { return ::ResourceLoader::get_resource_uid(p_path); } +Vector<String> ResourceLoader::list_directory(const String &p_directory) { + return ::ResourceLoader::list_directory(p_directory); +} + void ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE)); ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL_ARRAY); @@ -147,6 +151,7 @@ void ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cached_ref", "path"), &ResourceLoader::get_cached_ref); ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &ResourceLoader::exists, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_resource_uid", "path"), &ResourceLoader::get_resource_uid); + ClassDB::bind_method(D_METHOD("list_directory", "directory_path"), &ResourceLoader::list_directory); BIND_ENUM_CONSTANT(THREAD_LOAD_INVALID_RESOURCE); BIND_ENUM_CONSTANT(THREAD_LOAD_IN_PROGRESS); diff --git a/core/core_bind.h b/core/core_bind.h index 430ecdc906..3ae54017fe 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -86,6 +86,8 @@ public: bool exists(const String &p_path, const String &p_type_hint = ""); ResourceUID::ID get_resource_uid(const String &p_path); + Vector<String> list_directory(const String &p_directory); + ResourceLoader() { singleton = this; } }; diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index bfd1a53f3e..b9af1bfb57 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -48,7 +48,7 @@ Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t } void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted) { - String simplified_path = p_path.simplify_path(); + String simplified_path = p_path.simplify_path().trim_prefix("res://"); PathMD5 pmd5(simplified_path.md5_buffer()); bool exists = files.has(pmd5); @@ -68,13 +68,11 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64 } if (!exists) { - //search for dir - String p = simplified_path.replace_first("res://", ""); + // Search for directory. PackedDir *cd = root; - if (p.contains("/")) { //in a subdir - - Vector<String> ds = p.get_base_dir().split("/"); + if (simplified_path.contains("/")) { // In a subdirectory. + Vector<String> ds = simplified_path.get_base_dir().split("/"); for (int j = 0; j < ds.size(); j++) { if (!cd->subdirs.has(ds[j])) { @@ -89,13 +87,40 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64 } } String filename = simplified_path.get_file(); - // Don't add as a file if the path points to a directory + // Don't add as a file if the path points to a directory. if (!filename.is_empty()) { cd->files.insert(filename); } } } +void PackedData::remove_path(const String &p_path) { + String simplified_path = p_path.simplify_path().trim_prefix("res://"); + PathMD5 pmd5(simplified_path.md5_buffer()); + if (!files.has(pmd5)) { + return; + } + + // Search for directory. + PackedDir *cd = root; + + if (simplified_path.contains("/")) { // In a subdirectory. + Vector<String> ds = simplified_path.get_base_dir().split("/"); + + for (int j = 0; j < ds.size(); j++) { + if (!cd->subdirs.has(ds[j])) { + return; // Subdirectory does not exist, do not bother creating. + } else { + cd = cd->subdirs[ds[j]]; + } + } + } + + cd->files.erase(simplified_path.get_file()); + + files.erase(pmd5); +} + void PackedData::add_pack_source(PackSource *p_source) { if (p_source != nullptr) { sources.push_back(p_source); @@ -103,15 +128,32 @@ void PackedData::add_pack_source(PackSource *p_source) { } uint8_t *PackedData::get_file_hash(const String &p_path) { - PathMD5 pmd5(p_path.md5_buffer()); + String simplified_path = p_path.simplify_path().trim_prefix("res://"); + PathMD5 pmd5(simplified_path.md5_buffer()); HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5); - if (!E || E->value.offset == 0) { + if (!E) { return nullptr; } return E->value.md5; } +HashSet<String> PackedData::get_file_paths() const { + HashSet<String> file_paths; + _get_file_paths(root, root->name, file_paths); + return file_paths; +} + +void PackedData::_get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet<String> &r_paths) const { + for (const String &E : p_dir->files) { + r_paths.insert(p_parent_dir.path_join(E)); + } + + for (const KeyValue<String, PackedDir *> &E : p_dir->subdirs) { + _get_file_paths(E.value, p_parent_dir.path_join(E.key), r_paths); + } +} + void PackedData::clear() { files.clear(); _free_packed_dirs(root); @@ -269,13 +311,17 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, String path; path.parse_utf8(cs.ptr()); - uint64_t ofs = file_base + f->get_64(); + uint64_t ofs = f->get_64(); uint64_t size = f->get_64(); uint8_t md5[16]; f->get_buffer(md5, 16); uint32_t flags = f->get_32(); - PackedData::get_singleton()->add_path(p_path, path, ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED)); + if (flags & PACK_FILE_REMOVAL) { // The file was removed. + PackedData::get_singleton()->remove_path(path); + } else { + PackedData::get_singleton()->add_path(p_path, path, file_base + ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED)); + } } return true; diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 57b7a5f87f..bf44b3a2db 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -49,7 +49,8 @@ enum PackFlags { }; enum PackFileFlags { - PACK_FILE_ENCRYPTED = 1 << 0 + PACK_FILE_ENCRYPTED = 1 << 0, + PACK_FILE_REMOVAL = 1 << 1, }; class PackSource; @@ -107,11 +108,14 @@ private: bool disabled = false; void _free_packed_dirs(PackedDir *p_dir); + void _get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet<String> &r_paths) const; public: void add_pack_source(PackSource *p_source); void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource + void remove_path(const String &p_path); uint8_t *get_file_hash(const String &p_path); + HashSet<String> get_file_paths() const; void set_disabled(bool p_disabled) { disabled = p_disabled; } _FORCE_INLINE_ bool is_disabled() const { return disabled; } @@ -190,14 +194,11 @@ public: }; Ref<FileAccess> PackedData::try_open_path(const String &p_path) { - String simplified_path = p_path.simplify_path(); + String simplified_path = p_path.simplify_path().trim_prefix("res://"); PathMD5 pmd5(simplified_path.md5_buffer()); HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5); if (!E) { - return nullptr; //not found - } - if (E->value.offset == 0) { - return nullptr; //was erased + return nullptr; // Not found. } return E->value.src->get_file(p_path, &E->value); diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index 70fcad543a..237ba30a80 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -662,15 +662,16 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { chunk_left -= rec; if (chunk_left == 0) { - if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') { + const int chunk_size = chunk.size(); + if (chunk[chunk_size - 2] != '\r' || chunk[chunk_size - 1] != '\n') { ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)"); status = STATUS_CONNECTION_ERROR; break; } - ret.resize(chunk.size() - 2); + ret.resize(chunk_size - 2); uint8_t *w = ret.ptrw(); - memcpy(w, chunk.ptr(), chunk.size() - 2); + memcpy(w, chunk.ptr(), chunk_size - 2); chunk.clear(); } diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index fae3de2a98..08e5353174 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -105,6 +105,19 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { return ERR_UNAVAILABLE; } +/* Bogus GCC warning here: + * In member function 'int RingBuffer<T>::read(T*, int, bool) [with T = unsigned char]', + * inlined from 'virtual Error PacketPeerUDP::get_packet(const uint8_t**, int&)' at core/io/packet_peer_udp.cpp:112:9, + * inlined from 'virtual Error PacketPeerUDP::get_packet(const uint8_t**, int&)' at core/io/packet_peer_udp.cpp:99:7: + * Error: ./core/ring_buffer.h:68:46: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=] + * 68 | p_buf[dst++] = read[pos + i]; + * | ~~~~~~~~~~~~~^~~~~~~ + */ +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic warning "-Wstringop-overflow=0" +#endif + uint32_t size = 0; uint8_t ipv6[16] = {}; rb.read(ipv6, 16, true); @@ -115,6 +128,11 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { --queue_count; *r_buffer = packet_buffer; r_buffer_size = size; + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + return OK; } diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 8ccf74261f..c832ef5700 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -48,7 +48,8 @@ static int _get_pad(int p_alignment, int p_n) { void PCKPacker::_bind_methods() { ClassDB::bind_method(D_METHOD("pck_start", "pck_path", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_file", "target_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_file_removal", "target_path"), &PCKPacker::add_file_removal); ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false)); } @@ -106,23 +107,42 @@ Error PCKPacker::pck_start(const String &p_pck_path, int p_alignment, const Stri return OK; } -Error PCKPacker::add_file(const String &p_pck_path, const String &p_src, bool p_encrypt) { +Error PCKPacker::add_file_removal(const String &p_target_path) { ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use."); - Ref<FileAccess> f = FileAccess::open(p_src, FileAccess::READ); + File pf; + // Simplify path here and on every 'files' access so that paths that have extra '/' + // symbols or 'res://' in them still match the MD5 hash for the saved path. + pf.path = p_target_path.simplify_path().trim_prefix("res://"); + pf.ofs = ofs; + pf.size = 0; + pf.removal = true; + + pf.md5.resize(16); + pf.md5.fill(0); + + files.push_back(pf); + + return OK; +} + +Error PCKPacker::add_file(const String &p_target_path, const String &p_source_path, bool p_encrypt) { + ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use."); + + Ref<FileAccess> f = FileAccess::open(p_source_path, FileAccess::READ); if (f.is_null()) { return ERR_FILE_CANT_OPEN; } File pf; // Simplify path here and on every 'files' access so that paths that have extra '/' - // symbols in them still match to the MD5 hash for the saved path. - pf.path = p_pck_path.simplify_path(); - pf.src_path = p_src; + // symbols or 'res://' in them still match the MD5 hash for the saved path. + pf.path = p_target_path.simplify_path().trim_prefix("res://"); + pf.src_path = p_source_path; pf.ofs = ofs; pf.size = f->get_length(); - Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_src); + Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_source_path); { unsigned char hash[16]; CryptoCore::md5(data.ptr(), data.size(), hash); @@ -195,6 +215,9 @@ Error PCKPacker::flush(bool p_verbose) { if (files[i].encrypted) { flags |= PACK_FILE_ENCRYPTED; } + if (files[i].removal) { + flags |= PACK_FILE_REMOVAL; + } fhead->store_32(flags); } @@ -218,6 +241,10 @@ Error PCKPacker::flush(bool p_verbose) { int count = 0; for (int i = 0; i < files.size(); i++) { + if (files[i].removal) { + continue; + } + Ref<FileAccess> src = FileAccess::open(files[i].src_path, FileAccess::READ); uint64_t to_write = files[i].size; diff --git a/core/io/pck_packer.h b/core/io/pck_packer.h index 5aac833532..043a1dbdb8 100644 --- a/core/io/pck_packer.h +++ b/core/io/pck_packer.h @@ -53,13 +53,15 @@ class PCKPacker : public RefCounted { uint64_t ofs = 0; uint64_t size = 0; bool encrypted = false; + bool removal = false; Vector<uint8_t> md5; }; Vector<File> files; public: Error pck_start(const String &p_pck_path, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false); - Error add_file(const String &p_pck_path, const String &p_src, bool p_encrypt = false); + Error add_file(const String &p_target_path, const String &p_source_path, bool p_encrypt = false); + Error add_file_removal(const String &p_target_path); Error flush(bool p_verbose = false); PCKPacker() {} diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index e6136603d4..ed11f96d03 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -1577,6 +1577,10 @@ ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_pat return loader.uid; } +bool ResourceFormatLoaderBinary::has_custom_uid_support() const { + return true; +} + /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 222e633e58..ec8d7ead5d 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -118,6 +118,7 @@ public: virtual String get_resource_script_class(const String &p_path) const override; virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override; virtual ResourceUID::ID get_resource_uid(const String &p_path) const override; + virtual bool has_custom_uid_support() const override; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override; virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) override; }; diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index e603f9dfde..b7a14f2b88 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -387,6 +387,10 @@ ResourceUID::ID ResourceFormatImporter::get_resource_uid(const String &p_path) c return pat.uid; } +bool ResourceFormatImporter::has_custom_uid_support() const { + return true; +} + Error ResourceFormatImporter::get_resource_import_info(const String &p_path, StringName &r_type, ResourceUID::ID &r_uid, String &r_import_group_file) const { PathAndType pat; Error err = _get_path_and_type(p_path, pat); diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index 3ca8f7c05d..c3d3c4b67e 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -70,6 +70,7 @@ public: virtual bool handles_type(const String &p_type) const override; virtual String get_resource_type(const String &p_path) const override; virtual ResourceUID::ID get_resource_uid(const String &p_path) const override; + virtual bool has_custom_uid_support() const override; virtual Variant get_resource_metadata(const String &p_path) const; virtual bool is_import_valid(const String &p_path) const override; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override; @@ -147,7 +148,7 @@ public: virtual void handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {} virtual String get_option_group_file() const { return String(); } - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0; virtual bool can_import_threaded() const { return false; } virtual void import_threaded_begin() {} virtual void import_threaded_end() {} diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 59de2879e2..3fea697d0b 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/core_bind.h" +#include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/io/resource_importer.h" #include "core/object/script_language.h" @@ -40,6 +41,7 @@ #include "core/os/safe_binary_mutex.h" #include "core/string/print_string.h" #include "core/string/translation_server.h" +#include "core/templates/rb_set.h" #include "core/variant/variant_parser.h" #include "servers/rendering_server.h" @@ -112,10 +114,21 @@ String ResourceFormatLoader::get_resource_script_class(const String &p_path) con ResourceUID::ID ResourceFormatLoader::get_resource_uid(const String &p_path) const { int64_t uid = ResourceUID::INVALID_ID; - GDVIRTUAL_CALL(_get_resource_uid, p_path, uid); + if (has_custom_uid_support()) { + GDVIRTUAL_CALL(_get_resource_uid, p_path, uid); + } else { + Ref<FileAccess> file = FileAccess::open(p_path + ".uid", FileAccess::READ); + if (file.is_valid()) { + uid = ResourceUID::get_singleton()->text_to_id(file->get_line()); + } + } return uid; } +bool ResourceFormatLoader::has_custom_uid_support() const { + return GDVIRTUAL_IS_OVERRIDDEN(_get_resource_uid); +} + void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { if (p_type.is_empty() || handles_type(p_type)) { get_recognized_extensions(p_extensions); @@ -1159,6 +1172,21 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) { return ResourceUID::INVALID_ID; } +bool ResourceLoader::has_custom_uid_support(const String &p_path) { + String local_path = _validate_local_path(p_path); + + for (int i = 0; i < loader_count; i++) { + if (!loader[i]->recognize_path(local_path)) { + continue; + } + if (loader[i]->has_custom_uid_support()) { + return true; + } + } + + return false; +} + String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_remapped) { String new_path = p_path; @@ -1448,6 +1476,60 @@ bool ResourceLoader::is_cleaning_tasks() { return cleaning_tasks; } +Vector<String> ResourceLoader::list_directory(const String &p_directory) { + RBSet<String> files_found; + Ref<DirAccess> dir = DirAccess::open(p_directory); + if (dir.is_null()) { + return Vector<String>(); + } + + Error err = dir->list_dir_begin(); + if (err != OK) { + return Vector<String>(); + } + + String d = dir->get_next(); + while (!d.is_empty()) { + bool recognized = false; + if (dir->current_is_dir()) { + if (d != "." && d != "..") { + d += "/"; + recognized = true; + } + } else { + if (d.ends_with(".import") || d.ends_with(".remap") || d.ends_with(".uid")) { + d = d.substr(0, d.rfind(".")); + } + + if (d.ends_with(".gdc")) { + d = d.substr(0, d.rfind(".")); + d += ".gd"; + } + + const String full_path = p_directory.path_join(d); + // Try all loaders and pick the first match for the type hint. + for (int i = 0; i < loader_count; i++) { + if (loader[i]->recognize_path(full_path)) { + recognized = true; + break; + } + } + } + + if (recognized) { + files_found.insert(d); + } + d = dir->get_next(); + } + + Vector<String> ret; + for (const String &f : files_found) { + ret.push_back(f); + } + + return ret; +} + void ResourceLoader::initialize() {} void ResourceLoader::finalize() {} diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 0d802ed1f4..ebd6024033 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -81,6 +81,7 @@ public: virtual String get_resource_type(const String &p_path) const; virtual String get_resource_script_class(const String &p_path) const; virtual ResourceUID::ID get_resource_uid(const String &p_path) const; + virtual bool has_custom_uid_support() const; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map); virtual bool is_import_valid(const String &p_path) const { return true; } @@ -238,6 +239,7 @@ public: static String get_resource_type(const String &p_path); static String get_resource_script_class(const String &p_path); static ResourceUID::ID get_resource_uid(const String &p_path); + static bool has_custom_uid_support(const String &p_path); static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); static Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map); static bool is_import_valid(const String &p_path); @@ -302,6 +304,8 @@ public: static bool is_cleaning_tasks(); + static Vector<String> list_directory(const String &p_directory); + static void initialize(); static void finalize(); }; diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index c49e15a3a0..3f1c468fb3 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -223,13 +223,13 @@ void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) { } uint8_t StreamPeer::get_u8() { - uint8_t buf[1]; + uint8_t buf[1] = {}; get_data(buf, 1); return buf[0]; } int8_t StreamPeer::get_8() { - uint8_t buf[1]; + uint8_t buf[1] = {}; get_data(buf, 1); return buf[0]; } diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp index f2b3d5e56a..d69d1f1b29 100644 --- a/core/io/tcp_server.cpp +++ b/core/io/tcp_server.cpp @@ -112,7 +112,7 @@ Ref<StreamPeerTCP> TCPServer::take_connection() { return conn; } - conn = Ref<StreamPeerTCP>(memnew(StreamPeerTCP)); + conn.instantiate(); conn->accept_socket(ns, ip, port); return conn; } diff --git a/core/string/translation_domain.cpp b/core/string/translation_domain.cpp index cf6689efff..1ff8dcd752 100644 --- a/core/string/translation_domain.cpp +++ b/core/string/translation_domain.cpp @@ -247,7 +247,10 @@ PackedStringArray TranslationDomain::get_loaded_locales() const { PackedStringArray locales; for (const Ref<Translation> &E : translations) { ERR_CONTINUE(E.is_null()); - locales.push_back(E->get_locale()); + const String &locale = E->get_locale(); + if (!locales.has(locale)) { + locales.push_back(locale); + } } return locales; } diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index 7818ed0706..e681835c5a 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -110,6 +110,16 @@ static _FORCE_INLINE_ uint32_t hash_one_uint64(const uint64_t p_int) { return uint32_t(v); } +static _FORCE_INLINE_ uint64_t hash64_murmur3_64(uint64_t key, uint64_t seed) { + key ^= seed; + key ^= key >> 33; + key *= 0xff51afd7ed558ccd; + key ^= key >> 33; + key *= 0xc4ceb9fe1a85ec53; + key ^= key >> 33; + return key; +} + #define HASH_MURMUR3_SEED 0x7F07C65 // Murmurhash3 32-bit version. // All MurmurHash versions are public domain software, and the author disclaims all copyright to their code. diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp index d82aa3583d..afb889551e 100644 --- a/core/variant/callable_bind.cpp +++ b/core/variant/callable_bind.cpp @@ -43,7 +43,7 @@ bool CallableCustomBind::_equal_func(const CallableCustom *p_a, const CallableCu const CallableCustomBind *a = static_cast<const CallableCustomBind *>(p_a); const CallableCustomBind *b = static_cast<const CallableCustomBind *>(p_b); - if (!(a->callable != b->callable)) { + if (a->callable != b->callable) { return false; } @@ -185,7 +185,7 @@ bool CallableCustomUnbind::_equal_func(const CallableCustom *p_a, const Callable const CallableCustomUnbind *a = static_cast<const CallableCustomUnbind *>(p_a); const CallableCustomUnbind *b = static_cast<const CallableCustomUnbind *>(p_b); - if (!(a->callable != b->callable)) { + if (a->callable != b->callable) { return false; } diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 65bfc29a55..54936eb8a2 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -951,7 +951,7 @@ bool Variant::is_zero() const { return *reinterpret_cast<const ::RID *>(_data._mem) == ::RID(); } case OBJECT: { - return _get_obj().obj == nullptr; + return get_validated_object() == nullptr; } case CALLABLE: { return reinterpret_cast<const Callable *>(_data._mem)->is_null(); diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index 1652f81d99..560067fc08 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -141,6 +141,10 @@ void register_named_setters_getters() { REGISTER_MEMBER(Color, h); REGISTER_MEMBER(Color, s); REGISTER_MEMBER(Color, v); + + REGISTER_MEMBER(Color, ok_hsl_h); + REGISTER_MEMBER(Color, ok_hsl_s); + REGISTER_MEMBER(Color, ok_hsl_l); } void unregister_named_setters_getters() { diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 322d2ab9d4..92faa55fe0 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -25,6 +25,7 @@ <return type="Basis" /> <description> Constructs a [Basis] identical to the [constant IDENTITY]. + [b]Note:[/b] In C#, this constructs a [Basis] with all of its components set to [constant Vector3.ZERO]. </description> </constructor> <constructor name="Basis"> diff --git a/doc/classes/CharFXTransform.xml b/doc/classes/CharFXTransform.xml index 403033f85d..28f5d38ee4 100644 --- a/doc/classes/CharFXTransform.xml +++ b/doc/classes/CharFXTransform.xml @@ -8,7 +8,6 @@ </description> <tutorials> <link title="BBCode in RichTextLabel">$DOCS_URL/tutorials/ui/bbcode_in_richtextlabel.html</link> - <link title="RichTextEffect test project (third-party)">https://github.com/Eoin-ONeill-Yokai/Godot-Rich-Text-Effect-Test-Project</link> </tutorials> <members> <member name="color" type="Color" setter="set_color" getter="get_color" default="Color(0, 0, 0, 1)" keywords="colour"> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index 9fe42fff90..546d90fa3c 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -19,7 +19,7 @@ <return type="Color" /> <description> Constructs a default [Color] from opaque black. This is the same as [constant BLACK]. - [b]Note:[/b] in C#, constructs an empty color with all of its components set to [code]0.0[/code] (transparent black). + [b]Note:[/b] In C#, this constructs a [Color] with all of its components set to [code]0.0[/code] (transparent black). </description> </constructor> <constructor name="Color"> @@ -495,6 +495,15 @@ <member name="h" type="float" setter="" getter="" default="0.0"> The HSV hue of this color, on the range 0 to 1. </member> + <member name="ok_hsl_h" type="float" setter="" getter="" default="0.0"> + The OKHSL hue of this color, on the range 0 to 1. + </member> + <member name="ok_hsl_l" type="float" setter="" getter="" default="0.0"> + The OKHSL lightness of this color, on the range 0 to 1. + </member> + <member name="ok_hsl_s" type="float" setter="" getter="" default="0.0"> + The OKHSL saturation of this color, on the range 0 to 1. + </member> <member name="r" type="float" setter="" getter="" default="0.0"> The color's red component, typically on the range of 0 to 1. </member> diff --git a/doc/classes/Cubemap.xml b/doc/classes/Cubemap.xml index b7da3c4ec6..8a850920f7 100644 --- a/doc/classes/Cubemap.xml +++ b/doc/classes/Cubemap.xml @@ -6,8 +6,27 @@ <description> A cubemap is made of 6 textures organized in layers. They are typically used for faking reflections in 3D rendering (see [ReflectionProbe]). It can be used to make an object look as if it's reflecting its surroundings. This usually delivers much better performance than other reflection methods. This resource is typically used as a uniform in custom shaders. Few core Godot methods make use of [Cubemap] resources. - To create such a texture file yourself, reimport your image files using the Godot Editor import presets. - [b]Note:[/b] Godot doesn't support using cubemaps in a [PanoramaSkyMaterial]. You can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cubemap to an equirectangular sky map. + To create such a texture file yourself, reimport your image files using the Godot Editor import presets. The expected image order is X+, X-, Y+, Y-, Z+, Z- (in Godot's coordinate system, so Y+ is "up" and Z- is "forward"). You can use one of the following templates as a base: + - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_2x3.webp]2×3 cubemap template (default layout option)[/url] + - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_3x2.webp]3×2 cubemap template[/url] + - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_1x6.webp]1×6 cubemap template[/url] + - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_6x1.webp]6×1 cubemap template[/url] + [b]Note:[/b] Godot doesn't support using cubemaps in a [PanoramaSkyMaterial]. To use a cubemap as a skybox, convert the default [PanoramaSkyMaterial] to a [ShaderMaterial] using the [b]Convert to ShaderMaterial[/b] resource dropdown option, then replace its code with the following: + [codeblock lang=text] + shader_type sky; + + uniform samplerCube source_panorama : filter_linear, source_color, hint_default_black; + uniform float exposure : hint_range(0, 128) = 1.0; + + void sky() { + // If importing a cubemap from another engine, you may need to flip one of the `EYEDIR` components below + // by replacing it with `-EYEDIR`. + vec3 eyedir = vec3(EYEDIR.x, EYEDIR.y, EYEDIR.z); + COLOR = texture(source_panorama, eyedir).rgb * exposure; + } + [/codeblock] + After replacing the shader code and saving, specify the imported Cubemap resource in the Shader Parameters section of the ShaderMaterial in the inspector. + Alternatively, you can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cubemap to an equirectangular sky map and use [PanoramaSkyMaterial] as usual. </description> <tutorials> </tutorials> diff --git a/doc/classes/DirAccess.xml b/doc/classes/DirAccess.xml index 9c71addf0c..dcd2d527e2 100644 --- a/doc/classes/DirAccess.xml +++ b/doc/classes/DirAccess.xml @@ -60,6 +60,7 @@ } [/csharp] [/codeblocks] + Keep in mind that file names may change or be remapped after export. If you want to see the actual resource file list as it appears in the editor, use [method ResourceLoader.list_directory] instead. </description> <tutorials> <link title="File system">$DOCS_URL/tutorials/scripting/filesystem.html</link> diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index dc1cd89642..23e3f4139c 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -35,9 +35,16 @@ <param index="0" name="resource" type="Resource" /> <param index="1" name="path" type="String" /> <description> - Customize a resource. If changes are made to it, return the same or a new resource. Otherwise, return [code]null[/code]. - The [i]path[/i] argument is only used when customizing an actual file, otherwise this means that this resource is part of another one and it will be empty. + Customize a resource. If changes are made to it, return the same or a new resource. Otherwise, return [code]null[/code]. When a new resource is returned, [param resource] will be replaced by a copy of the new resource. + The [param path] argument is only used when customizing an actual file, otherwise this means that this resource is part of another one and it will be empty. Implementing this method is required if [method _begin_customize_resources] returns [code]true[/code]. + [b]Note:[/b] When customizing any of the following types and returning another resource, the other resource should not be skipped using [method skip] in [method _export_file]: + - [AtlasTexture] + - [CompressedCubemap] + - [CompressedCubemapArray] + - [CompressedTexture2D] + - [CompressedTexture2DArray] + - [CompressedTexture3D] </description> </method> <method name="_customize_scene" qualifiers="virtual"> diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 43059db8b2..0304499a61 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -301,6 +301,15 @@ See also [method Window.set_unparent_when_invisible]. </description> </method> + <method name="popup_method_selector"> + <return type="void" /> + <param index="0" name="object" type="Object" /> + <param index="1" name="callback" type="Callable" /> + <param index="2" name="current_value" type="String" default="""" /> + <description> + Pops up an editor dialog for selecting a method from [param object]. The [param callback] must take a single argument of type [String] which will contain the name of the selected method or be empty if the dialog is canceled. If [param current_value] is provided, the method will be selected automatically in the method list, if it exists. + </description> + </method> <method name="popup_node_selector"> <return type="void" /> <param index="0" name="callback" type="Callable" /> diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml index 2b1083393f..4ff541f72d 100644 --- a/doc/classes/EditorProperty.xml +++ b/doc/classes/EditorProperty.xml @@ -132,6 +132,13 @@ Emitted when a property was deleted. Used internally. </description> </signal> + <signal name="property_favorited"> + <param index="0" name="property" type="StringName" /> + <param index="1" name="favorited" type="bool" /> + <description> + Emit it if you want to mark a property as favorited, making it appear at the top of the inspector. + </description> + </signal> <signal name="property_keyed"> <param index="0" name="property" type="StringName" /> <description> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index ea5207e826..116b6573f2 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -1061,7 +1061,7 @@ Determines whether online features are enabled in the editor, such as the Asset Library or update checks. Disabling these online features helps alleviate privacy concerns by preventing the editor from making HTTP requests to the Godot website or third-party platforms hosting assets from the Asset Library. </member> <member name="network/debug/remote_host" type="String" setter="" getter=""> - The address to listen to when starting the remote debugger. This can be set to [code]0.0.0.0[/code] to allow external clients to connect to the remote debugger (instead of restricting the remote debugger to connections from [code]localhost[/code]). + The address to listen to when starting the remote debugger. This can be set to this device's local IP address to allow external clients to connect to the remote debugger (instead of restricting the remote debugger to connections from [code]localhost[/code]). </member> <member name="network/debug/remote_port" type="int" setter="" getter=""> The port to listen to when starting the remote debugger. Godot will try to use port numbers above the configured number if the configured number is already taken by another application. @@ -1091,9 +1091,11 @@ </member> <member name="run/bottom_panel/action_on_play" type="int" setter="" getter=""> The action to execute on the bottom panel when running the project. + [b]Note:[/b] This option won't do anything if the bottom panel switching is locked using the pin button in the corner of the bottom panel. </member> <member name="run/bottom_panel/action_on_stop" type="int" setter="" getter=""> The action to execute on the bottom panel when stopping the project. + [b]Note:[/b] This option won't do anything if the bottom panel switching is locked using the pin button in the corner of the bottom panel. </member> <member name="run/output/always_clear_output_on_play" type="bool" setter="" getter=""> If [code]true[/code], the editor will clear the Output panel when running the project. diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index 5888e30339..b782937a8a 100644 --- a/doc/classes/FileAccess.xml +++ b/doc/classes/FileAccess.xml @@ -34,7 +34,7 @@ [/codeblocks] In the example above, the file will be saved in the user data folder as specified in the [url=$DOCS_URL/tutorials/io/data_paths.html]Data paths[/url] documentation. [FileAccess] will close when it's freed, which happens when it goes out of scope or when it gets assigned with [code]null[/code]. [method close] can be used to close it before then explicitly. In C# the reference must be disposed manually, which can be done with the [code]using[/code] statement or by calling the [code]Dispose[/code] method directly. - [b]Note:[/b] To access project resources once exported, it is recommended to use [ResourceLoader] instead of [FileAccess], as some files are converted to engine-specific formats and their original source files might not be present in the exported PCK package. + [b]Note:[/b] To access project resources once exported, it is recommended to use [ResourceLoader] instead of [FileAccess], as some files are converted to engine-specific formats and their original source files might not be present in the exported PCK package. If using [FileAccess], make sure the file is included in the export by changing its import mode to [b]Keep File (exported as is)[/b] in the Import dock, or, for files where this option is not available, change the non-resource export filter in the Export dialog to include the file's extension (e.g. [code]*.txt[/code]). [b]Note:[/b] Files are automatically closed only if the process exits "normally" (such as by clicking the window manager's close button or pressing [b]Alt + F4[/b]). If you stop the project execution by pressing [b]F8[/b] while the project is running, the file won't be closed as the game process will be killed. You can work around this by calling [method flush] at regular intervals. </description> <tutorials> diff --git a/doc/classes/PCKPacker.xml b/doc/classes/PCKPacker.xml index ec0300c068..f8f7dbee01 100644 --- a/doc/classes/PCKPacker.xml +++ b/doc/classes/PCKPacker.xml @@ -26,11 +26,18 @@ <methods> <method name="add_file"> <return type="int" enum="Error" /> - <param index="0" name="pck_path" type="String" /> + <param index="0" name="target_path" type="String" /> <param index="1" name="source_path" type="String" /> <param index="2" name="encrypt" type="bool" default="false" /> <description> - Adds the [param source_path] file to the current PCK package at the [param pck_path] internal path (should start with [code]res://[/code]). + Adds the [param source_path] file to the current PCK package at the [param target_path] internal path. The [code]res://[/code] prefix for [param target_path] is optional and stripped internally. + </description> + </method> + <method name="add_file_removal"> + <return type="int" enum="Error" /> + <param index="0" name="target_path" type="String" /> + <description> + Registers a file removal of the [param target_path] internal path to the PCK. This is mainly used for patches. If the file at this path has been loaded from a previous PCK, it will be removed. The [code]res://[/code] prefix for [param target_path] is optional and stripped internally. </description> </method> <method name="flush"> diff --git a/doc/classes/PhysicalBone3D.xml b/doc/classes/PhysicalBone3D.xml index ca1948e8e1..d58ea9487e 100644 --- a/doc/classes/PhysicalBone3D.xml +++ b/doc/classes/PhysicalBone3D.xml @@ -21,6 +21,9 @@ <return type="void" /> <param index="0" name="impulse" type="Vector3" /> <description> + Applies a directional impulse without affecting rotation. + An impulse is time-independent! Applying an impulse every frame would result in a framerate-dependent force. For this reason, it should only be used when simulating one-time impacts (use the "_integrate_forces" functions otherwise). + This is equivalent to using [method apply_impulse] at the body's center of mass. </description> </method> <method name="apply_impulse"> @@ -28,21 +31,27 @@ <param index="0" name="impulse" type="Vector3" /> <param index="1" name="position" type="Vector3" default="Vector3(0, 0, 0)" /> <description> + Applies a positioned impulse to the PhysicsBone3D. + An impulse is time-independent! Applying an impulse every frame would result in a framerate-dependent force. For this reason, it should only be used when simulating one-time impacts (use the "_integrate_forces" functions otherwise). + [param position] is the offset from the PhysicsBone3D origin in global coordinates. </description> </method> <method name="get_bone_id" qualifiers="const"> <return type="int" /> <description> + Returns the unique identifier of the PhysicsBone3D. </description> </method> <method name="get_simulate_physics"> <return type="bool" /> <description> + Returns [code]true[/code] if the PhysicsBone3D is allowed to simulate physics. </description> </method> <method name="is_simulating_physics"> <return type="bool" /> <description> + Returns [code]true[/code] if the PhysicsBone3D is currently simulating physics. </description> </method> </methods> @@ -108,16 +117,22 @@ In this mode, the body's damping value replaces any value set in areas or the default value. </constant> <constant name="JOINT_TYPE_NONE" value="0" enum="JointType"> + No joint is applied to the PhysicsBone3D. </constant> <constant name="JOINT_TYPE_PIN" value="1" enum="JointType"> + A pin joint is applied to the PhysicsBone3D. </constant> <constant name="JOINT_TYPE_CONE" value="2" enum="JointType"> + A cone joint is applied to the PhysicsBone3D. </constant> <constant name="JOINT_TYPE_HINGE" value="3" enum="JointType"> + A hinge joint is applied to the PhysicsBone3D. </constant> <constant name="JOINT_TYPE_SLIDER" value="4" enum="JointType"> + A slider joint is applied to the PhysicsBone3D. </constant> <constant name="JOINT_TYPE_6DOF" value="5" enum="JointType"> + A 6 degrees of freedom joint is applied to the PhysicsBone3D. </constant> </constants> </class> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index fff6c8d3bb..f5a5045ad5 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2778,6 +2778,8 @@ [b]Forward Plus[/b]: High-end renderer designed for Desktop devices. Has a higher base overhead, but scales well with complex scenes. Not suitable for older devices or mobile. [b]Mobile[/b]: Modern renderer designed for mobile devices. Has a lower base overhead than Forward Plus, but does not scale as well to large scenes with many elements. [b]GL Compatibility[/b]: Low-end renderer designed for older devices. Based on the limitations of the OpenGL 3.3/ OpenGL ES 3.0 / WebGL 2 APIs. + This can be overridden using the [code]--rendering-method <method>[/code] command line argument. + [b]Note:[/b] The actual rendering method may be automatically changed by the engine as a result of a fallback, or a user-specified command line argument. To get the actual rendering method that is used at runtime, use [method RenderingServer.get_current_rendering_method] instead of reading this project setting's value. </member> <member name="rendering/renderer/rendering_method.mobile" type="String" setter="" getter="" default=""mobile""> Override for [member rendering/renderer/rendering_method] on mobile devices. @@ -2801,7 +2803,8 @@ Depending on the complexity of scenes, this value may be lowered or may need to be raised. </member> <member name="rendering/rendering_device/driver" type="String" setter="" getter=""> - Sets the driver to be used by the renderer when using a RenderingDevice-based renderer like the clustered renderer or the mobile renderer. This property can not be edited directly, instead, set the driver using the platform-specific overrides. + Sets the driver to be used by the renderer when using a RenderingDevice-based renderer like the clustered renderer or the mobile renderer. This property can't be edited directly. Instead, set the driver using the platform-specific overrides. This can be overridden using the [code]--rendering-driver <driver>[/code] command line argument. + [b]Note:[/b] The actual rendering driver may be automatically changed by the engine as a result of a fallback, or a user-specified command line argument. To get the actual rendering driver that is used at runtime, use [method RenderingServer.get_current_rendering_driver_name] instead of reading this project setting's value. </member> <member name="rendering/rendering_device/driver.android" type="String" setter="" getter=""> Android override for [member rendering/rendering_device/driver]. @@ -2890,11 +2893,11 @@ If [code]true[/code], forces vertex shading for all rendering. This can increase performance a lot, but also reduces quality immensely. Can be used to optimize performance on low-end mobile devices. </member> <member name="rendering/textures/canvas_textures/default_texture_filter" type="int" setter="" getter="" default="1"> - The default texture filtering mode to use on [CanvasItem]s. + The default texture filtering mode to use for [CanvasItem]s built-in texture. In shaders, this texture is accessed as [code]TEXTURE[/code]. [b]Note:[/b] For pixel art aesthetics, see also [member rendering/2d/snap/snap_2d_vertices_to_pixel] and [member rendering/2d/snap/snap_2d_transforms_to_pixel]. </member> <member name="rendering/textures/canvas_textures/default_texture_repeat" type="int" setter="" getter="" default="0"> - The default texture repeating mode to use on [CanvasItem]s. + The default texture repeating mode to use for [CanvasItem]s built-in texture. In shaders, this texture is accessed as [code]TEXTURE[/code]. </member> <member name="rendering/textures/decals/filter" type="int" setter="" getter="" default="3"> The filtering quality to use for [Decal] nodes. When using one of the anisotropic filtering modes, the anisotropic filtering level is controlled by [member rendering/textures/default_filters/anisotropic_filtering_level]. diff --git a/doc/classes/Projection.xml b/doc/classes/Projection.xml index 1665660da3..f781083abf 100644 --- a/doc/classes/Projection.xml +++ b/doc/classes/Projection.xml @@ -14,7 +14,8 @@ <constructor name="Projection"> <return type="Projection" /> <description> - Constructs a default-initialized [Projection] set to [constant IDENTITY]. + Constructs a default-initialized [Projection] identical to [constant IDENTITY]. + [b]Note:[/b] In C#, this constructs a [Projection] identical to [constant ZERO]. </description> </constructor> <constructor name="Projection"> diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml index 665c6335f2..c74a6453e0 100644 --- a/doc/classes/Quaternion.xml +++ b/doc/classes/Quaternion.xml @@ -22,6 +22,7 @@ <return type="Quaternion" /> <description> Constructs a [Quaternion] identical to the [constant IDENTITY]. + [b]Note:[/b] In C#, this constructs a [Quaternion] with all of its components set to [code]0.0[/code]. </description> </constructor> <constructor name="Quaternion"> diff --git a/doc/classes/RenderSceneBuffersRD.xml b/doc/classes/RenderSceneBuffersRD.xml index 212a65337e..7b5aac5b61 100644 --- a/doc/classes/RenderSceneBuffersRD.xml +++ b/doc/classes/RenderSceneBuffersRD.xml @@ -61,7 +61,7 @@ <param index="1" name="msaa" type="bool" default="false" /> <description> Returns the specified layer from the color texture we are rendering 3D content to. - If [param msaa] is [b]true[/b] and MSAA is enabled, this returns the MSAA variant of the buffer. + If [param msaa] is [code]true[/code] and MSAA is enabled, this returns the MSAA variant of the buffer. </description> </method> <method name="get_color_texture"> @@ -69,7 +69,7 @@ <param index="0" name="msaa" type="bool" default="false" /> <description> Returns the color texture we are rendering 3D content to. If multiview is used this will be a texture array with all views. - If [param msaa] is [b]true[/b] and MSAA is enabled, this returns the MSAA variant of the buffer. + If [param msaa] is [code]true[/code] and MSAA is enabled, this returns the MSAA variant of the buffer. </description> </method> <method name="get_depth_layer"> @@ -78,7 +78,7 @@ <param index="1" name="msaa" type="bool" default="false" /> <description> Returns the specified layer from the depth texture we are rendering 3D content to. - If [param msaa] is [b]true[/b] and MSAA is enabled, this returns the MSAA variant of the buffer. + If [param msaa] is [code]true[/code] and MSAA is enabled, this returns the MSAA variant of the buffer. </description> </method> <method name="get_depth_texture"> @@ -86,7 +86,7 @@ <param index="0" name="msaa" type="bool" default="false" /> <description> Returns the depth texture we are rendering 3D content to. If multiview is used this will be a texture array with all views. - If [param msaa] is [b]true[/b] and MSAA is enabled, this returns the MSAA variant of the buffer. + If [param msaa] is [code]true[/code] and MSAA is enabled, this returns the MSAA variant of the buffer. </description> </method> <method name="get_fsr_sharpness" qualifiers="const"> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 284dc07fd4..76949fb84e 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1548,6 +1548,20 @@ Tries to free an object in the RenderingServer. To avoid memory leaks, this should be called after using an object as memory management does not occur automatically when using RenderingServer directly. </description> </method> + <method name="get_current_rendering_driver_name" qualifiers="const"> + <return type="String" /> + <description> + Returns the name of the current rendering driver. This can be [code]vulkan[/code], [code]d3d12[/code], [code]metal[/code], [code]opengl3[/code], [code]opengl3_es[/code], or [code]opengl3_angle[/code]. See also [method get_current_rendering_method]. + The rendering driver is determined by [member ProjectSettings.rendering/rendering_device/driver], the [code]--rendering-driver[/code] command line argument that overrides this project setting, or an automatic fallback that is applied depending on the hardware. + </description> + </method> + <method name="get_current_rendering_method" qualifiers="const"> + <return type="String" /> + <description> + Returns the name of the current rendering method. This can be [code]forward_plus[/code], [code]mobile[/code], or [code]gl_compatibility[/code]. See also [method get_current_rendering_driver_name]. + The rendering method is determined by [member ProjectSettings.rendering/renderer/rendering_method], the [code]--rendering-method[/code] command line argument that overrides this project setting, or an automatic fallback that is applied depending on the hardware. + </description> + </method> <method name="get_default_clear_color"> <return type="Color" /> <description> diff --git a/doc/classes/ResourceFormatLoader.xml b/doc/classes/ResourceFormatLoader.xml index 4e4adc86c4..cdbf8b3f9a 100644 --- a/doc/classes/ResourceFormatLoader.xml +++ b/doc/classes/ResourceFormatLoader.xml @@ -57,6 +57,7 @@ <return type="int" /> <param index="0" name="path" type="String" /> <description> + Should return the unique ID for the resource associated with the given path. If this method is not overridden, a [code].uid[/code] file is generated along with the resource file, containing the unique ID. </description> </method> <method name="_handles_type" qualifiers="virtual const"> diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml index f718ad15d8..ae862dd52f 100644 --- a/doc/classes/ResourceLoader.xml +++ b/doc/classes/ResourceLoader.xml @@ -7,6 +7,7 @@ A singleton used to load resource files from the filesystem. It uses the many [ResourceFormatLoader] classes registered in the engine (either built-in or from a plugin) to load files into memory and convert them to a format that can be used by the engine. [b]Note:[/b] You have to import the files into the engine first to load them using [method load]. If you want to load [Image]s at run-time, you may use [method Image.load]. If you want to import audio files, you can use the snippet described in [member AudioStreamMP3.data]. + [b]Note:[/b] Non-resource files such as plain text files cannot be read using [ResourceLoader]. Use [FileAccess] for those files instead, and be aware that non-resource files are not exported by default (see notes in the [FileAccess] class description for instructions on exporting them). </description> <tutorials> <link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link> @@ -74,6 +75,13 @@ Once a resource has been loaded by the engine, it is cached in memory for faster access, and future calls to the [method load] method will use the cached version. The cached resource can be overridden by using [method Resource.take_over_path] on a new resource for that same path. </description> </method> + <method name="list_directory"> + <return type="PackedStringArray" /> + <param index="0" name="directory_path" type="String" /> + <description> + Lists a directory (as example: "res://assets/enemies"), returning all resources contained within. The resource files are the original file names as visible in the editor before exporting. + </description> + </method> <method name="load"> <return type="Resource" /> <param index="0" name="path" type="String" /> @@ -104,7 +112,7 @@ <param index="1" name="progress" type="Array" default="[]" /> <description> Returns the status of a threaded loading operation started with [method load_threaded_request] for the resource at [param path]. See [enum ThreadLoadStatus] for possible return values. - An array variable can optionally be passed via [param progress], and will return a one-element array containing the ratio of completion of the threaded loading (between [code]0.0[/code] and [code]1.0[/code]). + An array variable can optionally be passed via [param progress], and will return a one-element array containing the ratio of completion of the threaded loading (between [code]0.0[/code] and [code]1.0[/code]). [b]Note:[/b] The recommended way of using this method is to call it during different frames (e.g., in [method Node._process], instead of a loop). </description> </method> diff --git a/doc/classes/TileMapLayer.xml b/doc/classes/TileMapLayer.xml index 6cbec9c2aa..e814d76b73 100644 --- a/doc/classes/TileMapLayer.xml +++ b/doc/classes/TileMapLayer.xml @@ -22,6 +22,21 @@ [b]Note:[/b] If the properties of [param tile_data] object should change over time, use [method notify_runtime_tile_data_update] to notify the [TileMapLayer] it needs an update. </description> </method> + <method name="_update_cells" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="coords" type="Vector2i[]" /> + <param index="1" name="forced_cleanup" type="bool" /> + <description> + Called when this [TileMapLayer]'s cells need an internal update. This update may be caused from individual cells being modified or by a change in the [member tile_set] (causing all cells to be queued for an update). The first call to this function is always for initializing all the [TileMapLayer]'s cells. [param coords] contains the coordinates of all modified cells, roughly in the order they were modified. [param forced_cleanup] is [code]true[/code] when the [TileMapLayer]'s internals should be fully cleaned up. This is the case when: + - The layer is disabled; + - The layer is not visible; + - [member tile_set] is set to [code]null[/code]; + - The node is removed from the tree; + - The node is freed. + Note that any internal update happening while one of these conditions is verified is considered to be a "cleanup". See also [method update_internals]. + [b]Warning:[/b] Implementing this method may degrade the [TileMapLayer]'s performance. + </description> + </method> <method name="_use_tile_data_runtime_update" qualifiers="virtual"> <return type="bool" /> <param index="0" name="coords" type="Vector2i" /> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 756716433e..665e5e9d67 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -20,6 +20,7 @@ <return type="Transform2D" /> <description> Constructs a [Transform2D] identical to [constant IDENTITY]. + [b]Note:[/b] In C#, this constructs a [Transform2D] with all of its components set to [constant Vector2.ZERO]. </description> </constructor> <constructor name="Transform2D"> diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index e3e1045bfe..98e9d56adb 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -21,6 +21,7 @@ <return type="Transform3D" /> <description> Constructs a [Transform3D] identical to the [constant IDENTITY]. + [b]Note:[/b] In C#, this constructs a [Transform3D] with its [member origin] and the components of its [member basis] set to [constant Vector3.ZERO]. </description> </constructor> <constructor name="Transform3D"> diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index bc79b14d4a..076ba6d905 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -63,7 +63,6 @@ constexpr double FPS_DECIMAL = 1.0; constexpr double SECOND_DECIMAL = 0.0001; -constexpr double FPS_STEP_FRACTION = 0.0625; void AnimationTrackKeyEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj); @@ -3776,6 +3775,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re step->set_read_only(false); snap_keys->set_disabled(false); snap_timeline->set_disabled(false); + fps_compat->set_disabled(false); snap_mode->set_disabled(false); auto_fit->set_disabled(false); auto_fit_bezier->set_disabled(false); @@ -3798,6 +3798,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re step->set_read_only(true); snap_keys->set_disabled(true); snap_timeline->set_disabled(true); + fps_compat->set_disabled(true); snap_mode->set_disabled(true); bezier_edit_icon->set_disabled(true); auto_fit->set_disabled(true); @@ -5029,7 +5030,12 @@ void AnimationTrackEditor::_snap_mode_changed(int p_mode) { } marker_edit->set_use_fps(use_fps); // To ensure that the conversion results are consistent between serialization and load, the value is snapped with 0.0625 to be a rational number when FPS mode is used. - step->set_step(use_fps ? FPS_STEP_FRACTION : SECOND_DECIMAL); + step->set_step(use_fps ? FPS_DECIMAL : SECOND_DECIMAL); + if (use_fps) { + fps_compat->hide(); + } else { + fps_compat->show(); + } _update_step_spinbox(); } @@ -5045,7 +5051,6 @@ void AnimationTrackEditor::_update_step_spinbox() { } else { step->set_value(1.0 / animation->get_step()); } - } else { step->set_value(animation->get_step()); } @@ -5054,6 +5059,20 @@ void AnimationTrackEditor::_update_step_spinbox() { _update_snap_unit(); } +void AnimationTrackEditor::_update_fps_compat_mode(bool p_enabled) { + _update_snap_unit(); +} + +void AnimationTrackEditor::_update_nearest_fps_label() { + bool is_fps_invalid = nearest_fps == 0; + if (is_fps_invalid) { + nearest_fps_label->hide(); + } else { + nearest_fps_label->show(); + nearest_fps_label->set_text("Nearest FPS: " + itos(nearest_fps)); + } +} + void AnimationTrackEditor::_animation_update() { timeline->queue_redraw(); timeline->update_values(); @@ -5115,6 +5134,7 @@ void AnimationTrackEditor::_notification(int p_what) { bezier_edit_icon->set_button_icon(get_editor_theme_icon(SNAME("EditBezier"))); snap_timeline->set_button_icon(get_editor_theme_icon(SNAME("SnapTimeline"))); snap_keys->set_button_icon(get_editor_theme_icon(SNAME("SnapKeys"))); + fps_compat->set_button_icon(get_editor_theme_icon(SNAME("FPS"))); view_group->set_button_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"))); selected_filter->set_button_icon(get_editor_theme_icon(SNAME("AnimationFilter"))); imported_anim_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); @@ -5160,9 +5180,8 @@ void AnimationTrackEditor::_update_step(double p_new_step) { double step_value = p_new_step; if (timeline->is_using_fps()) { if (step_value != 0.0) { - // step_value must also be less than or equal to 1000 to ensure that no error accumulates due to interactions with retrieving values from inner range. + // A step_value should be less than or equal to 1000 to ensure that no error accumulates due to interactions with retrieving values from inner range. step_value = 1.0 / MIN(1000.0, p_new_step); - ; } timeline->queue_redraw(); } @@ -7336,19 +7355,30 @@ void AnimationTrackEditor::_selection_changed() { } void AnimationTrackEditor::_update_snap_unit() { + nearest_fps = 0; + if (step->get_value() <= 0) { snap_unit = 0; + _update_nearest_fps_label(); return; // Avoid zero div. } if (timeline->is_using_fps()) { + _clear_selection(true); // Needs to recreate a spinbox of the KeyEdit. snap_unit = 1.0 / step->get_value(); } else { - double integer; - double fraction = Math::modf(step->get_value(), &integer); - fraction = 1.0 / Math::round(1.0 / fraction); - snap_unit = integer + fraction; + if (fps_compat->is_pressed()) { + snap_unit = CLAMP(step->get_value(), 0.0, 1.0); + if (!Math::is_zero_approx(snap_unit)) { + real_t fps = Math::round(1.0 / snap_unit); + nearest_fps = int(fps); + snap_unit = 1.0 / fps; + } + } else { + snap_unit = step->get_value(); + } } + _update_nearest_fps_label(); } float AnimationTrackEditor::snap_time(float p_value, bool p_relative) { @@ -7369,6 +7399,10 @@ float AnimationTrackEditor::snap_time(float p_value, bool p_relative) { return p_value; } +float AnimationTrackEditor::get_snap_unit() { + return snap_unit; +} + void AnimationTrackEditor::_show_imported_anim_warning() { // It looks terrible on a single line but the TTR extractor doesn't support line breaks yet. EditorNode::get_singleton()->show_warning( @@ -7622,6 +7656,18 @@ AnimationTrackEditor::AnimationTrackEditor() { snap_keys->set_pressed(true); snap_keys->set_tooltip_text(TTR("Apply snapping to selected key(s).")); + fps_compat = memnew(Button); + fps_compat->set_flat(true); + bottom_hb->add_child(fps_compat); + fps_compat->set_disabled(true); + fps_compat->set_toggle_mode(true); + fps_compat->set_pressed(true); + fps_compat->set_tooltip_text(TTR("Apply snapping to the nearest integer FPS.")); + fps_compat->connect(SceneStringName(toggled), callable_mp(this, &AnimationTrackEditor::_update_fps_compat_mode)); + + nearest_fps_label = memnew(Label); + bottom_hb->add_child(nearest_fps_label); + step = memnew(EditorSpinSlider); step->set_min(0); step->set_max(1000000); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 1053468452..e7271f1941 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -600,6 +600,8 @@ class AnimationTrackEditor : public VBoxContainer { AnimationMarkerEdit *marker_edit = nullptr; HSlider *zoom = nullptr; EditorSpinSlider *step = nullptr; + Button *fps_compat = nullptr; + Label *nearest_fps_label = nullptr; TextureRect *zoom_icon = nullptr; Button *snap_keys = nullptr; Button *snap_timeline = nullptr; @@ -637,6 +639,8 @@ class AnimationTrackEditor : public VBoxContainer { void _track_grab_focus(int p_track); void _update_scroll(double); + void _update_nearest_fps_label(); + void _update_fps_compat_mode(bool p_enabled); void _update_step(double p_new_step); void _update_length(double p_new_len); void _dropped_track(int p_from_track, int p_to_track); @@ -853,6 +857,8 @@ class AnimationTrackEditor : public VBoxContainer { void _pick_track_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates); double snap_unit; + bool fps_compatible = true; + int nearest_fps = 0; void _update_snap_unit(); protected: @@ -935,6 +941,7 @@ public: bool can_add_reset_key() const; float get_moving_selection_offset() const; float snap_time(float p_value, bool p_relative = false); + float get_snap_unit(); bool is_grouping_tracks(); PackedStringArray get_selected_section() const; bool is_marker_selected(const StringName &p_marker) const; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 558eed98c6..cc81001efb 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1259,6 +1259,15 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir, } } } + + if (fi->uid == ResourceUID::INVALID_ID && ResourceLoader::exists(path) && !ResourceLoader::has_custom_uid_support(path) && !FileAccess::exists(path + ".uid")) { + // Create a UID. + Ref<FileAccess> f = FileAccess::open(path + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + fi->uid = ResourceUID::get_singleton()->create_id(); + f->store_line(ResourceUID::get_singleton()->id_to_text(fi->uid)); + } + } } if (fi->uid != ResourceUID::INVALID_ID) { @@ -2740,13 +2749,17 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin } } + if (uid == ResourceUID::INVALID_ID) { + uid = ResourceUID::get_singleton()->create_id(); + } + //finally, perform import!! String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(p_file); List<String> import_variants; List<String> gen_files; Variant meta; - Error err = importer->import(p_file, base_path, params, &import_variants, &gen_files, &meta); + Error err = importer->import(uid, p_file, base_path, params, &import_variants, &gen_files, &meta); // As import is complete, save the .import file. @@ -2767,10 +2780,6 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin f->store_line("type=\"" + importer->get_resource_type() + "\""); } - if (uid == ResourceUID::INVALID_ID) { - uid = ResourceUID::get_singleton()->create_id(); - } - f->store_line("uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\""); // Store in readable format. if (err == OK) { diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index d11bf7720c..b0c06475f8 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -151,7 +151,7 @@ void EditorHelpSearch::_update_results() { search_flags |= SEARCH_SHOW_HIERARCHY; } - search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags))); + search.instantiate(results_tree, results_tree, &tree_cache, term, search_flags); // Clear old search flags to force rebuild on short term. old_search_flags = 0; @@ -162,7 +162,7 @@ void EditorHelpSearch::_update_results() { hierarchy_button->set_disabled(true); // Always show hierarchy for short searches. - search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags | SEARCH_SHOW_HIERARCHY))); + search.instantiate(results_tree, results_tree, &tree_cache, term, search_flags | SEARCH_SHOW_HIERARCHY); old_search_flags = search_flags; set_process(true); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 91dd8e5019..1c23ce8ede 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -931,10 +931,19 @@ float EditorProperty::get_name_split_ratio() const { return split_ratio; } +void EditorProperty::set_favoritable(bool p_favoritable) { + can_favorite = p_favoritable; +} + +bool EditorProperty::is_favoritable() const { + return can_favorite; +} + void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) { object = p_object; property = p_property; - _update_pin_flags(); + + _update_flags(); } static bool _is_value_potential_override(Node *p_node, const String &p_property) { @@ -953,12 +962,14 @@ static bool _is_value_potential_override(Node *p_node, const String &p_property) } } -void EditorProperty::_update_pin_flags() { +void EditorProperty::_update_flags() { can_pin = false; pin_hidden = true; + if (read_only) { return; } + if (Node *node = Object::cast_to<Node>(object)) { // Avoid errors down the road by ignoring nodes which are not part of a scene if (!node->get_owner()) { @@ -1034,6 +1045,10 @@ void EditorProperty::menu_option(int p_option) { case MENU_COPY_PROPERTY_PATH: { DisplayServer::get_singleton()->clipboard_set(property_path); } break; + case MENU_FAVORITE_PROPERTY: { + emit_signal(SNAME("property_favorited"), property, !favorited); + queue_redraw(); + } break; case MENU_PIN_VALUE: { emit_signal(SNAME("property_pinned"), property, !pinned); queue_redraw(); @@ -1091,6 +1106,7 @@ void EditorProperty::_bind_methods() { ADD_SIGNAL(MethodInfo("property_deleted", PropertyInfo(Variant::STRING_NAME, "property"))); ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "checked"))); + ADD_SIGNAL(MethodInfo("property_favorited", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "favorited"))); ADD_SIGNAL(MethodInfo("property_pinned", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "pinned"))); ADD_SIGNAL(MethodInfo("property_can_revert_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "can_revert"))); ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); @@ -1129,8 +1145,21 @@ void EditorProperty::_update_popup() { menu->set_item_disabled(MENU_PASTE_VALUE, is_read_only()); menu->set_item_disabled(MENU_COPY_PROPERTY_PATH, internal); - if (!pin_hidden) { + if (can_favorite || !pin_hidden) { menu->add_separator(); + } + + if (can_favorite) { + if (favorited) { + menu->add_icon_item(get_editor_theme_icon(SNAME("Unfavorite")), TTR("Unfavorite Property"), MENU_FAVORITE_PROPERTY); + menu->set_item_tooltip(menu->get_item_index(MENU_FAVORITE_PROPERTY), TTR("Make this property be put back at its original place.")); + } else { + menu->add_icon_item(get_editor_theme_icon(SNAME("Favorites")), TTR("Favorite Property"), MENU_FAVORITE_PROPERTY); + menu->set_item_tooltip(menu->get_item_index(MENU_FAVORITE_PROPERTY), TTR("Make this property be placed at the top for all objects of this class.")); + } + } + + if (!pin_hidden) { if (can_pin) { menu->add_icon_check_item(get_editor_theme_icon(SNAME("Pin")), TTR("Pin Value"), MENU_PIN_VALUE); menu->set_item_checked(menu->get_item_index(MENU_PIN_VALUE), pinned); @@ -1219,9 +1248,14 @@ void EditorInspectorPlugin::_bind_methods() { void EditorInspectorCategory::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - menu->set_item_icon(menu->get_item_index(MENU_OPEN_DOCS), get_editor_theme_icon(SNAME("Help"))); + if (menu) { + if (is_favorite) { + menu->set_item_icon(menu->get_item_index(EditorInspector::MENU_UNFAVORITE_ALL), get_editor_theme_icon(SNAME("Unfavorite"))); + } else { + menu->set_item_icon(menu->get_item_index(MENU_OPEN_DOCS), get_editor_theme_icon(SNAME("Help"))); + } + } } break; case NOTIFICATION_DRAW: { Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); @@ -1278,6 +1312,15 @@ Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) cons return memnew(Control); // Make the standard tooltip invisible. } +void EditorInspectorCategory::set_as_favorite(EditorInspector *p_for_inspector) { + is_favorite = true; + + menu = memnew(PopupMenu); + menu->add_item(TTR("Unfavorite All"), EditorInspector::MENU_UNFAVORITE_ALL); + add_child(menu); + menu->connect(SceneStringName(id_pressed), callable_mp(p_for_inspector, &EditorInspector::_handle_menu_option)); +} + Size2 EditorInspectorCategory::get_minimum_size() const { Ref<Font> font = get_theme_font(SNAME("bold"), EditorStringName(EditorFonts)); int font_size = get_theme_font_size(SNAME("bold_size"), EditorStringName(EditorFonts)); @@ -1306,7 +1349,7 @@ void EditorInspectorCategory::_handle_menu_option(int p_option) { } void EditorInspectorCategory::gui_input(const Ref<InputEvent> &p_event) { - if (doc_class_name.is_empty()) { + if (!is_favorite && doc_class_name.is_empty()) { return; } @@ -1315,20 +1358,21 @@ void EditorInspectorCategory::gui_input(const Ref<InputEvent> &p_event) { return; } - menu->set_item_disabled(menu->get_item_index(MENU_OPEN_DOCS), !EditorHelp::get_doc_data()->class_list.has(doc_class_name)); + if (!is_favorite) { + if (!menu) { + menu = memnew(PopupMenu); + menu->add_icon_item(get_editor_theme_icon(SNAME("Help")), TTR("Open Documentation"), MENU_OPEN_DOCS); + add_child(menu); + menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorInspectorCategory::_handle_menu_option)); + } + menu->set_item_disabled(menu->get_item_index(MENU_OPEN_DOCS), !EditorHelp::get_doc_data()->class_list.has(doc_class_name)); + } menu->set_position(get_screen_position() + mb_event->get_position()); menu->reset_size(); menu->popup(); } -EditorInspectorCategory::EditorInspectorCategory() { - menu = memnew(PopupMenu); - menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorInspectorCategory::_handle_menu_option)); - menu->add_item(TTR("Open Documentation"), MENU_OPEN_DOCS); - add_child(menu); -} - //////////////////////////////////////////////// //////////////////////////////////////////////// @@ -1622,6 +1666,10 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) { } } +String EditorInspectorSection::get_section() const { + return section; +} + VBoxContainer *EditorInspectorSection::get_vbox() { return vbox; } @@ -2675,7 +2723,13 @@ String EditorInspector::get_selected_path() const { void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorInspectorSection *p_section, Ref<EditorInspectorPlugin> ped) { for (const EditorInspectorPlugin::AddedEditor &F : ped->added_editors) { EditorProperty *ep = Object::cast_to<EditorProperty>(F.property_editor); - current_vbox->add_child(F.property_editor); + + if (ep && current_favorites.has(F.properties[0])) { + ep->favorited = true; + favorites_vbox->add_child(F.property_editor); + } else { + current_vbox->add_child(F.property_editor); + } if (ep) { ep->object = object; @@ -2684,6 +2738,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), CONNECT_DEFERRED); ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value)); ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked)); + ep->connect("property_favorited", callable_mp(this, &EditorInspector::_set_property_favorited), CONNECT_DEFERRED); ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned)); ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected)); ep->connect("multiple_properties_changed", callable_mp(this, &EditorInspector::_multiple_properties_changed)); @@ -2727,7 +2782,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn ep->set_read_only(read_only); ep->update_property(); - ep->_update_pin_flags(); + ep->_update_flags(); ep->update_editor_property_status(); ep->set_deletable(deletable_properties); ep->update_cache(); @@ -2837,6 +2892,7 @@ void EditorInspector::update_tree() { String subgroup; String subgroup_base; int section_depth = 0; + bool disable_favorite = false; VBoxContainer *category_vbox = nullptr; List<PropertyInfo> plist; @@ -2844,13 +2900,17 @@ void EditorInspector::update_tree() { HashMap<VBoxContainer *, HashMap<String, VBoxContainer *>> vbox_per_path; HashMap<String, EditorInspectorArray *> editor_inspector_array_per_prefix; + HashMap<String, HashMap<String, LocalVector<EditorProperty *>>> favorites_to_add; Color sscolor = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)); // Get the lists of editors to add the beginning. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { ped->parse_begin(object); - _parse_added_editors(main_vbox, nullptr, ped); + _parse_added_editors(begin_vbox, nullptr, ped); + } + if (begin_vbox->get_child_count()) { + begin_vbox->show(); } StringName doc_name; @@ -2897,6 +2957,7 @@ void EditorInspector::update_tree() { subgroup = ""; subgroup_base = ""; section_depth = 0; + disable_favorite = false; vbox_per_path.clear(); editor_inspector_array_per_prefix.clear(); @@ -2960,6 +3021,11 @@ void EditorInspector::update_tree() { } else { category_icon = EditorNode::get_singleton()->get_object_icon(scr.ptr(), "Object"); } + + // Property favorites aren't compatible with built-in scripts. + if (scr->is_built_in()) { + disable_favorite = true; + } } } @@ -3058,6 +3124,11 @@ void EditorInspector::update_tree() { } } + // Don't allow to favorite array items. + if (!disable_favorite) { + disable_favorite = !array_prefix.is_empty(); + } + if (!array_prefix.is_empty()) { path = path.trim_prefix(array_prefix); int char_index = path.find("/"); @@ -3449,6 +3520,7 @@ void EditorInspector::update_tree() { ep->set_draw_warning(draw_warning); ep->set_use_folding(use_folding); + ep->set_favoritable(can_favorite && !disable_favorite); ep->set_checkable(checkable); ep->set_checked(checked); ep->set_keying(keying); @@ -3456,7 +3528,12 @@ void EditorInspector::update_tree() { ep->set_deletable(deletable_properties || p.name.begins_with("metadata/")); } - current_vbox->add_child(editors[i].property_editor); + if (ep && ep->is_favoritable() && current_favorites.has(p.name)) { + ep->favorited = true; + favorites_to_add[group][subgroup].push_back(ep); + } else { + current_vbox->add_child(editors[i].property_editor); + } if (ep) { // Eventually, set other properties/signals after the property editor got added to the tree. @@ -3465,6 +3542,7 @@ void EditorInspector::update_tree() { ep->connect("property_keyed", callable_mp(this, &EditorInspector::_property_keyed)); ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), CONNECT_DEFERRED); ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value)); + ep->connect("property_favorited", callable_mp(this, &EditorInspector::_set_property_favorited), CONNECT_DEFERRED); ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked)); ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned)); ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected)); @@ -3495,7 +3573,7 @@ void EditorInspector::update_tree() { ep->set_internal(p.usage & PROPERTY_USAGE_INTERNAL); ep->update_property(); - ep->_update_pin_flags(); + ep->_update_flags(); ep->update_editor_property_status(); ep->update_cache(); @@ -3506,6 +3584,79 @@ void EditorInspector::update_tree() { } } + if (!current_favorites.is_empty()) { + favorites_section->show(); + + // Organize the favorited properties in their sections, to keep context and differentiate from others with the same name. + bool is_localized = property_name_style == EditorPropertyNameProcessor::STYLE_LOCALIZED; + for (const KeyValue<String, HashMap<String, LocalVector<EditorProperty *>>> &KV : favorites_to_add) { + String section_name = KV.key; + String label; + String tooltip; + VBoxContainer *parent_vbox = favorites_vbox; + if (!section_name.is_empty()) { + if (is_localized) { + label = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + tooltip = section_name; + } else { + label = section_name; + tooltip = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + } + + EditorInspectorSection *section = memnew(EditorInspectorSection); + favorites_groups_vbox->add_child(section); + parent_vbox = section->get_vbox(); + section->setup("", section_name, object, sscolor, false); + section->set_tooltip_text(tooltip); + } + + for (const KeyValue<String, LocalVector<EditorProperty *>> &KV2 : KV.value) { + section_name = KV2.key; + VBoxContainer *vbox = parent_vbox; + if (!section_name.is_empty()) { + if (is_localized) { + label = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + tooltip = section_name; + } else { + label = section_name; + tooltip = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + } + + EditorInspectorSection *section = memnew(EditorInspectorSection); + vbox->add_child(section); + vbox = section->get_vbox(); + section->setup("", section_name, object, sscolor, false); + section->set_tooltip_text(tooltip); + } + + for (EditorProperty *ep : KV2.value) { + vbox->add_child(ep); + } + } + } + + // Show a separator if there's no category to clearly divide the properties. + favorites_separator->hide(); + if (main_vbox->get_child_count() > 0) { + EditorInspectorCategory *category = Object::cast_to<EditorInspectorCategory>(main_vbox->get_child(0)); + if (!category) { + favorites_separator->show(); + } + } + + // Clean up empty sections. + for (List<EditorInspectorSection *>::Element *I = sections.back(); I; I = I->prev()) { + EditorInspectorSection *section = I->get(); + if (section->get_vbox()->get_child_count() == 0) { + I = I->prev(); + + sections.erase(section); + vbox_per_path[main_vbox].erase(section->get_section()); + memdelete(section); + } + } + } + if (!hide_metadata && !object->call("_hide_metadata_from_inspector")) { // Add 4px of spacing between the "Add Metadata" button and the content above it. Control *spacer = memnew(Control); @@ -3548,6 +3699,19 @@ void EditorInspector::update_property(const String &p_prop) { } void EditorInspector::_clear(bool p_hide_plugins) { + begin_vbox->hide(); + while (begin_vbox->get_child_count()) { + memdelete(begin_vbox->get_child(0)); + } + + favorites_section->hide(); + while (favorites_vbox->get_child_count()) { + memdelete(favorites_vbox->get_child(0)); + } + while (favorites_groups_vbox->get_child_count()) { + memdelete(favorites_groups_vbox->get_child(0)); + } + while (main_vbox->get_child_count()) { memdelete(main_vbox->get_child(0)); } @@ -3594,6 +3758,10 @@ void EditorInspector::edit(Object *p_object) { update_scroll_request = scroll_cache[object->get_instance_id()]; //done this way because wait until full size is accommodated } object->connect(CoreStringName(property_list_changed), callable_mp(this, &EditorInspector::_changed_callback)); + + can_favorite = Object::cast_to<Node>(object) || Object::cast_to<Resource>(object); + _update_current_favorites(); + update_tree(); } @@ -4088,10 +4256,164 @@ void EditorInspector::_node_removed(Node *p_node) { } } +void EditorInspector::_update_current_favorites() { + current_favorites.clear(); + if (!can_favorite) { + return; + } + + HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties(); + + // Fetch script properties. + Ref<Script> scr = object->get_script(); + if (scr.is_valid()) { + List<PropertyInfo> plist; + // FIXME: Only properties from a saved script will be available, unsaved ones will be ignored. + // Can cause a little wonkiness, while nothing serious, would be nice to find a way to get + // unsaved ones without needing to get the entire property list of an object. + scr->get_script_property_list(&plist); + + String path; + HashMap<String, LocalVector<String>> props; + + for (PropertyInfo &p : plist) { + if (p.usage & PROPERTY_USAGE_CATEGORY) { + path = favorites.has(p.hint_string) ? p.hint_string : String(); + } else if (p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE && !path.is_empty()) { + props[path].push_back(p.name); + } + } + + // Add favorited properties while removing invalid ones. + bool invalid_props = false; + for (const KeyValue<String, LocalVector<String>> &KV : props) { + path = KV.key; + for (int i = 0; i < favorites[path].size(); i++) { + String prop = favorites[path][i]; + if (KV.value.has(prop)) { + current_favorites.append(prop); + } else { + invalid_props = true; + favorites[path].erase(prop); + i--; + } + } + + if (favorites[path].is_empty()) { + favorites.erase(path); + } + } + + if (invalid_props) { + EditorSettings::get_singleton()->set_favorite_properties(favorites); + } + } + + // Fetch built-in properties. + StringName class_name = object->get_class_name(); + for (const KeyValue<String, PackedStringArray> &KV : favorites) { + if (ClassDB::is_parent_class(class_name, KV.key)) { + current_favorites.append_array(KV.value); + } + } +} + +void EditorInspector::_set_property_favorited(const String &p_path, bool p_favorited) { + if (!object) { + return; + } + + StringName class_name = object->get_class_name(); + while (!class_name.is_empty()) { + bool has_prop = ClassDB::has_property(class_name, p_path, true); + if (has_prop) { + break; + } + + class_name = ClassDB::get_parent_class_nocheck(class_name); + } + + if (class_name.is_empty()) { + Ref<Script> scr = object->get_script(); + if (scr.is_valid()) { + List<PropertyInfo> plist; + scr->get_script_property_list(&plist); + + String path; + for (PropertyInfo &p : plist) { + if (p.usage & PROPERTY_USAGE_CATEGORY) { + path = p.hint_string; + } else if (p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE && p.name == p_path) { + class_name = path; + break; + } + } + } + + ERR_FAIL_COND_MSG(class_name.is_empty(), "Can't favorite invalid property. If said property was from a script and recently renamed, try saving it first."); + } + + HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties(); + if (p_favorited) { + current_favorites.append(p_path); + favorites[class_name].append(p_path); + } else { + current_favorites.erase(p_path); + + if (favorites.has(class_name) && favorites[class_name].has(p_path)) { + if (favorites[class_name].size() > 1) { + favorites[class_name].erase(p_path); + } else { + favorites.erase(class_name); + } + } + } + EditorSettings::get_singleton()->set_favorite_properties(favorites); + + update_tree(); +} + +void EditorInspector::_clear_current_favorites() { + current_favorites.clear(); + + HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties(); + + Ref<Script> scr = object->get_script(); + if (scr.is_valid()) { + List<PropertyInfo> plist; + scr->get_script_property_list(&plist); + + for (PropertyInfo &p : plist) { + if (p.usage & PROPERTY_USAGE_CATEGORY && favorites.has(p.hint_string)) { + favorites.erase(p.hint_string); + } + } + } + + StringName class_name = object->get_class_name(); + while (class_name) { + if (favorites.has(class_name)) { + favorites.erase(class_name); + } + + class_name = ClassDB::get_parent_class(class_name); + } + + EditorSettings::get_singleton()->set_favorite_properties(favorites); + update_tree(); +} + void EditorInspector::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - main_vbox->add_theme_constant_override("separation", get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector"))); + favorites_category->icon = get_editor_theme_icon(SNAME("Favorites")); + + int separation = get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector")); + base_vbox->add_theme_constant_override("separation", separation); + begin_vbox->add_theme_constant_override("separation", separation); + favorites_section->add_theme_constant_override("separation", separation); + favorites_groups_vbox->add_theme_constant_override("separation", separation); + main_vbox->add_theme_constant_override("separation", separation); } break; case NOTIFICATION_READY: { @@ -4189,6 +4511,7 @@ void EditorInspector::_notification(int p_what) { void EditorInspector::_changed_callback() { //this is called when property change is notified via notify_property_list_changed() if (object != nullptr) { + _update_current_favorites(); _edit_request_change(object, String()); } } @@ -4278,6 +4601,14 @@ void EditorInspector::_add_meta_confirm() { undo_redo->commit_action(); } +void EditorInspector::_handle_menu_option(int p_option) { + switch (p_option) { + case MENU_UNFAVORITE_ALL: + _clear_current_favorites(); + break; + } +} + void EditorInspector::_bind_methods() { ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change); ClassDB::bind_method("get_selected_path", &EditorInspector::get_selected_path); @@ -4296,9 +4627,36 @@ void EditorInspector::_bind_methods() { EditorInspector::EditorInspector() { object = nullptr; + + base_vbox = memnew(VBoxContainer); + base_vbox->set_h_size_flags(SIZE_EXPAND_FILL); + add_child(base_vbox); + + begin_vbox = memnew(VBoxContainer); + base_vbox->add_child(begin_vbox); + begin_vbox->hide(); + + favorites_section = memnew(VBoxContainer); + base_vbox->add_child(favorites_section); + favorites_section->hide(); + + favorites_category = memnew(EditorInspectorCategory); + favorites_category->set_as_favorite(this); + favorites_section->add_child(favorites_category); + favorites_category->label = TTR("Favorites"); + + favorites_vbox = memnew(VBoxContainer); + favorites_section->add_child(favorites_vbox); + favorites_groups_vbox = memnew(VBoxContainer); + favorites_section->add_child(favorites_groups_vbox); + + favorites_separator = memnew(HSeparator); + favorites_section->add_child(favorites_separator); + favorites_separator->hide(); + main_vbox = memnew(VBoxContainer); - main_vbox->set_h_size_flags(SIZE_EXPAND_FILL); - add_child(main_vbox); + base_vbox->add_child(main_vbox); + set_horizontal_scroll_mode(SCROLL_MODE_DISABLED); set_follow_focus(true); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 0309213b76..2e4633ccea 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -41,6 +41,7 @@ class Button; class ConfirmationDialog; class EditorInspector; class EditorValidationPanel; +class HSeparator; class LineEdit; class MarginContainer; class OptionButton; @@ -64,6 +65,7 @@ public: MENU_COPY_VALUE, MENU_PASTE_VALUE, MENU_COPY_PROPERTY_PATH, + MENU_FAVORITE_PROPERTY, MENU_PIN_VALUE, MENU_OPEN_DOCUMENTATION, }; @@ -112,6 +114,9 @@ private: bool pin_hidden = false; bool pinned = false; + bool can_favorite = false; + bool favorited = false; + bool use_folding = false; bool draw_top_bg = true; @@ -134,7 +139,7 @@ private: GDVIRTUAL0(_update_property) GDVIRTUAL1(_set_read_only, bool) - void _update_pin_flags(); + void _update_flags(); protected: bool has_borders = false; @@ -218,6 +223,9 @@ public: void set_name_split_ratio(float p_ratio); float get_name_split_ratio() const; + void set_favoritable(bool p_favoritable); + bool is_favoritable() const; + void set_object_and_property(Object *p_object, const StringName &p_property); virtual Control *make_custom_tooltip(const String &p_text) const override; @@ -285,6 +293,7 @@ class EditorInspectorCategory : public Control { String label; String doc_class_name; PopupMenu *menu = nullptr; + bool is_favorite = false; void _handle_menu_option(int p_option); @@ -293,10 +302,10 @@ protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; public: + void set_as_favorite(EditorInspector *p_for_inspector); + virtual Size2 get_minimum_size() const override; virtual Control *make_custom_tooltip(const String &p_text) const override; - - EditorInspectorCategory(); }; class EditorInspectorSection : public Container { @@ -331,6 +340,7 @@ public: virtual Size2 get_minimum_size() const override; void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable, int p_indent_depth = 0, int p_level = 1); + String get_section() const; VBoxContainer *get_vbox(); void unfold(); void fold(); @@ -480,13 +490,31 @@ public: class EditorInspector : public ScrollContainer { GDCLASS(EditorInspector, ScrollContainer); + friend class EditorInspectorCategory; + enum { MAX_PLUGINS = 1024 }; static Ref<EditorInspectorPlugin> inspector_plugins[MAX_PLUGINS]; static int inspector_plugin_count; + // Right-click context menu options. + enum ClassMenuOption { + MENU_UNFAVORITE_ALL, + }; + + bool can_favorite = false; + PackedStringArray current_favorites; + VBoxContainer *favorites_section = nullptr; + EditorInspectorCategory *favorites_category = nullptr; + VBoxContainer *favorites_vbox = nullptr; + VBoxContainer *favorites_groups_vbox = nullptr; + HSeparator *favorites_separator = nullptr; + EditorInspector *root_inspector = nullptr; + + VBoxContainer *base_vbox = nullptr; + VBoxContainer *begin_vbox = nullptr; VBoxContainer *main_vbox = nullptr; // Map used to cache the instantiated editors. @@ -557,6 +585,10 @@ class EditorInspector : public ScrollContainer { void _property_selected(const String &p_path, int p_focusable); void _object_id_selected(const String &p_path, ObjectID p_id); + void _update_current_favorites(); + void _set_property_favorited(const String &p_path, bool p_favorited); + void _clear_current_favorites(); + void _node_removed(Node *p_node); HashMap<StringName, int> per_array_page; @@ -584,6 +616,8 @@ class EditorInspector : public ScrollContainer { void _add_meta_confirm(); void _show_add_meta_dialog(); + void _handle_menu_option(int p_option); + protected: static void _bind_methods(); void _notification(int p_what); diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp index 264c80dcbf..304b9d4b8c 100644 --- a/editor/editor_interface.cpp +++ b/editor/editor_interface.cpp @@ -337,6 +337,19 @@ void EditorInterface::popup_property_selector(Object *p_object, const Callable & property_selector->connect(SNAME("canceled"), canceled_callback, CONNECT_DEFERRED); } +void EditorInterface::popup_method_selector(Object *p_object, const Callable &p_callback, const String &p_current_value) { + if (!method_selector) { + method_selector = memnew(PropertySelector); + get_base_control()->add_child(method_selector); + } + + method_selector->select_method_from_instance(p_object, p_current_value); + + const Callable callback = callable_mp(this, &EditorInterface::_method_selected); + method_selector->connect(SNAME("selected"), callback.bind(p_callback), CONNECT_DEFERRED); + method_selector->connect(SNAME("canceled"), callback.bind(String(), p_callback), CONNECT_DEFERRED); +} + void EditorInterface::popup_quick_open(const Callable &p_callback, const TypedArray<StringName> &p_base_types) { StringName required_type = SNAME("Resource"); Vector<StringName> base_types; @@ -372,6 +385,18 @@ void EditorInterface::_property_selection_canceled(const Callable &p_callback) { _call_dialog_callback(p_callback, NodePath(), "property selection canceled"); } +void EditorInterface::_method_selected(const String &p_method_name, const Callable &p_callback) { + const Callable callback = callable_mp(this, &EditorInterface::_method_selected); + method_selector->disconnect(SNAME("selected"), callback); + method_selector->disconnect(SNAME("canceled"), callback); + + if (p_method_name.is_empty()) { + _call_dialog_callback(p_callback, p_method_name, "method selection canceled"); + } else { + _call_dialog_callback(p_callback, p_method_name, "method selected"); + } +} + void EditorInterface::_quick_open(const String &p_file_path, const Callable &p_callback) { EditorQuickOpenDialog *quick_open = EditorNode::get_singleton()->get_quick_open_dialog(); quick_open->disconnect(SNAME("canceled"), callable_mp(this, &EditorInterface::_quick_open)); @@ -593,6 +618,7 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("popup_node_selector", "callback", "valid_types", "current_value"), &EditorInterface::popup_node_selector, DEFVAL(TypedArray<StringName>()), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("popup_property_selector", "object", "callback", "type_filter", "current_value"), &EditorInterface::popup_property_selector, DEFVAL(PackedInt32Array()), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("popup_method_selector", "object", "callback", "current_value"), &EditorInterface::popup_method_selector, DEFVAL(String())); ClassDB::bind_method(D_METHOD("popup_quick_open", "callback", "base_types"), &EditorInterface::popup_quick_open, DEFVAL(TypedArray<StringName>())); // Editor docks. diff --git a/editor/editor_interface.h b/editor/editor_interface.h index 4877444dac..c1032bf9b6 100644 --- a/editor/editor_interface.h +++ b/editor/editor_interface.h @@ -66,12 +66,14 @@ class EditorInterface : public Object { // Editor dialogs. PropertySelector *property_selector = nullptr; + PropertySelector *method_selector = nullptr; SceneTreeDialog *node_selector = nullptr; void _node_selected(const NodePath &p_node_paths, const Callable &p_callback); void _node_selection_canceled(const Callable &p_callback); void _property_selected(const String &p_property_name, const Callable &p_callback); void _property_selection_canceled(const Callable &p_callback); + void _method_selected(const String &p_property_name, const Callable &p_callback); void _quick_open(const String &p_file_path, const Callable &p_callback); void _call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context); @@ -139,6 +141,7 @@ public: void popup_node_selector(const Callable &p_callback, const TypedArray<StringName> &p_valid_types = TypedArray<StringName>(), Node *p_current_value = nullptr); // Must use Vector<int> because exposing Vector<Variant::Type> is not supported. void popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter = PackedInt32Array(), const String &p_current_value = String()); + void popup_method_selector(Object *p_object, const Callable &p_callback, const String &p_current_value = String()); void popup_quick_open(const Callable &p_callback, const TypedArray<StringName> &p_base_types = TypedArray<StringName>()); // Editor docks. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 36b43b7e9b..f8e23ecc9d 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2153,7 +2153,7 @@ void EditorNode::_dialog_action(String p_file) { } if (ml.is_null()) { - ml = Ref<MeshLibrary>(memnew(MeshLibrary)); + ml.instantiate(); } MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, merge_with_existing_library, apply_mesh_instance_transforms); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index ce75efa462..bdb5ed2ed9 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -462,10 +462,26 @@ void EditorPropertyPath::_set_read_only(bool p_read_only) { } void EditorPropertyPath::_path_selected(const String &p_path) { - emit_changed(get_edited_property(), p_path); + String full_path = p_path; + ResourceUID::ID id = ResourceLoader::get_resource_uid(full_path); + + if (id != ResourceUID::INVALID_ID) { + full_path = ResourceUID::get_singleton()->id_to_text(id); + } + + emit_changed(get_edited_property(), full_path); update_property(); } +String EditorPropertyPath::_get_path_text() { + String full_path = get_edited_property_value(); + if (full_path.begins_with("uid://")) { + full_path = ResourceUID::get_singleton()->get_id_path(ResourceUID::get_singleton()->text_to_id(full_path)); + } + + return full_path; +} + void EditorPropertyPath::_path_pressed() { if (!dialog) { dialog = memnew(EditorFileDialog); @@ -474,7 +490,7 @@ void EditorPropertyPath::_path_pressed() { add_child(dialog); } - String full_path = get_edited_property_value(); + String full_path = _get_path_text(); dialog->clear_filters(); @@ -502,7 +518,7 @@ void EditorPropertyPath::_path_pressed() { } void EditorPropertyPath::update_property() { - String full_path = get_edited_property_value(); + String full_path = _get_path_text(); path->set_text(full_path); path->set_tooltip_text(full_path); } @@ -547,8 +563,7 @@ void EditorPropertyPath::_drop_data_fw(const Point2 &p_point, const Variant &p_d return; } - emit_changed(get_edited_property(), filesPaths[0]); - update_property(); + _path_selected(filesPaths[0]); } bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 9cc72cd5c5..ae9c454195 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -142,6 +142,8 @@ class EditorPropertyPath : public EditorProperty { LineEdit *path = nullptr; Button *path_edit = nullptr; + String _get_path_text(); + void _path_selected(const String &p_path); void _path_pressed(); void _path_focus_exited(); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index eeb8ceb1ed..44690f81f0 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -1229,7 +1229,7 @@ fail: extra_config->set_value("init_projects", "list", list); } - singleton = Ref<EditorSettings>(memnew(EditorSettings)); + singleton.instantiate(); singleton->set_path(config_file_path, true); singleton->save_changed_setting = true; singleton->_load_defaults(extra_config); @@ -1496,10 +1496,26 @@ void EditorSettings::set_favorites(const Vector<String> &p_favorites) { } } +void EditorSettings::set_favorite_properties(const HashMap<String, PackedStringArray> &p_favorite_properties) { + favorite_properties = p_favorite_properties; + String favorite_properties_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorite_properties"); + + Ref<ConfigFile> cf; + cf.instantiate(); + for (const KeyValue<String, PackedStringArray> &kv : p_favorite_properties) { + cf->set_value(kv.key, "properties", kv.value); + } + cf->save(favorite_properties_file); +} + Vector<String> EditorSettings::get_favorites() const { return favorites; } +HashMap<String, PackedStringArray> EditorSettings::get_favorite_properties() const { + return favorite_properties; +} + void EditorSettings::set_recent_dirs(const Vector<String> &p_recent_dirs) { recent_dirs = p_recent_dirs; String recent_dirs_file; @@ -1522,23 +1538,51 @@ Vector<String> EditorSettings::get_recent_dirs() const { void EditorSettings::load_favorites_and_recent_dirs() { String favorites_file; + String favorite_properties_file; String recent_dirs_file; if (Engine::get_singleton()->is_project_manager_hint()) { favorites_file = EditorPaths::get_singleton()->get_config_dir().path_join("favorite_dirs"); + favorite_properties_file = EditorPaths::get_singleton()->get_config_dir().path_join("favorite_properties"); recent_dirs_file = EditorPaths::get_singleton()->get_config_dir().path_join("recent_dirs"); } else { favorites_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorites"); + favorite_properties_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorite_properties"); recent_dirs_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("recent_dirs"); } + + /// File Favorites + Ref<FileAccess> f = FileAccess::open(favorites_file, FileAccess::READ); if (f.is_valid()) { String line = f->get_line().strip_edges(); while (!line.is_empty()) { - favorites.push_back(line); + favorites.append(line); line = f->get_line().strip_edges(); } } + /// Inspector Favorites + + Ref<ConfigFile> cf; + cf.instantiate(); + if (cf->load(favorite_properties_file) == OK) { + List<String> secs; + cf->get_sections(&secs); + + for (String &E : secs) { + PackedStringArray properties = PackedStringArray(cf->get_value(E, "properties")); + if (EditorNode::get_editor_data().is_type_recognized(E) || ResourceLoader::exists(E, "Script")) { + for (const String &property : properties) { + if (!favorite_properties[E].has(property)) { + favorite_properties[E].push_back(property); + } + } + } + } + } + + /// Recent Directories + f = FileAccess::open(recent_dirs_file, FileAccess::READ); if (f.is_valid()) { String line = f->get_line().strip_edges(); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index d1ccedfe6c..3c8a4de866 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -102,6 +102,7 @@ private: HashMap<String, List<Ref<InputEvent>>> builtin_action_overrides; Vector<String> favorites; + HashMap<String, PackedStringArray> favorite_properties; Vector<String> recent_dirs; bool save_changed_setting = true; @@ -176,6 +177,8 @@ public: void set_favorites(const Vector<String> &p_favorites); Vector<String> get_favorites() const; + void set_favorite_properties(const HashMap<String, PackedStringArray> &p_favorite_properties); + HashMap<String, PackedStringArray> get_favorite_properties() const; void set_recent_dirs(const Vector<String> &p_recent_dirs); Vector<String> get_recent_dirs() const; void load_favorites_and_recent_dirs(); diff --git a/editor/export/codesign.cpp b/editor/export/codesign.cpp index 72d496b04d..cc53068d48 100644 --- a/editor/export/codesign.cpp +++ b/editor/export/codesign.cpp @@ -1381,14 +1381,14 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const r_error_msg = TTR("Invalid entitlements file."); ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid entitlements file."); } - cet = Ref<CodeSignEntitlementsText>(memnew(CodeSignEntitlementsText(entitlements))); - ceb = Ref<CodeSignEntitlementsBinary>(memnew(CodeSignEntitlementsBinary(entitlements))); + cet.instantiate(entitlements); + ceb.instantiate(entitlements); } print_verbose("CodeSign: Generating requirements..."); Ref<CodeSignRequirements> rq; String team_id = ""; - rq = Ref<CodeSignRequirements>(memnew(CodeSignRequirements())); + rq.instantiate(); // Sign executables. for (int i = 0; i < files_to_sign.size(); i++) { @@ -1487,7 +1487,7 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const print_verbose("CodeSign: Generating signature..."); Ref<CodeSignSignature> cs; - cs = Ref<CodeSignSignature>(memnew(CodeSignSignature())); + cs.instantiate(); print_verbose("CodeSign: Writing signature superblob..."); // Write signature data to the executable. diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 50fa49dc52..8b8fafcd32 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -59,6 +59,17 @@ static int _get_pad(int p_alignment, int p_n) { return pad; } +template <typename T> +static bool _has_pack_path(const T &p_paths, const String &p_path) { + for (const String &E : p_paths) { + if (E.simplify_path().trim_prefix("res://") == p_path) { + return true; + } + } + + return false; +} + #define PCK_PADDING 16 bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) { @@ -210,21 +221,23 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa PackData *pd = (PackData *)p_userdata; + String simplified_path = p_path.simplify_path(); + SavedData sd; - sd.path_utf8 = p_path.utf8(); + sd.path_utf8 = simplified_path.trim_prefix("res://").utf8(); sd.ofs = pd->f->get_position(); sd.size = p_data.size(); sd.encrypted = false; for (int i = 0; i < p_enc_in_filters.size(); ++i) { - if (p_path.matchn(p_enc_in_filters[i]) || p_path.replace("res://", "").matchn(p_enc_in_filters[i])) { + if (simplified_path.matchn(p_enc_in_filters[i]) || simplified_path.trim_prefix("res://").matchn(p_enc_in_filters[i])) { sd.encrypted = true; break; } } for (int i = 0; i < p_enc_ex_filters.size(); ++i) { - if (p_path.matchn(p_enc_ex_filters[i]) || p_path.replace("res://", "").matchn(p_enc_ex_filters[i])) { + if (simplified_path.matchn(p_enc_ex_filters[i]) || simplified_path.trim_prefix("res://").matchn(p_enc_ex_filters[i])) { sd.encrypted = false; break; } @@ -965,10 +978,10 @@ Error EditorExportPlatform::_export_project_files(const Ref<EditorExportPreset> ScriptCallbackData data; data.file_cb = p_save_func; data.so_cb = p_so_func; - return export_project_files(p_preset, p_debug, _script_save_file, &data, _script_add_shared_object); + return export_project_files(p_preset, p_debug, _script_save_file, nullptr, &data, _script_add_shared_object); } -Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func) { +Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_save_func, EditorExportRemoveFunction p_remove_func, void *p_udata, EditorExportSaveSharedObject p_so_func) { //figure out paths of files that will be exported HashSet<String> paths; Vector<String> path_remaps; @@ -1082,6 +1095,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Error err = OK; Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + Vector<String> extra_paths; struct SortByName { bool operator()(const Ref<EditorExportPlugin> &left, const Ref<EditorExportPlugin> &right) const { @@ -1102,10 +1116,12 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } } for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) { - err = p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } + + extra_paths.push_back(export_plugins[i]->extra_files[j].path); } export_plugins.write[i]->_clear(); @@ -1218,7 +1234,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) { - err = p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } @@ -1227,6 +1243,8 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & path_remaps.push_back(path); path_remaps.push_back(export_plugins[i]->extra_files[j].path); } + + extra_paths.push_back(export_plugins[i]->extra_files[j].path); } if (export_plugins[i]->skipped) { @@ -1248,7 +1266,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & if (importer_type == "keep") { // Just keep file as-is. Vector<uint8_t> array = FileAccess::get_file_as_bytes(path); - err = p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; @@ -1291,13 +1309,13 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & sarr.resize(cs.size()); memcpy(sarr.ptrw(), cs.ptr(), sarr.size()); - err = p_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } // Now actual remapped file: sarr = FileAccess::get_file_as_bytes(export_path); - err = p_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } @@ -1327,14 +1345,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & if (remap == "path") { String remapped_path = config->get_value("remap", remap); Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path); - err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); } else if (remap.begins_with("path.")) { String feature = remap.get_slice(".", 1); if (remap_features.has(feature)) { String remapped_path = config->get_value("remap", remap); Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path); - err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); } else { // Remove paths if feature not enabled. config->erase_section_key("remap", remap); @@ -1360,7 +1378,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & sarr.resize(cs.size()); memcpy(sarr.ptrw(), cs.ptr(), sarr.size()); - err = p_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; @@ -1381,7 +1399,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } Vector<uint8_t> array = FileAccess::get_file_as_bytes(export_path); - err = p_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } @@ -1445,7 +1463,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & new_file.write[j] = utf8[j]; } - err = p_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } @@ -1459,7 +1477,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Vector<String> forced_export = get_forced_export_files(); for (int i = 0; i < forced_export.size(); i++) { Vector<uint8_t> array = FileAccess::get_file_as_bytes(forced_export[i]); - err = p_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } @@ -1471,7 +1489,30 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Vector<uint8_t> data = FileAccess::get_file_as_bytes(engine_cfb); DirAccess::remove_file_or_error(engine_cfb); - return p_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key); + if (err != OK) { + return err; + } + + if (p_remove_func) { + for (const String &E : PackedData::get_singleton()->get_file_paths()) { + String simplified_path = E.simplify_path(); + if (simplified_path == config_file) { + continue; + } + + String pack_path = simplified_path.trim_suffix(".remap"); + + if (!_has_pack_path(paths, pack_path) && !_has_pack_path(extra_paths, pack_path) && !_has_pack_path(path_remaps, pack_path) && !_has_pack_path(forced_export, pack_path)) { + err = p_remove_func(p_udata, E); + if (err != OK) { + return err; + } + } + } + } + + return OK; } Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const SharedObject &p_so) { @@ -1483,6 +1524,29 @@ Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const Shar return OK; } +Error EditorExportPlatform::_remove_pack_file(void *p_userdata, const String &p_path) { + PackData *pd = (PackData *)p_userdata; + + SavedData sd; + sd.path_utf8 = p_path.utf8(); + sd.ofs = pd->f->get_position(); + sd.size = 0; + sd.removal = true; + + // This padding will likely never be added, as we should already be aligned when removals are added. + int pad = _get_pad(PCK_PADDING, pd->f->get_position()); + for (int i = 0; i < pad; i++) { + pd->f->store_8(0); + } + + sd.md5.resize(16); + sd.md5.fill(0); + + pd->file_ofs.push_back(sd); + + return OK; +} + Error EditorExportPlatform::_zip_add_shared_object(void *p_userdata, const SharedObject &p_so) { ZipData *zip_data = (ZipData *)p_userdata; if (zip_data->so_files) { @@ -1613,7 +1677,7 @@ Dictionary EditorExportPlatform::_save_pack(const Ref<EditorExportPreset> &p_pre Vector<SharedObject> so_files; int64_t embedded_start = 0; int64_t embedded_size = 0; - Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, nullptr, p_embed, &embedded_start, &embedded_size); + Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, nullptr, nullptr, p_embed, &embedded_start, &embedded_size); Dictionary ret; ret["result"] = err_code; @@ -1699,7 +1763,7 @@ Dictionary EditorExportPlatform::_save_zip_patch(const Ref<EditorExportPreset> & return ret; } -Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) { +Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func, EditorExportRemoveFunction p_remove_func, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) { EditorProgress ep("savepack", TTR("Packing"), 102, true); if (p_save_func == nullptr) { @@ -1722,7 +1786,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b pd.f = ftmp; pd.so_files = p_so_files; - Error err = export_project_files(p_preset, p_debug, p_save_func, &pd, _pack_add_shared_object); + Error err = export_project_files(p_preset, p_debug, p_save_func, p_remove_func, &pd, _pack_add_shared_object); // Close temp file. pd.f.unref(); @@ -1868,6 +1932,9 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b if (pd.file_ofs[i].encrypted) { flags |= PACK_FILE_ENCRYPTED; } + if (pd.file_ofs[i].removal) { + flags |= PACK_FILE_REMOVAL; + } fhead->store_32(flags); } @@ -1936,7 +2003,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b } Error EditorExportPlatform::save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) { - return save_pack(p_preset, p_debug, p_path, p_so_files, _save_pack_patch_file, p_embed, r_embedded_start, r_embedded_size); + return save_pack(p_preset, p_debug, p_path, p_so_files, _save_pack_patch_file, _remove_pack_file, p_embed, r_embedded_start, r_embedded_size); } Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func) { @@ -1957,7 +2024,7 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bo zd.zip = zip; zd.so_files = p_so_files; - Error err = export_project_files(p_preset, p_debug, p_save_func, &zd, _zip_add_shared_object); + Error err = export_project_files(p_preset, p_debug, p_save_func, nullptr, &zd, _zip_add_shared_object); if (err != OK && err != ERR_SKIP) { add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), TTR("Failed to export project files.")); } diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index ef3274c5e4..919fb2915a 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -54,6 +54,7 @@ protected: public: typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + typedef Error (*EditorExportRemoveFunction)(void *p_userdata, const String &p_path); typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so); enum DebugFlags { @@ -82,6 +83,7 @@ private: uint64_t ofs = 0; uint64_t size = 0; bool encrypted = false; + bool removal = false; Vector<uint8_t> md5; CharString path_utf8; @@ -116,6 +118,8 @@ private: static Error _save_pack_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); static Error _pack_add_shared_object(void *p_userdata, const SharedObject &p_so); + static Error _remove_pack_file(void *p_userdata, const String &p_path); + static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); static Error _save_zip_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so); @@ -287,7 +291,7 @@ public: Array get_current_presets() const; Error _export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, const Callable &p_save_func, const Callable &p_so_func); - Error export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func = nullptr); + Error export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_save_func, EditorExportRemoveFunction p_remove_func, void *p_udata, EditorExportSaveSharedObject p_so_func = nullptr); Dictionary _save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, bool p_embed = false); Dictionary _save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); @@ -295,7 +299,7 @@ public: Dictionary _save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); Dictionary _save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); - Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr); + Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr, EditorExportRemoveFunction p_remove_func = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr); Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr); Error save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr); diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp index 52f7a0cee8..4eff096840 100644 --- a/editor/export/editor_export_platform_pc.cpp +++ b/editor/export/editor_export_platform_pc.cpp @@ -194,7 +194,7 @@ Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> int64_t embedded_pos; int64_t embedded_size; - Error err = save_pack(p_preset, p_debug, pck_path, &so_files, nullptr, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size); + Error err = save_pack(p_preset, p_debug, pck_path, &so_files, nullptr, nullptr, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size); if (err == OK && p_preset->get("binary_format/embed_pck")) { if (embedded_size >= 0x100000000 && String(p_preset->get("binary_format/architecture")).contains("32")) { add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB.")); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 9b08d21bdc..fcd5a572b4 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1446,6 +1446,13 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ } } + if (p_item.is_file && FileAccess::exists(old_path + ".uid")) { + err = da->rename(old_path + ".uid", new_path + ".uid"); + if (err != OK) { + EditorNode::get_singleton()->add_io_error(TTR("Error moving:") + "\n" + old_path + ".uid\n"); + } + } + // Update scene if it is open. for (int i = 0; i < file_changed_paths.size(); ++i) { String new_item_path = p_item.is_file ? new_path : file_changed_paths[i].replace_first(old_path, new_path); @@ -1641,21 +1648,27 @@ String FileSystemDock::_get_unique_name(const FileOrFolder &p_entry, const Strin return new_path; } -void FileSystemDock::_update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const { - Vector<String> favorites_list = EditorSettings::get_singleton()->get_favorites(); - Vector<String> new_favorites; - - for (const String &old_path : favorites_list) { +void FileSystemDock::_update_favorites_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const { + Vector<String> favorite_files = EditorSettings::get_singleton()->get_favorites(); + Vector<String> new_favorite_files; + for (const String &old_path : favorite_files) { if (p_folders_renames.has(old_path)) { - new_favorites.push_back(p_folders_renames[old_path]); + new_favorite_files.push_back(p_folders_renames[old_path]); } else if (p_files_renames.has(old_path)) { - new_favorites.push_back(p_files_renames[old_path]); + new_favorite_files.push_back(p_files_renames[old_path]); } else { - new_favorites.push_back(old_path); + new_favorite_files.push_back(old_path); } } + EditorSettings::get_singleton()->set_favorites(new_favorite_files); - EditorSettings::get_singleton()->set_favorites(new_favorites); + HashMap<String, PackedStringArray> favorite_properties = EditorSettings::get_singleton()->get_favorite_properties(); + for (const KeyValue<String, String> &KV : p_files_renames) { + if (favorite_properties.has(KV.key)) { + favorite_properties.replace_key(KV.key, KV.value); + } + } + EditorSettings::get_singleton()->set_favorite_properties(favorite_properties); } void FileSystemDock::_make_scene_confirm() { @@ -1798,7 +1811,7 @@ void FileSystemDock::_rename_operation_confirm() { _update_resource_paths_after_move(file_renames, uids); _update_dependencies_after_move(file_renames, file_owners); _update_project_settings_after_move(file_renames, folder_renames); - _update_favorites_list_after_move(file_renames, folder_renames); + _update_favorites_after_move(file_renames, folder_renames); EditorSceneTabs::get_singleton()->set_current_tab(current_tab); @@ -1959,7 +1972,7 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_cop _update_resource_paths_after_move(file_renames, uids); _update_dependencies_after_move(file_renames, file_owners); _update_project_settings_after_move(file_renames, folder_renames); - _update_favorites_list_after_move(file_renames, folder_renames); + _update_favorites_after_move(file_renames, folder_renames); EditorSceneTabs::get_singleton()->set_current_tab(current_tab); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 72d5ac3a98..fe83129c07 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -275,7 +275,7 @@ private: void _before_move(HashMap<String, ResourceUID::ID> &r_uids, HashSet<String> &r_file_owners) const; void _update_dependencies_after_move(const HashMap<String, String> &p_renames, const HashSet<String> &p_file_owners) const; void _update_resource_paths_after_move(const HashMap<String, String> &p_renames, const HashMap<String, ResourceUID::ID> &p_uids) const; - void _update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const; + void _update_favorites_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const; void _update_project_settings_after_move(const HashMap<String, String> &p_renames, const HashMap<String, String> &p_folders_renames); String _get_unique_name(const FileOrFolder &p_entry, const String &p_at_path); diff --git a/editor/gui/editor_bottom_panel.cpp b/editor/gui/editor_bottom_panel.cpp index 2eb899f085..3cc1e37be0 100644 --- a/editor/gui/editor_bottom_panel.cpp +++ b/editor/gui/editor_bottom_panel.cpp @@ -31,35 +31,35 @@ #include "editor_bottom_panel.h" #include "editor/debugger/editor_debugger_node.h" -#include "editor/editor_about.h" #include "editor/editor_command_palette.h" #include "editor/editor_node.h" #include "editor/editor_string_names.h" -#include "editor/engine_update_label.h" #include "editor/gui/editor_toaster.h" #include "editor/gui/editor_version_button.h" #include "editor/themes/editor_scale.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/split_container.h" void EditorBottomPanel::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { + pin_button->set_button_icon(get_editor_theme_icon(SNAME("Pin"))); expand_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandBottomDock"))); } break; } } -void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control) { +void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock) { for (int i = 0; i < items.size(); i++) { if (items[i].control == p_control) { - _switch_to_item(p_visible, i); + _switch_to_item(p_visible, i, p_ignore_lock); return; } } } -void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) { +void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock) { ERR_FAIL_INDEX(p_idx, items.size()); if (items[p_idx].control->is_visible() == p_visible) { @@ -70,6 +70,10 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) { ERR_FAIL_NULL(center_split); if (p_visible) { + if (!p_ignore_lock && lock_panel_switching && pin_button->is_visible()) { + return; + } + for (int i = 0; i < items.size(); i++) { items[i].button->set_pressed_no_signal(i == p_idx); items[i].control->set_visible(i == p_idx); @@ -80,18 +84,23 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) { } else { add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles))); } + center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE); center_split->set_collapsed(false); + pin_button->show(); + + expand_button->show(); if (expand_button->is_pressed()) { EditorNode::get_top_split()->hide(); } - expand_button->show(); } else { add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles))); items[p_idx].button->set_pressed_no_signal(false); items[p_idx].control->set_visible(false); center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN); center_split->set_collapsed(true); + pin_button->hide(); + expand_button->hide(); if (expand_button->is_pressed()) { EditorNode::get_top_split()->show(); @@ -101,13 +110,17 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) { last_opened_control = items[p_idx].control; } +void EditorBottomPanel::_pin_button_toggled(bool p_pressed) { + lock_panel_switching = p_pressed; +} + void EditorBottomPanel::_expand_button_toggled(bool p_pressed) { EditorNode::get_top_split()->set_visible(!p_pressed); } bool EditorBottomPanel::_button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control) { if (!p_button->is_pressed()) { - _switch_by_control(true, p_control); + _switch_by_control(true, p_control, true); } return false; } @@ -149,7 +162,7 @@ void EditorBottomPanel::load_layout_from_config(Ref<ConfigFile> p_config_file, c Button *EditorBottomPanel::add_item(String p_text, Control *p_item, const Ref<Shortcut> &p_shortcut, bool p_at_front) { Button *tb = memnew(Button); tb->set_theme_type_variation("BottomPanelButton"); - tb->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item)); + tb->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item, true)); tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorBottomPanel::_button_drag_hover).bind(tb, p_item), Callable()); tb->set_text(p_text); tb->set_shortcut(p_shortcut); @@ -231,10 +244,10 @@ void EditorBottomPanel::toggle_last_opened_bottom_panel() { // Select by control instead of index, so that the last bottom panel is opened correctly // if it's been reordered since. if (last_opened_control) { - _switch_by_control(!last_opened_control->is_visible(), last_opened_control); + _switch_by_control(!last_opened_control->is_visible(), last_opened_control, true); } else { // Open the first panel in the list if no panel was opened this session. - _switch_to_item(true, 0); + _switch_to_item(true, 0, true); } } @@ -263,10 +276,17 @@ EditorBottomPanel::EditorBottomPanel() { Control *h_spacer = memnew(Control); bottom_hbox->add_child(h_spacer); + pin_button = memnew(Button); + bottom_hbox->add_child(pin_button); + pin_button->hide(); + pin_button->set_theme_type_variation("FlatMenuButton"); + pin_button->set_toggle_mode(true); + pin_button->set_tooltip_text(TTR("Pin Bottom Panel Switching")); + pin_button->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_pin_button_toggled)); + expand_button = memnew(Button); bottom_hbox->add_child(expand_button); expand_button->hide(); - expand_button->set_flat(false); expand_button->set_theme_type_variation("FlatMenuButton"); expand_button->set_toggle_mode(true); expand_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12)); diff --git a/editor/gui/editor_bottom_panel.h b/editor/gui/editor_bottom_panel.h index 3d44b3750a..950f0e2570 100644 --- a/editor/gui/editor_bottom_panel.h +++ b/editor/gui/editor_bottom_panel.h @@ -49,16 +49,19 @@ class EditorBottomPanel : public PanelContainer { }; Vector<BottomPanelItem> items; + bool lock_panel_switching = false; VBoxContainer *item_vbox = nullptr; HBoxContainer *bottom_hbox = nullptr; HBoxContainer *button_hbox = nullptr; EditorToaster *editor_toaster = nullptr; + Button *pin_button = nullptr; Button *expand_button = nullptr; Control *last_opened_control = nullptr; - void _switch_by_control(bool p_visible, Control *p_control); - void _switch_to_item(bool p_visible, int p_idx); + void _switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock = false); + void _switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock = false); + void _pin_button_toggled(bool p_pressed); void _expand_button_toggled(bool p_pressed); bool _button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control); diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp index 4ebd1922a7..ffea1736a3 100644 --- a/editor/gui/editor_toaster.cpp +++ b/editor/gui/editor_toaster.cpp @@ -375,7 +375,7 @@ Control *EditorToaster::popup(Control *p_control, Severity p_severity, double p_ if (p_time > 0.0) { Button *close_button = memnew(Button); close_button->set_flat(true); - close_button->connect(SceneStringName(pressed), callable_mp(this, &EditorToaster::close).bind(panel)); + close_button->connect(SceneStringName(pressed), callable_mp(this, &EditorToaster::instant_close).bind(panel)); hbox_container->add_child(close_button); toast.close_button = close_button; @@ -501,6 +501,11 @@ void EditorToaster::close(Control *p_control) { toasts[p_control].popped = false; } +void EditorToaster::instant_close(Control *p_control) { + close(p_control); + p_control->set_modulate(Color(1, 1, 1, 0)); +} + EditorToaster *EditorToaster::get_singleton() { return singleton; } diff --git a/editor/gui/editor_toaster.h b/editor/gui/editor_toaster.h index 35a4337746..6fcc2ce3e9 100644 --- a/editor/gui/editor_toaster.h +++ b/editor/gui/editor_toaster.h @@ -115,6 +115,7 @@ public: Control *popup(Control *p_control, Severity p_severity = SEVERITY_INFO, double p_time = 0.0, const String &p_tooltip = String()); void popup_str(const String &p_message, Severity p_severity = SEVERITY_INFO, const String &p_tooltip = String()); void close(Control *p_control); + void instant_close(Control *p_control); EditorToaster(); ~EditorToaster(); diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index 0ada9aa8b2..c11da5dfdb 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -369,16 +369,14 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { msg_temp += String::utf8("• ") + String(E.name) + "\n"; } } - } - if (num_connections >= 1 || num_groups >= 1) { - if (num_groups < 1) { - msg_temp += "\n"; - } - msg_temp += TTR("Click to show signals dock."); + } else { + msg_temp += "\n"; } Ref<Texture2D> icon_temp; SceneTreeEditorButton signal_temp = BUTTON_SIGNALS; + String msg_temp_end = TTR("Click to show signals dock."); + if (num_connections >= 1 && num_groups >= 1) { icon_temp = get_editor_theme_icon(SNAME("SignalsAndGroups")); } else if (num_connections >= 1) { @@ -386,9 +384,11 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { } else if (num_groups >= 1) { icon_temp = get_editor_theme_icon(SNAME("Groups")); signal_temp = BUTTON_GROUPS; + msg_temp_end = TTR("Click to show groups dock."); } if (num_connections >= 1 || num_groups >= 1) { + msg_temp += msg_temp_end; item->add_button(0, icon_temp, signal_temp, false, msg_temp); } } diff --git a/editor/icons/FPS.svg b/editor/icons/FPS.svg new file mode 100644 index 0000000000..5ee818c308 --- /dev/null +++ b/editor/icons/FPS.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M7.25 4h-2v8h2v-2c1.656 0 3-1.344 3-3 0-1.657-1.344-3-3-3zm0 4v-2c.553 0 1 .448 1 1s-.447 1-1 1zM.25 7v5h2v-2h2v-2h-2v-1c0-.553.447-1 1-1h1v-2h-1c-1.656 0-3 1.344-3 3zM13.25 7c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h2v-2h-2c-1.381 0-2.5 1.119-2.5 2.5s1.119 2.5 2.5 2.5c.276 0 .5.224.5.5s-.224.5-.5.5h-2v2h2c1.381 0 2.5-1.119 2.5-2.5s-1.119-2.5-2.5-2.5z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Unfavorite.svg b/editor/icons/Unfavorite.svg new file mode 100644 index 0000000000..78f1b90fd0 --- /dev/null +++ b/editor/icons/Unfavorite.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M 8 1.6992188 L 5.6269531 5.796875 L 1 6.8945312 L 4.2363281 10.302734 L 3.8769531 14.976562 L 8.0175781 12.998047 L 12.173828 14.941406 L 11.777344 10.287109 L 15 6.8945312 L 10.373047 5.796875 L 8 1.6992188 z M 8 4.2773438 L 9.4882812 6.8457031 L 12.388672 7.5332031 L 10.369141 9.6601562 L 10.617188 12.576172 L 8.0097656 11.359375 L 5.4160156 12.599609 L 5.640625 9.6699219 L 3.6113281 7.5332031 L 6.5117188 6.8457031 L 8 4.2773438 z"/></svg>
\ No newline at end of file diff --git a/editor/import/3d/editor_import_collada.cpp b/editor/import/3d/editor_import_collada.cpp index 04a3f23154..c04278fc55 100644 --- a/editor/import/3d/editor_import_collada.cpp +++ b/editor/import/3d/editor_import_collada.cpp @@ -1263,7 +1263,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres //bleh, must ignore invalid ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid), ERR_INVALID_DATA); - mesh = Ref<ImporterMesh>(memnew(ImporterMesh)); + mesh.instantiate(); const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid]; String name = meshdata.name; if (name.is_empty()) { diff --git a/editor/import/3d/resource_importer_obj.cpp b/editor/import/3d/resource_importer_obj.cpp index 3669844207..59d39152e9 100644 --- a/editor/import/3d/resource_importer_obj.cpp +++ b/editor/import/3d/resource_importer_obj.cpp @@ -646,7 +646,7 @@ bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const Stri return true; } -Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterOBJ::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { List<Ref<ImporterMesh>> meshes; Vector<uint8_t> src_lightmap_cache; diff --git a/editor/import/3d/resource_importer_obj.h b/editor/import/3d/resource_importer_obj.h index 9d299bc31a..c4a99428ef 100644 --- a/editor/import/3d/resource_importer_obj.h +++ b/editor/import/3d/resource_importer_obj.h @@ -61,7 +61,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; ResourceImporterOBJ(); }; diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index edf7aa66f0..86af9caf26 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -2872,7 +2872,7 @@ Error ResourceImporterScene::_check_resource_save_paths(const Dictionary &p_data return OK; } -Error ResourceImporterScene::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterScene::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { const String &src_path = p_source_file; Ref<EditorSceneFormatImporter> importer; @@ -3092,7 +3092,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p if (!scr.is_valid()) { EditorNode::add_io_error(TTR("Couldn't load post-import script:") + " " + post_import_script_path); } else { - post_import_script = Ref<EditorScenePostImport>(memnew(EditorScenePostImport)); + post_import_script.instantiate(); post_import_script->set_script(scr); if (!post_import_script->get_script_instance()) { EditorNode::add_io_error(TTR("Invalid/broken script for post-import (check console):") + " " + post_import_script_path); diff --git a/editor/import/3d/resource_importer_scene.h b/editor/import/3d/resource_importer_scene.h index daeab2ae03..b2f5fab0eb 100644 --- a/editor/import/3d/resource_importer_scene.h +++ b/editor/import/3d/resource_importer_scene.h @@ -299,7 +299,7 @@ public: void _compress_animations(AnimationPlayer *anim, int p_page_size_kb); Node *pre_import(const String &p_source_file, const HashMap<StringName, Variant> &p_options); - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool has_advanced_options() const override; virtual void show_advanced_options(const String &p_path) override; diff --git a/editor/import/audio_stream_import_settings.cpp b/editor/import/audio_stream_import_settings.cpp index 0367e5e3f3..dd45806385 100644 --- a/editor/import/audio_stream_import_settings.cpp +++ b/editor/import/audio_stream_import_settings.cpp @@ -580,12 +580,10 @@ AudioStreamImportSettingsDialog::AudioStreamImportSettingsDialog() { bar_beats_edit->set_max(32); bar_beats_edit->connect(SceneStringName(value_changed), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1)); interactive_hb->add_child(bar_beats_edit); - interactive_hb->add_spacer(); main_vbox->add_margin_child(TTR("Music Playback:"), interactive_hb); color_rect = memnew(ColorRect); - main_vbox->add_margin_child(TTR("Preview:"), color_rect); - + main_vbox->add_margin_child(TTR("Preview:"), color_rect, true); color_rect->set_custom_minimum_size(Size2(600, 200) * EDSCALE); color_rect->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/import/editor_import_plugin.cpp b/editor/import/editor_import_plugin.cpp index 3243dcf256..650c0e27ca 100644 --- a/editor/import/editor_import_plugin.cpp +++ b/editor/import/editor_import_plugin.cpp @@ -163,7 +163,7 @@ bool EditorImportPlugin::get_option_visibility(const String &p_path, const Strin ERR_FAIL_V_MSG(false, "Unimplemented _get_option_visibility in add-on."); } -Error EditorImportPlugin::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error EditorImportPlugin::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { Dictionary options; TypedArray<String> platform_variants, gen_files; diff --git a/editor/import/editor_import_plugin.h b/editor/import/editor_import_plugin.h index ea5cfc2682..df472b416b 100644 --- a/editor/import/editor_import_plugin.h +++ b/editor/import/editor_import_plugin.h @@ -69,7 +69,7 @@ public: virtual int get_import_order() const override; virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override; Error append_import_external_resource(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant p_generator_parameters = Variant()); }; diff --git a/editor/import/resource_importer_bitmask.cpp b/editor/import/resource_importer_bitmask.cpp index e7b7850b02..8441a49666 100644 --- a/editor/import/resource_importer_bitmask.cpp +++ b/editor/import/resource_importer_bitmask.cpp @@ -72,7 +72,7 @@ void ResourceImporterBitMap::get_import_options(const String &p_path, List<Impor r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.5)); } -Error ResourceImporterBitMap::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterBitMap::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { int create_from = p_options["create_from"]; float threshold = p_options["threshold"]; Ref<Image> image; diff --git a/editor/import/resource_importer_bitmask.h b/editor/import/resource_importer_bitmask.h index 30564bf0fe..fcb152b47d 100644 --- a/editor/import/resource_importer_bitmask.h +++ b/editor/import/resource_importer_bitmask.h @@ -48,7 +48,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/editor/import/resource_importer_bmfont.cpp b/editor/import/resource_importer_bmfont.cpp index 085ca1362d..b7efdbb6d6 100644 --- a/editor/import/resource_importer_bmfont.cpp +++ b/editor/import/resource_importer_bmfont.cpp @@ -67,7 +67,7 @@ void ResourceImporterBMFont::get_import_options(const String &p_path, List<Impor r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "scaling_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled (Integer),Enabled (Fractional)"), TextServer::FIXED_SIZE_SCALE_ENABLED)); } -Error ResourceImporterBMFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterBMFont::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { print_verbose("Importing BMFont font from: " + p_source_file); Array fallbacks = p_options["fallbacks"]; diff --git a/editor/import/resource_importer_bmfont.h b/editor/import/resource_importer_bmfont.h index 48f036ff13..74fef9ff16 100644 --- a/editor/import/resource_importer_bmfont.h +++ b/editor/import/resource_importer_bmfont.h @@ -48,7 +48,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp index c181011402..17f6070d35 100644 --- a/editor/import/resource_importer_csv_translation.cpp +++ b/editor/import/resource_importer_csv_translation.cpp @@ -72,7 +72,7 @@ void ResourceImporterCSVTranslation::get_import_options(const String &p_path, Li r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "delimiter", PROPERTY_HINT_ENUM, "Comma,Semicolon,Tab"), 0)); } -Error ResourceImporterCSVTranslation::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterCSVTranslation::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { bool compress = p_options["compress"]; String delimiter; @@ -147,6 +147,9 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const if (r_gen_files) { r_gen_files->push_back(save_path); } + + ResourceUID::ID save_id = hash64_murmur3_64(translations[i]->get_locale().hash64(), p_source_id); + ResourceSaver::set_uid(save_path, save_id); } return OK; diff --git a/editor/import/resource_importer_csv_translation.h b/editor/import/resource_importer_csv_translation.h index 9c83719ed1..63676c61a6 100644 --- a/editor/import/resource_importer_csv_translation.h +++ b/editor/import/resource_importer_csv_translation.h @@ -49,7 +49,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/editor/import/resource_importer_dynamic_font.cpp b/editor/import/resource_importer_dynamic_font.cpp index fa222b2790..a4a5e445e3 100644 --- a/editor/import/resource_importer_dynamic_font.cpp +++ b/editor/import/resource_importer_dynamic_font.cpp @@ -141,7 +141,7 @@ void ResourceImporterDynamicFont::show_advanced_options(const String &p_path) { DynamicFontImportSettingsDialog::get_singleton()->open_settings(p_path); } -Error ResourceImporterDynamicFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterDynamicFont::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { print_verbose("Importing dynamic font from: " + p_source_file); int antialiasing = p_options["antialiasing"]; diff --git a/editor/import/resource_importer_dynamic_font.h b/editor/import/resource_importer_dynamic_font.h index 7c7a16cf92..73ef96d583 100644 --- a/editor/import/resource_importer_dynamic_font.h +++ b/editor/import/resource_importer_dynamic_font.h @@ -58,7 +58,7 @@ public: bool has_advanced_options() const override; void show_advanced_options(const String &p_path) override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/editor/import/resource_importer_image.cpp b/editor/import/resource_importer_image.cpp index 4f6dd4e4ef..5a4f64d245 100644 --- a/editor/import/resource_importer_image.cpp +++ b/editor/import/resource_importer_image.cpp @@ -70,7 +70,7 @@ String ResourceImporterImage::get_preset_name(int p_idx) const { void ResourceImporterImage::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { } -Error ResourceImporterImage::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterImage::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ); ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file from path '" + p_source_file + "'."); diff --git a/editor/import/resource_importer_image.h b/editor/import/resource_importer_image.h index dd395009c1..da1925bc5c 100644 --- a/editor/import/resource_importer_image.h +++ b/editor/import/resource_importer_image.h @@ -50,7 +50,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp index f01381904d..44ae2b5ff1 100644 --- a/editor/import/resource_importer_imagefont.cpp +++ b/editor/import/resource_importer_imagefont.cpp @@ -75,7 +75,7 @@ void ResourceImporterImageFont::get_import_options(const String &p_path, List<Im r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "scaling_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled (Integer),Enabled (Fractional)"), TextServer::FIXED_SIZE_SCALE_ENABLED)); } -Error ResourceImporterImageFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterImageFont::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { print_verbose("Importing image font from: " + p_source_file); int columns = p_options["columns"]; diff --git a/editor/import/resource_importer_imagefont.h b/editor/import/resource_importer_imagefont.h index 6b30a3cd6e..79e9455d6d 100644 --- a/editor/import/resource_importer_imagefont.h +++ b/editor/import/resource_importer_imagefont.h @@ -48,7 +48,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 312195fcd7..0d0c89425d 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -289,7 +289,7 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons } } -Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterLayeredTexture::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { int compress_mode = p_options["compress/mode"]; float lossy = p_options["compress/lossy_quality"]; bool high_quality = p_options["compress/high_quality"]; diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h index d8b5bc2d14..271f1f4543 100644 --- a/editor/import/resource_importer_layered_texture.h +++ b/editor/import/resource_importer_layered_texture.h @@ -112,7 +112,7 @@ public: void _save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2); - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const override; virtual String get_import_settings_string() const override; diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp index b7508e7644..639ce48f75 100644 --- a/editor/import/resource_importer_shader_file.cpp +++ b/editor/import/resource_importer_shader_file.cpp @@ -89,7 +89,7 @@ static String _include_function(const String &p_path, void *userpointer) { return file_inc->get_as_utf8_string(); } -Error ResourceImporterShaderFile::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterShaderFile::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { /* STEP 1, Read shader code */ ERR_FAIL_COND_V_EDMSG((OS::get_singleton()->get_current_rendering_method() == "gl_compatibility"), ERR_UNAVAILABLE, "Cannot import custom .glsl shaders when using the gl_compatibility rendering_method. Please switch to the forward_plus or mobile rendering methods to use custom shaders."); ERR_FAIL_COND_V_EDMSG((DisplayServer::get_singleton()->get_name() == "headless"), ERR_UNAVAILABLE, "Cannot import custom .glsl shaders when running in headless mode."); diff --git a/editor/import/resource_importer_shader_file.h b/editor/import/resource_importer_shader_file.h index b28dea36d6..440a3d86b4 100644 --- a/editor/import/resource_importer_shader_file.h +++ b/editor/import/resource_importer_shader_file.h @@ -49,7 +49,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 24a14c60ad..71ccef4752 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -428,7 +428,7 @@ Dictionary ResourceImporterTexture::_load_editor_meta(const String &p_path) cons return f->get_var(); } -Error ResourceImporterTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterTexture::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { // Parse import options. int32_t loader_flags = ImageFormatLoader::FLAG_NONE; diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h index 6c87cd0abb..8aa044f3c8 100644 --- a/editor/import/resource_importer_texture.h +++ b/editor/import/resource_importer_texture.h @@ -100,7 +100,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp index d6ce39f6a6..7e645cc0d0 100644 --- a/editor/import/resource_importer_texture_atlas.cpp +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -91,7 +91,7 @@ String ResourceImporterTextureAtlas::get_option_group_file() const { return "atlas_file"; } -Error ResourceImporterTextureAtlas::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterTextureAtlas::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { /* If this happens, it's because the atlas_file field was not filled, so just import a broken texture */ //use an xpm because it's size independent, the editor images are vector and size dependent diff --git a/editor/import/resource_importer_texture_atlas.h b/editor/import/resource_importer_texture_atlas.h index e4ad9ac230..943f221679 100644 --- a/editor/import/resource_importer_texture_atlas.h +++ b/editor/import/resource_importer_texture_atlas.h @@ -64,7 +64,7 @@ public: virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; virtual String get_option_group_file() const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) override; virtual bool can_import_threaded() const override { return true; } diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 2654952e8a..f500ec4a07 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -94,7 +94,7 @@ void ResourceImporterWAV::get_import_options(const String &p_path, List<ImportOp r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "PCM (Uncompressed),IMA ADPCM,Quite OK Audio"), 2)); } -Error ResourceImporterWAV::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterWAV::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { /* STEP 1, READ WAVE FILE */ Error err; diff --git a/editor/import/resource_importer_wav.h b/editor/import/resource_importer_wav.h index 2253756554..361541c6c1 100644 --- a/editor/import/resource_importer_wav.h +++ b/editor/import/resource_importer_wav.h @@ -140,7 +140,7 @@ public: } } - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 34c0040cb7..eef890d013 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -1265,7 +1265,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendTreeEditor::_file_opened)); - animation_node_inspector_plugin = Ref<EditorInspectorPluginAnimationNodeAnimation>(memnew(EditorInspectorPluginAnimationNodeAnimation)); + animation_node_inspector_plugin.instantiate(); EditorInspector::add_inspector_plugin(animation_node_inspector_plugin); } diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index d089aedc63..4edd021b4d 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -580,8 +580,10 @@ float AnimationPlayerEditor::_get_editor_step() const { const Ref<Animation> anim = player->get_animation(current); ERR_FAIL_COND_V(anim.is_null(), 0.0); + float step = track_editor->get_snap_unit(); + // Use more precise snapping when holding Shift - return Input::get_singleton()->is_key_pressed(Key::SHIFT) ? anim->get_step() * 0.25 : anim->get_step(); + return Input::get_singleton()->is_key_pressed(Key::SHIFT) ? step * 0.25 : step; } void AnimationPlayerEditor::_animation_name_edited() { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index e3cf3dbbf2..62793fbcb5 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -5620,7 +5620,7 @@ CanvasItemEditor::CanvasItemEditor() { snap_dialog->connect(SceneStringName(confirmed), callable_mp(this, &CanvasItemEditor::_snap_changed)); add_child(snap_dialog); - select_sb = Ref<StyleBoxTexture>(memnew(StyleBoxTexture)); + select_sb.instantiate(); selection_menu = memnew(PopupMenu); add_child(selection_menu); diff --git a/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp index 39ae020d53..5a6527f876 100644 --- a/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp @@ -36,7 +36,7 @@ #include "scene/3d/marker_3d.h" Marker3DGizmoPlugin::Marker3DGizmoPlugin() { - pos3d_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + pos3d_mesh.instantiate(); Vector<Vector3> cursor_points; Vector<Color> cursor_colors; diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp index 429add4540..e3b59f9bfb 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.cpp +++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -35,8 +35,8 @@ Ref<OccluderPolygon2D> LightOccluder2DEditor::_ensure_occluder() const { Ref<OccluderPolygon2D> occluder = node->get_occluder_polygon(); - if (!occluder.is_valid()) { - occluder = Ref<OccluderPolygon2D>(memnew(OccluderPolygon2D)); + if (occluder.is_null()) { + occluder.instantiate(); node->set_occluder_polygon(occluder); } return occluder; diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp index 2eaab0fcbd..360e6b00da 100644 --- a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp +++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp @@ -115,9 +115,14 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam return EditorPlugin::AFTER_GUI_INPUT_PASS; } - Transform3D gt = obstacle_node->get_global_transform(); + // Use special transformation rules for NavigationObstacle3D: Only take global y-rotation into account and limit scaling to positive values. + Transform3D gt; + gt.origin = obstacle_node->get_global_position(); + gt.scale_basis(obstacle_node->get_global_basis().get_scale().abs().maxf(0.001)); + gt.rotate_basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y); Transform3D gi = gt.affine_inverse(); Plane p(Vector3(0.0, 1.0, 0.0), gt.origin); + point_lines_meshinstance->set_transform(gt.translated(Vector3(0.0, 0.0, 0.00001))); Ref<InputEventMouseButton> mb = p_event; @@ -374,9 +379,12 @@ void NavigationObstacle3DEditor::_polygon_draw() { point_handle_mesh->clear_surfaces(); point_lines_mesh->clear_surfaces(); point_lines_meshinstance->set_material_override(line_material); - point_lines_mesh->surface_begin(Mesh::PRIMITIVE_LINES); - Rect2 rect; + if (poly.is_empty()) { + return; + } + + point_lines_mesh->surface_begin(Mesh::PRIMITIVE_LINES); for (int i = 0; i < poly.size(); i++) { Vector2 p, p2; @@ -392,12 +400,6 @@ void NavigationObstacle3DEditor::_polygon_draw() { p2 = poly[(i + 1) % poly.size()]; } - if (i == 0) { - rect.position = p; - } else { - rect.expand_to(p); - } - Vector3 point = Vector3(p.x, 0.0, p.y); Vector3 next_point = Vector3(p2.x, 0.0, p2.y); @@ -411,58 +413,8 @@ void NavigationObstacle3DEditor::_polygon_draw() { //vpc->draw_texture(handle,point-handle->get_size()*0.5); } - rect = rect.grow(1); - - AABB r; - r.position.x = rect.position.x; - r.position.y = 0.0; - r.position.z = rect.position.y; - r.size.x = rect.size.x; - r.size.y = 0; - r.size.z = rect.size.y; - - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0.3, 0, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0.0, 0.3, 0)); - - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0)); - - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0)); - - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + r.size); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.3, 0, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + r.size); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0)); - point_lines_mesh->surface_end(); - if (poly.size() == 0) { - return; - } - Array point_handle_mesh_array; point_handle_mesh_array.resize(Mesh::ARRAY_MAX); Vector<Vector3> point_handle_mesh_vertices; @@ -541,8 +493,9 @@ NavigationObstacle3DEditor::NavigationObstacle3DEditor() { point_lines_mesh.instantiate(); point_lines_meshinstance->set_mesh(point_lines_mesh); point_lines_meshinstance->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001))); + point_lines_meshinstance->set_as_top_level(true); - line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + line_material.instantiate(); line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); @@ -550,7 +503,7 @@ NavigationObstacle3DEditor::NavigationObstacle3DEditor() { line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); line_material->set_albedo(Color(1, 1, 1)); - handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + handle_material.instantiate(); handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp index 3c71040408..c11a7cf20e 100644 --- a/editor/plugins/navigation_polygon_editor_plugin.cpp +++ b/editor/plugins/navigation_polygon_editor_plugin.cpp @@ -38,8 +38,8 @@ Ref<NavigationPolygon> NavigationPolygonEditor::_ensure_navpoly() const { Ref<NavigationPolygon> navpoly = node->get_navigation_polygon(); - if (!navpoly.is_valid()) { - navpoly = Ref<NavigationPolygon>(memnew(NavigationPolygon)); + if (navpoly.is_null()) { + navpoly.instantiate(); node->set_navigation_polygon(navpoly); } return navpoly; diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 5afe01025d..810d1674ca 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -7050,12 +7050,12 @@ void fragment() { col.a = EDITOR_GET("editors/3d/manipulator_gizmo_opacity"); - move_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); - move_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); - rotate_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); - scale_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); - scale_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); - axis_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); + move_gizmo[i].instantiate(); + move_plane_gizmo[i].instantiate(); + rotate_gizmo[i].instantiate(); + scale_gizmo[i].instantiate(); + scale_plane_gizmo[i].instantiate(); + axis_gizmo[i].instantiate(); Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D); mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); @@ -7286,7 +7286,7 @@ void fragment() { border_mat->set_shader(border_shader); border_mat->set_shader_parameter("albedo", Color(0.75, 0.75, 0.75, col.a / 3.0)); - rotate_gizmo[3] = Ref<ArrayMesh>(memnew(ArrayMesh)); + rotate_gizmo[3].instantiate(); rotate_gizmo[3]->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays); rotate_gizmo[3]->surface_set_material(0, border_mat); } @@ -8639,7 +8639,7 @@ Node3DEditor::Node3DEditor() { gizmo.visible = true; gizmo.scale = 1.0; - viewport_environment = Ref<Environment>(memnew(Environment)); + viewport_environment.instantiate(); VBoxContainer *vbc = this; custom_camera = nullptr; diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 91cff9f8e2..3a4b3a0ea2 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -922,7 +922,7 @@ Ref<EditorNode3DGizmo> Path3DGizmoPlugin::create_gizmo(Node3D *p_spatial) { Path3D *path = Object::cast_to<Path3D>(p_spatial); if (path) { - ref = Ref<Path3DGizmo>(memnew(Path3DGizmo(path, disk_size))); + ref.instantiate(path, disk_size); } return ref; diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 6cd04174a5..8ab08ff28f 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -275,7 +275,11 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { uv_button[UV_MODE_REMOVE_POLYGON]->hide(); uv_button[UV_MODE_PAINT_WEIGHT]->hide(); uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); - _uv_mode(UV_MODE_EDIT_POINT); + if (node->get_polygon().is_empty()) { + _uv_mode(UV_MODE_CREATE); + } else { + _uv_mode(UV_MODE_EDIT_POINT); + } bone_scroll_main_vb->hide(); bone_paint_strength->hide(); @@ -317,9 +321,16 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { uv_edit_draw->queue_redraw(); } +void Polygon2DEditor::_uv_edit_popup_show() { + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->connect("version_changed", callable_mp(this, &Polygon2DEditor::_update_available_modes)); +} + void Polygon2DEditor::_uv_edit_popup_hide() { EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "uv_editor", Rect2(uv_edit->get_position(), uv_edit->get_size())); _cancel_editing(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->disconnect("version_changed", callable_mp(this, &Polygon2DEditor::_update_available_modes)); } void Polygon2DEditor::_menu_option(int p_option) { @@ -346,6 +357,7 @@ void Polygon2DEditor::_menu_option(int p_option) { uv_edit->popup_centered_ratio(0.85); } _update_bone_list(); + _update_available_modes(); get_tree()->connect("process_frame", callable_mp(this, &Polygon2DEditor::_center_view), CONNECT_ONE_SHOT); } break; case UVEDIT_POLYGON_TO_UV: { @@ -408,6 +420,7 @@ void Polygon2DEditor::_cancel_editing() { node->set_polygons(polygons_prev); _update_polygon_editing_state(); + _update_available_modes(); } else if (uv_drag) { uv_drag = false; if (uv_edit_mode[0]->is_pressed()) { // Edit UV. @@ -566,6 +579,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_drag = false; uv_create = false; + _update_available_modes(); _uv_mode(UV_MODE_EDIT_POINT); _menu_option(MODE_EDIT); } else { @@ -973,6 +987,23 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } } +void Polygon2DEditor::_update_available_modes() { + // Force point editing mode if there's no polygon yet. + if (node->get_polygon().is_empty()) { + if (!uv_edit_mode[1]->is_pressed()) { + uv_edit_mode[1]->set_pressed(true); + _uv_edit_mode_select(1); + } + uv_edit_mode[0]->set_disabled(true); + uv_edit_mode[2]->set_disabled(true); + uv_edit_mode[3]->set_disabled(true); + } else { + uv_edit_mode[0]->set_disabled(false); + uv_edit_mode[2]->set_disabled(false); + uv_edit_mode[3]->set_disabled(false); + } +} + void Polygon2DEditor::_center_view() { Size2 texture_size; if (node->get_texture().is_valid()) { @@ -1324,6 +1355,7 @@ Polygon2DEditor::Polygon2DEditor() { add_child(uv_edit); uv_edit->connect(SceneStringName(confirmed), callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide)); uv_edit->connect("canceled", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide)); + uv_edit->connect("about_to_popup", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_show)); VBoxContainer *uv_main_vb = memnew(VBoxContainer); uv_edit->add_child(uv_main_vb); diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index cb082ec513..4e1cd7172e 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -142,6 +142,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { void _cancel_editing(); void _update_polygon_editing_state(); + void _update_available_modes(); void _center_view(); void _update_zoom_and_pan(bool p_zoom_at_center); @@ -157,6 +158,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { void _set_snap_step_y(real_t p_val); void _uv_edit_mode_select(int p_mode); + void _uv_edit_popup_show(); void _uv_edit_popup_hide(); void _bone_paint_selected(int p_index); diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp index 9bb2d81549..017504f0d6 100644 --- a/editor/plugins/polygon_3d_editor_plugin.cpp +++ b/editor/plugins/polygon_3d_editor_plugin.cpp @@ -554,7 +554,7 @@ Polygon3DEditor::Polygon3DEditor() { imgeom->set_mesh(imesh); imgeom->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001))); - line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + line_material.instantiate(); line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); @@ -562,7 +562,7 @@ Polygon3DEditor::Polygon3DEditor() { line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); line_material->set_albedo(Color(1, 1, 1)); - handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + handle_material.instantiate(); handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 19fbd2dd3b..369a1fc864 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -1186,8 +1186,8 @@ Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, Skel singleton = this; // Handle. - handle_material = Ref<ShaderMaterial>(memnew(ShaderMaterial)); - handle_shader = Ref<Shader>(memnew(Shader)); + handle_material.instantiate(); + handle_shader.instantiate(); handle_shader->set_code(R"( // Skeleton 3D gizmo handle shader. diff --git a/main/main.cpp b/main/main.cpp index fb21ee502f..bdadd24d35 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -653,7 +653,7 @@ void Main::print_help(const char *p_binary) { print_help_option("--delta-smoothing <enable>", "Enable or disable frame delta smoothing [\"enable\", \"disable\"].\n"); print_help_option("--print-fps", "Print the frames per second to the stdout.\n"); #ifdef TOOLS_ENABLED - print_help_option("--editor-pseudolocalization", "Enable pseudolocalization for the editor and the project manager.\n"); + print_help_option("--editor-pseudolocalization", "Enable pseudolocalization for the editor and the project manager.\n", CLI_OPTION_AVAILABILITY_EDITOR); #endif print_help_title("Standalone tools"); diff --git a/misc/dist/linux/org.godotengine.Godot.appdata.xml b/misc/dist/linux/org.godotengine.Godot.appdata.xml index af647bc866..8b27b6300d 100644 --- a/misc/dist/linux/org.godotengine.Godot.appdata.xml +++ b/misc/dist/linux/org.godotengine.Godot.appdata.xml @@ -6,7 +6,7 @@ <project_license>MIT</project_license> <name>Godot Engine</name> <summary>Multi-platform 2D and 3D game engine with a feature-rich editor</summary> - <launchable type="desktop-id">org.godotengine.Godot.desktop</launchable> + <launchable type="desktop-id">org.godotengine.Godot.desktop</launchable> <description> <p> Godot is an advanced, feature-packed, multi-platform 2D and 3D game diff --git a/modules/enet/enet_connection.cpp b/modules/enet/enet_connection.cpp index 2ccfd5d326..9c9302a51c 100644 --- a/modules/enet/enet_connection.cpp +++ b/modules/enet/enet_connection.cpp @@ -113,7 +113,7 @@ Ref<ENetPacketPeer> ENetConnection::connect_to_host(const String &p_address, int if (peer == nullptr) { return nullptr; } - out = Ref<ENetPacketPeer>(memnew(ENetPacketPeer(peer))); + out.instantiate(peer); peers.push_back(out); return out; } diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index aa482615da..7cac61304f 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -117,7 +117,7 @@ static Ref<ImporterMesh> _mesh_to_importer_mesh(Ref<Mesh> p_mesh) { mat_name = mat->get_name(); } else { // Assign default material when no material is assigned. - mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + mat.instantiate(); } importer_mesh->add_surface(p_mesh->surface_get_primitive_type(surface_i), array, p_mesh->surface_get_blend_shape_arrays(surface_i), p_mesh->surface_get_lods(surface_i), mat, @@ -5913,7 +5913,7 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd mat_name = mat->get_name(); } else { // Assign default material when no material is assigned. - mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + mat.instantiate(); } mesh->add_surface(csg_mesh->surface_get_primitive_type(surface_i), @@ -8560,7 +8560,7 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint Error err; Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat(R"(Can't open file at path "%s")", p_path)); ERR_FAIL_COND_V(file.is_null(), ERR_FILE_CANT_OPEN); String base_path = p_base_path; if (base_path.is_empty()) { diff --git a/modules/gltf/tests/test_gltf_extras.h b/modules/gltf/tests/test_gltf_extras.h index 37c8f6925c..73ef02e9f1 100644 --- a/modules/gltf/tests/test_gltf_extras.h +++ b/modules/gltf/tests/test_gltf_extras.h @@ -91,7 +91,7 @@ static Node *_gltf_export_then_import(Node *p_root, String &p_tempfilebase) { options["gltf/naming_version"] = 1; // Process gltf file, note that this generates `.scn` resource from the 2nd argument. - err = import_scene->import(p_tempfilebase + ".gltf", p_tempfilebase, options, nullptr, nullptr, nullptr); + err = import_scene->import(0, p_tempfilebase + ".gltf", p_tempfilebase, options, nullptr, nullptr, nullptr); CHECK_MESSAGE(err == OK, "GLTF import failed."); ResourceImporterScene::remove_scene_importer(import_gltf); diff --git a/modules/godot_physics_3d/godot_shape_3d.cpp b/modules/godot_physics_3d/godot_shape_3d.cpp index a7cccc5cb8..4356ebe2f2 100644 --- a/modules/godot_physics_3d/godot_shape_3d.cpp +++ b/modules/godot_physics_3d/godot_shape_3d.cpp @@ -1996,7 +1996,11 @@ bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vect Vector3 bounds_from = p_begin / BOUNDS_CHUNK_SIZE; Vector3 bounds_to = p_end / BOUNDS_CHUNK_SIZE; Vector3 bounds_offset = local_origin / BOUNDS_CHUNK_SIZE; - return _intersect_grid_segment(_heightmap_chunk_cull_segment, bounds_from, bounds_to, bounds_grid_width, bounds_grid_depth, bounds_offset, r_point, r_normal); + // Plus 1 here to width and depth of the chunk because _intersect_grid_segment() is used by cell level as well, + // and in _intersect_grid_segment() the loop will exit 1 early because for cell point triangle lookup, it dose x + 1, z + 1 etc for the vertex. + int bounds_width = bounds_grid_width + 1; + int bounds_depth = bounds_grid_depth + 1; + return _intersect_grid_segment(_heightmap_chunk_cull_segment, bounds_from, bounds_to, bounds_width, bounds_depth, bounds_offset, r_point, r_normal); } } diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index bd2792d92a..caa7a79874 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -34,11 +34,14 @@ #include "core/input/input.h" #include "core/os/keyboard.h" +#include "editor/editor_command_palette.h" #include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" +#include "editor/gui/editor_bottom_panel.h" +#include "editor/gui/editor_zoom_widget.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "editor/themes/editor_scale.h" #include "scene/3d/camera_3d.h" @@ -84,16 +87,10 @@ void GridMapEditor::_menu_option(int p_option) { } if (edit_axis != new_axis) { - int item1 = options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL); - int item2 = options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL); if (edit_axis == Vector3::AXIS_Y) { - options->get_popup()->set_item_text(item1, TTR("Next Plane")); - options->get_popup()->set_item_text(item2, TTR("Previous Plane")); - spin_box_label->set_text(TTR("Plane:")); + floor->set_tooltip_text("Change Grid Plane"); } else if (new_axis == Vector3::AXIS_Y) { - options->get_popup()->set_item_text(item1, TTR("Next Floor")); - options->get_popup()->set_item_text(item2, TTR("Previous Floor")); - spin_box_label->set_text(TTR("Floor:")); + floor->set_tooltip_text("Change Grid Floor"); } } edit_axis = Vector3::Axis(new_axis); @@ -251,14 +248,22 @@ void GridMapEditor::_menu_option(int p_option) { void GridMapEditor::_update_cursor_transform() { cursor_transform = Transform3D(); cursor_transform.origin = cursor_origin; - cursor_transform.basis = node->get_basis_with_orthogonal_index(cursor_rot); cursor_transform.basis *= node->get_cell_scale(); cursor_transform = node->get_global_transform() * cursor_transform; - if (selected_palette >= 0) { - if (node && !node->get_mesh_library().is_null()) { + if (mode_buttons_group->get_pressed_button() == paint_mode_button) { + // Rotation is only applied in paint mode, we don't want the cursor box to rotate otherwise. + cursor_transform.basis = node->get_basis_with_orthogonal_index(cursor_rot); + if (selected_palette >= 0 && node && node->get_mesh_library().is_valid()) { cursor_transform *= node->get_mesh_library()->get_item_mesh_transform(selected_palette); } + } else { + Transform3D xf; + xf.scale(node->get_cell_size()); + xf.origin.x = node->get_center_x() ? -node->get_cell_size().x / 2 : 0; + xf.origin.y = node->get_center_y() ? -node->get_cell_size().y / 2 : 0; + xf.origin.z = node->get_center_z() ? -node->get_cell_size().z / 2 : 0; + cursor_transform *= xf; } if (cursor_instance.is_valid()) { @@ -301,7 +306,7 @@ void GridMapEditor::_update_selection_transform() { xf2.basis.scale(scale); xf2.origin = position; - RenderingServer::get_singleton()->instance_set_transform(selection_level_instance[i], xf2); + RenderingServer::get_singleton()->instance_set_transform(selection_level_instance[i], node->get_global_transform() * xf2); } } } @@ -336,25 +341,22 @@ void GridMapEditor::_set_selection(bool p_active, const Vector3 &p_begin, const if (is_visible_in_tree()) { _update_selection_transform(); } - - options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_CLEAR), !selection.active); - options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_CUT), !selection.active); - options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_DUPLICATE), !selection.active); - options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_FILL), !selection.active); } bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, bool p_click) { if (!spatial_editor) { return false; } - - if (selected_palette < 0 && input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE) { + if (input_action == INPUT_TRANSFORM) { + return false; + } + if (selected_palette < 0 && input_action != INPUT_NONE && input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE) { return false; } if (mesh_library.is_null()) { return false; } - if (input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE && !mesh_library->has_item(selected_palette)) { + if (input_action != INPUT_NONE && input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE && !mesh_library->has_item(selected_palette)) { return false; } @@ -405,13 +407,17 @@ bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, b cursor_origin = (Vector3(cell[0], cell[1], cell[2]) + Vector3(0.5 * node->get_center_x(), 0.5 * node->get_center_y(), 0.5 * node->get_center_z())) * node->get_cell_size(); cursor_visible = true; - if (input_action == INPUT_SELECT || input_action == INPUT_PASTE) { + if (input_action == INPUT_PASTE) { cursor_visible = false; } _update_cursor_transform(); } + if (input_action == INPUT_NONE) { + return false; + } + if (input_action == INPUT_PASTE) { paste_indicator.current = Vector3i(cell[0], cell[1], cell[2]); _update_paste_indicator(); @@ -604,7 +610,18 @@ void GridMapEditor::_do_paste() { } if (reselect) { - undo_redo->add_do_method(this, "_set_selection", true, paste_indicator.begin + ofs, paste_indicator.end + ofs); + // We need to rotate the paste_indicator to find the selection begin and end: + Vector3 temp_end = rot.xform(paste_indicator.end - paste_indicator.begin) + paste_indicator.begin + ofs; + Vector3 temp_begin = paste_indicator.begin + ofs; + // _set_selection expects that selection_begin is the corner closer to the origin: + for (int i = 0; i < 3; ++i) { + if (temp_begin[i] > temp_end[i]) { + float p = temp_begin[i]; + temp_begin[i] = temp_end[i]; + temp_end[i] = p; + } + } + undo_redo->add_do_method(this, "_set_selection", true, temp_begin, temp_end); undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end); } @@ -613,13 +630,93 @@ void GridMapEditor::_do_paste() { _clear_clipboard_data(); } +void GridMapEditor::_show_viewports_transform_gizmo(bool p_value) { + Dictionary new_state; + new_state["transform_gizmo"] = p_value; + for (uint32_t i = 0; i < Node3DEditor::VIEWPORTS_COUNT; i++) { + Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_editor_viewport(i); + viewport->set_state(new_state); + } +} + EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event) { if (!node) { return EditorPlugin::AFTER_GUI_INPUT_PASS; } - Ref<InputEventMouseButton> mb = p_event; + Ref<InputEventKey> k = p_event; + if (k.is_valid() && k->is_pressed() && !k->is_echo()) { + // If we are in Transform mode we pass the events to the 3D editor, + // but if the Transform mode shortcut is pressed again, we go back to Selection mode. + if (mode_buttons_group->get_pressed_button() == transform_mode_button) { + if (transform_mode_button->get_shortcut().is_valid() && transform_mode_button->get_shortcut()->matches_event(p_event)) { + select_mode_button->set_pressed(true); + accept_event(); + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + return EditorPlugin::AFTER_GUI_INPUT_PASS; + } + + for (BaseButton *b : viewport_shortcut_buttons) { + if (b->is_disabled()) { + continue; + } + + if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) { + if (b->is_toggle_mode()) { + b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed()); + } else { + // Can't press a button without toggle mode, so just emit the signal directly. + b->emit_signal(SceneStringName(pressed)); + } + accept_event(); + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + } + } + + if (k.is_valid() && k->is_pressed() && !k->is_echo()) { + if (k->get_keycode() == Key::ESCAPE) { + if (input_action == INPUT_PASTE) { + _clear_clipboard_data(); + input_action = INPUT_NONE; + _update_paste_indicator(); + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } else if (selection.active) { + _set_selection(false); + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } else { + input_action = INPUT_NONE; + update_palette(); + _update_cursor_instance(); + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + } + + Ref<Shortcut> ed_shortcut = ED_GET_SHORTCUT("grid_map/previous_floor"); + if (ed_shortcut.is_valid() && ed_shortcut->matches_event(p_event)) { + accept_event(); + _menu_option(MENU_OPTION_PREV_LEVEL); + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + ed_shortcut = ED_GET_SHORTCUT("grid_map/next_floor"); + if (ed_shortcut.is_valid() && ed_shortcut->matches_event(p_event)) { + accept_event(); + _menu_option(MENU_OPTION_NEXT_LEVEL); + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + for (int i = 0; i < options->get_popup()->get_item_count(); ++i) { + const Ref<Shortcut> &shortcut = options->get_popup()->get_item_shortcut(i); + if (shortcut.is_valid() && shortcut->matches_event(p_event)) { + // Consume input to avoid conflicts with other plugins. + accept_event(); + _menu_option(options->get_popup()->get_item_id(i)); + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + } + } + Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::WHEEL_UP && (mb->is_command_or_control_pressed())) { if (mb->is_pressed()) { @@ -645,14 +742,17 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D input_action = INPUT_NONE; _update_paste_indicator(); return EditorPlugin::AFTER_GUI_INPUT_STOP; - } else if (mb->is_shift_pressed() && can_edit) { + } else if (mode_buttons_group->get_pressed_button() == select_mode_button && can_edit) { input_action = INPUT_SELECT; last_selection = selection; - } else if (mb->is_command_or_control_pressed() && can_edit) { + } else if (mode_buttons_group->get_pressed_button() == pick_mode_button && can_edit) { input_action = INPUT_PICK; - } else { + } else if (mode_buttons_group->get_pressed_button() == paint_mode_button && can_edit) { input_action = INPUT_PAINT; set_items.clear(); + } else if (mode_buttons_group->get_pressed_button() == erase_mode_button && can_edit) { + input_action = INPUT_ERASE; + set_items.clear(); } } else if (mb->get_button_index() == MouseButton::RIGHT) { if (input_action == INPUT_PASTE) { @@ -663,9 +763,6 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D } else if (selection.active) { _set_selection(false); return EditorPlugin::AFTER_GUI_INPUT_STOP; - } else { - input_action = INPUT_ERASE; - set_items.clear(); } } else { return EditorPlugin::AFTER_GUI_INPUT_PASS; @@ -676,7 +773,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D } return EditorPlugin::AFTER_GUI_INPUT_PASS; } else { - if ((mb->get_button_index() == MouseButton::RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_PAINT)) { + if ((mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_ERASE) || (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_PAINT)) { if (set_items.size()) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("GridMap Paint")); @@ -731,42 +828,6 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D return EditorPlugin::AFTER_GUI_INPUT_PASS; } - Ref<InputEventKey> k = p_event; - - if (k.is_valid()) { - if (k->is_pressed()) { - if (k->get_keycode() == Key::ESCAPE) { - if (input_action == INPUT_PASTE) { - _clear_clipboard_data(); - input_action = INPUT_NONE; - _update_paste_indicator(); - return EditorPlugin::AFTER_GUI_INPUT_STOP; - } else if (selection.active) { - _set_selection(false); - return EditorPlugin::AFTER_GUI_INPUT_STOP; - } else { - selected_palette = -1; - mesh_library_palette->deselect_all(); - update_palette(); - _update_cursor_instance(); - return EditorPlugin::AFTER_GUI_INPUT_STOP; - } - } - - // Consume input to avoid conflicts with other plugins. - if (k.is_valid() && k->is_pressed() && !k->is_echo()) { - for (int i = 0; i < options->get_popup()->get_item_count(); ++i) { - const Ref<Shortcut> &shortcut = options->get_popup()->get_item_shortcut(i); - if (shortcut.is_valid() && shortcut->matches_event(p_event)) { - accept_event(); - _menu_option(options->get_popup()->get_item_id(i)); - return EditorPlugin::AFTER_GUI_INPUT_STOP; - } - } - } - } - } - Ref<InputEventPanGesture> pan_gesture = p_event; if (pan_gesture.is_valid()) { if (pan_gesture->is_alt_pressed() && pan_gesture->is_command_or_control_pressed()) { @@ -833,11 +894,13 @@ void GridMapEditor::_mesh_library_palette_input(const Ref<InputEvent> &p_ie) { // Zoom in/out using Ctrl + mouse wheel if (mb.is_valid() && mb->is_pressed() && mb->is_command_or_control_pressed()) { if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) { - size_slider->set_value(size_slider->get_value() + 0.2); + zoom_widget->set_zoom(zoom_widget->get_zoom() + 0.2); + zoom_widget->emit_signal(SNAME("zoom_changed"), zoom_widget->get_zoom()); } if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) { - size_slider->set_value(size_slider->get_value() - 0.2); + zoom_widget->set_zoom(zoom_widget->get_zoom() - 0.2); + zoom_widget->emit_signal(SNAME("zoom_changed"), zoom_widget->get_zoom()); } } } @@ -855,9 +918,9 @@ void GridMapEditor::update_palette() { if (display_mode == DISPLAY_THUMBNAIL) { mesh_library_palette->set_max_columns(0); mesh_library_palette->set_icon_mode(ItemList::ICON_MODE_TOP); - mesh_library_palette->set_fixed_column_width(min_size * MAX(size_slider->get_value(), 1.5)); + mesh_library_palette->set_fixed_column_width(min_size * MAX(zoom_widget->get_zoom(), 1.5)); } else if (display_mode == DISPLAY_LIST) { - mesh_library_palette->set_max_columns(1); + mesh_library_palette->set_max_columns(0); mesh_library_palette->set_icon_mode(ItemList::ICON_MODE_LEFT); mesh_library_palette->set_fixed_column_width(0); } @@ -938,6 +1001,11 @@ void GridMapEditor::_update_mesh_library() { } update_palette(); + // Make sure we select the first tile as default possible. + if (mesh_library_palette->get_current() == -1 && mesh_library_palette->get_item_count() > 0) { + mesh_library_palette->set_current(0); + selected_palette = mesh_library_palette->get_item_metadata(0); + } // Update the cursor and grid in case the library is changed or removed. _update_cursor_instance(); update_grid(); @@ -1058,10 +1126,22 @@ void GridMapEditor::_draw_grids(const Vector3 &cell_size) { } void GridMapEditor::_update_theme() { - options->set_button_icon(get_theme_icon(SNAME("GridMap"), EditorStringName(EditorIcons))); + transform_mode_button->set_button_icon(get_theme_icon(SNAME("ToolMove"), EditorStringName(EditorIcons))); + select_mode_button->set_button_icon(get_theme_icon(SNAME("ToolSelect"), EditorStringName(EditorIcons))); + erase_mode_button->set_button_icon(get_theme_icon(SNAME("Eraser"), EditorStringName(EditorIcons))); + paint_mode_button->set_button_icon(get_theme_icon(SNAME("Paint"), EditorStringName(EditorIcons))); + pick_mode_button->set_button_icon(get_theme_icon(SNAME("ColorPick"), EditorStringName(EditorIcons))); + fill_action_button->set_button_icon(get_theme_icon(SNAME("Bucket"), EditorStringName(EditorIcons))); + move_action_button->set_button_icon(get_theme_icon(SNAME("ActionCut"), EditorStringName(EditorIcons))); + duplicate_action_button->set_button_icon(get_theme_icon(SNAME("ActionCopy"), EditorStringName(EditorIcons))); + delete_action_button->set_button_icon(get_theme_icon(SNAME("Clear"), EditorStringName(EditorIcons))); + rotate_x_button->set_button_icon(get_theme_icon(SNAME("RotateLeft"), EditorStringName(EditorIcons))); + rotate_y_button->set_button_icon(get_theme_icon(SNAME("ToolRotate"), EditorStringName(EditorIcons))); + rotate_z_button->set_button_icon(get_theme_icon(SNAME("RotateRight"), EditorStringName(EditorIcons))); search_box->set_right_icon(get_theme_icon(SNAME("Search"), EditorStringName(EditorIcons))); mode_thumbnail->set_button_icon(get_theme_icon(SNAME("FileThumbnail"), EditorStringName(EditorIcons))); mode_list->set_button_icon(get_theme_icon(SNAME("FileList"), EditorStringName(EditorIcons))); + options->set_button_icon(get_theme_icon(SNAME("Tools"), EditorStringName(EditorIcons))); } void GridMapEditor::_notification(int p_what) { @@ -1076,6 +1156,9 @@ void GridMapEditor::_notification(int p_what) { RenderingServer::get_singleton()->instance_set_layer_mask(selection_level_instance[i], 1 << Node3DEditorViewport::MISC_TOOL_LAYER); } + cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, get_tree()->get_root()->get_world_3d()->get_scenario()); + RenderingServer::get_singleton()->instance_set_layer_mask(cursor_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER); + RenderingServer::get_singleton()->instance_set_visible(cursor_instance, false); selection_instance = RenderingServer::get_singleton()->instance_create2(selection_mesh, get_tree()->get_root()->get_world_3d()->get_scenario()); RenderingServer::get_singleton()->instance_set_layer_mask(selection_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER); paste_instance = RenderingServer::get_singleton()->instance_create2(paste_mesh, get_tree()->get_root()->get_world_3d()->get_scenario()); @@ -1097,8 +1180,10 @@ void GridMapEditor::_notification(int p_what) { RenderingServer::get_singleton()->free(selection_level_instance[i]); } + RenderingServer::get_singleton()->free(cursor_instance); RenderingServer::get_singleton()->free(selection_instance); RenderingServer::get_singleton()->free(paste_instance); + cursor_instance = RID(); selection_instance = RID(); paste_instance = RID(); } break; @@ -1144,15 +1229,32 @@ void GridMapEditor::_update_cursor_instance() { } cursor_instance = RID(); - if (selected_palette >= 0) { - if (node && !node->get_mesh_library().is_null()) { + if (mode_buttons_group->get_pressed_button() == paint_mode_button) { + if (selected_palette >= 0 && node && node->get_mesh_library().is_valid()) { Ref<Mesh> mesh = node->get_mesh_library()->get_item_mesh(selected_palette); if (!mesh.is_null() && mesh->get_rid().is_valid()) { cursor_instance = RenderingServer::get_singleton()->instance_create2(mesh->get_rid(), get_tree()->get_root()->get_world_3d()->get_scenario()); - RenderingServer::get_singleton()->instance_set_transform(cursor_instance, cursor_transform); } } + } else if (mode_buttons_group->get_pressed_button() == select_mode_button) { + cursor_inner_mat->set_albedo(Color(default_color, 0.2)); + cursor_outer_mat->set_albedo(Color(default_color, 0.8)); + cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, get_tree()->get_root()->get_world_3d()->get_scenario()); + } else if (mode_buttons_group->get_pressed_button() == erase_mode_button) { + cursor_inner_mat->set_albedo(Color(erase_color, 0.2)); + cursor_outer_mat->set_albedo(Color(erase_color, 0.8)); + cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, get_tree()->get_root()->get_world_3d()->get_scenario()); + } else if (mode_buttons_group->get_pressed_button() == pick_mode_button) { + cursor_inner_mat->set_albedo(Color(pick_color, 0.2)); + cursor_outer_mat->set_albedo(Color(pick_color, 0.8)); + cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, get_tree()->get_root()->get_world_3d()->get_scenario()); } + _update_cursor_transform(); +} + +void GridMapEditor::_on_tool_mode_changed() { + _show_viewports_transform_gizmo(mode_buttons_group->get_pressed_button() == transform_mode_button); + _update_cursor_instance(); } void GridMapEditor::_item_selected_cbk(int idx) { @@ -1182,80 +1284,26 @@ void GridMapEditor::_bind_methods() { } GridMapEditor::GridMapEditor() { - ED_SHORTCUT("grid_map/previous_floor", TTR("Previous Floor"), Key::Q, true); - ED_SHORTCUT("grid_map/next_floor", TTR("Next Floor"), Key::E, true); - ED_SHORTCUT("grid_map/edit_x_axis", TTR("Edit X Axis"), Key::Z, true); - ED_SHORTCUT("grid_map/edit_y_axis", TTR("Edit Y Axis"), Key::X, true); - ED_SHORTCUT("grid_map/edit_z_axis", TTR("Edit Z Axis"), Key::C, true); - ED_SHORTCUT("grid_map/cursor_rotate_x", TTR("Cursor Rotate X"), Key::A, true); - ED_SHORTCUT("grid_map/cursor_rotate_y", TTR("Cursor Rotate Y"), Key::S, true); - ED_SHORTCUT("grid_map/cursor_rotate_z", TTR("Cursor Rotate Z"), Key::D, true); - ED_SHORTCUT("grid_map/cursor_back_rotate_x", TTR("Cursor Back Rotate X"), KeyModifierMask::SHIFT + Key::A, true); - ED_SHORTCUT("grid_map/cursor_back_rotate_y", TTR("Cursor Back Rotate Y"), KeyModifierMask::SHIFT + Key::S, true); - ED_SHORTCUT("grid_map/cursor_back_rotate_z", TTR("Cursor Back Rotate Z"), KeyModifierMask::SHIFT + Key::D, true); - ED_SHORTCUT("grid_map/cursor_clear_rotation", TTR("Cursor Clear Rotation"), Key::W, true); - ED_SHORTCUT("grid_map/paste_selects", TTR("Paste Selects")); - ED_SHORTCUT("grid_map/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::CTRL + Key::C); - ED_SHORTCUT("grid_map/cut_selection", TTR("Cut Selection"), KeyModifierMask::CTRL + Key::X); - ED_SHORTCUT("grid_map/clear_selection", TTR("Clear Selection"), Key::KEY_DELETE); - ED_SHORTCUT("grid_map/fill_selection", TTR("Fill Selection"), KeyModifierMask::CTRL + Key::F); - - int mw = EDITOR_GET("editors/grid_map/palette_min_width"); - Control *ec = memnew(Control); - ec->set_custom_minimum_size(Size2(mw, 0) * EDSCALE); - add_child(ec); - - spatial_editor_hb = memnew(HBoxContainer); - spatial_editor_hb->set_h_size_flags(SIZE_EXPAND_FILL); - spatial_editor_hb->set_alignment(BoxContainer::ALIGNMENT_END); - Node3DEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb); - - spin_box_label = memnew(Label); - spin_box_label->set_text(TTR("Floor:")); - spatial_editor_hb->add_child(spin_box_label); - - floor = memnew(SpinBox); - floor->set_min(-32767); - floor->set_max(32767); - floor->set_step(1); - floor->get_line_edit()->add_theme_constant_override("minimum_character_width", 16); - - spatial_editor_hb->add_child(floor); - floor->connect(SceneStringName(value_changed), callable_mp(this, &GridMapEditor::_floor_changed)); - floor->connect(SceneStringName(mouse_exited), callable_mp(this, &GridMapEditor::_floor_mouse_exited)); - floor->get_line_edit()->connect(SceneStringName(mouse_exited), callable_mp(this, &GridMapEditor::_floor_mouse_exited)); - - spatial_editor_hb->add_child(memnew(VSeparator)); + ED_SHORTCUT("grid_map/previous_floor", TTR("Previous Floor"), Key::KEY_1, true); + ED_SHORTCUT("grid_map/next_floor", TTR("Next Floor"), Key::KEY_3, true); + ED_SHORTCUT("grid_map/edit_x_axis", TTR("Edit X Axis"), KeyModifierMask::SHIFT + Key::Z, true); + ED_SHORTCUT("grid_map/edit_y_axis", TTR("Edit Y Axis"), KeyModifierMask::SHIFT + Key::X, true); + ED_SHORTCUT("grid_map/edit_z_axis", TTR("Edit Z Axis"), KeyModifierMask::SHIFT + Key::C, true); + ED_SHORTCUT("grid_map/keep_selected", TTR("Keep Selection")); + ED_SHORTCUT("grid_map/clear_rotation", TTR("Clear Rotation")); options = memnew(MenuButton); - spatial_editor_hb->add_child(options); - spatial_editor_hb->hide(); - - options->set_text(TTR("Grid Map")); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/previous_floor"), MENU_OPTION_PREV_LEVEL); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/next_floor"), MENU_OPTION_NEXT_LEVEL); + options->set_theme_type_variation("FlatButton"); options->get_popup()->add_separator(); options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_x_axis"), MENU_OPTION_X_AXIS); options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_y_axis"), MENU_OPTION_Y_AXIS); options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_z_axis"), MENU_OPTION_Z_AXIS); options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true); options->get_popup()->add_separator(); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_x"), MENU_OPTION_CURSOR_ROTATE_X); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_y"), MENU_OPTION_CURSOR_ROTATE_Y); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_z"), MENU_OPTION_CURSOR_ROTATE_Z); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_x"), MENU_OPTION_CURSOR_BACK_ROTATE_X); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_y"), MENU_OPTION_CURSOR_BACK_ROTATE_Y); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_clear_rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION); - options->get_popup()->add_separator(); // TRANSLATORS: This is a toggle to select after pasting the new content. - options->get_popup()->add_check_shortcut(ED_GET_SHORTCUT("grid_map/paste_selects"), MENU_OPTION_PASTE_SELECTS); - options->get_popup()->add_separator(); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/duplicate_selection"), MENU_OPTION_SELECTION_DUPLICATE); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cut_selection"), MENU_OPTION_SELECTION_CUT); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/clear_selection"), MENU_OPTION_SELECTION_CLEAR); - options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/fill_selection"), MENU_OPTION_SELECTION_FILL); - + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/clear_rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION); + options->get_popup()->add_check_shortcut(ED_GET_SHORTCUT("grid_map/keep_selected"), MENU_OPTION_PASTE_SELECTS); + options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS), true); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Settings..."), MENU_OPTION_GRIDMAP_SETTINGS); @@ -1275,40 +1323,180 @@ GridMapEditor::GridMapEditor() { options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &GridMapEditor::_menu_option)); - HBoxContainer *hb = memnew(HBoxContainer); - add_child(hb); - hb->set_h_size_flags(SIZE_EXPAND_FILL); + toolbar = memnew(HBoxContainer); + add_child(toolbar); + toolbar->set_h_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *mode_buttons = memnew(HBoxContainer); + toolbar->add_child(mode_buttons); + mode_buttons_group.instantiate(); + + transform_mode_button = memnew(Button); + transform_mode_button->set_theme_type_variation("FlatButton"); + transform_mode_button->set_toggle_mode(true); + transform_mode_button->set_button_group(mode_buttons_group); + transform_mode_button->set_shortcut(ED_SHORTCUT("grid_map/transform_tool", TTR("Transform"), Key::T, true)); + transform_mode_button->connect(SceneStringName(toggled), + callable_mp(this, &GridMapEditor::_on_tool_mode_changed).unbind(1)); + mode_buttons->add_child(transform_mode_button); + viewport_shortcut_buttons.push_back(transform_mode_button); + VSeparator *vsep = memnew(VSeparator); + mode_buttons->add_child(vsep); + + select_mode_button = memnew(Button); + select_mode_button->set_theme_type_variation("FlatButton"); + select_mode_button->set_toggle_mode(true); + select_mode_button->set_button_group(mode_buttons_group); + select_mode_button->set_shortcut(ED_SHORTCUT("grid_map/selection_tool", TTR("Selection"), Key::Q, true)); + select_mode_button->connect(SceneStringName(toggled), + callable_mp(this, &GridMapEditor::_on_tool_mode_changed).unbind(1)); + mode_buttons->add_child(select_mode_button); + viewport_shortcut_buttons.push_back(select_mode_button); + select_mode_button->set_pressed(true); + + erase_mode_button = memnew(Button); + erase_mode_button->set_theme_type_variation("FlatButton"); + erase_mode_button->set_toggle_mode(true); + erase_mode_button->set_button_group(mode_buttons_group); + erase_mode_button->set_shortcut(ED_SHORTCUT("grid_map/erase_tool", TTR("Erase"), Key::W, true)); + mode_buttons->add_child(erase_mode_button); + erase_mode_button->connect(SceneStringName(toggled), + callable_mp(this, &GridMapEditor::_on_tool_mode_changed).unbind(1)); + viewport_shortcut_buttons.push_back(erase_mode_button); + + paint_mode_button = memnew(Button); + paint_mode_button->set_theme_type_variation("FlatButton"); + paint_mode_button->set_toggle_mode(true); + paint_mode_button->set_button_group(mode_buttons_group); + paint_mode_button->set_shortcut(ED_SHORTCUT("grid_map/paint_tool", TTR("Paint"), Key::E, true)); + paint_mode_button->connect(SceneStringName(toggled), + callable_mp(this, &GridMapEditor::_on_tool_mode_changed).unbind(1)); + mode_buttons->add_child(paint_mode_button); + viewport_shortcut_buttons.push_back(paint_mode_button); + + pick_mode_button = memnew(Button); + pick_mode_button->set_theme_type_variation("FlatButton"); + pick_mode_button->set_toggle_mode(true); + pick_mode_button->set_button_group(mode_buttons_group); + pick_mode_button->set_shortcut(ED_SHORTCUT("grid_map/pick_tool", TTR("Pick"), Key::R, true)); + pick_mode_button->connect(SceneStringName(toggled), + callable_mp(this, &GridMapEditor::_on_tool_mode_changed).unbind(1)); + mode_buttons->add_child(pick_mode_button); + viewport_shortcut_buttons.push_back(pick_mode_button); + + vsep = memnew(VSeparator); + toolbar->add_child(vsep); + + HBoxContainer *action_buttons = memnew(HBoxContainer); + toolbar->add_child(action_buttons); + + fill_action_button = memnew(Button); + fill_action_button->set_theme_type_variation("FlatButton"); + fill_action_button->set_shortcut(ED_SHORTCUT("grid_map/fill_tool", TTR("Fill"), Key::Z, true)); + fill_action_button->connect(SceneStringName(pressed), + callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_FILL)); + action_buttons->add_child(fill_action_button); + + move_action_button = memnew(Button); + move_action_button->set_theme_type_variation("FlatButton"); + move_action_button->set_shortcut(ED_SHORTCUT("grid_map/move_tool", TTR("Move"), Key::X, true)); + move_action_button->connect(SceneStringName(pressed), + callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_CUT)); + action_buttons->add_child(move_action_button); + + duplicate_action_button = memnew(Button); + duplicate_action_button->set_theme_type_variation("FlatButton"); + duplicate_action_button->set_shortcut(ED_SHORTCUT("grid_map/duplicate_tool", TTR("Duplicate"), Key::C, true)); + duplicate_action_button->connect(SceneStringName(pressed), + callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_DUPLICATE)); + action_buttons->add_child(duplicate_action_button); + + delete_action_button = memnew(Button); + delete_action_button->set_theme_type_variation("FlatButton"); + delete_action_button->set_shortcut(ED_SHORTCUT("grid_map/delete_tool", TTR("Delete"), Key::V, true)); + delete_action_button->connect(SceneStringName(pressed), + callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_CLEAR)); + action_buttons->add_child(delete_action_button); + + vsep = memnew(VSeparator); + toolbar->add_child(vsep); + + HBoxContainer *rotation_buttons = memnew(HBoxContainer); + toolbar->add_child(rotation_buttons); + + rotate_x_button = memnew(Button); + rotate_x_button->set_theme_type_variation("FlatButton"); + rotate_x_button->set_shortcut(ED_SHORTCUT("grid_map/cursor_rotate_x", TTR("Cursor Rotate X"), Key::A, true)); + rotate_x_button->connect(SceneStringName(pressed), + callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_CURSOR_ROTATE_X)); + rotation_buttons->add_child(rotate_x_button); + + rotate_y_button = memnew(Button); + rotate_y_button->set_theme_type_variation("FlatButton"); + rotate_y_button->set_shortcut(ED_SHORTCUT("grid_map/cursor_rotate_y", TTR("Cursor Rotate Y"), Key::S, true)); + rotate_y_button->connect(SceneStringName(pressed), + callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_CURSOR_ROTATE_Y)); + rotation_buttons->add_child(rotate_y_button); + + rotate_z_button = memnew(Button); + rotate_z_button->set_theme_type_variation("FlatButton"); + rotate_z_button->set_shortcut(ED_SHORTCUT("grid_map/cursor_rotate_z", TTR("Cursor Rotate Z"), Key::D, true)); + rotate_z_button->connect(SceneStringName(pressed), + callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_CURSOR_ROTATE_Z)); + rotation_buttons->add_child(rotate_z_button); + + // Wide empty separation control. (like BoxContainer::add_spacer()) + Control *c = memnew(Control); + c->set_mouse_filter(MOUSE_FILTER_PASS); + c->set_h_size_flags(SIZE_EXPAND_FILL); + toolbar->add_child(c); + + floor = memnew(SpinBox); + floor->set_min(-32767); + floor->set_max(32767); + floor->set_step(1); + floor->set_tooltip_text( + TTR(vformat("Change Grid Floor:\nPrevious Plane (%s)\nNext Plane (%s)", + ED_GET_SHORTCUT("grid_map/previous_floor")->get_as_text(), + ED_GET_SHORTCUT("grid_map/next_floor")->get_as_text()))); + toolbar->add_child(floor); + floor->get_line_edit()->add_theme_constant_override("minimum_character_width", 2); + floor->get_line_edit()->set_context_menu_enabled(false); + floor->connect(SceneStringName(value_changed), callable_mp(this, &GridMapEditor::_floor_changed)); + floor->connect(SceneStringName(mouse_exited), callable_mp(this, &GridMapEditor::_floor_mouse_exited)); + floor->get_line_edit()->connect(SceneStringName(mouse_exited), callable_mp(this, &GridMapEditor::_floor_mouse_exited)); search_box = memnew(LineEdit); - search_box->set_h_size_flags(SIZE_EXPAND_FILL); + search_box->add_theme_constant_override("minimum_character_width", 10); search_box->set_placeholder(TTR("Filter Meshes")); search_box->set_clear_button_enabled(true); - hb->add_child(search_box); + toolbar->add_child(search_box); search_box->connect(SceneStringName(text_changed), callable_mp(this, &GridMapEditor::_text_changed)); search_box->connect(SceneStringName(gui_input), callable_mp(this, &GridMapEditor::_sbox_input)); + zoom_widget = memnew(EditorZoomWidget); + toolbar->add_child(zoom_widget); + zoom_widget->setup_zoom_limits(0.2, 4); + zoom_widget->set_zoom(1.0); + zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE); + zoom_widget->connect("zoom_changed", callable_mp(this, &GridMapEditor::_icon_size_changed)); + zoom_widget->set_shortcut_context(this); + mode_thumbnail = memnew(Button); mode_thumbnail->set_theme_type_variation("FlatButton"); mode_thumbnail->set_toggle_mode(true); mode_thumbnail->set_pressed(true); - hb->add_child(mode_thumbnail); + toolbar->add_child(mode_thumbnail); mode_thumbnail->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_set_display_mode).bind(DISPLAY_THUMBNAIL)); mode_list = memnew(Button); mode_list->set_theme_type_variation("FlatButton"); mode_list->set_toggle_mode(true); mode_list->set_pressed(false); - hb->add_child(mode_list); + toolbar->add_child(mode_list); mode_list->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_set_display_mode).bind(DISPLAY_LIST)); - size_slider = memnew(HSlider); - size_slider->set_h_size_flags(SIZE_EXPAND_FILL); - size_slider->set_min(0.2f); - size_slider->set_max(4.0f); - size_slider->set_step(0.1f); - size_slider->set_value(1.0f); - size_slider->connect(SceneStringName(value_changed), callable_mp(this, &GridMapEditor::_icon_size_changed)); - add_child(size_slider); + toolbar->add_child(options); mesh_library_palette = memnew(ItemList); mesh_library_palette->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); @@ -1330,6 +1518,7 @@ GridMapEditor::GridMapEditor() { edit_floor[1] = -1; edit_floor[2] = -1; + cursor_mesh = RenderingServer::get_singleton()->mesh_create(); selection_mesh = RenderingServer::get_singleton()->mesh_create(); paste_mesh = RenderingServer::get_singleton()->mesh_create(); @@ -1405,20 +1594,32 @@ GridMapEditor::GridMapEditor() { Array d; d.resize(RS::ARRAY_MAX); + default_color = Color(0.0, 0.565, 1.0); // blue 0.7, 0.7, 1.0 + erase_color = Color(1.0, 0.2, 0.2); // red + pick_color = Color(1, 0.7, 0); // orange/yellow + + cursor_inner_mat.instantiate(); + cursor_inner_mat->set_albedo(Color(default_color, 0.2)); + cursor_inner_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + cursor_inner_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + cursor_inner_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + + cursor_outer_mat.instantiate(); + cursor_outer_mat->set_albedo(Color(default_color, 0.8)); + cursor_outer_mat->set_on_top_of_alpha(); + cursor_outer_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + cursor_outer_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + cursor_outer_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + inner_mat.instantiate(); - inner_mat->set_albedo(Color(0.7, 0.7, 1.0, 0.2)); + inner_mat->set_albedo(Color(default_color, 0.2)); inner_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); inner_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); inner_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - d[RS::ARRAY_VERTEX] = triangles; - RenderingServer::get_singleton()->mesh_add_surface_from_arrays(selection_mesh, RS::PRIMITIVE_TRIANGLES, d); - RenderingServer::get_singleton()->mesh_surface_set_material(selection_mesh, 0, inner_mat->get_rid()); - outer_mat.instantiate(); - outer_mat->set_albedo(Color(0.7, 0.7, 1.0, 0.8)); + outer_mat->set_albedo(Color(default_color, 0.8)); outer_mat->set_on_top_of_alpha(); - outer_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); outer_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); outer_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); @@ -1429,6 +1630,18 @@ GridMapEditor::GridMapEditor() { selection_floor_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); selection_floor_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + d[RS::ARRAY_VERTEX] = triangles; + RenderingServer::get_singleton()->mesh_add_surface_from_arrays(cursor_mesh, RS::PRIMITIVE_TRIANGLES, d); + RenderingServer::get_singleton()->mesh_surface_set_material(cursor_mesh, 0, cursor_inner_mat->get_rid()); + + d[RS::ARRAY_VERTEX] = lines; + RenderingServer::get_singleton()->mesh_add_surface_from_arrays(cursor_mesh, RS::PRIMITIVE_LINES, d); + RenderingServer::get_singleton()->mesh_surface_set_material(cursor_mesh, 1, cursor_outer_mat->get_rid()); + + d[RS::ARRAY_VERTEX] = triangles; + RenderingServer::get_singleton()->mesh_add_surface_from_arrays(selection_mesh, RS::PRIMITIVE_TRIANGLES, d); + RenderingServer::get_singleton()->mesh_surface_set_material(selection_mesh, 0, inner_mat->get_rid()); + d[RS::ARRAY_VERTEX] = lines; RenderingServer::get_singleton()->mesh_add_surface_from_arrays(selection_mesh, RS::PRIMITIVE_LINES, d); RenderingServer::get_singleton()->mesh_surface_set_material(selection_mesh, 1, outer_mat->get_rid()); @@ -1471,9 +1684,6 @@ GridMapEditor::~GridMapEditor() { if (grid_instance[i].is_valid()) { RenderingServer::get_singleton()->free(grid_instance[i]); } - if (cursor_instance.is_valid()) { - RenderingServer::get_singleton()->free(cursor_instance); - } if (selection_level_instance[i].is_valid()) { RenderingServer::get_singleton()->free(selection_level_instance[i]); } @@ -1482,6 +1692,11 @@ GridMapEditor::~GridMapEditor() { } } + RenderingServer::get_singleton()->free(cursor_mesh); + if (cursor_instance.is_valid()) { + RenderingServer::get_singleton()->free(cursor_instance); + } + RenderingServer::get_singleton()->free(selection_mesh); if (selection_instance.is_valid()) { RenderingServer::get_singleton()->free(selection_instance); @@ -1493,24 +1708,6 @@ GridMapEditor::~GridMapEditor() { } } -void GridMapEditorPlugin::_notification(int p_what) { - switch (p_what) { - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/grid_map")) { - break; - } - switch ((int)EDITOR_GET("editors/grid_map/editor_side")) { - case 0: { // Left. - Node3DEditor::get_singleton()->move_control_to_left_panel(grid_map_editor); - } break; - case 1: { // Right. - Node3DEditor::get_singleton()->move_control_to_right_panel(grid_map_editor); - } break; - } - } break; - } -} - void GridMapEditorPlugin::edit(Object *p_object) { grid_map_editor->edit(Object::cast_to<GridMap>(p_object)); } @@ -1521,27 +1718,29 @@ bool GridMapEditorPlugin::handles(Object *p_object) const { void GridMapEditorPlugin::make_visible(bool p_visible) { if (p_visible) { - grid_map_editor->show(); - grid_map_editor->spatial_editor_hb->show(); + grid_map_editor->_on_tool_mode_changed(); + panel_button->show(); + EditorNode::get_bottom_panel()->make_item_visible(grid_map_editor); grid_map_editor->set_process(true); } else { - grid_map_editor->spatial_editor_hb->hide(); - grid_map_editor->hide(); + grid_map_editor->_show_viewports_transform_gizmo(true); + panel_button->hide(); + if (grid_map_editor->is_visible_in_tree()) { + EditorNode::get_bottom_panel()->hide_bottom_panel(); + } grid_map_editor->set_process(false); } } GridMapEditorPlugin::GridMapEditorPlugin() { grid_map_editor = memnew(GridMapEditor); - switch ((int)EDITOR_GET("editors/grid_map/editor_side")) { - case 0: { // Left. - Node3DEditor::get_singleton()->add_control_to_left_panel(grid_map_editor); - } break; - case 1: { // Right. - Node3DEditor::get_singleton()->add_control_to_right_panel(grid_map_editor); - } break; - } + grid_map_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); + grid_map_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); + grid_map_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE); grid_map_editor->hide(); + + panel_button = EditorNode::get_bottom_panel()->add_item(TTR("GridMap"), grid_map_editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_grid_map_bottom_panel", TTR("Toggle GridMap Bottom Panel"))); + panel_button->hide(); } GridMapEditorPlugin::~GridMapEditorPlugin() { diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h index 4294c93c93..2d43a5c830 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.h +++ b/modules/gridmap/editor/grid_map_editor_plugin.h @@ -44,6 +44,9 @@ class ConfirmationDialog; class MenuButton; class Node3DEditorPlugin; +class ButtonGroup; +class EditorZoomWidget; +class BaseButton; class GridMapEditor : public VBoxContainer { GDCLASS(GridMapEditor, VBoxContainer); @@ -54,6 +57,7 @@ class GridMapEditor : public VBoxContainer { enum InputAction { INPUT_NONE, + INPUT_TRANSFORM, INPUT_PAINT, INPUT_ERASE, INPUT_PICK, @@ -71,11 +75,31 @@ class GridMapEditor : public VBoxContainer { MenuButton *options = nullptr; SpinBox *floor = nullptr; double accumulated_floor_delta = 0.0; + + HBoxContainer *toolbar = nullptr; + List<BaseButton *> viewport_shortcut_buttons; + Ref<ButtonGroup> mode_buttons_group; + // mode + Button *transform_mode_button = nullptr; + Button *select_mode_button = nullptr; + Button *erase_mode_button = nullptr; + Button *paint_mode_button = nullptr; + Button *pick_mode_button = nullptr; + // action + Button *fill_action_button = nullptr; + Button *move_action_button = nullptr; + Button *duplicate_action_button = nullptr; + Button *delete_action_button = nullptr; + // rotation + Button *rotate_x_button = nullptr; + Button *rotate_y_button = nullptr; + Button *rotate_z_button = nullptr; + + EditorZoomWidget *zoom_widget = nullptr; Button *mode_thumbnail = nullptr; Button *mode_list = nullptr; LineEdit *search_box = nullptr; HSlider *size_slider = nullptr; - HBoxContainer *spatial_editor_hb = nullptr; ConfirmationDialog *settings_dialog = nullptr; VBoxContainer *settings_vbc = nullptr; SpinBox *settings_pick_distance = nullptr; @@ -102,6 +126,7 @@ class GridMapEditor : public VBoxContainer { RID grid[3]; RID grid_instance[3]; + RID cursor_mesh; RID cursor_instance; RID selection_mesh; RID selection_instance; @@ -119,7 +144,12 @@ class GridMapEditor : public VBoxContainer { List<ClipboardItem> clipboard_items; + Color default_color; + Color erase_color; + Color pick_color; Ref<StandardMaterial3D> indicator_mat; + Ref<StandardMaterial3D> cursor_inner_mat; + Ref<StandardMaterial3D> cursor_outer_mat; Ref<StandardMaterial3D> inner_mat; Ref<StandardMaterial3D> outer_mat; Ref<StandardMaterial3D> selection_floor_mat; @@ -196,6 +226,7 @@ class GridMapEditor : public VBoxContainer { void _item_selected_cbk(int idx); void _update_cursor_transform(); void _update_cursor_instance(); + void _on_tool_mode_changed(); void _update_theme(); void _text_changed(const String &p_text); @@ -208,6 +239,7 @@ class GridMapEditor : public VBoxContainer { void _set_clipboard_data(); void _update_paste_indicator(); void _do_paste(); + void _show_viewports_transform_gizmo(bool p_value); void _update_selection_transform(); void _validate_selection(); void _set_selection(bool p_active, const Vector3 &p_begin = Vector3(), const Vector3 &p_end = Vector3()); @@ -238,9 +270,7 @@ class GridMapEditorPlugin : public EditorPlugin { GDCLASS(GridMapEditorPlugin, EditorPlugin); GridMapEditor *grid_map_editor = nullptr; - -protected: - void _notification(int p_what); + Button *panel_button = nullptr; public: virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); } diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 71171be3f1..0588ba034a 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -801,8 +801,8 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) { if (!g.navigation_debug_edge_connections_instance.is_valid()) { g.navigation_debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create(); } - if (!g.navigation_debug_edge_connections_mesh.is_valid()) { - g.navigation_debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (g.navigation_debug_edge_connections_mesh.is_null()) { + g.navigation_debug_edge_connections_mesh.instantiate(); } _update_octant_navigation_debug_edge_connections_mesh(p_key); @@ -1386,8 +1386,8 @@ void GridMap::_update_octant_navigation_debug_edge_connections_mesh(const Octant g.navigation_debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create(); } - if (!g.navigation_debug_edge_connections_mesh.is_valid()) { - g.navigation_debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (g.navigation_debug_edge_connections_mesh.is_null()) { + g.navigation_debug_edge_connections_mesh.instantiate(); } g.navigation_debug_edge_connections_mesh->clear_surfaces(); diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp index e4b54ef050..f1f0a771ad 100644 --- a/modules/minimp3/resource_importer_mp3.cpp +++ b/modules/minimp3/resource_importer_mp3.cpp @@ -115,7 +115,7 @@ Ref<AudioStreamMP3> ResourceImporterMP3::import_mp3(const String &p_path) { return mp3_stream; } -Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterMP3::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { bool loop = p_options["loop"]; float loop_offset = p_options["loop_offset"]; double bpm = p_options["bpm"]; diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h index 037756328f..35cc761eb4 100644 --- a/modules/minimp3/resource_importer_mp3.h +++ b/modules/minimp3/resource_importer_mp3.h @@ -57,7 +57,7 @@ public: #endif static Ref<AudioStreamMP3> import_mp3(const String &p_path); - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 6ae9ce56c6..380b401683 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -2826,7 +2826,7 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const GDMonoCache::managed_callbacks.ScriptManagerBridge_GetOrCreateScriptBridgeForPath(&p_path, &scr); ERR_FAIL_COND_V_MSG(scr.is_null(), Ref<Resource>(), "Could not create C# script '" + real_path + "'."); } else { - scr = Ref<CSharpScript>(memnew(CSharpScript)); + scr.instantiate(); } #if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED) diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 788b46ab9a..74e04b46a1 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -177,7 +177,7 @@ namespace GodotTools private static readonly string[] VsCodeNames = { - "code", "code-oss", "vscode", "vscode-oss", "visual-studio-code", "visual-studio-code-oss" + "code", "code-oss", "vscode", "vscode-oss", "visual-studio-code", "visual-studio-code-oss", "codium" }; [UsedImplicitly] @@ -330,7 +330,7 @@ namespace GodotTools args.Add("-b"); args.Add(vscodeBundleId); - // The reusing of existing windows made by the 'open' command might not choose a wubdiw that is + // The reusing of existing windows made by the 'open' command might not choose a window that is // editing our folder. It's better to ask for a new window and let VSCode do the window management. args.Add("-n"); @@ -339,6 +339,28 @@ namespace GodotTools args.Add("--args"); } + + // Try VSCodium as a fallback if Visual Studio Code can't be found. + if (!macOSAppBundleInstalled) + { + const string VscodiumBundleId = "com.vscodium.codium"; + macOSAppBundleInstalled = Internal.IsMacOSAppBundleInstalled(VscodiumBundleId); + + if (macOSAppBundleInstalled) + { + args.Add("-b"); + args.Add(VscodiumBundleId); + + // The reusing of existing windows made by the 'open' command might not choose a window that is + // editing our folder. It's better to ask for a new window and let VSCode do the window management. + args.Add("-n"); + + // The open process must wait until the application finishes (which is instant in VSCode's case) + args.Add("--wait-apps"); + + args.Add("--args"); + } + } } args.Add(Path.GetDirectoryName(GodotSharpDirs.ProjectSlnPath)!); @@ -361,7 +383,7 @@ namespace GodotTools { if (!macOSAppBundleInstalled && string.IsNullOrEmpty(_vsCodePath)) { - GD.PushError("Cannot find code editor: VSCode"); + GD.PushError("Cannot find code editor: Visual Studio Code or VSCodium"); return Error.FileNotFound; } @@ -371,7 +393,7 @@ namespace GodotTools { if (string.IsNullOrEmpty(_vsCodePath)) { - GD.PushError("Cannot find code editor: VSCode"); + GD.PushError("Cannot find code editor: Visual Studio Code or VSCodium"); return Error.FileNotFound; } @@ -384,7 +406,7 @@ namespace GodotTools } catch (Exception e) { - GD.PushError($"Error when trying to run code editor: VSCode. Exception message: '{e.Message}'"); + GD.PushError($"Error when trying to run code editor: Visual Studio Code or VSCodium. Exception message: '{e.Message}'"); } break; @@ -550,7 +572,7 @@ namespace GodotTools { settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudio}" + $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" + - $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" + + $",Visual Studio Code and VSCodium:{(int)ExternalEditorId.VsCode}" + $",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" + $",Custom:{(int)ExternalEditorId.CustomEditor}"; } @@ -558,14 +580,14 @@ namespace GodotTools { settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudioForMac}" + $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" + - $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" + + $",Visual Studio Code and VSCodium:{(int)ExternalEditorId.VsCode}" + $",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" + $",Custom:{(int)ExternalEditorId.CustomEditor}"; } else if (OS.IsUnixLike) { settingsHintStr += $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" + - $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" + + $",Visual Studio Code and VSCodium:{(int)ExternalEditorId.VsCode}" + $",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" + $",Custom:{(int)ExternalEditorId.CustomEditor}"; } diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp index e245101eeb..dde14034e6 100644 --- a/modules/multiplayer/scene_multiplayer.cpp +++ b/modules/multiplayer/scene_multiplayer.cpp @@ -684,9 +684,9 @@ void SceneMultiplayer::_bind_methods() { SceneMultiplayer::SceneMultiplayer() { relay_buffer.instantiate(); - cache = Ref<SceneCacheInterface>(memnew(SceneCacheInterface(this))); - replicator = Ref<SceneReplicationInterface>(memnew(SceneReplicationInterface(this, cache.ptr()))); - rpc = Ref<SceneRPCInterface>(memnew(SceneRPCInterface(this, cache.ptr(), replicator.ptr()))); + cache.instantiate(this); + replicator.instantiate(this, cache.ptr()); + rpc.instantiate(this, cache.ptr(), replicator.ptr()); set_multiplayer_peer(Ref<OfflineMultiplayerPeer>(memnew(OfflineMultiplayerPeer))); } diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp index 0938d7ef99..b5f3889268 100644 --- a/modules/multiplayer/scene_rpc_interface.cpp +++ b/modules/multiplayer/scene_rpc_interface.cpp @@ -73,16 +73,6 @@ int get_packet_len(uint32_t p_node_target, int p_packet_len) { } } -bool SceneRPCInterface::_sort_rpc_names(const Variant &p_l, const Variant &p_r) { - if (likely(p_l.is_string() && p_r.is_string())) { - return p_l.operator String() < p_r.operator String(); - } - bool valid = false; - Variant res; - Variant::evaluate(Variant::OP_LESS, p_l, p_r, res, valid); - return valid ? res.operator bool() : false; -} - void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_node, RPCConfigCache &r_cache) { if (p_config.get_type() == Variant::NIL) { return; @@ -90,7 +80,7 @@ void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_no ERR_FAIL_COND(p_config.get_type() != Variant::DICTIONARY); const Dictionary config = p_config; Array names = config.keys(); - names.sort_custom(callable_mp_static(&SceneRPCInterface::_sort_rpc_names)); // Ensure ID order + names.sort_custom(callable_mp_static(&StringLikeVariantOrder::compare)); // Ensure ID order for (int i = 0; i < names.size(); i++) { ERR_CONTINUE(!names[i].is_string()); String name = names[i].operator String(); diff --git a/modules/multiplayer/scene_rpc_interface.h b/modules/multiplayer/scene_rpc_interface.h index 852cef7830..5c9b66d5f5 100644 --- a/modules/multiplayer/scene_rpc_interface.h +++ b/modules/multiplayer/scene_rpc_interface.h @@ -91,8 +91,6 @@ private: #endif protected: - static bool _sort_rpc_names(const Variant &p_l, const Variant &p_r); - void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount); diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp index 16cef0dd34..2339936ec4 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.cpp +++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp @@ -760,16 +760,14 @@ void NavMeshGenerator2D::generator_parse_source_geometry_data(Ref<NavigationPoly static void generator_recursive_process_polytree_items(List<TPPLPoly> &p_tppl_in_polygon, const Clipper2Lib::PolyPathD *p_polypath_item) { using namespace Clipper2Lib; - Vector<Vector2> polygon_vertices; + TPPLPoly tp; + int size = p_polypath_item->Polygon().size(); + tp.Init(size); + int j = 0; for (const PointD &polypath_point : p_polypath_item->Polygon()) { - polygon_vertices.push_back(Vector2(static_cast<real_t>(polypath_point.x), static_cast<real_t>(polypath_point.y))); - } - - TPPLPoly tp; - tp.Init(polygon_vertices.size()); - for (int j = 0; j < polygon_vertices.size(); j++) { - tp[j] = polygon_vertices[j]; + tp[j] = Vector2(static_cast<real_t>(polypath_point.x), static_cast<real_t>(polypath_point.y)); + ++j; } if (p_polypath_item->IsHole()) { @@ -842,87 +840,79 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation return; } - if (p_navigation_mesh->get_outline_count() == 0 && !p_source_geometry_data->has_data()) { - return; - } - - int outline_count = p_navigation_mesh->get_outline_count(); - - Vector<Vector<Vector2>> traversable_outlines; - Vector<Vector<Vector2>> obstruction_outlines; - Vector<NavigationMeshSourceGeometryData2D::ProjectedObstruction> projected_obstructions; - - p_source_geometry_data->get_data( - traversable_outlines, - obstruction_outlines, - projected_obstructions); - - if (outline_count == 0 && traversable_outlines.size() == 0) { - return; - } - using namespace Clipper2Lib; - PathsD traversable_polygon_paths; PathsD obstruction_polygon_paths; + int obstruction_polygon_path_size = 0; + { + RWLockRead read_lock(p_source_geometry_data->geometry_rwlock); - traversable_polygon_paths.reserve(outline_count + traversable_outlines.size()); - obstruction_polygon_paths.reserve(obstruction_outlines.size()); + const Vector<Vector<Vector2>> &traversable_outlines = p_source_geometry_data->traversable_outlines; + int outline_count = p_navigation_mesh->get_outline_count(); - for (int i = 0; i < outline_count; i++) { - const Vector<Vector2> &traversable_outline = p_navigation_mesh->get_outline(i); - PathD subject_path; - subject_path.reserve(traversable_outline.size()); - for (const Vector2 &traversable_point : traversable_outline) { - const PointD &point = PointD(traversable_point.x, traversable_point.y); - subject_path.push_back(point); + if (outline_count == 0 && (!p_source_geometry_data->has_data() || (traversable_outlines.is_empty()))) { + return; } - traversable_polygon_paths.push_back(subject_path); - } - for (const Vector<Vector2> &traversable_outline : traversable_outlines) { - PathD subject_path; - subject_path.reserve(traversable_outline.size()); - for (const Vector2 &traversable_point : traversable_outline) { - const PointD &point = PointD(traversable_point.x, traversable_point.y); - subject_path.push_back(point); - } - traversable_polygon_paths.push_back(subject_path); - } + const Vector<Vector<Vector2>> &obstruction_outlines = p_source_geometry_data->obstruction_outlines; + const Vector<NavigationMeshSourceGeometryData2D::ProjectedObstruction> &projected_obstructions = p_source_geometry_data->_projected_obstructions; + + traversable_polygon_paths.reserve(outline_count + traversable_outlines.size()); + obstruction_polygon_paths.reserve(obstruction_outlines.size()); - for (const Vector<Vector2> &obstruction_outline : obstruction_outlines) { - PathD clip_path; - clip_path.reserve(obstruction_outline.size()); - for (const Vector2 &obstruction_point : obstruction_outline) { - const PointD &point = PointD(obstruction_point.x, obstruction_point.y); - clip_path.push_back(point); + for (int i = 0; i < outline_count; i++) { + const Vector<Vector2> &traversable_outline = p_navigation_mesh->get_outline(i); + PathD subject_path; + subject_path.reserve(traversable_outline.size()); + for (const Vector2 &traversable_point : traversable_outline) { + subject_path.emplace_back(traversable_point.x, traversable_point.y); + } + traversable_polygon_paths.push_back(std::move(subject_path)); } - obstruction_polygon_paths.push_back(clip_path); - } - if (!projected_obstructions.is_empty()) { - for (const NavigationMeshSourceGeometryData2D::ProjectedObstruction &projected_obstruction : projected_obstructions) { - if (projected_obstruction.carve) { - continue; + for (const Vector<Vector2> &traversable_outline : traversable_outlines) { + PathD subject_path; + subject_path.reserve(traversable_outline.size()); + for (const Vector2 &traversable_point : traversable_outline) { + subject_path.emplace_back(traversable_point.x, traversable_point.y); } - if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 2 != 0) { - continue; + traversable_polygon_paths.push_back(std::move(subject_path)); + } + + if (!projected_obstructions.is_empty()) { + for (const NavigationMeshSourceGeometryData2D::ProjectedObstruction &projected_obstruction : projected_obstructions) { + if (projected_obstruction.carve) { + continue; + } + if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 2 != 0) { + continue; + } + + PathD clip_path; + clip_path.reserve(projected_obstruction.vertices.size() / 2); + for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) { + clip_path.emplace_back(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]); + } + if (!IsPositive(clip_path)) { + std::reverse(clip_path.begin(), clip_path.end()); + } + obstruction_polygon_paths.push_back(std::move(clip_path)); } + } + obstruction_polygon_path_size = obstruction_polygon_paths.size(); + for (const Vector<Vector2> &obstruction_outline : obstruction_outlines) { PathD clip_path; - clip_path.reserve(projected_obstruction.vertices.size() / 2); - for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) { - const PointD &point = PointD(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]); - clip_path.push_back(point); - } - if (!IsPositive(clip_path)) { - std::reverse(clip_path.begin(), clip_path.end()); + clip_path.reserve(obstruction_outline.size()); + for (const Vector2 &obstruction_point : obstruction_outline) { + clip_path.emplace_back(obstruction_point.x, obstruction_point.y); } - obstruction_polygon_paths.push_back(clip_path); + obstruction_polygon_paths.push_back(std::move(clip_path)); } } Rect2 baking_rect = p_navigation_mesh->get_baking_rect(); + PathsD area_obstruction_polygon_paths; if (baking_rect.has_area()) { Vector2 baking_rect_offset = p_navigation_mesh->get_baking_rect_offset(); @@ -934,48 +924,27 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation RectD clipper_rect = RectD(rect_begin_x, rect_begin_y, rect_end_x, rect_end_y); traversable_polygon_paths = RectClip(clipper_rect, traversable_polygon_paths); - obstruction_polygon_paths = RectClip(clipper_rect, obstruction_polygon_paths); + area_obstruction_polygon_paths = RectClip(clipper_rect, obstruction_polygon_paths); + } else { + area_obstruction_polygon_paths = obstruction_polygon_paths; } - PathsD path_solution; - // first merge all traversable polygons according to user specified fill rule PathsD dummy_clip_path; traversable_polygon_paths = Union(traversable_polygon_paths, dummy_clip_path, FillRule::NonZero); // merge all obstruction polygons, don't allow holes for what is considered "solid" 2D geometry - obstruction_polygon_paths = Union(obstruction_polygon_paths, dummy_clip_path, FillRule::NonZero); + area_obstruction_polygon_paths = Union(area_obstruction_polygon_paths, dummy_clip_path, FillRule::NonZero); - path_solution = Difference(traversable_polygon_paths, obstruction_polygon_paths, FillRule::NonZero); + PathsD path_solution = Difference(traversable_polygon_paths, area_obstruction_polygon_paths, FillRule::NonZero); real_t agent_radius_offset = p_navigation_mesh->get_agent_radius(); if (agent_radius_offset > 0.0) { path_solution = InflatePaths(path_solution, -agent_radius_offset, JoinType::Miter, EndType::Polygon); } - if (!projected_obstructions.is_empty()) { - obstruction_polygon_paths.resize(0); - for (const NavigationMeshSourceGeometryData2D::ProjectedObstruction &projected_obstruction : projected_obstructions) { - if (!projected_obstruction.carve) { - continue; - } - if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 2 != 0) { - continue; - } - - PathD clip_path; - clip_path.reserve(projected_obstruction.vertices.size() / 2); - for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) { - const PointD &point = PointD(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]); - clip_path.push_back(point); - } - if (!IsPositive(clip_path)) { - std::reverse(clip_path.begin(), clip_path.end()); - } - obstruction_polygon_paths.push_back(clip_path); - } - if (obstruction_polygon_paths.size() > 0) { - path_solution = Difference(path_solution, obstruction_polygon_paths, FillRule::NonZero); - } + if (obstruction_polygon_path_size > 0) { + obstruction_polygon_paths.resize(obstruction_polygon_path_size); + path_solution = Difference(path_solution, obstruction_polygon_paths, FillRule::NonZero); } //path_solution = RamerDouglasPeucker(path_solution, 0.025); // @@ -994,33 +963,11 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation path_solution = RectClip(clipper_rect, path_solution); } - Vector<Vector<Vector2>> new_baked_outlines; - - for (const PathD &scaled_path : path_solution) { - Vector<Vector2> polypath; - for (const PointD &scaled_point : scaled_path) { - polypath.push_back(Vector2(static_cast<real_t>(scaled_point.x), static_cast<real_t>(scaled_point.y))); - } - new_baked_outlines.push_back(polypath); - } - - if (new_baked_outlines.size() == 0) { + if (path_solution.size() == 0) { p_navigation_mesh->clear(); return; } - PathsD polygon_paths; - polygon_paths.reserve(new_baked_outlines.size()); - - for (const Vector<Vector2> &baked_outline : new_baked_outlines) { - PathD polygon_path; - for (const Vector2 &baked_outline_point : baked_outline) { - const PointD &point = PointD(baked_outline_point.x, baked_outline_point.y); - polygon_path.push_back(point); - } - polygon_paths.push_back(polygon_path); - } - ClipType clipper_cliptype = ClipType::Union; List<TPPLPoly> tppl_in_polygon, tppl_out_polygon; @@ -1028,7 +975,7 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation PolyTreeD polytree; ClipperD clipper_D; - clipper_D.AddSubject(polygon_paths); + clipper_D.AddSubject(path_solution); clipper_D.Execute(clipper_cliptype, FillRule::NonZero, polytree); for (size_t i = 0; i < polytree.Count(); i++) { diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp index ce1551e584..3d0697a7cf 100644 --- a/modules/navigation/3d/nav_mesh_generator_3d.cpp +++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp @@ -595,11 +595,17 @@ void NavMeshGenerator3D::generator_parse_navigationobstacle_node(const Ref<Navig return; } - const Transform3D node_xform = p_source_geometry_data->root_node_transform * Transform3D(Basis(), obstacle->get_global_position()); - + const float elevation = obstacle->get_global_position().y + p_source_geometry_data->root_node_transform.origin.y; + // Prevent non-positive scaling. + const Vector3 safe_scale = obstacle->get_global_basis().get_scale().abs().maxf(0.001); const float obstacle_radius = obstacle->get_radius(); if (obstacle_radius > 0.0) { + // Radius defined obstacle should be uniformly scaled from obstacle basis max scale axis. + const float scaling_max_value = safe_scale[safe_scale.max_axis_index()]; + const Vector3 uniform_max_scale = Vector3(scaling_max_value, scaling_max_value, scaling_max_value); + const Transform3D obstacle_circle_transform = p_source_geometry_data->root_node_transform * Transform3D(Basis().scaled(uniform_max_scale), obstacle->get_global_position()); + Vector<Vector3> obstruction_circle_vertices; // The point of this is that the moving obstacle can make a simple hole in the navigation mesh and affect the pathfinding. @@ -613,12 +619,15 @@ void NavMeshGenerator3D::generator_parse_navigationobstacle_node(const Ref<Navig for (int i = 0; i < circle_points; i++) { const float angle = i * circle_point_step; - circle_vertices_ptrw[i] = node_xform.xform(Vector3(Math::cos(angle) * obstacle_radius, 0.0, Math::sin(angle) * obstacle_radius)); + circle_vertices_ptrw[i] = obstacle_circle_transform.xform(Vector3(Math::cos(angle) * obstacle_radius, 0.0, Math::sin(angle) * obstacle_radius)); } - p_source_geometry_data->add_projected_obstruction(obstruction_circle_vertices, obstacle->get_global_position().y + p_source_geometry_data->root_node_transform.origin.y - obstacle_radius, obstacle_radius, obstacle->get_carve_navigation_mesh()); + p_source_geometry_data->add_projected_obstruction(obstruction_circle_vertices, elevation - obstacle_radius, scaling_max_value * obstacle_radius, obstacle->get_carve_navigation_mesh()); } + // Obstacles are projected to the xz-plane, so only rotation around the y-axis can be taken into account. + const Transform3D node_xform = p_source_geometry_data->root_node_transform * Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle->get_global_rotation().y), obstacle->get_global_position()); + const Vector<Vector3> &obstacle_vertices = obstacle->get_vertices(); if (obstacle_vertices.is_empty()) { @@ -635,7 +644,7 @@ void NavMeshGenerator3D::generator_parse_navigationobstacle_node(const Ref<Navig obstruction_shape_vertices_ptrw[i] = node_xform.xform(obstacle_vertices_ptr[i]); obstruction_shape_vertices_ptrw[i].y = 0.0; } - p_source_geometry_data->add_projected_obstruction(obstruction_shape_vertices, obstacle->get_global_position().y + p_source_geometry_data->root_node_transform.origin.y, obstacle->get_height(), obstacle->get_carve_navigation_mesh()); + p_source_geometry_data->add_projected_obstruction(obstruction_shape_vertices, elevation, safe_scale.y * obstacle->get_height(), obstacle->get_carve_navigation_mesh()); } void NavMeshGenerator3D::generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node) { diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp index 2d29b8a82c..1e3490d1ed 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp +++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp @@ -281,7 +281,7 @@ void OpenXRViewportCompositionLayerProvider::create_android_surface() { composition_layer_extension->create_android_surface_swapchain(&info, &android_surface.swapchain, &surface); if (surface) { - android_surface.surface = Ref<JavaObject>(memnew(JavaObject(JavaClassWrapper::get_singleton()->wrap("android.view.Surface"), surface))); + android_surface.surface.instantiate(JavaClassWrapper::get_singleton()->wrap("android.view.Surface"), surface); } } #endif diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 8b2c58acd5..372885b0b4 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -629,7 +629,7 @@ void VideoStreamPlaybackTheora::_streaming_thread(void *ud) { #endif VideoStreamPlaybackTheora::VideoStreamPlaybackTheora() { - texture = Ref<ImageTexture>(memnew(ImageTexture)); + texture.instantiate(); #ifdef THEORA_USE_THREAD_STREAMING int rb_power = nearest_shift(RB_SIZE_KB * 1024); diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp index 729a6f5561..a7423e2d7b 100644 --- a/modules/vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp @@ -95,7 +95,7 @@ void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) { } #endif -Error ResourceImporterOggVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterOggVorbis::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { bool loop = p_options["loop"]; double loop_offset = p_options["loop_offset"]; double bpm = p_options["bpm"]; diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h index f378b80694..a4e4441d82 100644 --- a/modules/vorbis/resource_importer_ogg_vorbis.h +++ b/modules/vorbis/resource_importer_ogg_vorbis.h @@ -63,7 +63,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override { return true; } diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index aea09583b7..ad7ce37819 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -3165,9 +3165,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP user_data.libs_directory = gradle_build_directory.path_join("libs"); user_data.debug = p_debug; if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) { - err = export_project_files(p_preset, p_debug, ignore_apk_file, &user_data, copy_gradle_so); + err = export_project_files(p_preset, p_debug, ignore_apk_file, nullptr, &user_data, copy_gradle_so); } else { - err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so); + err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, nullptr, &user_data, copy_gradle_so); } if (err != OK) { add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not export project files to gradle project.")); @@ -3558,7 +3558,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP APKExportData ed; ed.ep = &ep; ed.apk = unaligned_apk; - err = export_project_files(p_preset, p_debug, ignore_apk_file, &ed, save_apk_so); + err = export_project_files(p_preset, p_debug, ignore_apk_file, nullptr, &ed, save_apk_so); } else { if (apk_expansion) { err = save_apk_expansion_file(p_preset, p_debug, p_path); @@ -3570,7 +3570,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP APKExportData ed; ed.ep = &ep; ed.apk = unaligned_apk; - err = export_project_files(p_preset, p_debug, save_apk_file, &ed, save_apk_so); + err = export_project_files(p_preset, p_debug, save_apk_file, nullptr, &ed, save_apk_so); } } diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index d6cd2e0f3c..3c5a930bab 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -1082,7 +1082,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor } if (splash.is_null()) { - splash = Ref<Image>(memnew(Image(boot_splash_png))); + splash.instantiate(boot_splash_png); } // Using same image for both @2x and @3x diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index ddb0635f6b..20cbbc091a 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -75,7 +75,8 @@ void TileMap::_set_tile_map_data_using_compatibility_format(int p_layer, TileMap for (int i = 0; i < c; i += offset) { const uint8_t *ptr = (const uint8_t *)&r[i]; uint8_t local[12]; - for (int j = 0; j < ((p_format >= TileMapDataFormat::TILE_MAP_DATA_FORMAT_2) ? 12 : 8); j++) { + const int buffer_size = (format == TILE_MAP_DATA_FORMAT_2) ? 12 : 8; + for (int j = 0; j < buffer_size; j++) { local[j] = ptr[j]; } diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index 30bd59c1a2..c4a2f35d31 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -1460,6 +1460,24 @@ void TileMapLayer::_clear_runtime_update_tile_data_for_cell(CellData &r_cell_dat } } +void TileMapLayer::_update_cells_callback(bool p_force_cleanup) { + if (!GDVIRTUAL_IS_OVERRIDDEN(_update_cells)) { + return; + } + + // Check if we should cleanup everything. + bool forced_cleanup = p_force_cleanup || !enabled || tile_set.is_null() || !is_visible_in_tree(); + + // List all the dirty cell's positions to notify script of cell updates. + TypedArray<Vector2i> dirty_cell_positions; + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + dirty_cell_positions.push_back(cell_data.coords); + } + + GDVIRTUAL_CALL(_update_cells, dirty_cell_positions, forced_cleanup); +} + TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) const { if (tile_set.is_null()) { return TileSet::TerrainsPattern(); @@ -1673,6 +1691,10 @@ void TileMapLayer::_internal_update(bool p_force_cleanup) { // This may add cells to the dirty list if a runtime modification has been notified. _build_runtime_update_tile_data(p_force_cleanup); + // Callback for implementing custom subsystems. + // This may add to the dirty list if some cells are changed inside _update_cells. + _update_cells_callback(p_force_cleanup); + // Update all subsystems. _rendering_update(p_force_cleanup); _physics_update(p_force_cleanup); @@ -1861,6 +1883,7 @@ void TileMapLayer::_bind_methods() { GDVIRTUAL_BIND(_use_tile_data_runtime_update, "coords"); GDVIRTUAL_BIND(_tile_data_runtime_update, "coords", "tile_data"); + GDVIRTUAL_BIND(_update_cells, "coords", "forced_cleanup"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "tile_map_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_tile_map_data_from_array", "get_tile_map_data_as_array"); diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h index 6cb481849c..912fa2be15 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -321,6 +321,7 @@ private: bool _runtime_update_needs_all_cells_cleaned_up = false; void _clear_runtime_update_tile_data(); void _clear_runtime_update_tile_data_for_cell(CellData &r_cell_data); + void _update_cells_callback(bool p_force_cleanup); // Per-system methods. #ifdef DEBUG_ENABLED @@ -462,6 +463,7 @@ public: void notify_runtime_tile_data_update(); GDVIRTUAL1R(bool, _use_tile_data_runtime_update, Vector2i); GDVIRTUAL2(_tile_data_runtime_update, Vector2i, TileData *); + GDVIRTUAL2(_update_cells, TypedArray<Vector2i>, bool); // --- Shortcuts to methods defined in TileSet --- Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern); diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index b968f39ccb..220608a250 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -430,6 +430,6 @@ void TouchScreenButton::_bind_methods() { } TouchScreenButton::TouchScreenButton() { - unit_rect = Ref<RectangleShape2D>(memnew(RectangleShape2D)); + unit_rect.instantiate(); unit_rect->set_size(Vector2(1, 1)); } diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 6b3510a72a..01f15137be 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -318,7 +318,7 @@ Ref<TriangleMesh> Label3D::generate_triangle_mesh() const { facesw[j] = vtx; } - triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh.instantiate(); triangle_mesh->create(faces); return triangle_mesh; diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 9242c2a2ea..faf138896a 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -1081,8 +1081,8 @@ void NavigationAgent3D::_update_debug_path() { debug_path_instance = RenderingServer::get_singleton()->instance_create(); } - if (!debug_path_mesh.is_valid()) { - debug_path_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_path_mesh.is_null()) { + debug_path_mesh.instantiate(); } debug_path_mesh->clear_surfaces(); diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index 0cce21b9d0..9e29384fc9 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -56,8 +56,8 @@ void NavigationLink3D::_update_debug_mesh() { debug_instance = RenderingServer::get_singleton()->instance_create(); } - if (!debug_mesh.is_valid()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_mesh.is_null()) { + debug_mesh.instantiate(); } RID nav_map = get_world_3d()->get_navigation_map(); diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index 2eb04a0054..0735d5dde5 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -176,13 +176,19 @@ void NavigationObstacle3D::_notification(int p_what) { } #ifdef DEBUG_ENABLED if (fake_agent_radius_debug_instance.is_valid() && radius > 0.0) { - Transform3D debug_transform; - debug_transform.origin = get_global_position(); + // Prevent non-positive scaling. + const Vector3 safe_scale = get_global_basis().get_scale().abs().maxf(0.001); + // Agent radius is a scalar value and does not support non-uniform scaling, choose the largest axis. + const float scaling_max_value = safe_scale[safe_scale.max_axis_index()]; + const Vector3 uniform_max_scale = Vector3(scaling_max_value, scaling_max_value, scaling_max_value); + const Transform3D debug_transform = Transform3D(Basis().scaled(uniform_max_scale), get_global_position()); RS::get_singleton()->instance_set_transform(fake_agent_radius_debug_instance, debug_transform); } if (static_obstacle_debug_instance.is_valid() && get_vertices().size() > 0) { - Transform3D debug_transform; - debug_transform.origin = get_global_position(); + // Prevent non-positive scaling. + const Vector3 safe_scale = get_global_basis().get_scale().abs().maxf(0.001); + // Obstacles are projected to the xz-plane, so only rotation around the y-axis can be taken into account. + const Transform3D debug_transform = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), get_global_rotation().y), get_global_position()); RS::get_singleton()->instance_set_transform(static_obstacle_debug_instance, debug_transform); } #endif // DEBUG_ENABLED @@ -354,6 +360,25 @@ bool NavigationObstacle3D::get_carve_navigation_mesh() const { return carve_navigation_mesh; } +PackedStringArray NavigationObstacle3D::get_configuration_warnings() const { + PackedStringArray warnings = Node3D::get_configuration_warnings(); + + if (get_global_rotation().x != 0.0 || get_global_rotation().z != 0.0) { + warnings.push_back(RTR("NavigationObstacle3D only takes global rotation around the y-axis into account. Rotations around the x-axis or z-axis might lead to unexpected results.")); + } + + const Vector3 global_scale = get_global_basis().get_scale(); + if (global_scale.x < 0.001 || global_scale.y < 0.001 || global_scale.z < 0.001) { + warnings.push_back(RTR("NavigationObstacle3D does not support negative or zero scaling.")); + } + + if (radius > 0.0 && !get_global_basis().is_conformal()) { + warnings.push_back(RTR("The agent radius can only be scaled uniformly. The largest scale value along the three axes will be used.")); + } + + return warnings; +} + void NavigationObstacle3D::_update_map(RID p_map) { NavigationServer3D::get_singleton()->obstacle_set_map(obstacle, p_map); map_current = p_map; @@ -389,8 +414,8 @@ void NavigationObstacle3D::_update_fake_agent_radius_debug() { if (!fake_agent_radius_debug_instance.is_valid()) { fake_agent_radius_debug_instance = RenderingServer::get_singleton()->instance_create(); } - if (!fake_agent_radius_debug_mesh.is_valid()) { - fake_agent_radius_debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (fake_agent_radius_debug_mesh.is_null()) { + fake_agent_radius_debug_mesh.instantiate(); } fake_agent_radius_debug_mesh->clear_surfaces(); @@ -487,8 +512,8 @@ void NavigationObstacle3D::_update_static_obstacle_debug() { if (!static_obstacle_debug_instance.is_valid()) { static_obstacle_debug_instance = RenderingServer::get_singleton()->instance_create(); } - if (!static_obstacle_debug_mesh.is_valid()) { - static_obstacle_debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (static_obstacle_debug_mesh.is_null()) { + static_obstacle_debug_mesh.instantiate(); } static_obstacle_debug_mesh->clear_surfaces(); diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index 99288fc59e..111abad842 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -117,6 +117,8 @@ public: void set_carve_navigation_mesh(bool p_enabled); bool get_carve_navigation_mesh() const; + PackedStringArray get_configuration_warnings() const override; + private: void _update_map(RID p_map); void _update_position(const Vector3 p_position); diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index c0c254e7ed..2d67e4334e 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -492,8 +492,8 @@ void NavigationRegion3D::_update_debug_mesh() { debug_instance = RenderingServer::get_singleton()->instance_create(); } - if (!debug_mesh.is_valid()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_mesh.is_null()) { + debug_mesh.instantiate(); } debug_mesh->clear_surfaces(); @@ -669,8 +669,8 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create(); } - if (!debug_edge_connections_mesh.is_valid()) { - debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_edge_connections_mesh.is_null()) { + debug_edge_connections_mesh.instantiate(); } debug_edge_connections_mesh->clear_surfaces(); diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 64259a24b0..00c83101b9 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -88,8 +88,8 @@ void Path3D::_update_debug_mesh() { return; } - if (!debug_mesh.is_valid()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_mesh.is_null()) { + debug_mesh.instantiate(); } if (!(curve.is_valid())) { diff --git a/scene/3d/physics/ray_cast_3d.cpp b/scene/3d/physics/ray_cast_3d.cpp index a9272388c1..b9159f072b 100644 --- a/scene/3d/physics/ray_cast_3d.cpp +++ b/scene/3d/physics/ray_cast_3d.cpp @@ -464,7 +464,7 @@ void RayCast3D::_create_debug_shape() { } if (debug_mesh.is_null()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + debug_mesh.instantiate(); } } diff --git a/scene/3d/physics/shape_cast_3d.cpp b/scene/3d/physics/shape_cast_3d.cpp index 8ad651fdf5..19c74bc925 100644 --- a/scene/3d/physics/shape_cast_3d.cpp +++ b/scene/3d/physics/shape_cast_3d.cpp @@ -546,7 +546,7 @@ void ShapeCast3D::_create_debug_shape() { } if (debug_mesh.is_null()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + debug_mesh.instantiate(); } } diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 42460eec4c..6e3ada83ad 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -470,7 +470,7 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { facesw[j] = vtx; } - triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh.instantiate(); triangle_mesh->create(faces); return triangle_mesh; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 34f5095493..bd4770bcc3 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -205,6 +205,7 @@ void BaseButton::set_disabled(bool p_disabled) { status.pressing_inside = false; } queue_redraw(); + update_minimum_size(); } bool BaseButton::is_disabled() const { diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index b1c47d858d..7e07f4a202 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -3001,5 +3001,5 @@ GraphEdit::GraphEdit() { set_clip_contents(true); - arranger = Ref<GraphEditArranger>(memnew(GraphEditArranger(this))); + arranger.instantiate(this); } diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 60cecfcfe7..16b41c8f9b 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -899,7 +899,7 @@ Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() { return debug_contact_mesh; } - debug_contact_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + debug_contact_mesh.instantiate(); Ref<StandardMaterial3D> mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 5a90eb8f3e..1ee99099ec 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1213,7 +1213,7 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) { } } else { WARN_PRINT("Invalid world_2d"); - world_2d = Ref<World2D>(memnew(World2D)); + world_2d.instantiate(); } world_2d->register_viewport(this); @@ -4386,7 +4386,7 @@ void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) { own_world_3d = world_3d->duplicate(); world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed)); } else { - own_world_3d = Ref<World3D>(memnew(World3D)); + own_world_3d.instantiate(); } } @@ -4437,7 +4437,7 @@ void Viewport::set_use_own_world_3d(bool p_use_own_world_3d) { own_world_3d = world_3d->duplicate(); world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed)); } else { - own_world_3d = Ref<World3D>(memnew(World3D)); + own_world_3d.instantiate(); } } else { own_world_3d = Ref<World3D>(); @@ -4963,7 +4963,7 @@ void Viewport::_validate_property(PropertyInfo &p_property) const { } Viewport::Viewport() { - world_2d = Ref<World2D>(memnew(World2D)); + world_2d.instantiate(); world_2d->register_viewport(this); viewport = RenderingServer::get_singleton()->viewport_create(); diff --git a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h index b29c106fb5..2812925770 100644 --- a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h +++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h @@ -36,6 +36,8 @@ #include "scene/resources/2d/navigation_polygon.h" class NavigationMeshSourceGeometryData2D : public Resource { + friend class NavMeshGenerator2D; + GDCLASS(NavigationMeshSourceGeometryData2D, Resource); RWLock geometry_rwlock; diff --git a/scene/resources/3d/shape_3d.cpp b/scene/resources/3d/shape_3d.cpp index 5a79392ba5..259d82b7a0 100644 --- a/scene/resources/3d/shape_3d.cpp +++ b/scene/resources/3d/shape_3d.cpp @@ -73,7 +73,7 @@ Ref<ArrayMesh> Shape3D::get_debug_mesh() { Vector<Vector3> lines = get_debug_mesh_lines(); - debug_mesh_cache = Ref<ArrayMesh>(memnew(ArrayMesh)); + debug_mesh_cache.instantiate(); if (!lines.is_empty()) { //make mesh diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 848ae2713d..b0353b4f2c 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -385,7 +385,7 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { } } - triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh.instantiate(); triangle_mesh->create(faces); return triangle_mesh; diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 67ed65df0d..034d4d6996 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -392,8 +392,8 @@ Ref<ArrayMesh> NavigationMesh::get_debug_mesh() { return debug_mesh; } - if (!debug_mesh.is_valid()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_mesh.is_null()) { + debug_mesh.instantiate(); } else { debug_mesh->clear_surfaces(); } diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index bb3aad0f28..809a77a487 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -2195,7 +2195,7 @@ void PackedScene::replace_state(Ref<SceneState> p_by) { } void PackedScene::recreate_state() { - state = Ref<SceneState>(memnew(SceneState)); + state.instantiate(); state->set_path(get_path()); #ifdef TOOLS_ENABLED state->set_last_modified_time(get_last_modified_time()); @@ -2286,5 +2286,5 @@ void PackedScene::_bind_methods() { } PackedScene::PackedScene() { - state = Ref<SceneState>(memnew(SceneState)); + state.instantiate(); } diff --git a/scene/resources/portable_compressed_texture.cpp b/scene/resources/portable_compressed_texture.cpp index 06b5ec6d5a..55bbed7c47 100644 --- a/scene/resources/portable_compressed_texture.cpp +++ b/scene/resources/portable_compressed_texture.cpp @@ -89,7 +89,7 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { data_size -= mipsize; } - image = Ref<Image>(memnew(Image(size.width, size.height, mipmaps, format, image_data))); + image.instantiate(size.width, size.height, mipmaps, format, image_data); } break; case COMPRESSION_MODE_BASIS_UNIVERSAL: { @@ -100,7 +100,7 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { case COMPRESSION_MODE_S3TC: case COMPRESSION_MODE_ETC2: case COMPRESSION_MODE_BPTC: { - image = Ref<Image>(memnew(Image(size.width, size.height, mipmaps, format, p_data.slice(20)))); + image.instantiate(size.width, size.height, mipmaps, format, p_data.slice(20)); } break; } ERR_FAIL_COND(image.is_null()); diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 535903a1b6..6e03fe25c9 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -1526,6 +1526,10 @@ ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path) return loader.get_uid(f); } +bool ResourceFormatLoaderText::has_custom_uid_support() const { + return true; +} + void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); if (f.is_null()) { diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 8397bc985f..4c0bf3d917 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -155,6 +155,7 @@ public: virtual String get_resource_type(const String &p_path) const override; virtual String get_resource_script_class(const String &p_path) const override; virtual ResourceUID::ID get_resource_uid(const String &p_path) const override; + virtual bool has_custom_uid_support() const override; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override; virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) override; diff --git a/scene/resources/texture_rd.cpp b/scene/resources/texture_rd.cpp index 531dbcbe7e..8e7eeb0ff4 100644 --- a/scene/resources/texture_rd.cpp +++ b/scene/resources/texture_rd.cpp @@ -197,7 +197,7 @@ void TextureLayeredRD::_set_texture_rd_rid(RID p_texture_rd_rid) { RS::TextureLayeredType rs_layer_type; RD::TextureFormat tf = RD::get_singleton()->texture_get_format(p_texture_rd_rid); - ERR_FAIL_COND(tf.texture_type != RD::TEXTURE_TYPE_2D_ARRAY); + ERR_FAIL_COND(tf.texture_type != RD::TEXTURE_TYPE_2D_ARRAY && tf.texture_type != RD::TEXTURE_TYPE_CUBE && tf.texture_type != RD::TEXTURE_TYPE_CUBE_ARRAY); ERR_FAIL_COND(tf.depth > 1); switch (layer_type) { case LAYERED_TYPE_2D_ARRAY: { diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 42fce65b2d..8a9499dfc9 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1666,7 +1666,9 @@ void TextureStorage::texture_rd_initialize(RID p_texture, const RID &p_rd_textur ERR_FAIL_COND(tf.array_layers != 1); texture.type = TextureStorage::TYPE_2D; } break; - case RD::TEXTURE_TYPE_2D_ARRAY: { + case RD::TEXTURE_TYPE_2D_ARRAY: + case RD::TEXTURE_TYPE_CUBE: + case RD::TEXTURE_TYPE_CUBE_ARRAY: { // RenderingDevice doesn't distinguish between Array textures and Cube textures // this condition covers TextureArrays, TextureCube, and TextureCubeArray. ERR_FAIL_COND(tf.array_layers == 1); diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 119ac677eb..f52963f98f 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -489,7 +489,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene struct_code += _typestr(m->datatype); } struct_code += " "; - struct_code += m->name; + struct_code += _mkid(m->name); if (m->array_size > 0) { struct_code += "["; struct_code += itos(m->array_size); @@ -1448,7 +1448,13 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } break; case SL::Node::NODE_TYPE_MEMBER: { SL::MemberNode *mnode = (SL::MemberNode *)p_node; - code = _dump_node_code(mnode->owner, p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + "." + mnode->name; + String name; + if (mnode->basetype == SL::TYPE_STRUCT) { + name = _mkid(mnode->name); + } else { + name = mnode->name; + } + code = _dump_node_code(mnode->owner, p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + "." + name; if (mnode->index_expression != nullptr) { code += "["; code += _dump_node_code(mnode->index_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 272908aa49..c0c6f1e904 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2067,6 +2067,16 @@ void RenderingServer::_particles_set_trail_bind_poses(RID p_particles, const Typ particles_set_trail_bind_poses(p_particles, tbposes); } +String RenderingServer::get_current_rendering_driver_name() const { + // Needs to remain in OS, since it's actually OS that interacts with it, but it's better exposed here. + return ::OS::get_singleton()->get_current_rendering_driver_name(); +} + +String RenderingServer::get_current_rendering_method() const { + // Needs to remain in OS, since it's actually OS that interacts with it, but it's better exposed here. + return ::OS::get_singleton()->get_current_rendering_method(); +} + Vector<uint8_t> _convert_surface_version_1_to_surface_version_2(uint64_t p_format, Vector<uint8_t> p_vertex_data, uint32_t p_vertex_count, uint32_t p_old_stride, uint32_t p_vertex_size, uint32_t p_normal_size, uint32_t p_position_stride, uint32_t p_normal_tangent_stride) { Vector<uint8_t> new_vertex_data; new_vertex_data.resize(p_vertex_data.size()); @@ -3423,6 +3433,9 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_video_adapter_type"), &RenderingServer::get_video_adapter_type); ClassDB::bind_method(D_METHOD("get_video_adapter_api_version"), &RenderingServer::get_video_adapter_api_version); + ClassDB::bind_method(D_METHOD("get_current_rendering_driver_name"), &RenderingServer::get_current_rendering_driver_name); + ClassDB::bind_method(D_METHOD("get_current_rendering_method"), &RenderingServer::get_current_rendering_method); + ClassDB::bind_method(D_METHOD("make_sphere_mesh", "latitudes", "longitudes", "radius"), &RenderingServer::make_sphere_mesh); ClassDB::bind_method(D_METHOD("get_test_cube"), &RenderingServer::get_test_cube); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 90365a19eb..6de934aaeb 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1787,6 +1787,9 @@ public: virtual bool is_on_render_thread() = 0; virtual void call_on_render_thread(const Callable &p_callable) = 0; + String get_current_rendering_driver_name() const; + String get_current_rendering_method() const; + #ifdef TOOLS_ENABLED virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; #endif diff --git a/tests/core/string/test_translation.h b/tests/core/string/test_translation.h index 7c389191e3..2b1069d40c 100644 --- a/tests/core/string/test_translation.h +++ b/tests/core/string/test_translation.h @@ -161,7 +161,7 @@ TEST_CASE("[TranslationCSV] CSV import") { List<String> gen_files; - Error result = import_csv_translation->import(TestUtils::get_data_path("translations.csv"), + Error result = import_csv_translation->import(0, TestUtils::get_data_path("translations.csv"), "", options, nullptr, &gen_files); CHECK(result == OK); CHECK(gen_files.size() == 4); diff --git a/tests/core/string/test_translation_server.h b/tests/core/string/test_translation_server.h index ac1599f2e8..4edc36d29f 100644 --- a/tests/core/string/test_translation_server.h +++ b/tests/core/string/test_translation_server.h @@ -37,27 +37,37 @@ namespace TestTranslationServer { TEST_CASE("[TranslationServer] Translation operations") { - Ref<Translation> t = memnew(Translation); - t->set_locale("uk"); - t->add_message("Good Morning", String::utf8("Добрий ранок")); + Ref<Translation> t1 = memnew(Translation); + t1->set_locale("uk"); + t1->add_message("Good Morning", String(U"Добрий ранок")); + + Ref<Translation> t2 = memnew(Translation); + t2->set_locale("uk"); + t2->add_message("Hello Godot", String(U"你好戈多")); TranslationServer *ts = TranslationServer::get_singleton(); + // Adds translation for UK locale for the first time. int l_count_before = ts->get_loaded_locales().size(); - ts->add_translation(t); + ts->add_translation(t1); int l_count_after = ts->get_loaded_locales().size(); - // Newly created Translation object should be added to the list, so the counter should increase, too. CHECK(l_count_after > l_count_before); - Ref<Translation> trans = ts->get_translation_object("uk"); - CHECK(trans.is_valid()); + // Adds translation for UK locale again. + ts->add_translation(t2); + CHECK_EQ(ts->get_loaded_locales().size(), l_count_after); + + // Removing that translation. + ts->remove_translation(t2); + CHECK_EQ(ts->get_loaded_locales().size(), l_count_after); + + CHECK(ts->get_translation_object("uk").is_valid()); ts->set_locale("uk"); CHECK(ts->translate("Good Morning") == String::utf8("Добрий ранок")); - ts->remove_translation(t); - trans = ts->get_translation_object("uk"); - CHECK(trans.is_null()); + ts->remove_translation(t1); + CHECK(ts->get_translation_object("uk").is_null()); // If no suitable Translation object has been found - the original message should be returned. CHECK(ts->translate("Good Morning") == "Good Morning"); } diff --git a/tests/scene/test_audio_stream_wav.h b/tests/scene/test_audio_stream_wav.h index d3d5cc8a30..7276dd0878 100644 --- a/tests/scene/test_audio_stream_wav.h +++ b/tests/scene/test_audio_stream_wav.h @@ -162,7 +162,7 @@ void run_test(String file_name, AudioStreamWAV::Format data_format, bool stereo, // Compressed streams can't be saved, disable compression. options_map["compress/mode"] = 0; - REQUIRE(wav_importer->import(save_path, save_path, options_map, nullptr) == OK); + REQUIRE(wav_importer->import(0, save_path, save_path, options_map, nullptr) == OK); String load_path = save_path + "." + wav_importer->get_save_extension(); Ref<AudioStreamWAV> loaded_stream = ResourceLoader::load(load_path, "AudioStreamWAV", ResourceFormatImporter::CACHE_MODE_IGNORE, &error); diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h index dde37944ec..06a38f234b 100644 --- a/tests/scene/test_viewport.h +++ b/tests/scene/test_viewport.h @@ -1577,7 +1577,7 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") { PickingCollider pc; pc.a = memnew(TestArea2D); pc.c = memnew(CollisionShape2D); - pc.r = Ref<RectangleShape2D>(memnew(RectangleShape2D)); + pc.r.instantiate(); pc.r->set_size(Size2(150, 150)); pc.c->set_shape(pc.r); pc.a->add_child(pc.c); |
