Análisis del código fuente de Godot 4: importación dinámica de archivos de imagen

Al intentar compilar un software de libro electrónico con Godot 4, el efecto inicial ha aparecido y, a través de la interfaz de comunicación de la tubería, puede obtener, establecer atributos y funciones de llamada, y parece ser capaz de manejar varios asuntos.

De hecho, los factores externos funcionan a través de factores internos. Si no se comprende el interior y no se liberan las funciones, algunas necesidades no se pueden realizar.

Por ejemplo, si desea cargar dinámicamente nuevos libros ahora, este es un requisito práctico. Si cada libro electrónico debe exportarse una vez, entonces este software no es universal.

Al cargar imágenes antes, el código GDScript:

# 目标对象上加载图片
func loadPng(obj, pngFileName) -> bool:
	var texture = load(pngFileName) as Texture2D;
	if(texture != null):
		obj.set_texture(texture);
		obj.scale.y = get_viewport_rect().size.y / texture.get_height();
		obj.scale.x = obj.scale.y;
		obj.position.y = get_viewport_rect().size.y / 2;	# 垂直居中
		adjustPos();
		return true;
	return false;

Cargue otra imagen directamente, el resultado no es exitoso

 Esto hay que estudiarlo.

importar importar

Copie esta imagen en el entorno de desarrollo de Godot, y descubrí que al volver a Godot, un cuadro de diálogo de importación parpadeará rápidamente, entonces, ¿qué debe haber hecho Godot?

Vaya al administrador de recursos y vea un archivo .import correspondiente, ábralo y mire el contenido

[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://rv5gm15xbcaf"
path="res://.godot/imported/DrGraph_Page24.png-1dd935fbb11807a645ea1ea79ec38662.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://Pages/DrGraph_Page24.png"
dest_files=["res://.godot/imported/DrGraph_Page24.png-1dd935fbb11807a645ea1ea79ec38662.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Dado que se genera dinámicamente, debe escribirse en el código. Encuéntralo en el código fuente, ¿por dónde empezar? Verificar import y .import son muchos resultados. Intente contenido nuevamente, busque [reasignar], pocos resultados

 Mire la ubicación y vea que hay dos funciones _reimport_group y _reimport_file de la clase EditorFileSystem, por lo que se debe usar _reimport_file. Pero esta función no es accesible. Afortunadamente, el código fuente está disponible y es directamente público y accesible.

Pero esto debe llamarse a GDScript, y se debe hacer algo de trabajo, y debe agregarse a ClassDB.

Para simplificar, agréguelo directamente a la clase DllStream [Ver Godot 4 Source Code Analysis - Add Pipeline Communication_DrGraph's Blog - CSDN Blog ], agregue una función de importación

ClassDB::bind_method(D_METHOD("import", "fileName"), &DllStream::import);

String DllStream::import(String fileName) {
	if (FileAccess::exists(fileName + ".import") == false) 
		EditorFileSystem::get_singleton()->_reimport_file(fileName);
	return fileName;	
}

Después de ejecutar, todavía no tiene éxito. La depuración encontró que _find_file(p_file, &fs, cpos) devuelve falso. Y _find_file en realidad requiere que el archivo esté ubicado en el directorio "res://", es decir, debe estar en el directorio del proyecto.

bool EditorFileSystem::_find_file(const String &p_file, EditorFileSystemDirectory **r_d, int &r_file_pos) const {
	//todo make faster

	if (!filesystem || scanning) {
		return false;
	}

	String f = ProjectSettings::get_singleton()->localize_path(p_file);

	if (!f.begins_with("res://")) {
		return false;
	}
	f = f.substr(6, f.length());
	f = f.replace("\\", "/");

	Vector<String> path = f.split("/");

	if (path.size() == 0) {
		return false;
	}
	String file = path[path.size() - 1];
	path.resize(path.size() - 1);

	EditorFileSystemDirectory *fs = filesystem;

	for (int i = 0; i < path.size(); i++) {
		if (path[i].begins_with(".")) {
			return false;
		}

		int idx = -1;
		for (int j = 0; j < fs->get_subdir_count(); j++) {
			if (fs->get_subdir(j)->get_name() == path[i]) {
				idx = j;
				break;
			}
		}

		if (idx == -1) {
			//does not exist, create i guess?
			EditorFileSystemDirectory *efsd = memnew(EditorFileSystemDirectory);

			efsd->name = path[i];
			efsd->parent = fs;

			int idx2 = 0;
			for (int j = 0; j < fs->get_subdir_count(); j++) {
				if (efsd->name.naturalnocasecmp_to(fs->get_subdir(j)->get_name()) < 0) {
					break;
				}
				idx2++;
			}

			if (idx2 == fs->get_subdir_count()) {
				fs->subdirs.push_back(efsd);
			} else {
				fs->subdirs.insert(idx2, efsd);
			}
			fs = efsd;
		} else {
			fs = fs->get_subdir(idx);
		}
	}

	int cpos = -1;
	for (int i = 0; i < fs->files.size(); i++) {
		if (fs->files[i]->file == file) {
			cpos = i;
			break;
		}
	}

	r_file_pos = cpos;
	*r_d = fs;

	return cpos != -1;
}

Esta petición es un poco excesiva. Sin embargo, existen dos métodos para el procesamiento de programas: uno es copiar automáticamente las imágenes al directorio del proyecto y el otro es omitirlo.

Solo prueba uno, lanza una moneda y elige el segundo en la cola.

Dado que elige omitir, implemente directamente la función EditorFileSystem::_reimport_file, lo que evita la necesidad de hacer pública esta función privada. La llamada implementación consiste en copiar todo el código de EditorFileSystem::_reimport_file y luego cambiarlo:

Error DllStream::_import(String destFileName) {
	HashMap<StringName, Variant> params = HashMap<StringName, Variant>();	
	String importer_name; //empty by default though
	ResourceUID::ID uid = ResourceUID::INVALID_ID;
	Variant generator_parameters;

	Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_extension(destFileName.get_extension());
	if (importer.is_null()) 
		ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, "BUG: File queued for import, but can't be imported, importer for type '" + importer_name + "' not found.");

	//mix with default params, in case a parameter is missing

	List<ResourceImporter::ImportOption> opts;
	importer->get_import_options(destFileName, &opts);
	for (const ResourceImporter::ImportOption &E : opts) {
		if (!params.has(E.option.name)) { //this one is not present
			params[E.option.name] = E.default_value;
		}
	}

	if (ProjectSettings::get_singleton()->has_setting("importer_defaults/" + importer->get_importer_name())) {
		//use defaults if exist
		Dictionary d = GLOBAL_GET("importer_defaults/" + importer->get_importer_name());
		List<Variant> v;
		d.get_key_list(&v);

		for (const Variant &E : v) 
			params[E] = d[E];
	}

	//finally, perform import!!
	String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(destFileName);

	List<String> import_variants;
	List<String> gen_files;
	Variant meta;
	Error err = importer->import(destFileName, base_path, params, &import_variants, &gen_files, &meta);

	ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_UNRECOGNIZED, "Error importing '" + destFileName + "'.");

	//as import is complete, save the .import file

	Vector<String> dest_paths;
	{
		Ref<FileAccess> f = FileAccess::open(destFileName + ".import", FileAccess::WRITE);
		ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Cannot open file from path '" + destFileName + ".import'.");

		//write manually, as order matters ([remap] has to go first for performance).
		f->store_line("[remap]");
		f->store_line("");
		f->store_line("importer=\"" + importer->get_importer_name() + "\"");
		int version = importer->get_format_version();
		if (version > 0) {
			f->store_line("importer_version=" + itos(version));
		}
		if (!importer->get_resource_type().is_empty()) {
			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) {
			if (importer->get_save_extension().is_empty()) {
				//no path
			} else if (import_variants.size()) {
				//import with variants
				for (const String &E : import_variants) {
					String path = base_path.c_escape() + "." + E + "." + importer->get_save_extension();

					f->store_line("path." + E + "=\"" + path + "\"");
					dest_paths.push_back(path);
				}
			} else {
				String path = base_path + "." + importer->get_save_extension();
				f->store_line("path=\"" + path + "\"");
				dest_paths.push_back(path);
			}

		} else {
			f->store_line("valid=false");
		}

		if (meta != Variant()) {
			f->store_line("metadata=" + meta.get_construct_string());
		}

		if (generator_parameters != Variant()) {
			f->store_line("generator_parameters=" + generator_parameters.get_construct_string());
		}

		f->store_line("");

		f->store_line("[deps]\n");

		if (gen_files.size()) {
			Array genf;
			for (const String &E : gen_files) {
				genf.push_back(E);
				dest_paths.push_back(E);
			}

			String value;
			VariantWriter::write_to_string(genf, value);
			f->store_line("files=" + value);
			f->store_line("");
		}

		f->store_line("source_file=" + Variant(destFileName).get_construct_string());

		if (dest_paths.size()) {
			Array dp;
			for (int i = 0; i < dest_paths.size(); i++) {
				dp.push_back(dest_paths[i]);
			}
			f->store_line("dest_files=" + Variant(dp).get_construct_string() + "\n");
		}

		f->store_line("[params]");
		f->store_line("");

		//store options in provided order, to avoid file changing. Order is also important because first match is accepted first.

		for (const ResourceImporter::ImportOption &E : opts) {
			String base = E.option.name;
			String value;
			VariantWriter::write_to_string(params[base], value);
			f->store_line(base + "=" + value);
		}
	}

	// Store the md5's of the various files. These are stored separately so that the .import files can be version controlled.
	{
		Ref<FileAccess> md5s = FileAccess::open(base_path + ".md5", FileAccess::WRITE);
		ERR_FAIL_COND_V_MSG(md5s.is_null(), ERR_FILE_CANT_OPEN, "Cannot open MD5 file '" + base_path + ".md5'.");

		md5s->store_line("source_md5=\"" + FileAccess::get_md5(destFileName) + "\"");
		if (dest_paths.size()) {
			md5s->store_line("dest_md5=\"" + FileAccess::get_multiple_md5(dest_paths) + "\"\n");
		}
	}
	if (ResourceUID::get_singleton()->has_id(uid)) {
		ResourceUID::get_singleton()->set_id(uid, destFileName);
	} else {
		ResourceUID::get_singleton()->add_id(uid, destFileName);
	}
}

String DllStream::import(String fileName) {
	if (FileAccess::exists(fileName + ".import") == false) 
		_import(fileName);
	return fileName;	
}

Después de ejecutarse, aún falló, y luego siguió y descubrió que era

importer->import(destFileName, base_path, params, &import_variants, &gen_files, &meta)

fallar. La depuración de un solo paso encontró que los importadores de ResourceFormatImporter están vacíos. Encontrar su función add_importer desde el código fuente agregará un ResourceImporter.En el constructor EditorNode::EditorNode(), se agregan varios ResourceImporters.

	{
		// Register importers at the beginning, so dialogs are created with the right extensions.
		Ref<ResourceImporterTexture> import_texture;
		import_texture.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_texture);

		Ref<ResourceImporterLayeredTexture> import_cubemap;
		import_cubemap.instantiate();
		import_cubemap->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP);
		ResourceFormatImporter::get_singleton()->add_importer(import_cubemap);

		Ref<ResourceImporterLayeredTexture> import_array;
		import_array.instantiate();
		import_array->set_mode(ResourceImporterLayeredTexture::MODE_2D_ARRAY);
		ResourceFormatImporter::get_singleton()->add_importer(import_array);

		Ref<ResourceImporterLayeredTexture> import_cubemap_array;
		import_cubemap_array.instantiate();
		import_cubemap_array->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP_ARRAY);
		ResourceFormatImporter::get_singleton()->add_importer(import_cubemap_array);

		Ref<ResourceImporterLayeredTexture> import_3d;
		import_3d.instantiate();
		import_3d->set_mode(ResourceImporterLayeredTexture::MODE_3D);
		ResourceFormatImporter::get_singleton()->add_importer(import_3d);

		Ref<ResourceImporterImage> import_image;
		import_image.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_image);

		Ref<ResourceImporterTextureAtlas> import_texture_atlas;
		import_texture_atlas.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_texture_atlas);

		Ref<ResourceImporterDynamicFont> import_font_data_dynamic;
		import_font_data_dynamic.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_font_data_dynamic);

		Ref<ResourceImporterBMFont> import_font_data_bmfont;
		import_font_data_bmfont.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_font_data_bmfont);

		Ref<ResourceImporterImageFont> import_font_data_image;
		import_font_data_image.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_font_data_image);

		Ref<ResourceImporterCSVTranslation> import_csv_translation;
		import_csv_translation.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_csv_translation);

		Ref<ResourceImporterWAV> import_wav;
		import_wav.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_wav);

		Ref<ResourceImporterOBJ> import_obj;
		import_obj.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_obj);

		Ref<ResourceImporterShaderFile> import_shader_file;
		import_shader_file.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_shader_file);

		Ref<ResourceImporterScene> import_scene;
		import_scene.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_scene);

		Ref<ResourceImporterScene> import_animation;
		import_animation = Ref<ResourceImporterScene>(memnew(ResourceImporterScene(true)));
		ResourceFormatImporter::get_singleton()->add_importer(import_animation);

		{
			Ref<EditorSceneFormatImporterCollada> import_collada;
			import_collada.instantiate();
			ResourceImporterScene::add_importer(import_collada);

			Ref<EditorOBJImporter> import_obj2;
			import_obj2.instantiate();
			ResourceImporterScene::add_importer(import_obj2);

			Ref<EditorSceneFormatImporterESCN> import_escn;
			import_escn.instantiate();
			ResourceImporterScene::add_importer(import_escn);
		}

		Ref<ResourceImporterBitMap> import_bitmap;
		import_bitmap.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_bitmap);
	}

EditorNode se creará automáticamente en modo editor, pero para el programa en ejecución final, no hay ProjectManager ni EditorNode, por lo que si desea importarlo, debe crearlo usted mismo.

Desde el proceso de importación de imágenes, podemos ver que el libro electrónico solo necesita importar imágenes png, que están en formato de textura, por lo que solo se puede importar un tipo.

		Ref<ResourceImporterTexture> import_texture;
		import_texture.instantiate();
		ResourceFormatImporter::get_singleton()->add_importer(import_texture);

Ejecutar, la imagen se muestra con éxito.

El siguiente paso del trabajo.

En este punto, se completa el trabajo central y hay dos tareas principales para el siguiente paso:

Una es la importación de directorios, es decir, puede importar todos los archivos en el directorio donde se encuentran las imágenes del libro electrónico de destino al mismo tiempo.

La segunda es la importación dinámica forzada. La importación no obligatoria significa que, siempre que haya un archivo .import correspondiente, no es necesario volver a importarlo. La importación obligatoria significa que no importa si el archivo .import existe o no, es importado. Esto se utiliza para el procesamiento dinámico. Por ejemplo, después de buscar palabras clave en el archivo, las palabras clave deben resaltarse, lo que da como resultado imágenes diferentes. Estas imágenes se pueden colocar en el directorio dinámico y se pueden forzar a importar.

Supongo que te gusta

Origin blog.csdn.net/drgraph/article/details/132003507
Recomendado
Clasificación