Skip to content

Commit

Permalink
GLTF export improvements
Browse files Browse the repository at this point in the history
- GLBs produced by godot don't pass validation when there's no data in the buffer segment. The segment is dropped but the size of the chunk_header is still reported in total length (incorrectly)

- Remove empty "extensions" JSON object being appended to all nodes (if it's still empty). This is just cutting down on unnecessary bloat and consistent with the rest of the file's attempts to not emit any keys that are equal to their default value.

- Allow the case where root_nodes is empty. This is permitted by the GLTF spec. Moreover it can happen fairly naturally when using the ROOT_NODE_MODE_MULTI_ROOT root node mode on a scene with only a root node (which is valid in godot).

- Don't create an initial buffer until we're ready to write data into it (buffers of byteLength=0 don't pass validation).
  • Loading branch information
ogapo committed May 12, 2024
1 parent c4279fe commit 522f035
Showing 1 changed file with 57 additions and 16 deletions.
73 changes: 57 additions & 16 deletions modules/gltf/gltf_document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,6 @@ static Ref<ImporterMesh> _mesh_to_importer_mesh(Ref<Mesh> p_mesh) {
}

Error GLTFDocument::_serialize(Ref<GLTFState> p_state) {
if (!p_state->buffers.size()) {
p_state->buffers.push_back(Vector<uint8_t>());
}

for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
Error err = ext->export_preserialize(p_state);
Expand Down Expand Up @@ -243,15 +239,16 @@ Error GLTFDocument::_serialize_gltf_extensions(Ref<GLTFState> p_state) const {
}

Error GLTFDocument::_serialize_scenes(Ref<GLTFState> p_state) {
ERR_FAIL_COND_V_MSG(p_state->root_nodes.is_empty(), ERR_INVALID_DATA, "GLTF export: The scene must have at least one root node.");
// Godot only supports one scene per glTF file.
Array scenes;
Dictionary scene_dict;
scenes.append(scene_dict);
p_state->json["scenes"] = scenes;
p_state->json["scene"] = 0;
// Add nodes to the scene dict.
scene_dict["nodes"] = p_state->root_nodes;
if (!p_state->root_nodes.is_empty()) {
scene_dict["nodes"] = p_state->root_nodes;
}
if (!p_state->scene_name.is_empty()) {
scene_dict["name"] = p_state->scene_name;
}
Expand Down Expand Up @@ -458,9 +455,15 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> p_state) {
ERR_CONTINUE(err != OK);
}

if (extensions.is_empty()) {
node.erase("extensions");
}

nodes.push_back(node);
}
p_state->json["nodes"] = nodes;
if (!nodes.is_empty()) {
p_state->json["nodes"] = nodes;
}
return OK;
}

Expand Down Expand Up @@ -691,11 +694,11 @@ static Vector<uint8_t> _parse_base64_uri(const String &p_uri) {
Error GLTFDocument::_encode_buffer_glb(Ref<GLTFState> p_state, const String &p_path) {
print_verbose("glTF: Total buffers: " + itos(p_state->buffers.size()));

if (!p_state->buffers.size()) {
if (p_state->buffers.is_empty()) {
return OK;
}
Array buffers;
if (p_state->buffers.size()) {
if (!p_state->buffers.is_empty()) {
Vector<uint8_t> buffer_data = p_state->buffers[0];
Dictionary gltf_buffer;

Expand Down Expand Up @@ -730,7 +733,7 @@ Error GLTFDocument::_encode_buffer_glb(Ref<GLTFState> p_state, const String &p_p
Error GLTFDocument::_encode_buffer_bins(Ref<GLTFState> p_state, const String &p_path) {
print_verbose("glTF: Total buffers: " + itos(p_state->buffers.size()));

if (!p_state->buffers.size()) {
if (p_state->buffers.is_empty()) {
return OK;
}
Array buffers;
Expand Down Expand Up @@ -1543,6 +1546,9 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> p_state,
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
if (p_state->buffers.is_empty()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_SCALAR;
int component_type;
Expand Down Expand Up @@ -1654,6 +1660,9 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> p_state,
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
if (p_state->buffers.is_empty()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC2;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
Expand Down Expand Up @@ -1704,6 +1713,9 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> p_state
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
if (p_state->buffers.is_empty()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
Expand Down Expand Up @@ -1768,6 +1780,9 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> p_sta
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
if (p_state->buffers.is_empty()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
Expand Down Expand Up @@ -1816,6 +1831,9 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> p_stat
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
if (p_state->buffers.is_empty()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
Expand Down Expand Up @@ -1866,6 +1884,9 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> p
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
if (p_state->buffers.is_empty()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
Expand Down Expand Up @@ -1938,6 +1959,9 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> p_stat
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
if (p_state->buffers.is_empty()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_SCALAR;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
Expand Down Expand Up @@ -1985,6 +2009,9 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> p_state,
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
if (p_state->buffers.is_empty()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC3;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
Expand Down Expand Up @@ -2058,6 +2085,9 @@ GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref<GLTFState> p

Ref<GLTFAccessor> sparse_accessor;
sparse_accessor.instantiate();
if (p_state->buffers.is_empty()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC3;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
Expand Down Expand Up @@ -2160,6 +2190,9 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> p_state
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
if (p_state->buffers.is_empty()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_MAT4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
Expand Down Expand Up @@ -6944,7 +6977,7 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> p_state, const String p_path)
const uint32_t text_chunk_type = 0x4E4F534A; //JSON

uint32_t binary_data_length = 0;
if (p_state->buffers.size()) {
if (p_state->buffers.size() > 0) {
binary_data_length = p_state->buffers[0].size();
}
const uint32_t binary_chunk_length = ((binary_data_length + 3) & (~3));
Expand All @@ -6953,20 +6986,28 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> p_state, const String p_path)
file->create(FileAccess::ACCESS_RESOURCES);
file->store_32(magic);
file->store_32(p_state->major_version); // version
file->store_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_chunk_length); // length
uint32_t total_length = header_size + chunk_header_size + text_chunk_length;
if (binary_chunk_length) {
total_length += chunk_header_size + binary_chunk_length;
}
file->store_32(total_length);

// Write the JSON text chunk.
file->store_32(text_chunk_length);
file->store_32(text_chunk_type);
file->store_buffer((uint8_t *)&cs[0], cs.length());
for (uint32_t pad_i = text_data_length; pad_i < text_chunk_length; pad_i++) {
file->store_8(' ');
}

// Write a single binary chunk.
if (binary_chunk_length) {
file->store_32(binary_chunk_length);
file->store_32(binary_chunk_type);
file->store_buffer(p_state->buffers[0].ptr(), binary_data_length);
}
for (uint32_t pad_i = binary_data_length; pad_i < binary_chunk_length; pad_i++) {
file->store_8(0);
for (uint32_t pad_i = binary_data_length; pad_i < binary_chunk_length; pad_i++) {
file->store_8(0);
}
}
} else {
err = _encode_buffer_bins(p_state, p_path);
Expand Down Expand Up @@ -7073,7 +7114,7 @@ PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> p_state, Erro

const uint32_t text_chunk_type = 0x4E4F534A; //JSON
int32_t binary_data_length = 0;
if (p_state->buffers.size()) {
if (p_state->buffers.size() > 0) {
binary_data_length = p_state->buffers[0].size();
}
const int32_t binary_chunk_length = binary_data_length;
Expand Down

0 comments on commit 522f035

Please sign in to comment.