diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index adc32df8dc0..87d3d9bcb05 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -11,6 +11,16 @@ + + + + + + + Part of the export process. This method is run after [method _export_preflight] and before [method _export_node]. + Runs when converting the data from a Godot scene node. This method can be used to process the Godot scene node data into a format that can be used by [method _export_node]. + + @@ -18,23 +28,40 @@ + Part of the export process. This method is run after [method _convert_scene_node] and before [method _export_post]. + This method can be used to modify the final JSON of each node. + Part of the export process. This method is run last, after all other parts of the export process. + This method can be used to modify the final JSON of the generated GLTF file. + Part of the export process. This method is run first, before all other parts of the export process. + The return value is used to determine if this GLTFDocumentExtension class should be used for exporting a given GLTF file. If [constant OK], the export will use this GLTFDocumentExtension class. If not overridden, [constant OK] is returned. + + + + + + + + + Part of the import process. This method is run after [method _parse_node_extensions] and before [method _import_post_parse]. + 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. + Part of the import process. This method is run after [method _import_preflight] and before [method _parse_node_extensions]. Returns an array of the GLTF extensions supported by this GLTFDocumentExtension class. This is used to validate if a GLTF file with required extensions can be loaded. @@ -45,6 +72,8 @@ + Part of the import process. This method is run after [method _import_post_parse] and before [method _import_post]. + This method can be used to make modifications to each of the generated Godot scene nodes. @@ -52,12 +81,16 @@ + Part of the import process. This method is run last, after all other parts of the import process. + This method can be used to modify the final Godot scene generated by the import process. + Part of the import process. This method is run after [method _generate_scene_node] and before [method _import_node]. + This method can be used to modify any of the data imported so far, including any scene nodes, before running the final per-node import step. @@ -65,7 +98,18 @@ - This callback is run first. It is used to determine if this GLTFDocumentExtension class should be used for importing a given GLTF file. If [constant OK], the import will use this GLTFDocumentExtension class. + Part of the import process. This method is run first, before all other parts of the import process. + The return value is used to determine if this GLTFDocumentExtension class should be used for importing a given GLTF file. If [constant OK], the import will use this GLTFDocumentExtension class. If not overridden, [constant OK] is returned. + + + + + + + + + Part of the import process. This method is run after [method _get_supported_extensions] and before [method _generate_scene_node]. + Runs when parsing the node extensions of a GLTFNode. This method can be used to process the extension JSON data into a format that can be used by [method _generate_scene_node]. diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp index 56519f70d38..f997fe8f66c 100644 --- a/modules/gltf/extensions/gltf_document_extension.cpp +++ b/modules/gltf/extensions/gltf_document_extension.cpp @@ -31,35 +31,50 @@ #include "gltf_document_extension.h" void GLTFDocumentExtension::_bind_methods() { - GDVIRTUAL_BIND(_get_supported_extensions); + // Import process. GDVIRTUAL_BIND(_import_preflight, "state", "extensions"); + GDVIRTUAL_BIND(_get_supported_extensions); + GDVIRTUAL_BIND(_parse_node_extensions, "state", "gltf_node", "extensions"); + GDVIRTUAL_BIND(_generate_scene_node, "state", "gltf_node", "scene_parent"); GDVIRTUAL_BIND(_import_post_parse, "state"); GDVIRTUAL_BIND(_import_node, "state", "gltf_node", "json", "node"); GDVIRTUAL_BIND(_import_post, "state", "root"); + // Export process. GDVIRTUAL_BIND(_export_preflight, "root"); + GDVIRTUAL_BIND(_convert_scene_node, "state", "gltf_node", "scene_node"); GDVIRTUAL_BIND(_export_node, "state", "gltf_node", "json", "node"); GDVIRTUAL_BIND(_export_post, "state"); } +// Import process. +Error GLTFDocumentExtension::import_preflight(Ref p_state, Vector p_extensions) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + GDVIRTUAL_CALL(_import_preflight, p_state, p_extensions, err); + return Error(err); +} + Vector GLTFDocumentExtension::get_supported_extensions() { Vector ret; GDVIRTUAL_CALL(_get_supported_extensions, ret); return ret; } -Error GLTFDocumentExtension::import_post(Ref p_state, Node *p_root) { - ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); +Error GLTFDocumentExtension::parse_node_extensions(Ref p_state, Ref p_gltf_node, Dictionary &p_extensions) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); int err = OK; - GDVIRTUAL_CALL(_import_post, p_state, p_root, err); + GDVIRTUAL_CALL(_parse_node_extensions, p_state, p_gltf_node, p_extensions, err); return Error(err); } -Error GLTFDocumentExtension::import_preflight(Ref p_state, Vector p_extensions) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - int err = OK; - GDVIRTUAL_CALL(_import_preflight, p_state, p_extensions, err); - return Error(err); +Node3D *GLTFDocumentExtension::generate_scene_node(Ref p_state, Ref p_gltf_node, Node *p_scene_parent) { + ERR_FAIL_NULL_V(p_state, nullptr); + ERR_FAIL_NULL_V(p_gltf_node, nullptr); + ERR_FAIL_NULL_V(p_scene_parent, nullptr); + Node3D *ret_node = nullptr; + GDVIRTUAL_CALL(_generate_scene_node, p_state, p_gltf_node, p_scene_parent, ret_node); + return ret_node; } Error GLTFDocumentExtension::import_post_parse(Ref p_state) { @@ -69,19 +84,6 @@ Error GLTFDocumentExtension::import_post_parse(Ref p_state) { return Error(err); } -Error GLTFDocumentExtension::export_post(Ref p_state) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - int err = OK; - GDVIRTUAL_CALL(_export_post, p_state, err); - return Error(err); -} -Error GLTFDocumentExtension::export_preflight(Node *p_root) { - ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); - int err = OK; - GDVIRTUAL_CALL(_export_preflight, p_root, err); - return Error(err); -} - Error GLTFDocumentExtension::import_node(Ref p_state, Ref p_gltf_node, Dictionary &r_dict, Node *p_node) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); @@ -91,6 +93,29 @@ Error GLTFDocumentExtension::import_node(Ref p_state, Ref p return Error(err); } +Error GLTFDocumentExtension::import_post(Ref p_state, Node *p_root) { + ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + GDVIRTUAL_CALL(_import_post, p_state, p_root, err); + return Error(err); +} + +// Export process. +Error GLTFDocumentExtension::export_preflight(Node *p_root) { + ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); + int err = OK; + GDVIRTUAL_CALL(_export_preflight, p_root, err); + return Error(err); +} + +void GLTFDocumentExtension::convert_scene_node(Ref p_state, Ref p_gltf_node, Node *p_scene_node) { + ERR_FAIL_NULL(p_state); + ERR_FAIL_NULL(p_gltf_node); + ERR_FAIL_NULL(p_scene_node); + GDVIRTUAL_CALL(_convert_scene_node, p_state, p_gltf_node, p_scene_node); +} + Error GLTFDocumentExtension::export_node(Ref p_state, Ref p_gltf_node, Dictionary &r_dict, Node *p_node) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); @@ -99,3 +124,10 @@ Error GLTFDocumentExtension::export_node(Ref p_state, Ref p GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err); return Error(err); } + +Error GLTFDocumentExtension::export_post(Ref p_state) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + GDVIRTUAL_CALL(_export_post, p_state, err); + return Error(err); +} diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h index 43e29ea0ab3..7cc9ca592fc 100644 --- a/modules/gltf/extensions/gltf_document_extension.h +++ b/modules/gltf/extensions/gltf_document_extension.h @@ -40,20 +40,31 @@ protected: static void _bind_methods(); public: - virtual Vector get_supported_extensions(); + // Import process. virtual Error import_preflight(Ref p_state, Vector p_extensions); + virtual Vector get_supported_extensions(); + virtual Error parse_node_extensions(Ref p_state, Ref p_gltf_node, Dictionary &p_extensions); + virtual Node3D *generate_scene_node(Ref p_state, Ref p_gltf_node, Node *p_scene_parent); virtual Error import_post_parse(Ref p_state); - virtual Error export_post(Ref p_state); - virtual Error import_post(Ref p_state, Node *p_node); - virtual Error export_preflight(Node *p_state); virtual Error import_node(Ref p_state, Ref p_gltf_node, Dictionary &r_json, Node *p_node); + virtual Error import_post(Ref p_state, Node *p_node); + // Export process. + virtual Error export_preflight(Node *p_state); + virtual void convert_scene_node(Ref p_state, Ref p_gltf_node, Node *p_scene_node); virtual Error export_node(Ref p_state, Ref p_gltf_node, Dictionary &r_json, Node *p_node); - GDVIRTUAL0R(Vector, _get_supported_extensions); + virtual Error export_post(Ref p_state); + + // Import process. GDVIRTUAL2R(int, _import_preflight, Ref, Vector); + GDVIRTUAL0R(Vector, _get_supported_extensions); + GDVIRTUAL3R(int, _parse_node_extensions, Ref, Ref, Dictionary); + GDVIRTUAL3R(Node3D *, _generate_scene_node, Ref, Ref, Node *); GDVIRTUAL1R(int, _import_post_parse, Ref); GDVIRTUAL4R(int, _import_node, Ref, Ref, Dictionary, Node *); GDVIRTUAL2R(int, _import_post, Ref, Node *); + // Export process. GDVIRTUAL1R(int, _export_preflight, Node *); + GDVIRTUAL3(_convert_scene_node, Ref, Ref, Node *); GDVIRTUAL4R(int, _export_node, Ref, Ref, Dictionary, Node *); GDVIRTUAL1R(int, _export_post, Ref); }; diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 49797bb8fac..faa8ed267ac 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -625,6 +625,11 @@ Error GLTFDocument::_parse_nodes(Ref state) { node->light = light; } } + for (Ref ext : document_extensions) { + ERR_CONTINUE(ext.is_null()); + Error err = ext->parse_node_extensions(state, node, extensions); + ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing node extensions for node " + node->get_name() + " in file " + state->filename + ". Continuing."); + } } if (n.has("children")) { @@ -5266,6 +5271,10 @@ void GLTFDocument::_convert_scene_node(Ref state, Node *p_current, co AnimationPlayer *animation_player = Object::cast_to(p_current); _convert_animation_player_to_gltf(animation_player, state, p_gltf_parent, p_gltf_root, gltf_node, p_current); } + for (Ref ext : document_extensions) { + ERR_CONTINUE(ext.is_null()); + ext->convert_scene_node(state, gltf_node, p_current); + } GLTFNodeIndex current_node_i = state->nodes.size(); GLTFNodeIndex gltf_root = p_gltf_root; if (gltf_root == -1) { @@ -5589,21 +5598,32 @@ void GLTFDocument::_generate_scene_node(Ref state, Node *scene_parent // and attach it to the bone_attachment scene_parent = bone_attachment; } - if (gltf_node->mesh >= 0) { - current_node = _generate_mesh_instance(state, node_index); - } else if (gltf_node->camera >= 0) { - current_node = _generate_camera(state, node_index); - } else if (gltf_node->light >= 0) { - current_node = _generate_light(state, node_index); + // Check if any GLTFDocumentExtension classes want to generate a node for us. + for (Ref ext : document_extensions) { + ERR_CONTINUE(ext.is_null()); + current_node = ext->generate_scene_node(state, gltf_node, scene_parent); + if (current_node) { + break; + } } - - // We still have not managed to make a node. + // If none of our GLTFDocumentExtension classes generated us a node, we generate one. if (!current_node) { - current_node = _generate_spatial(state, node_index); + if (gltf_node->mesh >= 0) { + current_node = _generate_mesh_instance(state, node_index); + } else if (gltf_node->camera >= 0) { + current_node = _generate_camera(state, node_index); + } else if (gltf_node->light >= 0) { + current_node = _generate_light(state, node_index); + } else { + current_node = _generate_spatial(state, node_index); + } } + // Add the node we generated and set the owner to the scene root. scene_parent->add_child(current_node, true); if (current_node != scene_root) { - current_node->set_owner(scene_root); + Array args; + args.append(scene_root); + current_node->propagate_call(StringName("set_owner"), args); } current_node->set_transform(gltf_node->xform); current_node->set_name(gltf_node->get_name()); @@ -5669,19 +5689,32 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref state, Node *scen // and attach it to the bone_attachment scene_parent = bone_attachment; } - - // We still have not managed to make a node - if (gltf_node->mesh >= 0) { - current_node = _generate_mesh_instance(state, node_index); - } else if (gltf_node->camera >= 0) { - current_node = _generate_camera(state, node_index); - } else if (gltf_node->light >= 0) { - current_node = _generate_light(state, node_index); + // Check if any GLTFDocumentExtension classes want to generate a node for us. + for (Ref ext : document_extensions) { + ERR_CONTINUE(ext.is_null()); + current_node = ext->generate_scene_node(state, gltf_node, scene_parent); + if (current_node) { + break; + } } - + // If none of our GLTFDocumentExtension classes generated us a node, we generate one. + if (!current_node) { + if (gltf_node->mesh >= 0) { + current_node = _generate_mesh_instance(state, node_index); + } else if (gltf_node->camera >= 0) { + current_node = _generate_camera(state, node_index); + } else if (gltf_node->light >= 0) { + current_node = _generate_light(state, node_index); + } else { + current_node = _generate_spatial(state, node_index); + } + } + // Add the node we generated and set the owner to the scene root. scene_parent->add_child(current_node, true); if (current_node != scene_root) { - current_node->set_owner(scene_root); + Array args; + args.append(scene_root); + current_node->propagate_call(StringName("set_owner"), args); } // Do not set transform here. Transform is already applied to our bone. current_node->set_name(gltf_node->get_name());