diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index 1cf388b33a79..c857d5492514 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -59,11 +59,9 @@ bool FileAccess::exists(const String &p_name) { return true; } - Ref f = open(p_name, READ); - if (f.is_null()) { - return false; - } - return true; + // Using file_exists because it's faster than trying to open the file. + Ref ret = create_for_path(p_name); + return ret->file_exists(p_name); } void FileAccess::_set_access_type(AccessType p_access) { diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index 9e6f3ba31427..1ae50d2d0d93 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -35,6 +35,8 @@ #include "core/os/os.h" #include "core/variant/variant_parser.h" +ResourceFormatImporterLoadOnStartup ResourceImporter::load_on_startup = nullptr; + bool ResourceFormatImporter::SortImporterByName::operator()(const Ref &p_a, const Ref &p_b) const { return p_a->get_importer_name() < p_b->get_importer_name(); } @@ -137,6 +139,20 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy } Ref ResourceFormatImporter::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { +#ifdef TOOLS_ENABLED + // When loading a resource on startup, we use the load_on_startup callback, + // which executes the loading in the EditorFileSystem. It can reimport + // the resource and retry the load, allowing the resource to be loaded + // even if it is not yet imported. + if (ResourceImporter::load_on_startup != nullptr) { + return ResourceImporter::load_on_startup(this, p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); + } +#endif + + return load_internal(p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode, false); +} + +Ref ResourceFormatImporter::load_internal(const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode, bool p_silence_errors) { PathAndType pat; Error err = _get_path_and_type(p_path, pat); @@ -148,6 +164,13 @@ Ref ResourceFormatImporter::load(const String &p_path, const String &p return Ref(); } + if (p_silence_errors) { + // Note: Some importers do not create files in the .godot folder, so we need to check if the path is empty. + if (!pat.path.is_empty() && !FileAccess::exists(pat.path)) { + return Ref(); + } + } + Ref res = ResourceLoader::_load(pat.path, p_path, pat.type, p_cache_mode, r_error, p_use_sub_threads, r_progress); #ifdef TOOLS_ENABLED @@ -364,6 +387,23 @@ ResourceUID::ID ResourceFormatImporter::get_resource_uid(const String &p_path) c return pat.uid; } +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); + + if (err == OK) { + r_type = pat.type; + r_uid = pat.uid; + r_import_group_file = pat.group_file; + } else { + r_type = ""; + r_uid = ResourceUID::INVALID_ID; + r_import_group_file = ""; + } + + return err; +} + Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const { PathAndType pat; Error err = _get_path_and_type(p_path, pat); @@ -467,7 +507,7 @@ bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) con for (int i = 0; i < importers.size(); i++) { if (importers[i]->get_importer_name() == pat.importer) { - if (!importers[i]->are_import_settings_valid(p_path)) { //importer thinks this is not valid + if (!importers[i]->are_import_settings_valid(p_path, pat.metadata)) { //importer thinks this is not valid return false; } } diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index dbd9e70d16be..221f38494b3c 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -35,6 +35,9 @@ #include "core/io/resource_saver.h" class ResourceImporter; +class ResourceFormatImporter; + +typedef Ref (*ResourceFormatImporterLoadOnStartup)(ResourceFormatImporter *p_importer, const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, ResourceFormatLoader::CacheMode p_cache_mode); class ResourceFormatImporter : public ResourceFormatLoader { struct PathAndType { @@ -60,6 +63,7 @@ class ResourceFormatImporter : public ResourceFormatLoader { public: static ResourceFormatImporter *get_singleton() { return singleton; } virtual Ref load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; + Ref load_internal(const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode, bool p_silence_errors); virtual void get_recognized_extensions(List *p_extensions) const override; virtual void get_recognized_extensions_for_type(const String &p_type, List *p_extensions) const override; virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const override; @@ -93,6 +97,8 @@ class ResourceFormatImporter : public ResourceFormatLoader { String get_import_settings_hash() const; String get_import_base_path(const String &p_for_file) const; + Error get_resource_import_info(const String &p_path, StringName &r_type, ResourceUID::ID &r_uid, String &r_import_group_file) const; + ResourceFormatImporter(); }; @@ -103,6 +109,8 @@ class ResourceImporter : public RefCounted { static void _bind_methods(); public: + static ResourceFormatImporterLoadOnStartup load_on_startup; + virtual String get_importer_name() const = 0; virtual String get_visible_name() const = 0; virtual void get_recognized_extensions(List *p_extensions) const = 0; @@ -145,7 +153,7 @@ class ResourceImporter : public RefCounted { virtual void import_threaded_end() {} virtual Error import_group_file(const String &p_group_file, const HashMap> &p_source_file_options, const HashMap &p_base_paths) { return ERR_UNAVAILABLE; } - virtual bool are_import_settings_valid(const String &p_path) const { return true; } + virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { return true; } virtual String get_import_settings_string() const { return String(); } }; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 928bb95de3a8..d1a8ea26a547 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -1060,36 +1060,39 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem new_path = path_remaps[new_path]; } else { // Try file remap. - Error err; - Ref f = FileAccess::open(new_path + ".remap", FileAccess::READ, &err); - if (f.is_valid()) { - VariantParser::StreamFile stream; - stream.f = f; - - String assign; - Variant value; - VariantParser::Tag next_tag; - - int lines = 0; - String error_text; - while (true) { - assign = Variant(); - next_tag.fields.clear(); - next_tag.name = String(); - - err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true); - if (err == ERR_FILE_EOF) { - break; - } else if (err != OK) { - ERR_PRINT("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + "."); - break; - } + // Usually, there's no remap file and FileAccess::exists() is faster than FileAccess::open(). + if (FileAccess::exists(new_path + ".remap")) { + Error err; + Ref f = FileAccess::open(new_path + ".remap", FileAccess::READ, &err); + if (f.is_valid()) { + VariantParser::StreamFile stream; + stream.f = f; + + String assign; + Variant value; + VariantParser::Tag next_tag; + + int lines = 0; + String error_text; + while (true) { + assign = Variant(); + next_tag.fields.clear(); + next_tag.name = String(); + + err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true); + if (err == ERR_FILE_EOF) { + break; + } else if (err != OK) { + ERR_PRINT("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + "."); + break; + } - if (assign == "path") { - new_path = value; - break; - } else if (next_tag.name != "remap") { - break; + if (assign == "path") { + new_path = value; + break; + } else if (next_tag.name != "remap") { + break; + } } } } diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 4d82a32dae62..2cd3f19158ec 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -205,8 +205,11 @@ If [code]true[/code], displays folders in the FileSystem dock's bottom pane when split mode is enabled. If [code]false[/code], only files will be displayed in the bottom pane. Split mode can be toggled by pressing the icon next to the [code]res://[/code] folder path. [b]Note:[/b] This setting has no effect when split mode is disabled (which is the default). + + A comma separated list of unsupported file extensions to show in the FileSystem dock, e.g. [code]"ico,icns"[/code]. + - List of file extensions to consider as editable text files in the FileSystem dock (by double-clicking on the files). + A comma separated list of file extensions to consider as editable text files in the FileSystem dock (by double-clicking on the files), e.g. [code]"txt,md,cfg,ini,log,json,yml,yaml,toml,xml"[/code]. The thumbnail size to use in the FileSystem dock (in pixels). See also [member filesystem/file_dialog/thumbnail_size]. diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index ea8b42b2e4cd..d086e723c2b7 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -383,7 +383,6 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { if (!err) { return status.st_mtime; } else { - WARN_PRINT("Failed to get modified time for: " + p_file); return 0; } } diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 1508a4740b0a..897d4f6f145c 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -585,7 +585,6 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { } } - print_verbose("Failed to get modified time for: " + p_file); return 0; } diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index f75e438582c8..f7915bc7e634 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -33,8 +33,6 @@ #include "core/config/project_settings.h" #include "core/extension/gdextension_manager.h" #include "core/io/file_access.h" -#include "core/io/resource_importer.h" -#include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/object/worker_thread_pool.h" #include "core/os/os.h" @@ -95,25 +93,35 @@ String EditorFileSystemDirectory::get_file(int p_idx) const { } String EditorFileSystemDirectory::get_path() const { - String p; - const EditorFileSystemDirectory *d = this; - while (d->parent) { - p = d->name.path_join(p); - d = d->parent; + int parents = 0; + const EditorFileSystemDirectory *efd = this; + // Determine the level of nesting. + while (efd->parent) { + parents++; + efd = efd->parent; } - return "res://" + p; -} + if (parents == 0) { + return "res://"; + } -String EditorFileSystemDirectory::get_file_path(int p_idx) const { - String file = get_file(p_idx); - const EditorFileSystemDirectory *d = this; - while (d->parent) { - file = d->name.path_join(file); - d = d->parent; + // Using PackedStringArray, because the path is built in reverse order. + PackedStringArray path_bits; + // Allocate an array based on nesting. It will store path bits. + path_bits.resize(parents + 2); // Last String is empty, so paths end with /. + String *path_write = path_bits.ptrw(); + path_write[0] = "res:/"; + + efd = this; + for (int i = parents; i > 0; i--) { + path_write[i] = efd->name; + efd = efd->parent; } + return String("/").join(path_bits); +} - return "res://" + file; +String EditorFileSystemDirectory::get_file_path(int p_idx) const { + return get_path().path_join(get_file(p_idx)); } Vector EditorFileSystemDirectory::get_file_deps(int p_idx) const { @@ -228,15 +236,18 @@ EditorFileSystem::ScannedDirectory::~ScannedDirectory() { } void EditorFileSystem::_first_scan_filesystem() { + EditorProgress ep = EditorProgress("first_scan_filesystem", TTR("Project initialization"), 5); Ref d = DirAccess::create(DirAccess::ACCESS_RESOURCES); first_scan_root_dir = memnew(ScannedDirectory); first_scan_root_dir->full_path = "res://"; HashSet existing_class_names; + ep.step(TTR("Scanning file structure..."), 0, true); nb_files_total = _scan_new_dir(first_scan_root_dir, d); // This loads the global class names from the scripts and ensures that even if the // global_script_class_cache.cfg was missing or invalid, the global class names are valid in ScriptServer. + ep.step(TTR("Loading global class names..."), 1, true); _first_scan_process_scripts(first_scan_root_dir, existing_class_names); // Removing invalid global class to prevent having invalid paths in ScriptServer. @@ -245,8 +256,13 @@ void EditorFileSystem::_first_scan_filesystem() { // Now that all the global class names should be loaded, create autoloads and plugins. // This is done after loading the global class names because autoloads and plugins can use // global class names. + ep.step(TTR("Creating autoload scripts..."), 3, true); ProjectSettingsEditor::get_singleton()->init_autoloads(); + + ep.step(TTR("Initializing plugins..."), 4, true); EditorNode::get_singleton()->init_plugins(); + + ep.step(TTR("Starting file scan..."), 5, true); } void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet &p_existing_class_names) { @@ -255,6 +271,21 @@ void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_sca } for (const String &scan_file : p_scan_dir->files) { + // Optimization to skip the ResourceLoader::get_resource_type for files + // that are not scripts. Some loader get_resource_type methods read the file + // which can be very slow on large projects. + String ext = scan_file.get_extension().to_lower(); + bool is_script = false; + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + if (ScriptServer::get_language(i)->get_extension() == ext) { + is_script = true; + break; + } + } + if (!is_script) { + continue; // Not a script. + } + String path = p_scan_dir->full_path.path_join(scan_file); String type = ResourceLoader::get_resource_type(path); @@ -342,6 +373,11 @@ void EditorFileSystem::_scan_filesystem() { fc.script_class_name = split[7].get_slice("<>", 0); fc.script_class_extends = split[7].get_slice("<>", 1); fc.script_class_icon_path = split[7].get_slice("<>", 2); + fc.import_md5 = split[7].get_slice("<>", 3); + String dest_paths = split[7].get_slice("<>", 4); + if (!dest_paths.is_empty()) { + fc.import_dest_paths = dest_paths.split("<*>"); + } String deps = split[8].strip_edges(); if (deps.length()) { @@ -428,12 +464,33 @@ void EditorFileSystem::_thread_func(void *_userdata) { sd->_scan_filesystem(); } -bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_imported_files) { - if (!reimport_on_missing_imported_files && p_only_imported_files) { - return false; +bool EditorFileSystem::_is_test_for_reimport_needed(const String &p_path, uint64_t p_last_modification_time, uint64_t p_modification_time, uint64_t p_last_import_modification_time, uint64_t p_import_modification_time, const Vector &p_import_dest_paths) { + // The idea here is to trust the cache. If the last modification times in the cache correspond + // to the last modification times of the files on disk, it means the files have not changed since + // the last import, and the files in .godot/imported (p_import_dest_paths) should all be valid. + if (p_last_modification_time != p_modification_time) { + return true; + } + if (p_last_import_modification_time != p_import_modification_time) { + return true; } + if (reimport_on_missing_imported_files) { + for (const String &path : p_import_dest_paths) { + if (!FileAccess::exists(path)) { + return true; + } + } + } + return false; +} - if (!FileAccess::exists(p_path + ".import")) { +bool EditorFileSystem::_test_for_reimport(const String &p_path, const String &p_expected_import_md5) { + if (p_expected_import_md5.is_empty()) { + // Marked as reimportation needed. + return true; + } + String new_md5 = FileAccess::get_md5(p_path + ".import"); + if (p_expected_import_md5 != new_md5) { return true; } @@ -454,7 +511,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo int lines = 0; String error_text; - List to_check; + Vector to_check; String importer_name; String source_file = ""; @@ -463,6 +520,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo String dest_md5 = ""; int version = 0; bool found_uid = false; + Variant meta; while (true) { assign = Variant(); @@ -487,8 +545,8 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo to_check.push_back(value); } else if (assign == "files") { Array fa = value; - for (int i = 0; i < fa.size(); i++) { - to_check.push_back(fa[i]); + for (const Variant &check_path : fa) { + to_check.push_back(check_path); } } else if (assign == "importer_version") { version = value; @@ -496,12 +554,12 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo importer_name = value; } else if (assign == "uid") { found_uid = true; - } else if (!p_only_imported_files) { - if (assign == "source_file") { - source_file = value; - } else if (assign == "dest_files") { - dest_files = value; - } + } else if (assign == "source_file") { + source_file = value; + } else if (assign == "dest_files") { + dest_files = value; + } else if (assign == "metadata") { + meta = value; } } else if (next_tag.name != "remap" && next_tag.name != "deps") { @@ -509,33 +567,40 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo } } - if (!ResourceFormatImporter::get_singleton()->are_import_settings_valid(p_path)) { - // Reimport settings are out of sync with project settings, reimport. - return true; - } - if (importer_name == "keep" || importer_name == "skip") { - return false; //keep mode, do not reimport + return false; // Keep mode, do not reimport. } if (!found_uid) { - return true; //UID not found, old format, reimport. + return true; // UID not found, old format, reimport. + } + + // Imported files are gone, reimport. + for (const String &E : to_check) { + if (!FileAccess::exists(E)) { + return true; + } } Ref importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name); if (importer.is_null()) { - return true; // the importer has possibly changed, try to reimport. + return true; // The importer has possibly changed, try to reimport. } if (importer->get_format_version() > version) { - return true; // version changed, reimport + return true; // Version changed, reimport. + } + + if (!importer->are_import_settings_valid(p_path, meta)) { + // Reimport settings are out of sync with project settings, reimport. + return true; } - // Read the md5's from a separate file (so the import parameters aren't dependent on the file version + // Read the md5's from a separate file (so the import parameters aren't dependent on the file version). String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(p_path); Ref md5s = FileAccess::open(base_path + ".md5", FileAccess::READ, &err); - if (md5s.is_null()) { // No md5's stored for this resource + if (md5s.is_null()) { // No md5's stored for this resource. return true; } @@ -553,50 +618,101 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo break; } else if (err != OK) { ERR_PRINT("ResourceFormatImporter::load - '" + p_path + ".import.md5:" + itos(lines) + "' error '" + error_text + "'."); - return false; // parse error + return false; // Parse error. } if (!assign.is_empty()) { - if (!p_only_imported_files) { - if (assign == "source_md5") { - source_md5 = value; - } else if (assign == "dest_md5") { - dest_md5 = value; - } + if (assign == "source_md5") { + source_md5 = value; + } else if (assign == "dest_md5") { + dest_md5 = value; } } } - //imported files are gone, reimport - for (const String &E : to_check) { - if (!FileAccess::exists(E)) { + // Check source md5 matching. + if (!source_file.is_empty() && source_file != p_path) { + return true; // File was moved, reimport. + } + + if (source_md5.is_empty()) { + return true; // Lacks md5, so just reimport. + } + + String md5 = FileAccess::get_md5(p_path); + if (md5 != source_md5) { + return true; + } + + if (!dest_files.is_empty() && !dest_md5.is_empty()) { + md5 = FileAccess::get_multiple_md5(dest_files); + if (md5 != dest_md5) { return true; } } - //check source md5 matching - if (!p_only_imported_files) { - if (!source_file.is_empty() && source_file != p_path) { - return true; //file was moved, reimport - } + return false; // Nothing changed. +} - if (source_md5.is_empty()) { - return true; //lacks md5, so just reimport - } +Vector EditorFileSystem::_get_import_dest_paths(const String &p_path) { + Error err; + Ref f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); - String md5 = FileAccess::get_md5(p_path); - if (md5 != source_md5) { - return true; + if (f.is_null()) { // No import file, reimport. + return Vector(); + } + + VariantParser::StreamFile stream; + stream.f = f; + + String assign; + Variant value; + VariantParser::Tag next_tag; + + int lines = 0; + String error_text; + + Vector dest_paths; + String importer_name; + + while (true) { + assign = Variant(); + next_tag.fields.clear(); + next_tag.name = String(); + + err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true); + if (err == ERR_FILE_EOF) { + break; + } else if (err != OK) { + ERR_PRINT("ResourceFormatImporter::load - '" + p_path + ".import:" + itos(lines) + "' error '" + error_text + "'."); + // Parse error, skip and let user attempt manual reimport to avoid reimport loop. + return Vector(); } - if (dest_files.size() && !dest_md5.is_empty()) { - md5 = FileAccess::get_multiple_md5(dest_files); - if (md5 != dest_md5) { - return true; + if (!assign.is_empty()) { + if (assign == "valid" && value.operator bool() == false) { + // Invalid import (failed previous import), skip and let user attempt manual reimport to avoid reimport loop. + return Vector(); + } + if (assign.begins_with("path")) { + dest_paths.push_back(value); + } else if (assign == "files") { + Array fa = value; + for (const Variant &dest_path : fa) { + dest_paths.push_back(dest_path); + } + } else if (assign == "importer") { + importer_name = value; } + } else if (next_tag.name != "remap" && next_tag.name != "deps") { + break; } } - return false; //nothing changed + if (importer_name == "keep" || importer_name == "skip") { + return Vector(); + } + + return dest_paths; } bool EditorFileSystem::_scan_import_support(const Vector &reimports) { @@ -650,6 +766,12 @@ bool EditorFileSystem::_update_scan_actions() { Vector reimports; Vector reloads; + EditorProgress *ep = nullptr; + if (scan_actions.size() > 1) { + ep = memnew(EditorProgress("_update_scan_actions", TTR("Scanning actions..."), scan_actions.size())); + } + + int step_count = 0; for (const ItemAction &ia : scan_actions) { switch (ia.action) { case ItemAction::ACTION_NONE: { @@ -733,20 +855,9 @@ bool EditorFileSystem::_update_scan_actions() { ERR_CONTINUE(idx == -1); String full_path = ia.dir->get_file_path(idx); - bool need_reimport = _test_for_reimport(full_path, false); - // Workaround GH-94416 for the Android editor for now. - // `import_mt` seems to always be 0 and force a reimport on any fs scan. -#ifndef ANDROID_ENABLED - if (!need_reimport && FileAccess::exists(full_path + ".import")) { - uint64_t import_mt = ia.dir->get_file_import_modified_time(idx); - if (import_mt != FileAccess::get_modified_time(full_path + ".import")) { - need_reimport = true; - } - } -#endif - + bool need_reimport = _test_for_reimport(full_path, ia.dir->files[idx]->import_md5); if (need_reimport) { - //must reimport + // Must reimport. reimports.push_back(full_path); Vector dependencies = _get_dependencies(full_path); for (const String &dep : dependencies) { @@ -756,10 +867,14 @@ bool EditorFileSystem::_update_scan_actions() { } } } else { - //must not reimport, all was good - //update modified times, to avoid reimport + // Must not reimport, all was good. + // Update modified times, md5 and destination paths, to avoid reimport. ia.dir->files[idx]->modified_time = FileAccess::get_modified_time(full_path); ia.dir->files[idx]->import_modified_time = FileAccess::get_modified_time(full_path + ".import"); + if (ia.dir->files[idx]->import_md5.is_empty()) { + ia.dir->files[idx]->import_md5 = FileAccess::get_md5(full_path + ".import"); + } + ia.dir->files[idx]->import_dest_paths = _get_import_dest_paths(full_path); } fs_changed = true; @@ -781,8 +896,14 @@ bool EditorFileSystem::_update_scan_actions() { } break; } + + if (ep) { + ep->step(ia.file, step_count++, false); + } } + memdelete_notnull(ep); + if (_scan_extensions()) { //needs editor restart //extensions also may provide filetypes to be imported, so they must run before importing @@ -862,6 +983,7 @@ void EditorFileSystem::scan() { // Set first_scan to false before the signals so the function doing_first_scan can return false // in editor_node to start the export if needed. first_scan = false; + ResourceImporter::load_on_startup = nullptr; emit_signal(SNAME("filesystem_changed")); emit_signal(SNAME("sources_changed"), sources_changed.size() > 0); } else { @@ -872,8 +994,6 @@ void EditorFileSystem::scan() { scan_total = 0; s.priority = Thread::PRIORITY_LOW; thread.start(_thread_func, this, s); - //tree->hide(); - //progress->show(); } } @@ -981,26 +1101,36 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir, if (import_extensions.has(ext)) { //is imported - uint64_t import_mt = 0; - if (FileAccess::exists(path + ".import")) { - import_mt = FileAccess::get_modified_time(path + ".import"); - } + uint64_t import_mt = FileAccess::get_modified_time(path + ".import"); - if (fc && fc->modification_time == mt && fc->import_modification_time == import_mt && !_test_for_reimport(path, true)) { + if (fc) { fi->type = fc->type; fi->resource_script_class = fc->resource_script_class; fi->uid = fc->uid; fi->deps = fc->deps; - fi->modified_time = fc->modification_time; - fi->import_modified_time = fc->import_modification_time; - + fi->modified_time = mt; + fi->import_modified_time = import_mt; + fi->import_md5 = fc->import_md5; + fi->import_dest_paths = fc->import_dest_paths; fi->import_valid = fc->import_valid; fi->script_class_name = fc->script_class_name; fi->import_group_file = fc->import_group_file; fi->script_class_extends = fc->script_class_extends; fi->script_class_icon_path = fc->script_class_icon_path; - if (revalidate_import_files && !ResourceFormatImporter::get_singleton()->are_import_settings_valid(path)) { + // Ensures backward compatibility when the project is loaded for the first time with the added import_md5 + // and import_dest_paths properties in the file cache. + if (fc->import_md5.is_empty()) { + fi->import_md5 = FileAccess::get_md5(path + ".import"); + fi->import_dest_paths = _get_import_dest_paths(path); + } + + // The method _is_test_for_reimport_needed checks if the files were modified and ensures that + // all the destination files still exist without reading the .import file. + // If something is different, we will queue a test for reimportation that will check + // the md5 of all files and import settings and, if necessary, execute a reimportation. + if (_is_test_for_reimport_needed(path, fc->modification_time, mt, fc->import_modification_time, import_mt, fi->import_dest_paths) || + (revalidate_import_files && !ResourceFormatImporter::get_singleton()->are_import_settings_valid(path))) { ItemAction ia; ia.action = ItemAction::ACTION_FILE_TEST_REIMPORT; ia.dir = p_dir; @@ -1022,13 +1152,14 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir, } } else { - fi->type = ResourceFormatImporter::get_singleton()->get_resource_type(path); - fi->uid = ResourceFormatImporter::get_singleton()->get_resource_uid(path); - fi->import_group_file = ResourceFormatImporter::get_singleton()->get_import_group_file(path); + // Using get_resource_import_info() to prevent calling 3 times ResourceFormatImporter::_get_path_and_type. + ResourceFormatImporter::get_singleton()->get_resource_import_info(path, fi->type, fi->uid, fi->import_group_file); fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path); fi->modified_time = 0; fi->import_modified_time = 0; - fi->import_valid = fi->type == "TextFile" ? true : ResourceLoader::is_import_valid(path); + fi->import_md5 = ""; + fi->import_dest_paths = Vector(); + fi->import_valid = (fi->type == "TextFile" || fi->type == "OtherFile") ? true : ResourceLoader::is_import_valid(path); ItemAction ia; ia.action = ItemAction::ACTION_FILE_TEST_REIMPORT; @@ -1042,9 +1173,11 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir, fi->type = fc->type; fi->resource_script_class = fc->resource_script_class; fi->uid = fc->uid; - fi->modified_time = fc->modification_time; + fi->modified_time = mt; fi->deps = fc->deps; fi->import_modified_time = 0; + fi->import_md5 = ""; + fi->import_dest_paths = Vector(); fi->import_valid = true; fi->script_class_name = fc->script_class_name; fi->script_class_extends = fc->script_class_extends; @@ -1071,11 +1204,16 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir, if (fi->type == "" && textfile_extensions.has(ext)) { fi->type = "TextFile"; } + if (fi->type == "" && other_file_extensions.has(ext)) { + fi->type = "OtherFile"; + } fi->uid = ResourceLoader::get_resource_uid(path); fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path); fi->deps = _get_dependencies(path); fi->modified_time = mt; fi->import_modified_time = 0; + fi->import_md5 = ""; + fi->import_dest_paths = Vector(); fi->import_valid = true; // Files in dep_update_list are forced for rescan to update dependencies. They don't need other updates. @@ -1197,13 +1335,18 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr String path = cd.path_join(fi->file); fi->modified_time = FileAccess::get_modified_time(path); fi->import_modified_time = 0; + fi->import_md5 = ""; + fi->import_dest_paths = Vector(); fi->type = ResourceLoader::get_resource_type(path); fi->resource_script_class = ResourceLoader::get_resource_script_class(path); if (fi->type == "" && textfile_extensions.has(ext)) { fi->type = "TextFile"; } + if (fi->type == "" && other_file_extensions.has(ext)) { + fi->type = "OtherFile"; + } fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path); - fi->import_valid = fi->type == "TextFile" ? true : ResourceLoader::is_import_valid(path); + fi->import_valid = (fi->type == "TextFile" || fi->type == "OtherFile") ? true : ResourceLoader::is_import_valid(path); fi->import_group_file = ResourceLoader::get_import_group_file(path); { @@ -1248,26 +1391,13 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr String path = cd.path_join(p_dir->files[i]->file); if (import_extensions.has(p_dir->files[i]->file.get_extension().to_lower())) { - //check here if file must be imported or not - + // Check here if file must be imported or not. + // Same logic as in _process_file_system, the last modifications dates + // needs to be trusted to prevent reading all the .import files and the md5 + // each time the user switch back to Godot. uint64_t mt = FileAccess::get_modified_time(path); - - bool reimport = false; - - if (mt != p_dir->files[i]->modified_time) { - reimport = true; //it was modified, must be reimported. - } else if (!FileAccess::exists(path + ".import")) { - reimport = true; //no .import file, obviously reimport - } else { - uint64_t import_mt = FileAccess::get_modified_time(path + ".import"); - if (import_mt != p_dir->files[i]->import_modified_time) { - reimport = true; - } else if (_test_for_reimport(path, true)) { - reimport = true; - } - } - - if (reimport) { + uint64_t import_mt = FileAccess::get_modified_time(path + ".import"); + if (_is_test_for_reimport_needed(path, p_dir->files[i]->modified_time, mt, p_dir->files[i]->import_modified_time, import_mt, p_dir->files[i]->import_dest_paths)) { ItemAction ia; ia.action = ItemAction::ACTION_FILE_TEST_REIMPORT; ia.dir = p_dir; @@ -1475,6 +1605,7 @@ void EditorFileSystem::_notification(int p_what) { // Set first_scan to false before the signals so the function doing_first_scan can return false // in editor_node to start the export if needed. first_scan = false; + ResourceImporter::load_on_startup = nullptr; if (changed) { emit_signal(SNAME("filesystem_changed")); } @@ -1497,6 +1628,7 @@ void EditorFileSystem::_notification(int p_what) { // Set first_scan to false before the signals so the function doing_first_scan can return false // in editor_node to start the export if needed. first_scan = false; + ResourceImporter::load_on_startup = nullptr; emit_signal(SNAME("filesystem_changed")); emit_signal(SNAME("sources_changed"), sources_changed.size() > 0); } @@ -1549,7 +1681,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir, cache_string.append(itos(file_info->import_modified_time)); cache_string.append(itos(file_info->import_valid)); cache_string.append(file_info->import_group_file); - cache_string.append(String("<>").join({ file_info->script_class_name, file_info->script_class_extends, file_info->script_class_icon_path })); + cache_string.append(String("<>").join({ file_info->script_class_name, file_info->script_class_extends, file_info->script_class_icon_path, file_info->import_md5, String("<*>").join(file_info->import_dest_paths) })); cache_string.append(String("<>").join(file_info->deps)); p_file->store_line(String("::").join(cache_string)); @@ -1830,10 +1962,21 @@ void EditorFileSystem::_update_script_classes() { update_script_mutex.lock(); + EditorProgress *ep = nullptr; + if (update_script_paths.size() > 1) { + ep = memnew(EditorProgress("update_scripts_classes", TTR("Registering global classes..."), update_script_paths.size())); + } + + int step_count = 0; for (const KeyValue &E : update_script_paths) { _register_global_class_script(E.key, E.key, E.value.type, E.value.script_class_name, E.value.script_class_extends, E.value.script_class_icon_path); + if (ep) { + ep->step(E.value.script_class_name, step_count++, false); + } } + memdelete_notnull(ep); + update_script_paths.clear(); update_script_mutex.unlock(); @@ -1858,6 +2001,12 @@ void EditorFileSystem::_update_script_documentation() { update_script_mutex.lock(); + EditorProgress *ep = nullptr; + if (update_script_paths_documentation.size() > 1) { + ep = memnew(EditorProgress("update_script_paths_documentation", TTR("Updating scripts documentation"), update_script_paths_documentation.size())); + } + + int step_count = 0; for (const String &path : update_script_paths_documentation) { int index = -1; EditorFileSystemDirectory *efd = find_file(path, &index); @@ -1880,8 +2029,14 @@ void EditorFileSystem::_update_script_documentation() { } } } + + if (ep) { + ep->step(efd->files[index]->file, step_count++, false); + } } + memdelete_notnull(ep); + update_script_paths_documentation.clear(); update_script_mutex.unlock(); } @@ -1916,7 +2071,7 @@ void EditorFileSystem::_update_scene_groups() { EditorProgress *ep = nullptr; if (update_scene_paths.size() > 20) { - ep = memnew(EditorProgress("update_scene_groups", TTR("Update Scene Groups"), update_scene_paths.size())); + ep = memnew(EditorProgress("update_scene_groups", TTR("Updating Scene Groups"), update_scene_paths.size())); } int step_count = 0; @@ -1938,7 +2093,7 @@ void EditorFileSystem::_update_scene_groups() { } if (ep) { - ep->step(TTR("Updating Scene Groups..."), step_count++); + ep->step(efd->files[index]->file, step_count++, false); } } @@ -2024,6 +2179,9 @@ void EditorFileSystem::update_files(const Vector &p_script_paths) { if (type.is_empty() && textfile_extensions.has(file.get_extension())) { type = "TextFile"; } + if (type.is_empty() && other_file_extensions.has(file.get_extension())) { + type = "OtherFile"; + } String script_class = ResourceLoader::get_resource_script_class(file); ResourceUID::ID uid = ResourceLoader::get_resource_uid(file); @@ -2043,7 +2201,9 @@ void EditorFileSystem::update_files(const Vector &p_script_paths) { EditorFileSystemDirectory::FileInfo *fi = memnew(EditorFileSystemDirectory::FileInfo); fi->file = file_name; fi->import_modified_time = 0; - fi->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file); + fi->import_valid = (type == "TextFile" || type == "OtherFile") ? true : ResourceLoader::is_import_valid(file); + fi->import_md5 = ""; + fi->import_dest_paths = Vector(); if (idx == fs->files.size()) { fs->files.push_back(fi); @@ -2067,7 +2227,7 @@ void EditorFileSystem::update_files(const Vector &p_script_paths) { fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(file); fs->files[cpos]->modified_time = FileAccess::get_modified_time(file); fs->files[cpos]->deps = _get_dependencies(file); - fs->files[cpos]->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file); + fs->files[cpos]->import_valid = (type == "TextFile" || type == "OtherFile") ? true : ResourceLoader::is_import_valid(file); if (uid != ResourceUID::INVALID_ID) { if (ResourceUID::get_singleton()->has_id(uid)) { @@ -2319,12 +2479,17 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector //update modified times, to avoid reimport fs->files[cpos]->modified_time = FileAccess::get_modified_time(file); fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(file + ".import"); + fs->files[cpos]->import_md5 = FileAccess::get_md5(file + ".import"); + fs->files[cpos]->import_dest_paths = dest_paths; fs->files[cpos]->deps = _get_dependencies(file); fs->files[cpos]->uid = uid; fs->files[cpos]->type = importer->get_resource_type(); if (fs->files[cpos]->type == "" && textfile_extensions.has(file.get_extension())) { fs->files[cpos]->type = "TextFile"; } + if (fs->files[cpos]->type == "" && other_file_extensions.has(file.get_extension())) { + fs->files[cpos]->type = "OtherFile"; + } fs->files[cpos]->import_valid = err == OK; if (ResourceUID::get_singleton()->has_id(uid)) { @@ -2351,14 +2516,16 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector return err; } -Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap &p_custom_options, const String &p_custom_importer, Variant *p_generator_parameters) { +Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap &p_custom_options, const String &p_custom_importer, Variant *p_generator_parameters, bool p_update_file_system) { print_verbose(vformat("EditorFileSystem: Importing file: %s", p_file)); uint64_t start_time = OS::get_singleton()->get_ticks_msec(); EditorFileSystemDirectory *fs = nullptr; int cpos = -1; - bool found = _find_file(p_file, &fs, cpos); - ERR_FAIL_COND_V_MSG(!found, ERR_FILE_NOT_FOUND, "Can't find file '" + p_file + "'."); + if (p_update_file_system) { + bool found = _find_file(p_file, &fs, cpos); + ERR_FAIL_COND_V_MSG(!found, ERR_FILE_NOT_FOUND, "Can't find file '" + p_file + "'."); + } //try to obtain existing params @@ -2412,12 +2579,16 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMapfiles[cpos]->modified_time = FileAccess::get_modified_time(p_file); - fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import"); - fs->files[cpos]->deps.clear(); - fs->files[cpos]->type = ""; - fs->files[cpos]->import_valid = false; - EditorResourcePreview::get_singleton()->check_for_invalidation(p_file); + if (p_update_file_system) { + fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file); + fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import"); + fs->files[cpos]->import_md5 = FileAccess::get_md5(p_file + ".import"); + fs->files[cpos]->import_dest_paths = Vector(); + fs->files[cpos]->deps.clear(); + fs->files[cpos]->type = ""; + fs->files[cpos]->import_valid = false; + EditorResourcePreview::get_singleton()->check_for_invalidation(p_file); + } return OK; } Ref importer; @@ -2576,16 +2747,20 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMapfiles[cpos]->modified_time = FileAccess::get_modified_time(p_file); - fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import"); - fs->files[cpos]->deps = _get_dependencies(p_file); - fs->files[cpos]->type = importer->get_resource_type(); - fs->files[cpos]->uid = uid; - fs->files[cpos]->import_valid = fs->files[cpos]->type == "TextFile" ? true : ResourceLoader::is_import_valid(p_file); + // Update modified times, to avoid reimport. + fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file); + fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import"); + fs->files[cpos]->import_md5 = FileAccess::get_md5(p_file + ".import"); + fs->files[cpos]->import_dest_paths = dest_paths; + fs->files[cpos]->deps = _get_dependencies(p_file); + fs->files[cpos]->type = importer->get_resource_type(); + fs->files[cpos]->uid = uid; + fs->files[cpos]->import_valid = fs->files[cpos]->type == "TextFile" ? true : ResourceLoader::is_import_valid(p_file); + } if (ResourceUID::get_singleton()->has_id(uid)) { ResourceUID::get_singleton()->set_id(uid, p_file); @@ -2654,13 +2829,25 @@ void EditorFileSystem::reimport_files(const Vector &p_files) { Vector reloads; - EditorProgress pr("reimport", TTR("(Re)Importing Assets"), p_files.size()); + EditorProgress *ep = memnew(EditorProgress("reimport", TTR("(Re)Importing Assets"), p_files.size())); + + // The method reimport_files runs on the main thread, and if VSync is enabled + // or Update Continuously is disabled, Main::Iteration takes longer each frame. + // Each EditorProgress::step can trigger a redraw, and when there are many files to import, + // this could lead to a slow import process, especially when the editor is unfocused. + // Temporarily disabling VSync and low_processor_usage_mode while reimporting fixes this. + const bool old_low_processor_usage_mode = OS::get_singleton()->is_in_low_processor_usage_mode(); + const DisplayServer::VSyncMode old_vsync_mode = DisplayServer::get_singleton()->window_get_vsync_mode(DisplayServer::MAIN_WINDOW_ID); + OS::get_singleton()->set_low_processor_usage_mode(false); + DisplayServer::get_singleton()->window_set_vsync_mode(DisplayServer::VSyncMode::VSYNC_DISABLED); Vector reimport_files; HashSet groups_to_reimport; for (int i = 0; i < p_files.size(); i++) { + ep->step(TTR("Preparing files to reimport..."), i, false); + String file = p_files[i]; ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(file); @@ -2700,6 +2887,8 @@ void EditorFileSystem::reimport_files(const Vector &p_files) { reimport_files.sort(); + ep->step(TTR("Executing pre-reimport operations..."), 0, true); + // Emit the resource_reimporting signal for the single file before the actual importation. emit_signal(SNAME("resources_reimporting"), reloads); @@ -2720,7 +2909,7 @@ void EditorFileSystem::reimport_files(const Vector &p_files) { if (i + 1 == reimport_files.size() || reimport_files[i + 1].importer != reimport_files[from].importer || groups_to_reimport.has(reimport_files[i + 1].path)) { if (from - i == 0) { // Single file, do not use threads. - pr.step(reimport_files[i].path.get_file(), i); + ep->step(reimport_files[i].path.get_file(), i, false); _reimport_file(reimport_files[i].path); } else { Ref importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(reimport_files[from].importer); @@ -2742,7 +2931,7 @@ void EditorFileSystem::reimport_files(const Vector &p_files) { do { if (current_index < tdata.max_index.get()) { current_index = tdata.max_index.get(); - pr.step(reimport_files[current_index].path.get_file(), current_index); + ep->step(reimport_files[current_index].path.get_file(), current_index, false); } OS::get_singleton()->delay_usec(1); } while (!WorkerThreadPool::get_singleton()->is_group_task_completed(group_task)); @@ -2756,7 +2945,7 @@ void EditorFileSystem::reimport_files(const Vector &p_files) { } } else { - pr.step(reimport_files[i].path.get_file(), i); + ep->step(reimport_files[i].path.get_file(), i, false); _reimport_file(reimport_files[i].path); // We need to increment the counter, maybe the next file is multithreaded @@ -2773,7 +2962,7 @@ void EditorFileSystem::reimport_files(const Vector &p_files) { HashMap> group_files; _find_group_files(filesystem, group_files, groups_to_reimport); for (const KeyValue> &E : group_files) { - pr.step(E.key.get_file(), from++); + ep->step(E.key.get_file(), from++, false); Error err = _reimport_group(E.key, E.value); reloads.push_back(E.key); reloads.append_array(E.value); @@ -2782,17 +2971,28 @@ void EditorFileSystem::reimport_files(const Vector &p_files) { } } } + ep->step(TTR("Finalizing Asset Import..."), p_files.size()); ResourceUID::get_singleton()->update_cache(); // After reimporting, update the cache. - _save_filesystem_cache(); + + memdelete_notnull(ep); + _process_update_pending(); + + // Revert to previous values to restore editor settings for VSync and Update Continuously. + OS::get_singleton()->set_low_processor_usage_mode(old_low_processor_usage_mode); + DisplayServer::get_singleton()->window_set_vsync_mode(old_vsync_mode); + importing = false; + + ep = memnew(EditorProgress("reimport", TTR("(Re)Importing Assets"), p_files.size())); + ep->step(TTR("Executing post-reimport operations..."), 0, true); if (!is_scanning()) { emit_signal(SNAME("filesystem_changed")); } - emit_signal(SNAME("resources_reimported"), reloads); + memdelete_notnull(ep); } Error EditorFileSystem::reimport_append(const String &p_file, const HashMap &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters) { @@ -2820,6 +3020,33 @@ Error EditorFileSystem::_resource_import(const String &p_path) { return OK; } +Ref EditorFileSystem::_load_resource_on_startup(ResourceFormatImporter *p_importer, const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, ResourceFormatLoader::CacheMode p_cache_mode) { + ERR_FAIL_NULL_V(p_importer, Ref()); + + if (!FileAccess::exists(p_path)) { + ERR_FAIL_V_MSG(Ref(), vformat("Failed loading resource: %s. The file doesn't seem to exist.", p_path)); + } + + Ref res; + bool can_retry = true; + bool retry = true; + while (retry) { + retry = false; + + res = p_importer->load_internal(p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode, can_retry); + + if (res.is_null() && can_retry) { + can_retry = false; + Error err = singleton->_reimport_file(p_path, HashMap(), "", nullptr, false); + if (err == OK) { + retry = true; + } + } + } + + return res; +} + bool EditorFileSystem::_should_skip_directory(const String &p_path) { String project_data_path = ProjectSettings::get_singleton()->get_project_data_path(); if (p_path == project_data_path || p_path.begins_with(project_data_path + "/")) { @@ -2893,6 +3120,35 @@ void EditorFileSystem::move_group_file(const String &p_path, const String &p_new } } +void EditorFileSystem::add_new_directory(const String &p_path) { + String path = p_path.get_base_dir(); + EditorFileSystemDirectory *parent = filesystem; + int base = p_path.count("/"); + int max_bit = base + 1; + + while (path != "res://") { + EditorFileSystemDirectory *dir = get_filesystem_path(path); + if (dir) { + parent = dir; + break; + } + path = path.get_base_dir(); + base--; + } + + for (int i = base; i < max_bit; i++) { + EditorFileSystemDirectory *efd = memnew(EditorFileSystemDirectory); + efd->parent = parent; + efd->name = p_path.get_slice("/", i); + parent->subdirs.push_back(efd); + + if (i == base) { + parent->subdirs.sort_custom(); + } + parent = efd; + } +} + ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate) { if (!p_path.is_resource_file() || p_path.begins_with(ProjectSettings::get_singleton()->get_project_data_path())) { // Saved externally (configuration file) or internal file, do not assign an ID. @@ -3014,6 +3270,7 @@ void EditorFileSystem::_update_extensions() { valid_extensions.clear(); import_extensions.clear(); textfile_extensions.clear(); + other_file_extensions.clear(); List extensionsl; ResourceLoader::get_recognized_extensions_for_type("", &extensionsl); @@ -3029,6 +3286,14 @@ void EditorFileSystem::_update_extensions() { valid_extensions.insert(E); textfile_extensions.insert(E); } + const Vector other_file_ext = ((String)(EDITOR_GET("docks/filesystem/other_file_extensions"))).split(",", false); + for (const String &E : other_file_ext) { + if (valid_extensions.has(E)) { + continue; + } + valid_extensions.insert(E); + other_file_extensions.insert(E); + } extensionsl.clear(); ResourceFormatImporter::get_singleton()->get_recognized_extensions(&extensionsl); @@ -3064,6 +3329,10 @@ EditorFileSystem::EditorFileSystem() { scan_total = 0; ResourceSaver::set_get_resource_id_for_path(_resource_saver_get_resource_id_for_path); + + // Set the callback method that the ResourceFormatImporter will use + // if resources are loaded during the first scan. + ResourceImporter::load_on_startup = _load_resource_on_startup; } EditorFileSystem::~EditorFileSystem() { diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 1bc24416ebc1..ea38ff57aff9 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -32,6 +32,8 @@ #define EDITOR_FILE_SYSTEM_H #include "core/io/dir_access.h" +#include "core/io/resource_importer.h" +#include "core/io/resource_loader.h" #include "core/os/thread.h" #include "core/os/thread_safe.h" #include "core/templates/hash_set.h" @@ -58,6 +60,8 @@ class EditorFileSystemDirectory : public Object { ResourceUID::ID uid = ResourceUID::INVALID_ID; uint64_t modified_time = 0; uint64_t import_modified_time = 0; + String import_md5; + Vector import_dest_paths; bool import_valid = false; String import_group_file; Vector deps; @@ -204,6 +208,8 @@ class EditorFileSystem : public Node { ResourceUID::ID uid = ResourceUID::INVALID_ID; uint64_t modification_time = 0; uint64_t import_modification_time = 0; + String import_md5; + Vector import_dest_paths; Vector deps; bool import_valid = false; String import_group_file; @@ -222,6 +228,12 @@ class EditorFileSystem : public Node { void increment(); }; + struct DirectoryComparator { + bool operator()(const EditorFileSystemDirectory *p_a, const EditorFileSystemDirectory *p_b) const { + return p_a->name.filenocasecmp_to(p_b->name) < 0; + } + }; + void _save_filesystem_cache(); void _save_filesystem_cache(EditorFileSystemDirectory *p_dir, Ref p_file); @@ -233,6 +245,7 @@ class EditorFileSystem : public Node { int _insert_actions_delete_files_directory(EditorFileSystemDirectory *p_dir); HashSet textfile_extensions; + HashSet other_file_extensions; HashSet valid_extensions; HashSet import_extensions; @@ -252,10 +265,12 @@ class EditorFileSystem : public Node { void _update_extensions(); - Error _reimport_file(const String &p_file, const HashMap &p_custom_options = HashMap(), const String &p_custom_importer = String(), Variant *generator_parameters = nullptr); + Error _reimport_file(const String &p_file, const HashMap &p_custom_options = HashMap(), const String &p_custom_importer = String(), Variant *generator_parameters = nullptr, bool p_update_file_system = true); Error _reimport_group(const String &p_group_file, const Vector &p_files); - bool _test_for_reimport(const String &p_path, bool p_only_imported_files); + bool _test_for_reimport(const String &p_path, const String &p_expected_import_md5); + bool _is_test_for_reimport_needed(const String &p_path, uint64_t p_last_modification_time, uint64_t p_modification_time, uint64_t p_last_import_modification_time, uint64_t p_import_modification_time, const Vector &p_import_dest_paths); + Vector _get_import_dest_paths(const String &p_path); bool reimport_on_missing_imported_files; @@ -296,6 +311,7 @@ class EditorFileSystem : public Node { String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const; static Error _resource_import(const String &p_path); + static Ref _load_resource_on_startup(ResourceFormatImporter *p_importer, const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, ResourceFormatLoader::CacheMode p_cache_mode); bool using_fat32_or_exfat; // Workaround for projects in FAT32 or exFAT filesystem (pendrives, most of the time) @@ -359,6 +375,8 @@ class EditorFileSystem : public Node { bool is_group_file(const String &p_path) const; void move_group_file(const String &p_path, const String &p_new_path); + void add_new_directory(const String &p_path); + static bool _should_skip_directory(const String &p_path); void add_import_format_support_query(Ref p_query); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index d45fbad23320..91ee89250194 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -816,6 +816,7 @@ void EditorNode::_notification(int p_what) { if (EditorSettings::get_singleton()->check_changed_settings_in_group("docks/filesystem")) { HashSet updated_textfile_extensions; + HashSet updated_other_file_extensions; bool extensions_match = true; const Vector textfile_ext = ((String)(EDITOR_GET("docks/filesystem/textfile_extensions"))).split(",", false); for (const String &E : textfile_ext) { @@ -824,9 +825,17 @@ void EditorNode::_notification(int p_what) { extensions_match = false; } } + const Vector other_file_ext = ((String)(EDITOR_GET("docks/filesystem/other_file_extensions"))).split(",", false); + for (const String &E : other_file_ext) { + updated_other_file_extensions.insert(E); + if (extensions_match && !other_file_extensions.has(E)) { + extensions_match = false; + } + } - if (!extensions_match || updated_textfile_extensions.size() < textfile_extensions.size()) { + if (!extensions_match || updated_textfile_extensions.size() < textfile_extensions.size() || updated_other_file_extensions.size() < other_file_extensions.size()) { textfile_extensions = updated_textfile_extensions; + other_file_extensions = updated_other_file_extensions; EditorFileSystem::get_singleton()->scan(); } } @@ -1072,30 +1081,32 @@ void EditorNode::_resources_reimporting(const Vector &p_resources) { // the inherited scene. Then, get_modified_properties_for_node will return the mesh property, // which will trigger a recopy of the previous mesh, preventing the reload. scenes_modification_table.clear(); - List scenes; + scenes_reimported.clear(); + resources_reimported.clear(); + EditorFileSystem *editor_file_system = EditorFileSystem::get_singleton(); for (const String &res_path : p_resources) { - if (ResourceLoader::get_resource_type(res_path) == "PackedScene") { - scenes.push_back(res_path); + // It's faster to use EditorFileSystem::get_file_type than fetching the resource type from disk. + // This makes a big difference when reimporting many resources. + String file_type = editor_file_system->get_file_type(res_path); + if (file_type.is_empty()) { + file_type = ResourceLoader::get_resource_type(res_path); + } + if (file_type == "PackedScene") { + scenes_reimported.push_back(res_path); + } else { + resources_reimported.push_back(res_path); } } - if (scenes.size() > 0) { - preload_reimporting_with_path_in_edited_scenes(scenes); + if (scenes_reimported.size() > 0) { + preload_reimporting_with_path_in_edited_scenes(scenes_reimported); } } void EditorNode::_resources_reimported(const Vector &p_resources) { - List scenes; int current_tab = scene_tabs->get_current_tab(); - for (const String &res_path : p_resources) { - String file_type = ResourceLoader::get_resource_type(res_path); - if (file_type == "PackedScene") { - scenes.push_back(res_path); - // Reload later if needed, first go with normal resources. - continue; - } - + for (const String &res_path : resources_reimported) { if (!ResourceCache::has(res_path)) { // Not loaded, no need to reload. continue; @@ -1109,16 +1120,20 @@ void EditorNode::_resources_reimported(const Vector &p_resources) { // Editor may crash when related animation is playing while re-importing GLTF scene, stop it in advance. AnimationPlayer *ap = AnimationPlayerEditor::get_singleton()->get_player(); - if (ap && scenes.size() > 0) { + if (ap && scenes_reimported.size() > 0) { ap->stop(true); } - for (const String &E : scenes) { + for (const String &E : scenes_reimported) { reload_scene(E); } reload_instances_with_path_in_edited_scenes(); + scenes_modification_table.clear(); + scenes_reimported.clear(); + resources_reimported.clear(); + _set_current_scene_nocheck(current_tab); } @@ -1303,6 +1318,9 @@ Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_d res = ResourceLoader::load(p_resource, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); } else if (textfile_extensions.has(p_resource.get_extension())) { res = ScriptEditor::get_singleton()->open_file(p_resource); + } else if (other_file_extensions.has(p_resource.get_extension())) { + OS::get_singleton()->shell_open(ProjectSettings::get_singleton()->globalize_path(p_resource)); + return OK; } ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN); @@ -5236,6 +5254,8 @@ void EditorNode::save_editor_layout_delayed() { } void EditorNode::_load_editor_layout() { + EditorProgress ep("loading_editor_layout", TTR("Loading editor"), 5); + ep.step(TTR("Loading editor layout..."), 0, true); Ref config; config.instantiate(); Error err = config->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg")); @@ -5257,11 +5277,19 @@ void EditorNode::_load_editor_layout() { return; } + ep.step(TTR("Loading docks..."), 1, true); editor_dock_manager->load_docks_from_config(config, "docks"); + + ep.step(TTR("Reopening scenes..."), 2, true); _load_open_scenes_from_config(config); + + ep.step(TTR("Loading central editor layout..."), 3, true); _load_central_editor_layout_from_config(config); + ep.step(TTR("Loading plugin window layout..."), 4, true); editor_data.set_plugin_window_layout(config); + + ep.step(TTR("Editor layout ready."), 5, true); } void EditorNode::_save_central_editor_layout_to_config(Ref p_config_file) { @@ -6329,8 +6357,6 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() { editor_data.restore_edited_scene_state(editor_selection, &editor_history); - scenes_modification_table.clear(); - progress.step(TTR("Reloading done."), editor_data.get_edited_scene_count()); } @@ -6903,6 +6929,10 @@ EditorNode::EditorNode() { for (const String &E : textfile_ext) { textfile_extensions.insert(E); } + const Vector other_file_ext = ((String)(EDITOR_GET("docks/filesystem/other_file_extensions"))).split(",", false); + for (const String &E : other_file_ext) { + other_file_extensions.insert(E); + } resource_preview = memnew(EditorResourcePreview); add_child(resource_preview); diff --git a/editor/editor_node.h b/editor/editor_node.h index 222b1cf90c7b..52ac6ee34861 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -487,6 +487,7 @@ class EditorNode : public Node { String import_reload_fn; HashSet textfile_extensions; + HashSet other_file_extensions; HashSet file_dialogs; HashSet editor_file_dialogs; @@ -845,6 +846,8 @@ class EditorNode : public Node { }; HashMap scenes_modification_table; + List scenes_reimported; + List resources_reimported; void update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 8b892c9d8d6e..ee1d8a9468cc 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -65,7 +65,7 @@ bool EditorSettings::_set(const StringName &p_name, const Variant &p_value) { _THREAD_SAFE_METHOD_ bool changed = _set_only(p_name, p_value); - if (changed) { + if (changed && initialized) { changed_settings.insert(p_name); emit_signal(SNAME("settings_changed")); } @@ -330,6 +330,10 @@ bool EditorSettings::has_default_value(const String &p_setting) const { return props[p_setting].has_default_value; } +void EditorSettings::_set_initialized() { + initialized = true; +} + void EditorSettings::_load_defaults(Ref p_extra_config) { _THREAD_SAFE_METHOD_ // Sets up the editor setting with a default value and hint PropertyInfo. @@ -586,6 +590,7 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "docks/filesystem/thumbnail_size", 64, "32,128,16") _initial_set("docks/filesystem/always_show_folders", true); _initial_set("docks/filesystem/textfile_extensions", "txt,md,cfg,ini,log,json,yml,yaml,toml,xml"); + _initial_set("docks/filesystem/other_file_extensions", "ico,icns"); // Property editor EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "docks/property_editor/auto_refresh_interval", 0.2, "0.01,1,0.001"); // Update 5 times per second by default. @@ -1926,7 +1931,5 @@ EditorSettings::EditorSettings() { last_order = 0; _load_defaults(); -} - -EditorSettings::~EditorSettings() { + callable_mp(this, &EditorSettings::_set_initialized).call_deferred(); } diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 62ac0c60f34f..6338f9105c58 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -98,6 +98,7 @@ class EditorSettings : public Resource { bool save_changed_setting = true; bool optimize_save = true; //do not save stuff that came from config but was not set from engine + bool initialized = false; bool _set(const StringName &p_name, const Variant &p_value); bool _set_only(const StringName &p_name, const Variant &p_value); @@ -108,6 +109,7 @@ class EditorSettings : public Resource { bool _property_can_revert(const StringName &p_name) const; bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + void _set_initialized(); void _load_defaults(Ref p_extra_config = Ref()); void _load_godot2_text_editor_theme(); void _load_default_visual_shader_editor_theme(); @@ -196,7 +198,6 @@ class EditorSettings : public Resource { #endif EditorSettings(); - ~EditorSettings(); }; //not a macro any longer diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 3103e504b9cc..268fe822c266 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -903,7 +903,7 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem if (p_export_filter == EditorExportPreset::EXPORT_SELECTED_SCENES && type != "PackedScene") { continue; } - if (type == "TextFile") { + if (type == "TextFile" || type == "OtherFile") { continue; } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 535ea553e282..0256863aab2d 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -270,7 +270,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory List file_list; for (int i = 0; i < p_dir->get_file_count(); i++) { String file_type = p_dir->get_file_type(i); - if (file_type != "TextFile" && _is_file_type_disabled_by_feature_profile(file_type)) { + if (file_type != "TextFile" && file_type != "OtherFile" && _is_file_type_disabled_by_feature_profile(file_type)) { // If type is disabled, file won't be displayed. continue; } @@ -1308,6 +1308,15 @@ void FileSystemDock::_fs_changed() { set_process(false); } +void FileSystemDock::_directory_created(const String &p_path) { + if (!DirAccess::exists(p_path)) { + return; + } + EditorFileSystem::get_singleton()->add_new_directory(p_path); + _update_tree(get_uncollapsed_paths()); + _update_file_list(true); +} + void FileSystemDock::_set_scanning_mode() { button_hist_prev->set_disabled(true); button_hist_next->set_disabled(true); @@ -4138,7 +4147,7 @@ FileSystemDock::FileSystemDock() { make_dir_dialog = memnew(DirectoryCreateDialog); add_child(make_dir_dialog); - make_dir_dialog->connect("dir_created", callable_mp(this, &FileSystemDock::_rescan).unbind(1)); + make_dir_dialog->connect("dir_created", callable_mp(this, &FileSystemDock::_directory_created)); make_scene_dialog = memnew(SceneCreateDialog); add_child(make_scene_dialog); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 2f54cb91db0a..907f843523e6 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -260,6 +260,7 @@ class FileSystemDock : public VBoxContainer { void _toggle_file_display(); void _set_file_display(bool p_active); void _fs_changed(); + void _directory_created(const String &p_path); void _select_file(const String &p_path, bool p_select_in_favorites = false); void _tree_activate_file(); diff --git a/editor/gui/editor_dir_dialog.cpp b/editor/gui/editor_dir_dialog.cpp index e64761d5b523..90ea0decc746 100644 --- a/editor/gui/editor_dir_dialog.cpp +++ b/editor/gui/editor_dir_dialog.cpp @@ -216,8 +216,9 @@ EditorDirDialog::EditorDirDialog() { makedir->connect(SceneStringName(pressed), callable_mp(this, &EditorDirDialog::_make_dir)); tree = memnew(Tree); - vb->add_child(tree); + tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); + vb->add_child(tree); tree->connect("item_activated", callable_mp(this, &EditorDirDialog::_item_activated)); tree->connect("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed), CONNECT_DEFERRED); diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 46882e95cb6f..50524a6e689d 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -433,22 +433,20 @@ String ResourceImporterLayeredTexture::get_import_settings_string() const { return s; } -bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path) const { +bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { //will become invalid if formats are missing to import - Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path); - - if (!meta.has("vram_texture")) { + if (!p_meta.has("vram_texture")) { return false; } - bool vram = meta["vram_texture"]; + bool vram = p_meta["vram_texture"]; if (!vram) { return true; //do not care about non vram } Vector formats_imported; - if (meta.has("imported_formats")) { - formats_imported = meta["imported_formats"]; + if (p_meta.has("imported_formats")) { + formats_imported = p_meta["imported_formats"]; } int index = 0; diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h index 5a21651de33f..26495eed8d7e 100644 --- a/editor/import/resource_importer_layered_texture.h +++ b/editor/import/resource_importer_layered_texture.h @@ -114,7 +114,7 @@ class ResourceImporterLayeredTexture : public ResourceImporter { virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap &p_options, List *r_platform_variants, List *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; - virtual bool are_import_settings_valid(const String &p_path) const 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; void set_mode(Mode p_mode) { mode = p_mode; } diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 487b8fc175c2..a205123df1ba 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -740,10 +740,8 @@ String ResourceImporterTexture::get_import_settings_string() const { return s; } -bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) const { - Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path); - - if (meta.has("has_editor_variant")) { +bool ResourceImporterTexture::are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { + if (p_meta.has("has_editor_variant")) { String imported_path = ResourceFormatImporter::get_singleton()->get_internal_resource_path(p_path); if (!FileAccess::exists(imported_path)) { return false; @@ -760,19 +758,19 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co } } - if (!meta.has("vram_texture")) { + if (!p_meta.has("vram_texture")) { return false; } - bool vram = meta["vram_texture"]; + bool vram = p_meta["vram_texture"]; if (!vram) { return true; // Do not care about non-VRAM. } // Will become invalid if formats are missing to import. Vector formats_imported; - if (meta.has("imported_formats")) { - formats_imported = meta["imported_formats"]; + if (p_meta.has("imported_formats")) { + formats_imported = p_meta["imported_formats"]; } int index = 0; diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h index f2539a8f5268..6d74c4e2f93a 100644 --- a/editor/import/resource_importer_texture.h +++ b/editor/import/resource_importer_texture.h @@ -104,7 +104,7 @@ class ResourceImporterTexture : public ResourceImporter { void update_imports(); - virtual bool are_import_settings_valid(const String &p_path) const 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; ResourceImporterTexture(bool p_singleton = false); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index fa8211ce81e3..6ee3c043bf16 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -7332,9 +7332,15 @@ void Node3DEditor::_init_grid() { bool orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL; - Vector grid_colors[3]; - Vector grid_points[3]; - Vector grid_normals[3]; + static LocalVector grid_colors[3]; + static LocalVector grid_points[3]; + static LocalVector grid_normals[3]; + + for (uint32_t n = 0; n < 3; n++) { + grid_colors[n].clear(); + grid_points[n].clear(); + grid_normals[n].clear(); + } Color primary_grid_color = EDITOR_GET("editors/3d/primary_grid_color"); Color secondary_grid_color = EDITOR_GET("editors/3d/secondary_grid_color"); @@ -7410,10 +7416,9 @@ void Node3DEditor::_init_grid() { grid_mat[c]->set_shader_parameter("grid_size", grid_fade_size); grid_mat[c]->set_shader_parameter("orthogonal", orthogonal); - // Cache these so we don't have to re-access memory. - Vector &ref_grid = grid_points[c]; - Vector &ref_grid_normals = grid_normals[c]; - Vector &ref_grid_colors = grid_colors[c]; + LocalVector &ref_grid = grid_points[c]; + LocalVector &ref_grid_normals = grid_normals[c]; + LocalVector &ref_grid_colors = grid_colors[c]; // Count our elements same as code below it. int expected_size = 0; @@ -7458,12 +7463,12 @@ void Node3DEditor::_init_grid() { line_end[a] = position_a; line_bgn[b] = bgn_b; line_end[b] = end_b; - ref_grid.set(idx, line_bgn); - ref_grid.set(idx + 1, line_end); - ref_grid_colors.set(idx, line_color); - ref_grid_colors.set(idx + 1, line_color); - ref_grid_normals.set(idx, normal); - ref_grid_normals.set(idx + 1, normal); + ref_grid[idx] = line_bgn; + ref_grid[idx + 1] = line_end; + ref_grid_colors[idx] = line_color; + ref_grid_colors[idx + 1] = line_color; + ref_grid_normals[idx] = normal; + ref_grid_normals[idx + 1] = normal; idx += 2; } @@ -7474,12 +7479,12 @@ void Node3DEditor::_init_grid() { line_end[b] = position_b; line_bgn[a] = bgn_a; line_end[a] = end_a; - ref_grid.set(idx, line_bgn); - ref_grid.set(idx + 1, line_end); - ref_grid_colors.set(idx, line_color); - ref_grid_colors.set(idx + 1, line_color); - ref_grid_normals.set(idx, normal); - ref_grid_normals.set(idx + 1, normal); + ref_grid[idx] = line_bgn; + ref_grid[idx + 1] = line_end; + ref_grid_colors[idx] = line_color; + ref_grid_colors[idx + 1] = line_color; + ref_grid_normals[idx] = normal; + ref_grid_normals[idx + 1] = normal; idx += 2; } } @@ -7488,9 +7493,9 @@ void Node3DEditor::_init_grid() { grid[c] = RenderingServer::get_singleton()->mesh_create(); Array d; d.resize(RS::ARRAY_MAX); - d[RenderingServer::ARRAY_VERTEX] = grid_points[c]; - d[RenderingServer::ARRAY_COLOR] = grid_colors[c]; - d[RenderingServer::ARRAY_NORMAL] = grid_normals[c]; + d[RenderingServer::ARRAY_VERTEX] = (Vector)grid_points[c]; + d[RenderingServer::ARRAY_COLOR] = (Vector)grid_colors[c]; + d[RenderingServer::ARRAY_NORMAL] = (Vector)grid_normals[c]; RenderingServer::get_singleton()->mesh_add_surface_from_arrays(grid[c], RenderingServer::PRIMITIVE_LINES, d); RenderingServer::get_singleton()->mesh_surface_set_material(grid[c], 0, grid_mat[c]->get_rid()); grid_instance[c] = RenderingServer::get_singleton()->instance_create2(grid[c], get_tree()->get_root()->get_world_3d()->get_scenario()); diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp index c21723d1ba77..2f345e516161 100644 --- a/editor/progress_dialog.cpp +++ b/editor/progress_dialog.cpp @@ -202,14 +202,13 @@ void ProgressDialog::add_task(const String &p_task, const String &p_label, int p bool ProgressDialog::task_step(const String &p_task, const String &p_state, int p_step, bool p_force_redraw) { ERR_FAIL_COND_V(!tasks.has(p_task), canceled); + Task &t = tasks[p_task]; if (!p_force_redraw) { uint64_t tus = OS::get_singleton()->get_ticks_usec(); - if (tus - last_progress_tick < 200000) { //200ms + if (tus - t.last_progress_tick < 200000) { //200ms return canceled; } } - - Task &t = tasks[p_task]; if (p_step < 0) { t.progress->set_value(t.progress->get_value() + 1); } else { @@ -217,7 +216,7 @@ bool ProgressDialog::task_step(const String &p_task, const String &p_state, int } t.state->set_text(p_state); - last_progress_tick = OS::get_singleton()->get_ticks_usec(); + t.last_progress_tick = OS::get_singleton()->get_ticks_usec(); _update_ui(); return canceled; @@ -252,7 +251,6 @@ ProgressDialog::ProgressDialog() { main->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); set_exclusive(true); set_flag(Window::FLAG_POPUP, false); - last_progress_tick = 0; singleton = this; cancel_hb = memnew(HBoxContainer); main->add_child(cancel_hb); diff --git a/editor/progress_dialog.h b/editor/progress_dialog.h index 82d59219da19..355812b0b7a6 100644 --- a/editor/progress_dialog.h +++ b/editor/progress_dialog.h @@ -71,13 +71,13 @@ class ProgressDialog : public PopupPanel { VBoxContainer *vb = nullptr; ProgressBar *progress = nullptr; Label *state = nullptr; + uint64_t last_progress_tick = 0; }; HBoxContainer *cancel_hb = nullptr; Button *cancel = nullptr; HashMap tasks; VBoxContainer *main = nullptr; - uint64_t last_progress_tick; LocalVector host_windows;