Browse Source

Merge pull request #68981 from aaronfranke/gltf-methods

Add three new methods to GLTFDocumentExtension and document all of its methods
Rémi Verschelde 2 years ago
parent
commit
3f2ca44534

+ 45 - 1
modules/gltf/doc_classes/GLTFDocumentExtension.xml

@@ -11,6 +11,16 @@
 	<tutorials>
 	</tutorials>
 	<methods>
+		<method name="_convert_scene_node" qualifiers="virtual">
+			<return type="void" />
+			<param index="0" name="state" type="GLTFState" />
+			<param index="1" name="gltf_node" type="GLTFNode" />
+			<param index="2" name="scene_node" type="Node" />
+			<description>
+				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].
+			</description>
+		</method>
 		<method name="_export_node" qualifiers="virtual">
 			<return type="int" />
 			<param index="0" name="state" type="GLTFState" />
@@ -18,23 +28,40 @@
 			<param index="2" name="json" type="Dictionary" />
 			<param index="3" name="node" type="Node" />
 			<description>
+				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.
 			</description>
 		</method>
 		<method name="_export_post" qualifiers="virtual">
 			<return type="int" />
 			<param index="0" name="state" type="GLTFState" />
 			<description>
+				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.
 			</description>
 		</method>
 		<method name="_export_preflight" qualifiers="virtual">
 			<return type="int" />
 			<param index="0" name="root" type="Node" />
 			<description>
+				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.
+			</description>
+		</method>
+		<method name="_generate_scene_node" qualifiers="virtual">
+			<return type="Node3D" />
+			<param index="0" name="state" type="GLTFState" />
+			<param index="1" name="gltf_node" type="GLTFNode" />
+			<param index="2" name="scene_parent" type="Node" />
+			<description>
+				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.
 			</description>
 		</method>
 		<method name="_get_supported_extensions" qualifiers="virtual">
 			<return type="PackedStringArray" />
 			<description>
+				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.
 			</description>
 		</method>
@@ -45,6 +72,8 @@
 			<param index="2" name="json" type="Dictionary" />
 			<param index="3" name="node" type="Node" />
 			<description>
+				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.
 			</description>
 		</method>
 		<method name="_import_post" qualifiers="virtual">
@@ -52,12 +81,16 @@
 			<param index="0" name="state" type="GLTFState" />
 			<param index="1" name="root" type="Node" />
 			<description>
+				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.
 			</description>
 		</method>
 		<method name="_import_post_parse" qualifiers="virtual">
 			<return type="int" />
 			<param index="0" name="state" type="GLTFState" />
 			<description>
+				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.
 			</description>
 		</method>
 		<method name="_import_preflight" qualifiers="virtual">
@@ -65,7 +98,18 @@
 			<param index="0" name="state" type="GLTFState" />
 			<param index="1" name="extensions" type="PackedStringArray" />
 			<description>
-				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.
+			</description>
+		</method>
+		<method name="_parse_node_extensions" qualifiers="virtual">
+			<return type="int" />
+			<param index="0" name="state" type="GLTFState" />
+			<param index="1" name="gltf_node" type="GLTFNode" />
+			<param index="2" name="extensions" type="Dictionary" />
+			<description>
+				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].
 			</description>
 		</method>
 	</methods>

+ 48 - 16
modules/gltf/extensions/gltf_document_extension.cpp

@@ -31,50 +31,77 @@
 #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<GLTFState> p_state, Vector<String> 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<String> GLTFDocumentExtension::get_supported_extensions() {
 	Vector<String> ret;
 	GDVIRTUAL_CALL(_get_supported_extensions, ret);
 	return ret;
 }
 
-Error GLTFDocumentExtension::import_post(Ref<GLTFState> p_state, Node *p_root) {
-	ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER);
+Error GLTFDocumentExtension::parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> 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<GLTFState> p_state, Vector<String> p_extensions) {
+Node3D *GLTFDocumentExtension::generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> 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<GLTFState> p_state) {
 	ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
 	int err = OK;
-	GDVIRTUAL_CALL(_import_preflight, p_state, p_extensions, err);
+	GDVIRTUAL_CALL(_import_post_parse, p_state, err);
 	return Error(err);
 }
 
-Error GLTFDocumentExtension::import_post_parse(Ref<GLTFState> p_state) {
+Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> 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);
+	ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
 	int err = OK;
-	GDVIRTUAL_CALL(_import_post_parse, p_state, err);
+	GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err);
 	return Error(err);
 }
 
-Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) {
+Error GLTFDocumentExtension::import_post(Ref<GLTFState> 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(_export_post, p_state, err);
+	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;
@@ -82,20 +109,25 @@ Error GLTFDocumentExtension::export_preflight(Node *p_root) {
 	return Error(err);
 }
 
-Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) {
+void GLTFDocumentExtension::convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> 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<GLTFState> p_state, Ref<GLTFNode> 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);
 	ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
 	int err = OK;
-	GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err);
+	GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err);
 	return Error(err);
 }
 
-Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) {
+Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) {
 	ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
-	ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER);
-	ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
 	int err = OK;
-	GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err);
+	GDVIRTUAL_CALL(_export_post, p_state, err);
 	return Error(err);
 }

+ 15 - 4
modules/gltf/extensions/gltf_document_extension.h

@@ -40,20 +40,31 @@ protected:
 	static void _bind_methods();
 
 public:
-	virtual Vector<String> get_supported_extensions();
+	// Import process.
 	virtual Error import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions);
+	virtual Vector<String> get_supported_extensions();
+	virtual Error parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions);
+	virtual Node3D *generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent);
 	virtual Error import_post_parse(Ref<GLTFState> p_state);
-	virtual Error export_post(Ref<GLTFState> p_state);
+	virtual Error import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node);
 	virtual Error import_post(Ref<GLTFState> p_state, Node *p_node);
+	// Export process.
 	virtual Error export_preflight(Node *p_state);
-	virtual Error import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node);
+	virtual void convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node);
 	virtual Error export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node);
-	GDVIRTUAL0R(Vector<String>, _get_supported_extensions);
+	virtual Error export_post(Ref<GLTFState> p_state);
+
+	// Import process.
 	GDVIRTUAL2R(int, _import_preflight, Ref<GLTFState>, Vector<String>);
+	GDVIRTUAL0R(Vector<String>, _get_supported_extensions);
+	GDVIRTUAL3R(int, _parse_node_extensions, Ref<GLTFState>, Ref<GLTFNode>, Dictionary);
+	GDVIRTUAL3R(Node3D *, _generate_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *);
 	GDVIRTUAL1R(int, _import_post_parse, Ref<GLTFState>);
 	GDVIRTUAL4R(int, _import_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *);
 	GDVIRTUAL2R(int, _import_post, Ref<GLTFState>, Node *);
+	// Export process.
 	GDVIRTUAL1R(int, _export_preflight, Node *);
+	GDVIRTUAL3(_convert_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *);
 	GDVIRTUAL4R(int, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *);
 	GDVIRTUAL1R(int, _export_post, Ref<GLTFState>);
 };

+ 53 - 20
modules/gltf/gltf_document.cpp

@@ -625,6 +625,11 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> state) {
 					node->light = light;
 				}
 			}
+			for (Ref<GLTFDocumentExtension> 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<GLTFState> state, Node *p_current, co
 		AnimationPlayer *animation_player = Object::cast_to<AnimationPlayer>(p_current);
 		_convert_animation_player_to_gltf(animation_player, state, p_gltf_parent, p_gltf_root, gltf_node, p_current);
 	}
+	for (Ref<GLTFDocumentExtension> 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<GLTFState> 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<GLTFDocumentExtension> 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<GLTFState> 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<GLTFDocumentExtension> 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());