diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index 927ffb6aaeb8..95e676357907 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -59,6 +59,12 @@ Runs when generating a Godot scene node from a GLTFNode. The returned node will be added to the scene tree. Multiple nodes can be generated in this step if they are added as a child of the returned node. + + + + Returns the file extension to use for saving image data into, for example, [code]".png"[/code]. If defined, when this extension is used to handle images, and the images are saved to a separate file, the image bytes will be copied to a file with this extension. If this is set, there should be a [ResourceImporter] class able to import the file. If not defined or empty, Godot will save the image into a PNG file. + + diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp index 2804a8b0a221..ce1a0176f5b5 100644 --- a/modules/gltf/extensions/gltf_document_extension.cpp +++ b/modules/gltf/extensions/gltf_document_extension.cpp @@ -36,6 +36,7 @@ void GLTFDocumentExtension::_bind_methods() { GDVIRTUAL_BIND(_get_supported_extensions); GDVIRTUAL_BIND(_parse_node_extensions, "state", "gltf_node", "extensions"); GDVIRTUAL_BIND(_parse_image_data, "state", "image_data", "mime_type", "ret_image"); + GDVIRTUAL_BIND(_get_image_file_extension); GDVIRTUAL_BIND(_parse_texture_json, "state", "texture_json", "ret_gltf_texture"); GDVIRTUAL_BIND(_generate_scene_node, "state", "gltf_node", "scene_parent"); GDVIRTUAL_BIND(_import_post_parse, "state"); @@ -78,6 +79,12 @@ Error GLTFDocumentExtension::parse_image_data(Ref p_state, const Pack return err; } +String GLTFDocumentExtension::get_image_file_extension() { + String ret; + GDVIRTUAL_CALL(_get_image_file_extension, ret); + return ret; +} + Error GLTFDocumentExtension::parse_texture_json(Ref p_state, const Dictionary &p_texture_json, Ref r_gltf_texture) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(r_gltf_texture, ERR_INVALID_PARAMETER); diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h index d922588a291a..14735704b710 100644 --- a/modules/gltf/extensions/gltf_document_extension.h +++ b/modules/gltf/extensions/gltf_document_extension.h @@ -45,6 +45,7 @@ public: virtual Vector get_supported_extensions(); virtual Error parse_node_extensions(Ref p_state, Ref p_gltf_node, Dictionary &p_extensions); virtual Error parse_image_data(Ref p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref r_image); + virtual String get_image_file_extension(); virtual Error parse_texture_json(Ref p_state, const Dictionary &p_texture_json, Ref r_gltf_texture); virtual Node3D *generate_scene_node(Ref p_state, Ref p_gltf_node, Node *p_scene_parent); virtual Error import_post_parse(Ref p_state); @@ -61,6 +62,7 @@ public: GDVIRTUAL0R(Vector, _get_supported_extensions); GDVIRTUAL3R(Error, _parse_node_extensions, Ref, Ref, Dictionary); GDVIRTUAL4R(Error, _parse_image_data, Ref, PackedByteArray, String, Ref); + GDVIRTUAL0R(String, _get_image_file_extension); GDVIRTUAL3R(Error, _parse_texture_json, Ref, Dictionary, Ref); GDVIRTUAL3R(Node3D *, _generate_scene_node, Ref, Ref, Node *); GDVIRTUAL1R(Error, _import_post_parse, Ref); diff --git a/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp b/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp index e1a13bb61602..73c869be3b57 100644 --- a/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp +++ b/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp @@ -51,6 +51,10 @@ Error GLTFDocumentExtensionTextureWebP::parse_image_data(Ref p_state, return OK; } +String GLTFDocumentExtensionTextureWebP::get_image_file_extension() { + return ".webp"; +} + Error GLTFDocumentExtensionTextureWebP::parse_texture_json(Ref p_state, const Dictionary &p_texture_json, Ref r_gltf_texture) { if (!p_texture_json.has("extensions")) { return OK; diff --git a/modules/gltf/extensions/gltf_document_extension_texture_webp.h b/modules/gltf/extensions/gltf_document_extension_texture_webp.h index 9abf09a41f07..d2654aae8cf9 100644 --- a/modules/gltf/extensions/gltf_document_extension_texture_webp.h +++ b/modules/gltf/extensions/gltf_document_extension_texture_webp.h @@ -41,6 +41,7 @@ public: Error import_preflight(Ref p_state, Vector p_extensions) override; Vector get_supported_extensions() override; Error parse_image_data(Ref p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref r_image) override; + String get_image_file_extension() override; Error parse_texture_json(Ref p_state, const Dictionary &p_texture_json, Ref r_gltf_texture) override; }; diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 5d3fd2f19c2a..493eb00991a8 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -3064,7 +3064,7 @@ Error GLTFDocument::_serialize_images(Ref p_state, const String &p_pa return OK; } -Ref GLTFDocument::_parse_image_bytes_into_image(Ref p_state, const Vector &p_bytes, const String &p_mime_type, int p_index) { +Ref GLTFDocument::_parse_image_bytes_into_image(Ref p_state, const Vector &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension) { Ref r_image; r_image.instantiate(); // Check if any GLTFDocumentExtensions want to import this data as an image. @@ -3073,6 +3073,7 @@ Ref GLTFDocument::_parse_image_bytes_into_image(Ref p_state, c Error err = ext->parse_image_data(p_state, p_bytes, p_mime_type, r_image); ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->filename + ". Continuing."); if (!r_image->is_empty()) { + r_file_extension = ext->get_image_file_extension(); return r_image; } } @@ -3080,8 +3081,10 @@ Ref GLTFDocument::_parse_image_bytes_into_image(Ref p_state, c // First we honor the mime types if they were defined. if (p_mime_type == "image/png") { // Load buffer as PNG. r_image->load_png_from_buffer(p_bytes); + r_file_extension = ".png"; } else if (p_mime_type == "image/jpeg") { // Loader buffer as JPEG. r_image->load_jpg_from_buffer(p_bytes); + r_file_extension = ".jpg"; } // If we didn't pass the above tests, we attempt loading as PNG and then JPEG directly. // This covers URIs with base64-encoded data with application/* type but @@ -3102,7 +3105,7 @@ Ref GLTFDocument::_parse_image_bytes_into_image(Ref p_state, c return r_image; } -void GLTFDocument::_parse_image_save_image(Ref p_state, const String &p_mime_type, int p_index, Ref p_image) { +void GLTFDocument::_parse_image_save_image(Ref p_state, const Vector &p_bytes, const String &p_file_extension, int p_index, Ref p_image) { GLTFState::GLTFHandleBinary handling = GLTFState::GLTFHandleBinary(p_state->handle_binary_image); if (p_image->is_empty() || handling == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) { p_state->images.push_back(Ref()); @@ -3119,11 +3122,11 @@ void GLTFDocument::_parse_image_save_image(Ref p_state, const String p_state->images.push_back(Ref()); p_state->source_images.push_back(Ref()); } else { - Error err = OK; bool must_import = true; Vector img_data = p_image->get_data(); Dictionary generator_parameters; - String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + p_image->get_name() + ".png"; + String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + p_image->get_name(); + file_path += p_file_extension.is_empty() ? ".png" : p_file_extension; if (FileAccess::exists(file_path + ".import")) { Ref config; config.instantiate(); @@ -3144,8 +3147,18 @@ void GLTFDocument::_parse_image_save_image(Ref p_state, const String } } if (must_import) { - err = p_image->save_png(file_path); - ERR_FAIL_COND(err != OK); + Error err = OK; + if (p_file_extension.is_empty()) { + // If a file extension was not specified, save the image data to a PNG file. + err = p_image->save_png(file_path); + ERR_FAIL_COND(err != OK); + } else { + // If a file extension was specified, save the original bytes to a file with that extension. + Ref file = FileAccess::open(file_path, FileAccess::WRITE, &err); + ERR_FAIL_COND(err != OK); + file->store_buffer(p_bytes); + file->close(); + } // ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed. HashMap custom_options; custom_options[SNAME("mipmaps/generate")] = true; @@ -3295,9 +3308,10 @@ Error GLTFDocument::_parse_images(Ref p_state, const String &p_base_p continue; } // Parse the image data from bytes into an Image resource and save if needed. - Ref img = _parse_image_bytes_into_image(p_state, data, mime_type, i); + String file_extension; + Ref img = _parse_image_bytes_into_image(p_state, data, mime_type, i, file_extension); img->set_name(image_name); - _parse_image_save_image(p_state, mime_type, i, img); + _parse_image_save_image(p_state, data, file_extension, i, img); } print_verbose("glTF: Total images: " + itos(p_state->images.size())); diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index c21cedf2b7ae..f8bd156feb4f 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -151,8 +151,8 @@ private: Error _serialize_texture_samplers(Ref p_state); Error _serialize_images(Ref p_state, const String &p_path); Error _serialize_lights(Ref p_state); - Ref _parse_image_bytes_into_image(Ref p_state, const Vector &p_bytes, const String &p_mime_type, int p_index); - void _parse_image_save_image(Ref p_state, const String &p_mime_type, int p_index, Ref p_image); + Ref _parse_image_bytes_into_image(Ref p_state, const Vector &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension); + void _parse_image_save_image(Ref p_state, const Vector &p_bytes, const String &p_file_extension, int p_index, Ref p_image); Error _parse_images(Ref p_state, const String &p_base_path); Error _parse_textures(Ref p_state); Error _parse_texture_samplers(Ref p_state);