Browse Source

Merge pull request #66026 from aaronfranke/gltf-extension

Change the way GLTFDocumentExtension classes are registered
Rémi Verschelde 2 years ago
parent
commit
d5d83eefd3

+ 9 - 4
modules/gltf/doc_classes/GLTFDocument.xml

@@ -50,6 +50,15 @@
 			<description>
 			</description>
 		</method>
+		<method name="register_gltf_document_extension" qualifiers="static">
+			<return type="void" />
+			<param index="0" name="extension" type="GLTFDocumentExtension" />
+			<param index="1" name="first_priority" type="bool" default="false" />
+			<description>
+				Registers this GLTFDocumentExtension instance with GLTFDocument. If [param first_priority] is true, this extension will be ran first. Otherwise, it will be ran last.
+				[b]Note:[/b] Like GLTFDocument itself, all GLTFDocumentExtension classes must be stateless in order to function properly. If you need to store data, use the [code]set_additional_data[/code] and [code]get_additional_data[/code] methods in [GLTFState] or [GLTFNode].
+			</description>
+		</method>
 		<method name="write_to_filesystem">
 			<return type="int" enum="Error" />
 			<param index="0" name="state" type="GLTFState" />
@@ -58,8 +67,4 @@
 			</description>
 		</method>
 	</methods>
-	<members>
-		<member name="extensions" type="GLTFDocumentExtension[]" setter="set_extensions" getter="get_extensions" default="[]">
-		</member>
-	</members>
 </class>

+ 4 - 0
modules/gltf/doc_classes/GLTFDocumentExtension.xml

@@ -5,6 +5,8 @@
 	</brief_description>
 	<description>
 		Extends the functionality of the [GLTFDocument] class by allowing you to run arbitrary code at various stages of GLTF import or export.
+		To use, make a new class extending GLTFDocumentExtension, override any methods you need, make an instance of your class, and register it using [method GLTFDocument.register_gltf_document_extension].
+		[b]Note:[/b] Like GLTFDocument itself, all GLTFDocumentExtension classes must be stateless in order to function properly. If you need to store data, use the [code]set_additional_data[/code] and [code]get_additional_data[/code] methods in [GLTFState] or [GLTFNode].
 	</description>
 	<tutorials>
 	</tutorials>
@@ -61,7 +63,9 @@
 		<method name="_import_preflight" qualifiers="virtual">
 			<return type="int" />
 			<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.
 			</description>
 		</method>
 	</methods>

+ 0 - 3
modules/gltf/editor/editor_scene_importer_gltf.h

@@ -33,9 +33,6 @@
 
 #ifdef TOOLS_ENABLED
 
-#include "../gltf_document_extension.h"
-#include "../gltf_state.h"
-
 #include "editor/import/resource_importer_scene.h"
 
 class Animation;

+ 3 - 3
modules/gltf/gltf_document_extension.cpp → modules/gltf/extensions/gltf_document_extension.cpp

@@ -32,7 +32,7 @@
 
 void GLTFDocumentExtension::_bind_methods() {
 	GDVIRTUAL_BIND(_get_supported_extensions);
-	GDVIRTUAL_BIND(_import_preflight, "state");
+	GDVIRTUAL_BIND(_import_preflight, "state", "extensions");
 	GDVIRTUAL_BIND(_import_post_parse, "state");
 	GDVIRTUAL_BIND(_import_node, "state", "gltf_node", "json", "node");
 	GDVIRTUAL_BIND(_import_post, "state", "root");
@@ -55,10 +55,10 @@ Error GLTFDocumentExtension::import_post(Ref<GLTFState> p_state, Node *p_root) {
 	return Error(err);
 }
 
-Error GLTFDocumentExtension::import_preflight(Ref<GLTFState> p_state) {
+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, err);
+	GDVIRTUAL_CALL(_import_preflight, p_state, p_extensions, err);
 	return Error(err);
 }
 

+ 3 - 4
modules/gltf/gltf_document_extension.h → modules/gltf/extensions/gltf_document_extension.h

@@ -31,8 +31,7 @@
 #ifndef GLTF_DOCUMENT_EXTENSION_H
 #define GLTF_DOCUMENT_EXTENSION_H
 
-#include "gltf_state.h"
-#include "structures/gltf_node.h"
+#include "../gltf_state.h"
 
 class GLTFDocumentExtension : public Resource {
 	GDCLASS(GLTFDocumentExtension, Resource);
@@ -42,7 +41,7 @@ protected:
 
 public:
 	virtual Vector<String> get_supported_extensions();
-	virtual Error import_preflight(Ref<GLTFState> p_state);
+	virtual Error import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions);
 	virtual Error import_post_parse(Ref<GLTFState> p_state);
 	virtual Error export_post(Ref<GLTFState> p_state);
 	virtual Error import_post(Ref<GLTFState> p_state, Node *p_node);
@@ -50,7 +49,7 @@ public:
 	virtual Error import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_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);
-	GDVIRTUAL1R(int, _import_preflight, Ref<GLTFState>);
+	GDVIRTUAL2R(int, _import_preflight, Ref<GLTFState>, Vector<String>);
 	GDVIRTUAL1R(int, _import_post_parse, Ref<GLTFState>);
 	GDVIRTUAL4R(int, _import_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *);
 	GDVIRTUAL2R(int, _import_post, Ref<GLTFState>, Node *);

+ 1 - 1
modules/gltf/gltf_document_extension_convert_importer_mesh.cpp → modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp

@@ -30,7 +30,7 @@
 
 #include "gltf_document_extension_convert_importer_mesh.h"
 
-#include "gltf_state.h"
+#include "../gltf_state.h"
 
 #include "core/error/error_macros.h"
 #include "scene/3d/mesh_instance_3d.h"

+ 0 - 0
modules/gltf/gltf_document_extension_convert_importer_mesh.h → modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h


+ 31 - 44
modules/gltf/gltf_document.cpp

@@ -31,8 +31,6 @@
 #include "gltf_document.h"
 
 #include "extensions/gltf_spec_gloss.h"
-#include "gltf_document_extension.h"
-#include "gltf_document_extension_convert_importer_mesh.h"
 #include "gltf_state.h"
 
 #include "core/crypto/crypto_core.h"
@@ -215,8 +213,7 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) {
 		return Error::FAILED;
 	}
 
-	for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
-		Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+	for (Ref<GLTFDocumentExtension> ext : document_extensions) {
 		ERR_CONTINUE(ext.is_null());
 		err = ext->export_post(state);
 		ERR_FAIL_COND_V(err != OK, err);
@@ -454,8 +451,7 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) {
 			node["children"] = children;
 		}
 
-		for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
-			Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+		for (Ref<GLTFDocumentExtension> ext : document_extensions) {
 			ERR_CONTINUE(ext.is_null());
 			ERR_CONTINUE(!state->scene_nodes.find(i));
 			Error err = ext->export_node(state, gltf_node, node, state->scene_nodes[i]);
@@ -6586,11 +6582,13 @@ Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess>
 	state->major_version = version.get_slice(".", 0).to_int();
 	state->minor_version = version.get_slice(".", 1).to_int();
 
-	for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
-		Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+	document_extensions.clear();
+	for (Ref<GLTFDocumentExtension> ext : all_document_extensions) {
 		ERR_CONTINUE(ext.is_null());
-		err = ext->import_preflight(state);
-		ERR_FAIL_COND_V(err != OK, err);
+		err = ext->import_preflight(state, state->json["extensionsUsed"]);
+		if (err == OK) {
+			document_extensions.push_back(ext);
+		}
 	}
 
 	err = _parse_gltf_state(state, p_path, p_bake_fps);
@@ -6728,14 +6726,8 @@ void GLTFDocument::_bind_methods() {
 	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"),
-			&GLTFDocument::get_extensions);
-	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "extensions", PROPERTY_HINT_ARRAY_TYPE,
-						 vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "GLTFDocumentExtension"),
-						 PROPERTY_USAGE_DEFAULT),
-			"set_extensions", "get_extensions");
+	ClassDB::bind_static_method("GLTFDocument", D_METHOD("register_gltf_document_extension", "extension", "first_priority"),
+			&GLTFDocument::register_gltf_document_extension, DEFVAL(false));
 }
 
 void GLTFDocument::_build_parent_hierachy(Ref<GLTFState> state) {
@@ -6752,22 +6744,20 @@ void GLTFDocument::_build_parent_hierachy(Ref<GLTFState> state) {
 	}
 }
 
-void GLTFDocument::set_extensions(TypedArray<GLTFDocumentExtension> p_extensions) {
-	document_extensions = p_extensions;
-}
+Vector<Ref<GLTFDocumentExtension>> GLTFDocument::all_document_extensions;
 
-TypedArray<GLTFDocumentExtension> GLTFDocument::get_extensions() const {
-	return document_extensions;
+void GLTFDocument::register_gltf_document_extension(Ref<GLTFDocumentExtension> p_extension, bool p_first_priority) {
+	if (all_document_extensions.find(p_extension) == -1) {
+		if (p_first_priority) {
+			all_document_extensions.insert(0, p_extension);
+		} else {
+			all_document_extensions.push_back(p_extension);
+		}
+	}
 }
 
-GLTFDocument::GLTFDocument() {
-	bool is_editor = ::Engine::get_singleton()->is_editor_hint();
-	if (is_editor) {
-		return;
-	}
-	Ref<GLTFDocumentExtensionConvertImporterMesh> extension_editor;
-	extension_editor.instantiate();
-	document_extensions.push_back(extension_editor);
+void GLTFDocument::unregister_all_gltf_document_extensions() {
+	all_document_extensions.clear();
 }
 
 PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> state, Error *r_err) {
@@ -6852,8 +6842,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) {
 	}
 	for (KeyValue<GLTFNodeIndex, Node *> E : state->scene_nodes) {
 		ERR_CONTINUE(!E.value);
-		for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
-			Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+		for (Ref<GLTFDocumentExtension> ext : document_extensions) {
 			ERR_CONTINUE(ext.is_null());
 			ERR_CONTINUE(!state->json.has("nodes"));
 			Array nodes = state->json["nodes"];
@@ -6865,8 +6854,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) {
 			ERR_CONTINUE(err != OK);
 		}
 	}
-	for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
-		Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+	for (Ref<GLTFDocumentExtension> ext : document_extensions) {
 		ERR_CONTINUE(ext.is_null());
 		err = ext->import_post(state, root);
 		ERR_CONTINUE(err != OK);
@@ -6880,11 +6868,13 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> state, uint32
 	state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
 	state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
 
-	for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
-		Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+	document_extensions.clear();
+	for (Ref<GLTFDocumentExtension> ext : all_document_extensions) {
 		ERR_CONTINUE(ext.is_null());
 		Error err = ext->export_preflight(p_node);
-		ERR_FAIL_COND_V(err != OK, FAILED);
+		if (err == OK) {
+			document_extensions.push_back(ext);
+		}
 	}
 	_convert_scene_node(state, p_node, -1, -1);
 	if (!state->buffers.size()) {
@@ -6906,8 +6896,7 @@ Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_pa
 	state->base_path = p_base_path.get_base_dir();
 	err = _parse(state, state->base_path, file_access, p_bake_fps);
 	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];
+	for (Ref<GLTFDocumentExtension> ext : document_extensions) {
 		ERR_CONTINUE(ext.is_null());
 		err = ext->import_post_parse(state);
 		ERR_FAIL_COND_V(err != OK, err);
@@ -7030,8 +7019,7 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint
 	r_state->base_path = base_path;
 	err = _parse(r_state, base_path, f, p_bake_fps);
 	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];
+	for (Ref<GLTFDocumentExtension> ext : document_extensions) {
 		ERR_CONTINUE(ext.is_null());
 		err = ext->import_post_parse(r_state);
 		ERR_FAIL_COND_V(err != OK, err);
@@ -7053,8 +7041,7 @@ Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> state) {
 	supported_extensions.insert("KHR_lights_punctual");
 	supported_extensions.insert("KHR_materials_pbrSpecularGlossiness");
 	supported_extensions.insert("KHR_texture_transform");
-	for (int ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
-		Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+	for (Ref<GLTFDocumentExtension> ext : document_extensions) {
 		ERR_CONTINUE(ext.is_null());
 		Vector<String> ext_supported_extensions = ext->get_supported_extensions();
 		for (int i = 0; i < ext_supported_extensions.size(); ++i) {

+ 5 - 5
modules/gltf/gltf_document.h

@@ -31,7 +31,7 @@
 #ifndef GLTF_DOCUMENT_H
 #define GLTF_DOCUMENT_H
 
-#include "gltf_defines.h"
+#include "extensions/gltf_document_extension.h"
 #include "structures/gltf_animation.h"
 
 #include "scene/3d/bone_attachment_3d.h"
@@ -44,13 +44,13 @@
 
 class GLTFDocument : public Resource {
 	GDCLASS(GLTFDocument, Resource);
-	TypedArray<GLTFDocumentExtension> document_extensions;
+	static Vector<Ref<GLTFDocumentExtension>> all_document_extensions;
+	Vector<Ref<GLTFDocumentExtension>> document_extensions;
 
 private:
 	const float BAKE_FPS = 30.0f;
 
 public:
-	GLTFDocument();
 	const int32_t JOINT_GROUP_SIZE = 4;
 
 	enum {
@@ -76,8 +76,8 @@ protected:
 	static void _bind_methods();
 
 public:
-	void set_extensions(TypedArray<GLTFDocumentExtension> p_extensions);
-	TypedArray<GLTFDocumentExtension> get_extensions() const;
+	static void register_gltf_document_extension(Ref<GLTFDocumentExtension> p_extension, bool p_first_priority = false);
+	static void unregister_all_gltf_document_extensions();
 
 private:
 	void _build_parent_hierachy(Ref<GLTFState> state);

+ 12 - 2
modules/gltf/register_types.cpp

@@ -32,11 +32,10 @@
 
 #ifndef _3D_DISABLED
 
+#include "extensions/gltf_document_extension_convert_importer_mesh.h"
 #include "extensions/gltf_light.h"
 #include "extensions/gltf_spec_gloss.h"
 #include "gltf_document.h"
-#include "gltf_document_extension.h"
-#include "gltf_document_extension_convert_importer_mesh.h"
 #include "gltf_state.h"
 #include "structures/gltf_accessor.h"
 #include "structures/gltf_animation.h"
@@ -109,6 +108,11 @@ static void _editor_init() {
 }
 #endif // TOOLS_ENABLED
 
+#define GLTF_REGISTER_DOCUMENT_EXTENSION(m_doc_ext_class) \
+	Ref<m_doc_ext_class> extension_##m_doc_ext_class;     \
+	extension_##m_doc_ext_class.instantiate();            \
+	GLTFDocument::register_gltf_document_extension(extension_##m_doc_ext_class);
+
 void initialize_gltf_module(ModuleInitializationLevel p_level) {
 	if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
 		// glTF API available at runtime.
@@ -128,6 +132,11 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) {
 		GDREGISTER_CLASS(GLTFState);
 		GDREGISTER_CLASS(GLTFTexture);
 		GDREGISTER_CLASS(GLTFTextureSampler);
+		// Register GLTFDocumentExtension classes with GLTFDocument.
+		bool is_editor = ::Engine::get_singleton()->is_editor_hint();
+		if (!is_editor) {
+			GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionConvertImporterMesh);
+		}
 	}
 
 #ifdef TOOLS_ENABLED
@@ -161,6 +170,7 @@ void uninitialize_gltf_module(ModuleInitializationLevel p_level) {
 	if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
 		return;
 	}
+	GLTFDocument::unregister_all_gltf_document_extensions();
 }
 
 #endif // _3D_DISABLED

+ 0 - 2
modules/gltf/structures/gltf_buffer_view.cpp

@@ -30,8 +30,6 @@
 
 #include "gltf_buffer_view.h"
 
-#include "../gltf_document_extension.h"
-
 void GLTFBufferView::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_buffer"), &GLTFBufferView::get_buffer);
 	ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &GLTFBufferView::set_buffer);