Răsfoiți Sursa

Merge pull request #87063 from aaronfranke/gltf-misc-before-audio

Misc changes to the GLTF module before audio PR
Rémi Verschelde 1 an în urmă
părinte
comite
fb45210e2c

+ 5 - 0
modules/gltf/doc_classes/GLTFAccessor.xml

@@ -1,14 +1,19 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <class name="GLTFAccessor" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
 	<brief_description>
+		Represents a GLTF accessor.
 	</brief_description>
 	<description>
+		GLTFAccessor is a data structure representing GLTF a [code]accessor[/code] that would be found in the [code]"accessors"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer. An accessor is a typed interpretation of the data in a buffer view.
+		Most custom data stored in GLTF does not need accessors, only buffer views (see [GLTFBufferView]). Accessors are for more advanced use cases such as interleaved mesh data encoded for the GPU.
 	</description>
 	<tutorials>
+		<link title="Buffers, BufferViews, and Accessors in Khronos glTF specification">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md</link>
 		<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
 	</tutorials>
 	<members>
 		<member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="-1">
+			The index of the buffer view this accessor is referencing. If [code]-1[/code], this accessor is not referencing any buffer view.
 		</member>
 		<member name="byte_offset" type="int" setter="set_byte_offset" getter="get_byte_offset" default="0">
 		</member>

+ 18 - 0
modules/gltf/doc_classes/GLTFBufferView.xml

@@ -1,22 +1,40 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <class name="GLTFBufferView" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
 	<brief_description>
+		Represents a GLTF buffer view.
 	</brief_description>
 	<description>
+		GLTFBufferView is a data structure representing GLTF a [code]bufferView[/code] that would be found in the [code]"bufferViews"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer that can be used to identify and extract data from the buffer.
+		Most custom uses of buffers only need to use the [member buffer], [member byte_length], and [member byte_offset]. The [member byte_stride] and [member indices] properties are for more advanced use cases such as interleaved mesh data encoded for the GPU.
 	</description>
 	<tutorials>
+		<link title="Buffers, BufferViews, and Accessors in Khronos glTF specification">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md</link>
 		<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
 	</tutorials>
+	<methods>
+		<method name="load_buffer_view_data" qualifiers="const">
+			<return type="PackedByteArray" />
+			<param index="0" name="state" type="GLTFState" />
+			<description>
+				Loads the buffer view data from the buffer referenced by this buffer view in the given [GLTFState]. Interleaved data with a byte stride is not yet supported by this method. The data is returned as a [PackedByteArray].
+			</description>
+		</method>
+	</methods>
 	<members>
 		<member name="buffer" type="int" setter="set_buffer" getter="get_buffer" default="-1">
+			The index of the buffer this buffer view is referencing. If [code]-1[/code], this buffer view is not referencing any buffer.
 		</member>
 		<member name="byte_length" type="int" setter="set_byte_length" getter="get_byte_length" default="0">
+			The length, in bytes, of this buffer view. If [code]0[/code], this buffer view is empty.
 		</member>
 		<member name="byte_offset" type="int" setter="set_byte_offset" getter="get_byte_offset" default="0">
+			The offset, in bytes, from the start of the buffer to the start of this buffer view.
 		</member>
 		<member name="byte_stride" type="int" setter="set_byte_stride" getter="get_byte_stride" default="-1">
+			The stride, in bytes, between interleaved data. If [code]-1[/code], this buffer view is not interleaved.
 		</member>
 		<member name="indices" type="bool" setter="set_indices" getter="get_indices" default="false">
+			True if the GLTFBufferView's OpenGL GPU buffer type is an [code]ELEMENT_ARRAY_BUFFER[/code] used for vertex indices (integer constant [code]34963[/code]). False if the buffer type is [code]ARRAY_BUFFER[/code] used for vertex attributes (integer constant [code]34962[/code]) or when any other value. See [url=https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md]Buffers, BufferViews, and Accessors[/url] for possible values. This property is set but never used, setting this property will do nothing.
 		</member>
 	</members>
 </class>

+ 2 - 2
modules/gltf/doc_classes/GLTFDocumentExtension.xml

@@ -30,7 +30,7 @@
 			<param index="3" name="node" type="Node" />
 			<description>
 				Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _export_post]. If this [GLTFDocumentExtension] is used for exporting images, this runs after [method _serialize_texture_json].
-				This method can be used to modify the final JSON of each node.
+				This method can be used to modify the final JSON of each node. Data should be primarily stored in [param gltf_node] prior to serializing the JSON, but the original Godot [param node] is also provided if available. The node may be null if not available, such as when exporting GLTF data not generated from a Godot scene.
 			</description>
 		</method>
 		<method name="_export_post" qualifiers="virtual">
@@ -114,7 +114,7 @@
 			<param index="0" name="state" type="GLTFState" />
 			<description>
 				Part of the import process. This method is run after [method _parse_node_extensions] and before [method _generate_scene_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.
+				This method can be used to modify any of the data imported so far after parsing, before generating the nodes and then running the final per-node import step.
 			</description>
 		</method>
 		<method name="_import_preflight" qualifiers="virtual">

+ 8 - 0
modules/gltf/doc_classes/GLTFState.xml

@@ -20,6 +20,14 @@
 				Appends an extension to the list of extensions used by this GLTF file during serialization. If [param required] is true, the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically.
 			</description>
 		</method>
+		<method name="append_data_to_buffers">
+			<return type="int" />
+			<param index="0" name="data" type="PackedByteArray" />
+			<param index="1" name="deduplication" type="bool" />
+			<description>
+				Appends the given byte array data to the buffers and creates a [GLTFBufferView] for it. The index of the destination [GLTFBufferView] is returned. If [param deduplication] is true, the buffers will first be searched for duplicate data, otherwise new bytes will always be appended.
+			</description>
+		</method>
 		<method name="get_accessors">
 			<return type="GLTFAccessor[]" />
 			<description>

+ 0 - 1
modules/gltf/extensions/gltf_document_extension.cpp

@@ -185,7 +185,6 @@ Error GLTFDocumentExtension::serialize_texture_json(Ref<GLTFState> p_state, Dict
 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);
 	Error err = OK;
 	GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err);
 	return err;

+ 13 - 7
modules/gltf/gltf_document.cpp

@@ -405,6 +405,7 @@ static Vector<real_t> _xform_to_array(const Transform3D p_transform) {
 
 Error GLTFDocument::_serialize_nodes(Ref<GLTFState> p_state) {
 	Array nodes;
+	const int scene_node_count = p_state->scene_nodes.size();
 	for (int i = 0; i < p_state->nodes.size(); i++) {
 		Dictionary node;
 		Ref<GLTFNode> gltf_node = p_state->nodes[i];
@@ -452,10 +453,13 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> p_state) {
 			node["children"] = children;
 		}
 
+		Node *scene_node = nullptr;
+		if (i < scene_node_count) {
+			scene_node = p_state->scene_nodes[i];
+		}
 		for (Ref<GLTFDocumentExtension> ext : document_extensions) {
 			ERR_CONTINUE(ext.is_null());
-			ERR_CONTINUE(!p_state->scene_nodes.find(i));
-			Error err = ext->export_node(p_state, gltf_node, node, p_state->scene_nodes[i]);
+			Error err = ext->export_node(p_state, gltf_node, node, scene_node);
 			ERR_CONTINUE(err != OK);
 		}
 
@@ -7471,11 +7475,13 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo
 		ERR_CONTINUE(!E.value);
 		for (Ref<GLTFDocumentExtension> ext : document_extensions) {
 			ERR_CONTINUE(ext.is_null());
-			ERR_CONTINUE(!p_state->json.has("nodes"));
-			Array nodes = p_state->json["nodes"];
-			ERR_CONTINUE(E.key >= nodes.size());
-			ERR_CONTINUE(E.key < 0);
-			Dictionary node_json = nodes[E.key];
+			Dictionary node_json;
+			if (p_state->json.has("nodes")) {
+				Array nodes = p_state->json["nodes"];
+				if (0 <= E.key && E.key < nodes.size()) {
+					node_json = nodes[E.key];
+				}
+			}
 			Ref<GLTFNode> gltf_node = p_state->nodes[E.key];
 			err = ext->import_node(p_state, gltf_node, node_json, E.value);
 			ERR_CONTINUE(err != OK);

+ 28 - 0
modules/gltf/gltf_state.cpp

@@ -34,6 +34,8 @@
 
 void GLTFState::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("add_used_extension", "extension_name", "required"), &GLTFState::add_used_extension);
+	ClassDB::bind_method(D_METHOD("append_data_to_buffers", "data", "deduplication"), &GLTFState::append_data_to_buffers);
+
 	ClassDB::bind_method(D_METHOD("get_json"), &GLTFState::get_json);
 	ClassDB::bind_method(D_METHOD("set_json", "json"), &GLTFState::set_json);
 	ClassDB::bind_method(D_METHOD("get_major_version"), &GLTFState::get_major_version);
@@ -399,3 +401,29 @@ Variant GLTFState::get_additional_data(const StringName &p_extension_name) {
 void GLTFState::set_additional_data(const StringName &p_extension_name, Variant p_additional_data) {
 	additional_data[p_extension_name] = p_additional_data;
 }
+
+GLTFBufferViewIndex GLTFState::append_data_to_buffers(const Vector<uint8_t> &p_data, const bool p_deduplication = false) {
+	if (p_deduplication) {
+		for (int i = 0; i < buffer_views.size(); i++) {
+			Ref<GLTFBufferView> buffer_view = buffer_views[i];
+			Vector<uint8_t> buffer_view_data = buffer_view->load_buffer_view_data(this);
+			if (buffer_view_data == p_data) {
+				return i;
+			}
+		}
+	}
+	// Append the given data to a buffer and create a buffer view for it.
+	if (unlikely(buffers.is_empty())) {
+		buffers.push_back(Vector<uint8_t>());
+	}
+	Vector<uint8_t> &destination_buffer = buffers.write[0];
+	Ref<GLTFBufferView> buffer_view;
+	buffer_view.instantiate();
+	buffer_view->set_buffer(0);
+	buffer_view->set_byte_offset(destination_buffer.size());
+	buffer_view->set_byte_length(p_data.size());
+	destination_buffer.append_array(p_data);
+	const int new_index = buffer_views.size();
+	buffer_views.push_back(buffer_view);
+	return new_index;
+}

+ 1 - 0
modules/gltf/gltf_state.h

@@ -105,6 +105,7 @@ protected:
 
 public:
 	void add_used_extension(const String &p_extension, bool p_required = false);
+	GLTFBufferViewIndex append_data_to_buffers(const Vector<uint8_t> &p_data, const bool p_deduplication);
 
 	enum GLTFHandleBinary {
 		HANDLE_BINARY_DISCARD_TEXTURES = 0,

+ 1 - 0
modules/gltf/register_types.cpp

@@ -118,6 +118,7 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) {
 		GDREGISTER_CLASS(GLTFTexture);
 		GDREGISTER_CLASS(GLTFTextureSampler);
 		// Register GLTFDocumentExtension classes with GLTFDocument.
+		// Ensure physics is first in this list so that physics nodes are created before other nodes.
 		GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionPhysics);
 		GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionTextureKTX);
 		GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionTextureWebP);

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

@@ -30,7 +30,11 @@
 
 #include "gltf_buffer_view.h"
 
+#include "../gltf_state.h"
+
 void GLTFBufferView::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("load_buffer_view_data", "state"), &GLTFBufferView::load_buffer_view_data);
+
 	ClassDB::bind_method(D_METHOD("get_buffer"), &GLTFBufferView::get_buffer);
 	ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &GLTFBufferView::set_buffer);
 	ClassDB::bind_method(D_METHOD("get_byte_offset"), &GLTFBufferView::get_byte_offset);
@@ -88,3 +92,12 @@ bool GLTFBufferView::get_indices() {
 void GLTFBufferView::set_indices(bool p_indices) {
 	indices = p_indices;
 }
+
+Vector<uint8_t> GLTFBufferView::load_buffer_view_data(const Ref<GLTFState> p_state) const {
+	ERR_FAIL_COND_V_MSG(byte_stride > 0, Vector<uint8_t>(), "Buffer views with byte stride are not yet supported by this method.");
+	const TypedArray<Vector<uint8_t>> &buffers = p_state->get_buffers();
+	ERR_FAIL_INDEX_V(buffer, buffers.size(), Vector<uint8_t>());
+	const PackedByteArray &buffer_data = buffers[buffer];
+	const int64_t byte_end = byte_offset + byte_length;
+	return buffer_data.slice(byte_offset, byte_end);
+}

+ 2 - 1
modules/gltf/structures/gltf_buffer_view.h

@@ -64,7 +64,8 @@ public:
 
 	bool get_indices();
 	void set_indices(bool p_indices);
-	// matrices need to be transformed to this
+
+	Vector<uint8_t> load_buffer_view_data(const Ref<GLTFState> p_state) const;
 };
 
 #endif // GLTF_BUFFER_VIEW_H