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());