Jelajahi Sumber

Allow change import type without restarting editor

kobewi 2 tahun lalu
induk
melakukan
244968c8d1

+ 3 - 3
core/io/resource_format_binary.cpp

@@ -1775,9 +1775,9 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 		case Variant::OBJECT: {
 			f->store_32(VARIANT_OBJECT);
 			Ref<Resource> res = p_property;
-			if (res.is_null()) {
+			if (res.is_null() || res->get_meta(SNAME("_skip_save_"), false)) {
 				f->store_32(OBJECT_EMPTY);
-				return; // don't save it
+				return; // Don't save it.
 			}
 
 			if (!res->is_built_in()) {
@@ -1942,7 +1942,7 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
 		case Variant::OBJECT: {
 			Ref<Resource> res = p_variant;
 
-			if (res.is_null() || external_resources.has(res)) {
+			if (res.is_null() || external_resources.has(res) || res->get_meta(SNAME("_skip_save_"), false)) {
 				return;
 			}
 

+ 74 - 11
editor/import_dock.cpp

@@ -35,6 +35,7 @@
 #include "editor/editor_resource_preview.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 
 class ImportDockParameters : public Object {
 	GDCLASS(ImportDockParameters, Object);
@@ -459,7 +460,6 @@ static bool _find_owners(EditorFileSystemDirectory *efsd, const String &p_path)
 }
 
 void ImportDock::_reimport_attempt() {
-	bool need_restart = false;
 	bool used_in_resources = false;
 
 	String importer_name;
@@ -476,14 +476,15 @@ void ImportDock::_reimport_attempt() {
 
 		String imported_with = config->get_value("remap", "importer");
 		if (imported_with != importer_name) {
-			need_restart = true;
+			need_cleanup.push_back(params->paths[i]);
 			if (_find_owners(EditorFileSystem::get_singleton()->get_filesystem(), params->paths[i])) {
 				used_in_resources = true;
 			}
 		}
 	}
 
-	if (need_restart) {
+	if (!need_cleanup.is_empty() || used_in_resources) {
+		cleanup_warning->set_visible(!need_cleanup.is_empty());
 		label_warning->set_visible(used_in_resources);
 		reimport_confirm->popup_centered();
 		return;
@@ -492,11 +493,42 @@ void ImportDock::_reimport_attempt() {
 	_reimport();
 }
 
-void ImportDock::_reimport_and_restart() {
-	EditorNode::get_singleton()->save_all_scenes();
-	EditorResourcePreview::get_singleton()->stop(); //don't try to re-create previews after import
+void ImportDock::_reimport_and_cleanup() {
+	HashMap<String, Ref<Resource>> old_resources;
+
+	for (const String &path : need_cleanup) {
+		Ref<Resource> res = ResourceLoader::load(path);
+		res->set_path("");
+		res->set_meta(SNAME("_skip_save_"), true);
+		old_resources[path] = res;
+	}
+
+	EditorResourcePreview::get_singleton()->stop(); // Don't try to re-create previews after import.
 	_reimport();
-	EditorNode::get_singleton()->restart_editor();
+
+	if (need_cleanup.is_empty()) {
+		return;
+	}
+
+	// After changing resource type we need to make sure that all old instances are unloaded or replaced.
+	EditorNode::get_singleton()->push_item(nullptr);
+	EditorUndoRedoManager::get_singleton()->clear_history();
+
+	List<Ref<Resource>> external_resources;
+	ResourceCache::get_cached_resources(&external_resources);
+
+	for (const String &path : need_cleanup) {
+		Ref<Resource> old_res = old_resources[path];
+		Ref<Resource> new_res = ResourceLoader::load(path);
+
+		for (int j = 0; j < EditorNode::get_editor_data().get_edited_scene_count(); j++) {
+			_replace_resource_in_object(EditorNode::get_editor_data().get_edited_scene_root(j), old_res, new_res);
+		}
+		for (Ref<Resource> res : external_resources) {
+			_replace_resource_in_object(res.ptr(), old_res, new_res);
+		}
+	}
+	need_cleanup.clear();
 }
 
 void ImportDock::_advanced_options() {
@@ -561,6 +593,37 @@ void ImportDock::_reimport() {
 	_set_dirty(false);
 }
 
+void ImportDock::_replace_resource_in_object(Object *p_object, const Ref<Resource> &old_resource, const Ref<Resource> &new_resource) {
+	ERR_FAIL_NULL(p_object);
+
+	List<PropertyInfo> props;
+	p_object->get_property_list(&props);
+
+	for (const PropertyInfo &p : props) {
+		if (p.type != Variant::OBJECT || p.hint != PROPERTY_HINT_RESOURCE_TYPE) {
+			continue;
+		}
+
+		Ref<Resource> res = p_object->get(p.name);
+		if (res.is_null()) {
+			continue;
+		}
+
+		if (res == old_resource) {
+			p_object->set(p.name, new_resource);
+		} else {
+			_replace_resource_in_object(res.ptr(), old_resource, new_resource);
+		}
+	}
+
+	Node *n = Object::cast_to<Node>(p_object);
+	if (n) {
+		for (int i = 0; i < n->get_child_count(); i++) {
+			_replace_resource_in_object(n->get_child(i), old_resource, new_resource);
+		}
+	}
+}
+
 void ImportDock::_notification(int p_what) {
 	switch (p_what) {
 		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
@@ -673,13 +736,13 @@ ImportDock::ImportDock() {
 	advanced->connect("pressed", callable_mp(this, &ImportDock::_advanced_options));
 
 	reimport_confirm = memnew(ConfirmationDialog);
-	reimport_confirm->set_ok_button_text(TTR("Save Scenes, Re-Import, and Restart"));
 	content->add_child(reimport_confirm);
-	reimport_confirm->connect("confirmed", callable_mp(this, &ImportDock::_reimport_and_restart));
+	reimport_confirm->connect("confirmed", callable_mp(this, &ImportDock::_reimport_and_cleanup));
 
 	VBoxContainer *vbc_confirm = memnew(VBoxContainer());
-	vbc_confirm->add_child(memnew(Label(TTR("Changing the type of an imported file requires editor restart."))));
-	label_warning = memnew(Label(TTR("WARNING: Assets exist that use this resource, they may stop loading properly.")));
+	cleanup_warning = memnew(Label(TTR("The imported resource is currently loaded. All instances will be replaced and undo history will be cleared.")));
+	vbc_confirm->add_child(cleanup_warning);
+	label_warning = memnew(Label(TTR("WARNING: Assets exist that use this resource. They may stop loading properly after changing type.")));
 	vbc_confirm->add_child(label_warning);
 	reimport_confirm->add_child(vbc_confirm);
 

+ 5 - 1
editor/import_dock.h

@@ -54,8 +54,10 @@ class ImportDock : public VBoxContainer {
 	HashMap<StringName, Variant> property_values;
 
 	ConfirmationDialog *reimport_confirm = nullptr;
+	Label *cleanup_warning = nullptr;
 	Label *label_warning = nullptr;
 	Button *import = nullptr;
+	List<String> need_cleanup;
 
 	Control *advanced_spacer = nullptr;
 	Button *advanced = nullptr;
@@ -75,9 +77,11 @@ class ImportDock : public VBoxContainer {
 	void _property_toggled(const StringName &p_prop, bool p_checked);
 	void _set_dirty(bool p_dirty);
 	void _reimport_attempt();
-	void _reimport_and_restart();
+	void _reimport_and_cleanup();
 	void _reimport();
 
+	void _replace_resource_in_object(Object *p_object, const Ref<Resource> &old_resource, const Ref<Resource> &new_resource);
+
 	void _advanced_options();
 	enum {
 		ITEM_SET_AS_DEFAULT = 100,

+ 5 - 1
scene/resources/resource_format_text.cpp

@@ -1828,6 +1828,10 @@ String ResourceFormatSaverTextInstance::_write_resources(void *ud, const Ref<Res
 }
 
 String ResourceFormatSaverTextInstance::_write_resource(const Ref<Resource> &res) {
+	if (res->get_meta(SNAME("_skip_save_"), false)) {
+		return "null";
+	}
+
 	if (external_resources.has(res)) {
 		return "ExtResource(\"" + external_resources[res] + "\")";
 	} else {
@@ -1852,7 +1856,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
 		case Variant::OBJECT: {
 			Ref<Resource> res = p_variant;
 
-			if (res.is_null() || external_resources.has(res)) {
+			if (res.is_null() || external_resources.has(res) || res->get_meta(SNAME("_skip_save_"), false)) {
 				return;
 			}