diff options
author | K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> | 2021-10-19 09:06:23 -0700 |
---|---|---|
committer | K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> | 2022-01-04 06:06:03 -0800 |
commit | d8923d8c74d9219ee2a25573b688da102ae525be (patch) | |
tree | 539331ce577f5635f9b6a960a4327378f2477786 /modules | |
parent | 28174d531b7128f0281fc2b88da2f4962fd3513e (diff) |
Add import glb from buffer
Split functions from gltf document import and export into six functions.
Use base path to allow two code paths based on an empty base path or a full base path.
Add uri decode in _parse_buffers.
Diffstat (limited to 'modules')
-rw-r--r-- | modules/gltf/doc_classes/GLTFDocument.xml | 51 | ||||
-rw-r--r-- | modules/gltf/editor_scene_exporter_gltf_plugin.cpp | 11 | ||||
-rw-r--r-- | modules/gltf/editor_scene_importer_gltf.cpp | 10 | ||||
-rw-r--r-- | modules/gltf/gltf_document.cpp | 581 | ||||
-rw-r--r-- | modules/gltf/gltf_document.h | 32 |
5 files changed, 377 insertions, 308 deletions
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index 8d8e25e8b3..ed7c018cb9 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -3,30 +3,57 @@ <brief_description> </brief_description> <description> + Append a glTF2 3d format from a file, buffer or scene and then write to the filesystem, buffer or scene. </description> <tutorials> </tutorials> <methods> - <method name="import_scene"> - <return type="Node" /> + <method name="append_from_buffer"> + <return type="int" enum="Error" /> + <argument index="0" name="bytes" type="PackedByteArray" /> + <argument index="1" name="base_path" type="String" /> + <argument index="2" name="state" type="GLTFState" /> + <argument index="3" name="flags" type="int" default="0" /> + <argument index="4" name="bake_fps" type="int" default="30" /> + <description> + </description> + </method> + <method name="append_from_file"> + <return type="int" enum="Error" /> <argument index="0" name="path" type="String" /> - <argument index="1" name="flags" type="int" default="0" /> - <argument index="2" name="bake_fps" type="int" default="30" /> - <argument index="3" name="state" type="GLTFState" default="null" /> + <argument index="1" name="state" type="GLTFState" /> + <argument index="2" name="flags" type="int" default="0" /> + <argument index="3" name="bake_fps" type="int" default="30" /> <description> - Import a scene from glTF2 ".gltf" or ".glb" file. </description> </method> - <method name="save_scene"> + <method name="append_from_scene"> <return type="int" enum="Error" /> <argument index="0" name="node" type="Node" /> + <argument index="1" name="state" type="GLTFState" /> + <argument index="2" name="flags" type="int" default="0" /> + <argument index="3" name="bake_fps" type="int" default="30" /> + <description> + </description> + </method> + <method name="generate_buffer"> + <return type="PackedByteArray" /> + <argument index="0" name="state" type="GLTFState" /> + <description> + </description> + </method> + <method name="generate_scene"> + <return type="Node" /> + <argument index="0" name="state" type="GLTFState" /> + <argument index="1" name="bake_fps" type="int" default="30" /> + <description> + </description> + </method> + <method name="write_to_filesystem"> + <return type="int" enum="Error" /> + <argument index="0" name="state" type="GLTFState" /> <argument index="1" name="path" type="String" /> - <argument index="2" name="src_path" type="String" /> - <argument index="3" name="flags" type="int" default="0" /> - <argument index="4" name="bake_fps" type="float" default="30" /> - <argument index="5" name="state" type="GLTFState" default="null" /> <description> - Save a scene as a glTF2 ".glb" or ".gltf" file. </description> </method> </methods> diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp index 25fda7ef3b..38a3cd966f 100644 --- a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp +++ b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp @@ -36,6 +36,7 @@ #include "core/templates/vector.h" #include "editor/editor_file_system.h" #include "gltf_document.h" +#include "gltf_state.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/gui/check_box.h" #include "scene/main/node.h" @@ -75,7 +76,15 @@ void SceneExporterGLTFPlugin::_gltf2_dialog_action(String p_file) { List<String> deps; Ref<GLTFDocument> doc; doc.instantiate(); - Error err = doc->save_scene(root, p_file, p_file, 0, 30.0f, Ref<GLTFState>()); + Ref<GLTFState> state; + state.instantiate(); + int32_t flags = 0; + flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; + Error err = doc->append_from_scene(root, state, flags, 30.0f); + if (err != OK) { + ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err))); + } + err = doc->write_to_filesystem(state, p_file); if (err != OK) { ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err))); } diff --git a/modules/gltf/editor_scene_importer_gltf.cpp b/modules/gltf/editor_scene_importer_gltf.cpp index 1a172877a0..83881bd351 100644 --- a/modules/gltf/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor_scene_importer_gltf.cpp @@ -53,7 +53,15 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, Error *r_err) { Ref<GLTFDocument> doc; doc.instantiate(); - return doc->import_scene_gltf(p_path, p_flags, p_bake_fps, Ref<GLTFState>(), r_missing_deps, r_err); + Ref<GLTFState> state; + state.instantiate(); + Error err = doc->append_from_file(p_path, state, p_flags, p_bake_fps); + if (err != OK) { + *r_err = err; + return nullptr; + } + Node *root = doc->generate_scene(state, p_bake_fps); + return root; } Ref<Animation> EditorSceneFormatImporterGLTF::import_animation(const String &p_path, diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 1c1ee5cd10..2c2d4b4360 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -48,6 +48,7 @@ #include "core/error/error_macros.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" +#include "core/io/file_access_memory.h" #include "core/io/json.h" #include "core/math/disjoint_set.h" #include "core/math/vector2.h" @@ -62,6 +63,7 @@ #include "scene/3d/camera_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/multimesh_instance_3d.h" +#include "scene/3d/node_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/importer_mesh.h" #include "scene/resources/mesh.h" @@ -82,13 +84,7 @@ #include <cstdint> #include <limits> -Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &p_path) { - uint64_t begin_time = OS::get_singleton()->get_ticks_usec(); - - state->skeleton3d_to_gltf_skeleton.clear(); - state->skin_and_skeleton3d_to_gltf_skin.clear(); - - _convert_scene_node(state, p_root, -1, -1); +Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) { if (!state->buffers.size()) { state->buffers.push_back(Vector<uint8_t>()); } @@ -184,16 +180,6 @@ Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String & return Error::FAILED; } - /* STEP 17 SERIALIZE FILE */ - err = _serialize_file(state, p_path); - if (err != OK) { - return Error::FAILED; - } - uint64_t elapsed = OS::get_singleton()->get_ticks_usec() - begin_time; - float elapsed_sec = double(elapsed) / 1000000.0; - elapsed_sec = Math::snapped(elapsed_sec, 0.01f); - print_verbose("glTF: Export time elapsed seconds " + rtos(elapsed_sec).pad_decimals(2)); - return OK; } @@ -255,18 +241,14 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) { return OK; } -Error GLTFDocument::_parse_glb(const String &p_path, Ref<GLTFState> state) { - Error err; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); - if (!f) { - return err; - } - +Error GLTFDocument::_parse_glb(FileAccess *f, Ref<GLTFState> state) { + ERR_FAIL_NULL_V(f, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(f->get_position() != 0, ERR_FILE_CANT_READ); uint32_t magic = f->get_32(); ERR_FAIL_COND_V(magic != 0x46546C67, ERR_FILE_UNRECOGNIZED); //glTF f->get_32(); // version f->get_32(); // length - uint32_t chunk_length = f->get_32(); uint32_t chunk_type = f->get_32(); @@ -280,9 +262,9 @@ Error GLTFDocument::_parse_glb(const String &p_path, Ref<GLTFState> state) { text.parse_utf8((const char *)json_data.ptr(), json_data.size()); JSON json; - err = json.parse(text); + Error err = json.parse(text); if (err != OK) { - _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT); + _err_print_error("", "", json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT); return err; } @@ -753,6 +735,8 @@ Error GLTFDocument::_parse_buffers(Ref<GLTFState> state, const String &p_base_pa } buffer_data = _parse_base64_uri(uri); } else { // Relative path to an external image file. + ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER); + uri = uri.uri_decode(); uri = p_base_path.plus_file(uri).replace("\\", "/"); // Fix for Windows. buffer_data = FileAccess::get_file_as_array(uri); ERR_FAIL_COND_V_MSG(buffer.size() == 0, ERR_PARSE_ERROR, "glTF: Couldn't load binary file as an array: " + uri); @@ -2952,7 +2936,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path Ref<Image> image = state->images[i]->get_image(); ERR_CONTINUE(image.is_null()); - if (p_path.to_lower().ends_with("glb")) { + if (p_path.to_lower().ends_with("glb") || p_path.is_empty()) { GLTFBufferViewIndex bvi; Ref<GLTFBufferView> bv; @@ -2981,6 +2965,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path d["bufferView"] = bvi; d["mimeType"] = "image/png"; } else { + ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER); String name = state->images[i]->get_name(); if (name.is_empty()) { name = itos(i); @@ -2988,13 +2973,14 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path name = _gen_unique_name(state, name); name = name.pad_zeros(3) + ".png"; String texture_dir = "textures"; - String new_texture_dir = p_path.get_base_dir() + "/" + texture_dir; - DirAccessRef da = DirAccess::open(p_path.get_base_dir()); + String path = p_path.get_base_dir(); + String new_texture_dir = path + "/" + texture_dir; + DirAccessRef da = DirAccess::open(path); if (!da->dir_exists(new_texture_dir)) { da->make_dir(new_texture_dir); } image->save_png(new_texture_dir.plus_file(name)); - d["uri"] = texture_dir.plus_file(name); + d["uri"] = texture_dir.plus_file(name).uri_encode(); } images.push_back(d); } @@ -3010,6 +2996,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path } Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_path) { + ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER); if (!state->json.has("images")) { return OK; } @@ -3069,6 +3056,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat } } } else { // Relative path to an external image file. + ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER); uri = uri.uri_decode(); uri = p_base_path.plus_file(uri).replace("\\", "/"); // Fix for Windows. // ResourceLoader will rely on the file extension to use the relevant loader. @@ -5099,7 +5087,7 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> state, MeshInst return mesh_i; } -ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index) { +ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> state, const GLTFNodeIndex node_index) { Ref<GLTFNode> gltf_node = state->nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->mesh, state->meshes.size(), nullptr); @@ -5119,7 +5107,7 @@ ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> sta return mi; } -Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) { +Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index) { Ref<GLTFNode> gltf_node = state->nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->light, state->lights.size(), nullptr); @@ -5171,7 +5159,7 @@ Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent, return memnew(Node3D); } -Camera3D *GLTFDocument::_generate_camera(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) { +Camera3D *GLTFDocument::_generate_camera(Ref<GLTFState> state, const GLTFNodeIndex node_index) { Ref<GLTFNode> gltf_node = state->nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->camera, state->cameras.size(), nullptr); @@ -5249,7 +5237,7 @@ void GLTFDocument::_convert_spatial(Ref<GLTFState> state, Node3D *p_spatial, Ref p_node->position = xform.origin; } -Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) { +Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> state, const GLTFNodeIndex node_index) { Ref<GLTFNode> gltf_node = state->nodes[node_index]; Node3D *spatial = memnew(Node3D); @@ -5616,19 +5604,18 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent scene_parent = bone_attachment; } if (gltf_node->mesh >= 0) { - current_node = _generate_mesh_instance(state, scene_parent, node_index); + current_node = _generate_mesh_instance(state, node_index); } else if (gltf_node->camera >= 0) { - current_node = _generate_camera(state, scene_parent, node_index); + current_node = _generate_camera(state, node_index); } else if (gltf_node->light >= 0) { - current_node = _generate_light(state, scene_parent, node_index); + current_node = _generate_light(state, node_index); } // We still have not managed to make a node. if (!current_node) { - current_node = _generate_spatial(state, scene_parent, node_index); + current_node = _generate_spatial(state, node_index); } - - scene_parent->add_child(current_node, true); + scene_parent->add_child(current_node); if (current_node != scene_root) { current_node->set_owner(scene_root); } @@ -5699,11 +5686,11 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen // We still have not managed to make a node if (gltf_node->mesh >= 0) { - current_node = _generate_mesh_instance(state, scene_parent, node_index); + current_node = _generate_mesh_instance(state, node_index); } else if (gltf_node->camera >= 0) { - current_node = _generate_camera(state, scene_parent, node_index); + current_node = _generate_camera(state, node_index); } else if (gltf_node->light >= 0) { - current_node = _generate_light(state, scene_parent, node_index); + current_node = _generate_light(state, node_index); } scene_parent->add_child(current_node, true); @@ -6084,7 +6071,9 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) { continue; } MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(mi_element->get()); - ERR_CONTINUE(!mi); + if (!mi) { + continue; + } Transform3D mi_xform = mi->get_transform(); node->scale = mi_xform.basis.get_scale(); node->rotation = mi_xform.basis.get_rotation_quaternion(); @@ -6282,39 +6271,8 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state ERR_CONTINUE(err != OK); p_track.rotation_track.values.write[key_i] = rotation; } - } else if (path.find(":transform") != -1) { - p_track.position_track.times = times; - p_track.position_track.interpolation = gltf_interpolation; - p_track.rotation_track.times = times; - p_track.rotation_track.interpolation = gltf_interpolation; - p_track.scale_track.times = times; - p_track.scale_track.interpolation = gltf_interpolation; - - p_track.scale_track.values.resize(key_count); - p_track.scale_track.interpolation = gltf_interpolation; - p_track.position_track.values.resize(key_count); - p_track.position_track.interpolation = gltf_interpolation; - p_track.rotation_track.values.resize(key_count); - p_track.rotation_track.interpolation = gltf_interpolation; - for (int32_t key_i = 0; key_i < key_count; key_i++) { - Transform3D xform = p_animation->track_get_key_value(p_track_i, key_i); - p_track.position_track.values.write[key_i] = xform.get_origin(); - p_track.rotation_track.values.write[key_i] = xform.basis.get_rotation_quaternion(); - p_track.scale_track.values.write[key_i] = xform.basis.get_scale(); - } } else if (track_type == Animation::TYPE_VALUE) { - if (path.find("/rotation_quat") != -1) { - p_track.rotation_track.times = times; - p_track.rotation_track.interpolation = gltf_interpolation; - - p_track.rotation_track.values.resize(key_count); - p_track.rotation_track.interpolation = gltf_interpolation; - - for (int32_t key_i = 0; key_i < key_count; key_i++) { - Quaternion rotation_track = p_animation->track_get_key_value(p_track_i, key_i); - p_track.rotation_track.values.write[key_i] = rotation_track; - } - } else if (path.find(":position") != -1) { + if (path.find(":position") != -1) { p_track.position_track.times = times; p_track.position_track.interpolation = gltf_interpolation; @@ -6589,142 +6547,57 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, } } -Error GLTFDocument::parse(Ref<GLTFState> state, String p_path, bool p_read_binary) { +Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, FileAccess *f, int p_bake_fps) { Error err; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); if (!f) { - return err; + return FAILED; } + f->seek(0); uint32_t magic = f->get_32(); if (magic == 0x46546C67) { //binary file //text file - err = _parse_glb(p_path, state); - if (err) { - return FAILED; + f->seek(0); + err = _parse_glb(f, state); + if (err != OK) { + return err; } } else { - //text file - err = _parse_json(p_path, state); - if (err) { - return FAILED; + f->seek(0); + String text = f->get_as_utf8_string(); + JSON json; + err = json.parse(text); + if (err != OK) { + _err_print_error("", "", json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT); } + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + state->json = json.get_data(); } f->close(); - // get file's name, use for scene name if none - state->filename = p_path.get_file().get_slice(".", 0); - - ERR_FAIL_COND_V(!state->json.has("asset"), Error::FAILED); + if (!state->json.has("asset")) { + return ERR_PARSE_ERROR; + } Dictionary asset = state->json["asset"]; - ERR_FAIL_COND_V(!asset.has("version"), Error::FAILED); + if (!asset.has("version")) { + return ERR_PARSE_ERROR; + } String version = asset["version"]; state->major_version = version.get_slice(".", 0).to_int(); state->minor_version = version.get_slice(".", 1).to_int(); - /* STEP 0 PARSE SCENE */ - err = _parse_scenes(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 1 PARSE NODES */ - err = _parse_nodes(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 2 PARSE BUFFERS */ - err = _parse_buffers(state, p_path.get_base_dir()); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 3 PARSE BUFFER VIEWS */ - err = _parse_buffer_views(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 4 PARSE ACCESSORS */ - err = _parse_accessors(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 5 PARSE IMAGES */ - err = _parse_images(state, p_path.get_base_dir()); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 6 PARSE TEXTURES */ - err = _parse_textures(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 7 PARSE TEXTURES */ - err = _parse_materials(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 9 PARSE SKINS */ - err = _parse_skins(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 10 DETERMINE SKELETONS */ - err = _determine_skeletons(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 11 CREATE SKELETONS */ - err = _create_skeletons(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 12 CREATE SKINS */ - err = _create_skins(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 13 PARSE MESHES (we have enough info now) */ - err = _parse_meshes(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 14 PARSE LIGHTS */ - err = _parse_lights(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 15 PARSE CAMERAS */ - err = _parse_cameras(state); - if (err != OK) { - return Error::FAILED; - } - - /* STEP 16 PARSE ANIMATIONS */ - err = _parse_animations(state); - if (err != OK) { - return Error::FAILED; + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->import_preflight(this); + ERR_FAIL_COND_V(err != OK, FAILED); } - - /* STEP 17 ASSIGN SCENE NAMES */ - _assign_scene_names(state); - + err = _parse_gltf_state(state, p_path, p_bake_fps); + ERR_FAIL_COND_V(err != OK, err); return OK; } @@ -6843,89 +6716,20 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) { return err; } -Error GLTFDocument::save_scene(Node *p_node, const String &p_path, - const String &p_src_path, uint32_t p_flags, - float p_bake_fps, Ref<GLTFState> r_state) { - ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); - - Ref<GLTFDocument> gltf_document; - gltf_document.instantiate(); - for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { - Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; - ERR_CONTINUE(ext.is_null()); - Error err = ext->export_preflight(this, p_node); - ERR_FAIL_COND_V(err != OK, err); - } - - if (r_state == Ref<GLTFState>()) { - r_state.instantiate(); - } - Error err = gltf_document->serialize(r_state, p_node, p_path); - ERR_FAIL_COND_V(err != OK, err); - for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { - Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; - ERR_CONTINUE(ext.is_null()); - err = ext->export_post(this); - ERR_FAIL_COND_V(err != OK, err); - } - return OK; -} - -Node *GLTFDocument::import_scene_gltf(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state, List<String> *r_missing_deps, Error *r_err) { - // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire - if (r_state == Ref<GLTFState>()) { - r_state.instantiate(); - } - r_state->use_named_skin_binds = - p_flags & EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; - - Ref<GLTFDocument> gltf_document; - gltf_document.instantiate(); - for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { - Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; - ERR_CONTINUE(ext.is_null()); - Error err = ext->import_preflight(this); - if (r_err) { - *r_err = err; - } - ERR_FAIL_COND_V(err != OK, nullptr); - } - Error err = gltf_document->parse(r_state, p_path); - if (r_err) { - *r_err = err; - } - ERR_FAIL_COND_V(err != Error::OK, nullptr); - - Node3D *root = memnew(Node3D); - for (int32_t root_i = 0; root_i < r_state->root_nodes.size(); root_i++) { - gltf_document->_generate_scene_node(r_state, root, root, r_state->root_nodes[root_i]); - } - gltf_document->_process_mesh_instances(r_state, root); - if (r_state->animations.size()) { - AnimationPlayer *ap = memnew(AnimationPlayer); - root->add_child(ap, true); - ap->set_owner(root); - for (int i = 0; i < r_state->animations.size(); i++) { - gltf_document->_import_animation(r_state, ap, i, p_bake_fps); - } - } - for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { - Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; - ERR_CONTINUE(ext.is_null()); - err = ext->import_post(this, root); - if (r_err) { - *r_err = err; - } - ERR_FAIL_COND_V(err != OK, nullptr); - } - return root; -} - void GLTFDocument::_bind_methods() { - ClassDB::bind_method(D_METHOD("save_scene", "node", "path", "src_path", "flags", "bake_fps", "state"), - &GLTFDocument::save_scene, DEFVAL(0), DEFVAL(30), DEFVAL(Ref<GLTFState>())); - ClassDB::bind_method(D_METHOD("import_scene", "path", "flags", "bake_fps", "state"), - &GLTFDocument::import_scene, DEFVAL(0), DEFVAL(30), DEFVAL(Ref<GLTFState>())); + ClassDB::bind_method(D_METHOD("append_from_file", "path", "state", "flags", "bake_fps"), + &GLTFDocument::append_from_file, DEFVAL(0), DEFVAL(30)); + ClassDB::bind_method(D_METHOD("append_from_buffer", "bytes", "base_path", "state", "flags", "bake_fps"), + &GLTFDocument::append_from_buffer, DEFVAL(0), DEFVAL(30)); + ClassDB::bind_method(D_METHOD("append_from_scene", "node", "state", "flags", "bake_fps"), + &GLTFDocument::append_from_scene, DEFVAL(0), DEFVAL(30)); + ClassDB::bind_method(D_METHOD("generate_scene", "state", "bake_fps"), + &GLTFDocument::generate_scene, DEFVAL(30)); + ClassDB::bind_method(D_METHOD("generate_buffer", "state"), + &GLTFDocument::generate_buffer); + ClassDB::bind_method(D_METHOD("write_to_filesystem", "state", "path"), + &GLTFDocument::write_to_filesystem); + ClassDB::bind_method(D_METHOD("set_extensions", "extensions"), &GLTFDocument::set_extensions); ClassDB::bind_method(D_METHOD("get_extensions"), @@ -6950,16 +6754,6 @@ void GLTFDocument::_build_parent_hierachy(Ref<GLTFState> state) { } } -Node *GLTFDocument::import_scene(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state) { - Error err = FAILED; - List<String> deps; - Node *node = import_scene_gltf(p_path, p_flags, p_bake_fps, r_state, &deps, &err); - if (err != OK) { - return nullptr; - } - return node; -} - void GLTFDocument::set_extensions(TypedArray<GLTFDocumentExtension> p_extensions) { document_extensions = p_extensions; } @@ -6977,3 +6771,226 @@ GLTFDocument::GLTFDocument() { extension_editor.instantiate(); document_extensions.push_back(extension_editor); } + +PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> state, Error *r_err) { + Error err = _encode_buffer_glb(state, ""); + if (r_err) { + *r_err = err; + } + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + String json = Variant(state->json).to_json_string(); + + const uint32_t magic = 0x46546C67; // GLTF + const int32_t header_size = 12; + const int32_t chunk_header_size = 8; + + for (int32_t pad_i = 0; pad_i < (chunk_header_size + json.utf8().length()) % 4; pad_i++) { + json += " "; + } + CharString cs = json.utf8(); + const uint32_t text_chunk_length = cs.length(); + + const uint32_t text_chunk_type = 0x4E4F534A; //JSON + int32_t binary_data_length = 0; + if (state->buffers.size()) { + binary_data_length = state->buffers[0].size(); + } + const int32_t binary_chunk_length = binary_data_length; + const int32_t binary_chunk_type = 0x004E4942; //BIN + + Ref<StreamPeerBuffer> buffer; + buffer.instantiate(); + buffer->put_32(magic); + buffer->put_32(state->major_version); // version + buffer->put_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_data_length); // length + buffer->put_32(text_chunk_length); + buffer->put_32(text_chunk_type); + buffer->put_data((uint8_t *)&cs[0], cs.length()); + if (binary_chunk_length) { + buffer->put_32(binary_chunk_length); + buffer->put_32(binary_chunk_type); + buffer->put_data(state->buffers[0].ptr(), binary_data_length); + } + return buffer->get_data_array(); +} + +PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> state) { + ERR_FAIL_NULL_V(state, PackedByteArray()); + Error err = _serialize(state, ""); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + PackedByteArray bytes = _serialize_glb_buffer(state, &err); + return bytes; +} + +Error GLTFDocument::write_to_filesystem(Ref<GLTFState> state, const String &p_path) { + ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER); + + Error err = _serialize(state, p_path); + if (err != OK) { + return err; + } + + err = _serialize_file(state, p_path); + if (err != OK) { + return Error::FAILED; + } + return OK; +} + +Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) { + ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr); + GLTFNodeIndex gltf_root = state->root_nodes.write[0]; + Node *gltf_root_node = state->get_scene_node(gltf_root); + Node *root = gltf_root_node->get_parent(); + ERR_FAIL_NULL_V(root, nullptr); + _process_mesh_instances(state, root); + if (state->animations.size()) { + AnimationPlayer *ap = memnew(AnimationPlayer); + root->add_child(ap); + ap->set_owner(root); + for (int i = 0; i < state->animations.size(); i++) { + _import_animation(state, ap, i, p_bake_fps); + } + } + + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + Error err = ext->import_post(this, root); + ERR_FAIL_COND_V(err != OK, nullptr); + } + ERR_FAIL_NULL_V(root, nullptr); + return root; +} + +Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> state, uint32_t p_flags, int32_t p_bake_fps) { + ERR_FAIL_COND_V(state.is_null(), FAILED); + state->use_named_skin_binds = + p_flags & EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; + + _convert_scene_node(state, p_node, -1, -1); + if (!state->buffers.size()) { + state->buffers.push_back(Vector<uint8_t>()); + } + + /* STEP 1 CONVERT MESH INSTANCES */ + _convert_mesh_instances(state); + + /* STEP 2 CREATE SKINS */ + Error err = _serialize_skins(state); + return err; +} + +Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> state, uint32_t p_flags, int32_t p_bake_fps) { + ERR_FAIL_COND_V(state.is_null(), FAILED); + // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire + Error err = FAILED; + state->use_named_skin_binds = + p_flags & EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; + FileAccessMemory *file_access = memnew(FileAccessMemory); + file_access->open_custom(p_bytes.ptr(), p_bytes.size()); + err = _parse(state, p_base_path.get_base_dir(), file_access, p_bake_fps); + ERR_FAIL_COND_V(err != OK, FAILED); + return OK; +} + +Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> state, const String &p_search_path, float p_bake_fps) { + Error err; + /* STEP 0 PARSE SCENE */ + err = _parse_scenes(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 1 PARSE NODES */ + err = _parse_nodes(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 2 PARSE BUFFERS */ + err = _parse_buffers(state, p_search_path); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 3 PARSE BUFFER VIEWS */ + err = _parse_buffer_views(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 4 PARSE ACCESSORS */ + err = _parse_accessors(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 5 PARSE IMAGES */ + err = _parse_images(state, p_search_path); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 6 PARSE TEXTURES */ + err = _parse_textures(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 7 PARSE TEXTURES */ + err = _parse_materials(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 9 PARSE SKINS */ + err = _parse_skins(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 10 DETERMINE SKELETONS */ + err = _determine_skeletons(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 11 CREATE SKELETONS */ + err = _create_skeletons(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 12 CREATE SKINS */ + err = _create_skins(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 13 PARSE MESHES (we have enough info now) */ + err = _parse_meshes(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 14 PARSE LIGHTS */ + err = _parse_lights(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 15 PARSE CAMERAS */ + err = _parse_cameras(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 16 PARSE ANIMATIONS */ + err = _parse_animations(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* STEP 17 ASSIGN SCENE NAMES */ + _assign_scene_names(state); + + Node3D *root = memnew(Node3D); + for (int32_t root_i = 0; root_i < state->root_nodes.size(); root_i++) { + _generate_scene_node(state, root, root, state->root_nodes[root_i]); + } + return OK; +} + +Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags, int32_t p_bake_fps) { + // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire + if (r_state == Ref<GLTFState>()) { + r_state.instantiate(); + } + r_state->filename = p_path.get_file().get_basename(); + r_state->use_named_skin_binds = + p_flags & EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN); + ERR_FAIL_NULL_V(f, ERR_FILE_CANT_OPEN); + + err = _parse(r_state, p_path.get_base_dir(), f, p_bake_fps); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + return err; +} diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index f8d9521733..fddc2fbfdb 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -33,6 +33,7 @@ #include "gltf_animation.h" +#include "core/error/error_list.h" #include "core/variant/dictionary.h" #include "core/variant/variant.h" #include "gltf_document_extension_convert_importer_mesh.h" @@ -120,11 +121,6 @@ protected: static void _bind_methods(); public: - Node *import_scene(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state); - Node *import_scene_gltf(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state, List<String> *r_missing_deps, Error *r_err = nullptr); - Error save_scene(Node *p_node, const String &p_path, - const String &p_src_path, uint32_t p_flags, - float p_bake_fps, Ref<GLTFState> r_state); void set_extensions(TypedArray<GLTFDocumentExtension> p_extensions); TypedArray<GLTFDocumentExtension> get_extensions() const; @@ -200,7 +196,7 @@ private: Ref<Texture2D> _get_texture(Ref<GLTFState> state, const GLTFTextureIndex p_texture); Error _parse_json(const String &p_path, Ref<GLTFState> state); - Error _parse_glb(const String &p_path, Ref<GLTFState> state); + Error _parse_glb(FileAccess *f, Ref<GLTFState> state); void _compute_node_heights(Ref<GLTFState> state); Error _parse_buffers(Ref<GLTFState> state, const String &p_base_path); Error _parse_buffer_views(Ref<GLTFState> state); @@ -287,10 +283,10 @@ private: Skeleton3D *skeleton, const GLTFNodeIndex node_index, const GLTFNodeIndex bone_index); - ImporterMeshInstance3D *_generate_mesh_instance(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); - Camera3D *_generate_camera(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); - Node3D *_generate_light(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); - Node3D *_generate_spatial(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); + ImporterMeshInstance3D *_generate_mesh_instance(Ref<GLTFState> state, const GLTFNodeIndex node_index); + Camera3D *_generate_camera(Ref<GLTFState> state, const GLTFNodeIndex node_index); + Node3D *_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index); + Node3D *_generate_spatial(Ref<GLTFState> state, const GLTFNodeIndex node_index); void _assign_scene_names(Ref<GLTFState> state); template <class T> T _interpolate_track(const Vector<real_t> &p_times, const Vector<T> &p_values, @@ -361,6 +357,7 @@ private: GLTFNodeIndex p_node_i); Error _encode_buffer_bins(Ref<GLTFState> state, const String &p_path); Error _encode_buffer_glb(Ref<GLTFState> state, const String &p_path); + PackedByteArray _serialize_glb_buffer(Ref<GLTFState> state, Error *r_err); Dictionary _serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material); Dictionary _serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material); Error _serialize_version(Ref<GLTFState> state); @@ -384,6 +381,17 @@ private: static float get_max_component(const Color &p_color); public: + Error append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30); + Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30); + Error append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30); + +public: + Node *generate_scene(Ref<GLTFState> state, int32_t p_bake_fps = 30.0f); + PackedByteArray generate_buffer(Ref<GLTFState> state); + Error write_to_filesystem(Ref<GLTFState> state, const String &p_path); + +public: + Error _parse_gltf_state(Ref<GLTFState> state, const String &p_search_path, float p_bake_fps); void _process_mesh_instances(Ref<GLTFState> state, Node *scene_root); void _generate_scene_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, @@ -447,8 +455,8 @@ public: MeshInstance3D *p_mesh_instance); void _convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, String p_animation_track_name); - Error serialize(Ref<GLTFState> state, Node *p_root, const String &p_path); - Error parse(Ref<GLTFState> state, String p_paths, bool p_read_binary = false); + Error _serialize(Ref<GLTFState> state, const String &p_path); + Error _parse(Ref<GLTFState> state, String p_path, FileAccess *f, int p_bake_fps); }; #endif // GLTF_DOCUMENT_H |