summaryrefslogtreecommitdiffstats
path: root/modules/gltf/gltf_document.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gltf/gltf_document.cpp')
-rw-r--r--modules/gltf/gltf_document.cpp292
1 files changed, 237 insertions, 55 deletions
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 984052d2ca..4060f7f626 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -42,7 +42,6 @@
#include "core/io/stream_peer.h"
#include "core/math/disjoint_set.h"
#include "core/version.h"
-#include "drivers/png/png_driver_common.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/importer_mesh_instance_3d.h"
@@ -67,8 +66,10 @@
#endif // MODULE_GRIDMAP_ENABLED
// FIXME: Hardcoded to avoid editor dependency.
+#define GLTF_IMPORT_GENERATE_TANGENT_ARRAYS 8
#define GLTF_IMPORT_USE_NAMED_SKIN_BINDS 16
#define GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS 32
+#define GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION 64
#include <stdio.h>
#include <stdlib.h>
@@ -572,9 +573,12 @@ Error GLTFDocument::_parse_scenes(Ref<GLTFState> p_state) {
// Determine what to use for the scene name.
if (scene_dict.has("name") && !String(scene_dict["name"]).is_empty() && !((String)scene_dict["name"]).begins_with("Scene")) {
p_state->scene_name = scene_dict["name"];
- } else {
+ } else if (p_state->scene_name.is_empty()) {
p_state->scene_name = p_state->filename;
}
+ if (_naming_version == 0) {
+ p_state->scene_name = _gen_unique_name(p_state, p_state->scene_name);
+ }
}
return OK;
@@ -2209,7 +2213,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> p_state) {
}
Array array = import_mesh->get_surface_arrays(surface_i);
- uint32_t format = import_mesh->get_surface_format(surface_i);
+ uint64_t format = import_mesh->get_surface_format(surface_i);
int32_t vertex_num = 0;
Dictionary attributes;
{
@@ -2569,7 +2573,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
import_mesh->set_name(_gen_unique_name(p_state, vformat("%s_%s", p_state->scene_name, mesh_name)));
for (int j = 0; j < primitives.size(); j++) {
- uint32_t flags = 0;
+ uint64_t flags = RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
Dictionary p = primitives[j];
Array array;
@@ -2796,7 +2800,28 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
array[Mesh::ARRAY_INDEX] = indices;
}
- bool generate_tangents = (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL"));
+ bool generate_tangents = p_state->force_generate_tangents && (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("NORMAL"));
+
+ if (generate_tangents && !a.has("TEXCOORD_0")) {
+ // If we don't have UVs we provide a dummy tangent array.
+ Vector<float> tangents;
+ tangents.resize(vertex_num * 4);
+ float *tangentsw = tangents.ptrw();
+
+ Vector<Vector3> normals = array[Mesh::ARRAY_NORMAL];
+ for (int k = 0; k < vertex_num; k++) {
+ Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normals[k]);
+ tangentsw[k * 4 + 0] = tan.x;
+ tangentsw[k * 4 + 1] = tan.y;
+ tangentsw[k * 4 + 2] = tan.z;
+ tangentsw[k * 4 + 3] = 1.0;
+ }
+ array[Mesh::ARRAY_TANGENT] = tangents;
+ }
+
+ if (p_state->force_disable_compression || !a.has("POSITION") || !a.has("NORMAL") || p.has("targets") || (a.has("JOINTS_0") || a.has("JOINTS_1"))) {
+ flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
+ }
Ref<SurfaceTool> mesh_surface_tool;
mesh_surface_tool.instantiate();
@@ -2805,7 +2830,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
mesh_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS);
}
mesh_surface_tool->index();
- if (generate_tangents) {
+ if (generate_tangents && a.has("TEXCOORD_0")) {
//must generate mikktspace tangents.. ergh..
mesh_surface_tool->generate_tangents();
}
@@ -2936,7 +2961,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
// Enforce blend shape mask array format
for (int l = 0; l < Mesh::ARRAY_MAX; l++) {
- if (!(Mesh::ARRAY_FORMAT_BLEND_SHAPE_MASK & (1 << l))) {
+ if (!(Mesh::ARRAY_FORMAT_BLEND_SHAPE_MASK & (1ULL << l))) {
array_copy[l] = Variant();
}
}
@@ -3001,8 +3026,43 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
return OK;
}
+void GLTFDocument::set_naming_version(int p_version) {
+ _naming_version = p_version;
+}
+
+int GLTFDocument::get_naming_version() const {
+ return _naming_version;
+}
+
+void GLTFDocument::set_image_format(const String &p_image_format) {
+ _image_format = p_image_format;
+}
+
+String GLTFDocument::get_image_format() const {
+ return _image_format;
+}
+
+void GLTFDocument::set_lossy_quality(float p_lossy_quality) {
+ _lossy_quality = p_lossy_quality;
+}
+
+float GLTFDocument::get_lossy_quality() const {
+ return _lossy_quality;
+}
+
Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
Array images;
+ // Check if any extension wants to be the image saver.
+ _image_save_extension = Ref<GLTFDocumentExtension>();
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
+ ERR_CONTINUE(ext.is_null());
+ Vector<String> image_formats = ext->get_saveable_image_formats();
+ if (image_formats.has(_image_format)) {
+ _image_save_extension = ext;
+ break;
+ }
+ }
+ // Serialize every image in the state's images array.
for (int i = 0; i < p_state->images.size(); i++) {
Dictionary image_dict;
@@ -3010,6 +3070,10 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
Ref<Image> image = p_state->images[i]->get_image();
ERR_CONTINUE(image.is_null());
+ if (image->is_compressed()) {
+ image->decompress();
+ ERR_FAIL_COND_V_MSG(image->is_compressed(), ERR_INVALID_DATA, "GLTF: Image was compressed, but could not be decompressed.");
+ }
if (p_state->filename.to_lower().ends_with("gltf")) {
String img_name = p_state->images[i]->get_name();
@@ -3017,14 +3081,28 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
img_name = itos(i);
}
img_name = _gen_unique_name(p_state, img_name);
- img_name = img_name.pad_zeros(3) + ".png";
+ img_name = img_name.pad_zeros(3);
String relative_texture_dir = "textures";
String full_texture_dir = p_state->base_path.path_join(relative_texture_dir);
Ref<DirAccess> da = DirAccess::open(p_state->base_path);
+ ERR_FAIL_COND_V(da.is_null(), FAILED);
+
if (!da->dir_exists(full_texture_dir)) {
da->make_dir(full_texture_dir);
}
- image->save_png(full_texture_dir.path_join(img_name));
+ if (_image_save_extension.is_valid()) {
+ img_name = img_name + _image_save_extension->get_image_file_extension();
+ Error err = _image_save_extension->save_image_at_path(p_state, image, full_texture_dir.path_join(img_name), _image_format, _lossy_quality);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "GLTF: Failed to save image in '" + _image_format + "' format as a separate file.");
+ } else if (_image_format == "PNG") {
+ img_name = img_name + ".png";
+ image->save_png(full_texture_dir.path_join(img_name));
+ } else if (_image_format == "JPEG") {
+ img_name = img_name + ".jpg";
+ image->save_jpg(full_texture_dir.path_join(img_name), _lossy_quality);
+ } else {
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "GLTF: Unknown image format '" + _image_format + "'.");
+ }
image_dict["uri"] = relative_texture_dir.path_join(img_name).uri_encode();
} else {
GLTFBufferViewIndex bvi;
@@ -3042,8 +3120,20 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
if (img_tex.is_valid()) {
image = img_tex->get_image();
}
- Error err = PNGDriverCommon::image_to_png(image, buffer);
- ERR_FAIL_COND_V_MSG(err, err, "Can't convert image to PNG.");
+ // Save in various image formats. Note that if the format is "None",
+ // the state's images will be empty, so this code will not be reached.
+ if (_image_save_extension.is_valid()) {
+ buffer = _image_save_extension->serialize_image_to_bytes(p_state, image, image_dict, _image_format, _lossy_quality);
+ } else if (_image_format == "PNG") {
+ buffer = image->save_png_to_buffer();
+ image_dict["mimeType"] = "image/png";
+ } else if (_image_format == "JPEG") {
+ buffer = image->save_jpg_to_buffer(_lossy_quality);
+ image_dict["mimeType"] = "image/jpeg";
+ } else {
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "GLTF: Unknown image format '" + _image_format + "'.");
+ }
+ ERR_FAIL_COND_V_MSG(buffer.is_empty(), ERR_INVALID_DATA, "GLTF: Failed to save image in '" + _image_format + "' format.");
bv->byte_length = buffer.size();
p_state->buffers.write[bi].resize(p_state->buffers[bi].size() + bv->byte_length);
@@ -3053,7 +3143,6 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
p_state->buffer_views.push_back(bv);
bvi = p_state->buffer_views.size() - 1;
image_dict["bufferView"] = bvi;
- image_dict["mimeType"] = "image/png";
}
images.push_back(image_dict);
}
@@ -3332,9 +3421,13 @@ Error GLTFDocument::_serialize_textures(Ref<GLTFState> p_state) {
for (int32_t i = 0; i < p_state->textures.size(); i++) {
Dictionary texture_dict;
Ref<GLTFTexture> gltf_texture = p_state->textures[i];
- ERR_CONTINUE(gltf_texture->get_src_image() == -1);
- texture_dict["source"] = gltf_texture->get_src_image();
-
+ if (_image_save_extension.is_valid()) {
+ Error err = _image_save_extension->serialize_texture_json(p_state, texture_dict, gltf_texture, _image_format);
+ ERR_FAIL_COND_V(err != OK, err);
+ } else {
+ ERR_CONTINUE(gltf_texture->get_src_image() == -1);
+ texture_dict["source"] = gltf_texture->get_src_image();
+ }
GLTFTextureSamplerIndex sampler_index = gltf_texture->get_sampler();
if (sampler_index != -1) {
texture_dict["sampler"] = sampler_index;
@@ -3543,7 +3636,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) {
arr.push_back(c.a);
mr["baseColorFactor"] = arr;
}
- {
+ if (_image_format != "None") {
Dictionary bct;
Ref<Texture2D> albedo_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
GLTFTextureIndex gltf_texture_index = -1;
@@ -3820,7 +3913,6 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) {
} else {
material->set_name(vformat("material_%s", itos(i)));
}
- material->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
Dictionary material_extensions;
if (material_dict.has("extensions")) {
material_extensions = material_dict["extensions"];
@@ -4276,7 +4368,7 @@ Error GLTFDocument::_expand_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin) {
}
Error GLTFDocument::_verify_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin) {
- // This may seem duplicated from expand_skins, but this is really a sanity check! (so it kinda is)
+ // This may seem duplicated from expand_skins, but this is really a safety check! (so it kinda is)
// In case additional interpolating logic is added to the skins, this will help ensure that you
// do not cause it to self implode into a fiery blaze
@@ -5277,12 +5369,22 @@ void GLTFDocument::_assign_node_names(Ref<GLTFState> p_state) {
}
String gltf_node_name = gltf_node->get_name();
if (gltf_node_name.is_empty()) {
- if (gltf_node->mesh >= 0) {
- gltf_node_name = "Mesh";
- } else if (gltf_node->camera >= 0) {
- gltf_node_name = "Camera3D";
+ if (_naming_version == 0) {
+ if (gltf_node->mesh >= 0) {
+ gltf_node_name = _gen_unique_name(p_state, "Mesh");
+ } else if (gltf_node->camera >= 0) {
+ gltf_node_name = _gen_unique_name(p_state, "Camera3D");
+ } else {
+ gltf_node_name = _gen_unique_name(p_state, "Node");
+ }
} else {
- gltf_node_name = "Node";
+ if (gltf_node->mesh >= 0) {
+ gltf_node_name = "Mesh";
+ } else if (gltf_node->camera >= 0) {
+ gltf_node_name = "Camera";
+ } else {
+ gltf_node_name = "Node";
+ }
}
}
gltf_node->set_name(_gen_unique_name(p_state, gltf_node_name));
@@ -5536,7 +5638,7 @@ void GLTFDocument::_create_gltf_node(Ref<GLTFState> p_state, Node *p_scene_paren
}
void GLTFDocument::_convert_animation_player_to_gltf(AnimationPlayer *p_animation_player, Ref<GLTFState> p_state, GLTFNodeIndex p_gltf_current, GLTFNodeIndex p_gltf_root_index, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) {
- ERR_FAIL_COND(!p_animation_player);
+ ERR_FAIL_NULL(p_animation_player);
p_state->animation_players.push_back(p_animation_player);
print_verbose(String("glTF: Converting animation player: ") + p_animation_player->get_name());
}
@@ -5555,7 +5657,7 @@ void GLTFDocument::_check_visibility(Node *p_node, bool &r_retflag) {
}
void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node) {
- ERR_FAIL_COND(!camera);
+ ERR_FAIL_NULL(camera);
GLTFCameraIndex camera_index = _convert_camera(p_state, camera);
if (camera_index != -1) {
p_gltf_node->camera = camera_index;
@@ -5563,7 +5665,7 @@ void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> p_st
}
void GLTFDocument::_convert_light_to_gltf(Light3D *light, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node) {
- ERR_FAIL_COND(!light);
+ ERR_FAIL_NULL(light);
GLTFLightIndex light_index = _convert_light(p_state, light);
if (light_index != -1) {
p_gltf_node->light = light_index;
@@ -5605,7 +5707,7 @@ void GLTFDocument::_convert_multi_mesh_instance_to_gltf(
GLTFNodeIndex p_parent_node_index,
GLTFNodeIndex p_root_node_index,
Ref<GLTFNode> p_gltf_node, Ref<GLTFState> p_state) {
- ERR_FAIL_COND(!p_multi_mesh_instance);
+ ERR_FAIL_NULL(p_multi_mesh_instance);
Ref<MultiMesh> multi_mesh = p_multi_mesh_instance->get_multimesh();
if (multi_mesh.is_null()) {
return;
@@ -5782,6 +5884,10 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
BoneAttachment3D *bone_attachment = _generate_bone_attachment(p_state, active_skeleton, p_node_index, gltf_node->parent);
p_scene_parent->add_child(bone_attachment, true);
+
+ // Find the correct bone_idx so we can properly serialize it.
+ bone_attachment->set_bone_idx(active_skeleton->find_bone(gltf_node->get_name()));
+
bone_attachment->set_owner(p_scene_root);
// There is no gltf_node that represent this, so just directly create a unique name
@@ -5821,15 +5927,22 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
current_node = _generate_spatial(p_state, p_node_index);
}
}
- // Add the node we generated and set the owner to the scene root.
- p_scene_parent->add_child(current_node, true);
- if (current_node != p_scene_root) {
+ String gltf_node_name = gltf_node->get_name();
+ if (!gltf_node_name.is_empty()) {
+ current_node->set_name(gltf_node_name);
+ }
+ // Note: p_scene_parent and p_scene_root must either both be null or both be valid.
+ if (p_scene_root == nullptr) {
+ // If the root node argument is null, this is the root node.
+ p_scene_root = current_node;
+ } else {
+ // Add the node we generated and set the owner to the scene root.
+ p_scene_parent->add_child(current_node, true);
Array args;
args.append(p_scene_root);
current_node->propagate_call(StringName("set_owner"), args);
+ current_node->set_transform(gltf_node->xform);
}
- current_node->set_transform(gltf_node->xform);
- current_node->set_name(gltf_node->get_name());
p_state->scene_nodes.insert(p_node_index, current_node);
for (int i = 0; i < gltf_node->children.size(); ++i) {
@@ -5878,6 +5991,10 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> p_state, const GL
BoneAttachment3D *bone_attachment = _generate_bone_attachment(p_state, active_skeleton, p_node_index, p_node_index);
p_scene_parent->add_child(bone_attachment, true);
+
+ // Find the correct bone_idx so we can properly serialize it.
+ bone_attachment->set_bone_idx(active_skeleton->find_bone(gltf_node->get_name()));
+
bone_attachment->set_owner(p_scene_root);
// There is no gltf_node that represent this, so just directly create a unique name
@@ -6080,7 +6197,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
const Ref<GLTFNode> gltf_node = p_state->nodes[track_i.key];
Node *root = p_animation_player->get_parent();
- ERR_FAIL_COND(root == nullptr);
+ ERR_FAIL_NULL(root);
HashMap<GLTFNodeIndex, Node *>::Iterator node_element = p_state->scene_nodes.find(node_index);
ERR_CONTINUE_MSG(!node_element, vformat("Unable to find node %d for animation.", node_index));
node_path = root->get_path_to(node_element->value);
@@ -6093,7 +6210,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
if (gltf_node->skeleton >= 0) {
const Skeleton3D *sk = p_state->skeletons[gltf_node->skeleton]->godot_skeleton;
- ERR_FAIL_COND(sk == nullptr);
+ ERR_FAIL_NULL(sk);
const String path = p_animation_player->get_parent()->get_path_to(sk);
const String bone = gltf_node->get_name();
@@ -6153,7 +6270,9 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
if (p_remove_immutable_tracks) {
Vector3 base_pos = p_state->nodes[track_i.key]->position;
for (int i = 0; i < track.position_track.times.size(); i++) {
- Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
+ int value_index = track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i;
+ ERR_FAIL_COND_MSG(value_index >= track.position_track.values.size(), "Animation sampler output accessor with 'CUBICSPLINE' interpolation doesn't have enough elements.");
+ Vector3 value = track.position_track.values[value_index];
if (!value.is_equal_approx(base_pos)) {
is_default = false;
break;
@@ -6173,7 +6292,9 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
if (p_remove_immutable_tracks) {
Quaternion base_rot = p_state->nodes[track_i.key]->rotation.normalized();
for (int i = 0; i < track.rotation_track.times.size(); i++) {
- Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized();
+ int value_index = track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i;
+ ERR_FAIL_COND_MSG(value_index >= track.rotation_track.values.size(), "Animation sampler output accessor with 'CUBICSPLINE' interpolation doesn't have enough elements.");
+ Quaternion value = track.rotation_track.values[value_index].normalized();
if (!value.is_equal_approx(base_rot)) {
is_default = false;
break;
@@ -6193,7 +6314,9 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
if (p_remove_immutable_tracks) {
Vector3 base_scale = p_state->nodes[track_i.key]->scale;
for (int i = 0; i < track.scale_track.times.size(); i++) {
- Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
+ int value_index = track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i;
+ ERR_FAIL_COND_MSG(value_index >= track.scale_track.values.size(), "Animation sampler output accessor with 'CUBICSPLINE' interpolation doesn't have enough elements.");
+ Vector3 value = track.scale_track.values[value_index];
if (!value.is_equal_approx(base_scale)) {
is_default = false;
break;
@@ -6443,7 +6566,7 @@ float GLTFDocument::get_max_component(const Color &p_color) {
return MAX(MAX(r, g), b);
}
-void GLTFDocument::_process_mesh_instances(Ref<GLTFState> p_state, Node *p_scene_root) {
+void GLTFDocument::_process_mesh_instances(Ref<GLTFState> p_state) {
for (GLTFNodeIndex node_i = 0; node_i < p_state->nodes.size(); ++node_i) {
Ref<GLTFNode> node = p_state->nodes[node_i];
@@ -7158,6 +7281,21 @@ void GLTFDocument::_bind_methods() {
ClassDB::bind_method(D_METHOD("write_to_filesystem", "state", "path"),
&GLTFDocument::write_to_filesystem);
+ BIND_ENUM_CONSTANT(ROOT_NODE_MODE_SINGLE_ROOT);
+ BIND_ENUM_CONSTANT(ROOT_NODE_MODE_KEEP_ROOT);
+ BIND_ENUM_CONSTANT(ROOT_NODE_MODE_MULTI_ROOT);
+
+ ClassDB::bind_method(D_METHOD("set_image_format", "image_format"), &GLTFDocument::set_image_format);
+ ClassDB::bind_method(D_METHOD("get_image_format"), &GLTFDocument::get_image_format);
+ ClassDB::bind_method(D_METHOD("set_lossy_quality", "lossy_quality"), &GLTFDocument::set_lossy_quality);
+ ClassDB::bind_method(D_METHOD("get_lossy_quality"), &GLTFDocument::get_lossy_quality);
+ ClassDB::bind_method(D_METHOD("set_root_node_mode", "root_node_mode"), &GLTFDocument::set_root_node_mode);
+ ClassDB::bind_method(D_METHOD("get_root_node_mode"), &GLTFDocument::get_root_node_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "image_format"), "set_image_format", "get_image_format");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lossy_quality"), "set_lossy_quality", "get_lossy_quality");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "root_node_mode"), "set_root_node_mode", "get_root_node_mode");
+
ClassDB::bind_static_method("GLTFDocument", D_METHOD("register_gltf_document_extension", "extension", "first_priority"),
&GLTFDocument::register_gltf_document_extension, DEFVAL(false));
ClassDB::bind_static_method("GLTFDocument", D_METHOD("unregister_gltf_document_extension", "extension"),
@@ -7266,15 +7404,44 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_
return OK;
}
+Node *GLTFDocument::_generate_scene_node_tree(Ref<GLTFState> p_state) {
+ // Generate the skeletons and skins (if any).
+ Error err = _create_skeletons(p_state);
+ ERR_FAIL_COND_V_MSG(err != OK, nullptr, "GLTF: Failed to create skeletons.");
+ err = _create_skins(p_state);
+ ERR_FAIL_COND_V_MSG(err != OK, nullptr, "GLTF: Failed to create skins.");
+ // Generate the node tree.
+ Node *single_root;
+ if (p_state->extensions_used.has("GODOT_single_root")) {
+ _generate_scene_node(p_state, 0, nullptr, nullptr);
+ single_root = p_state->scene_nodes[0];
+ } else {
+ single_root = memnew(Node3D);
+ for (int32_t root_i = 0; root_i < p_state->root_nodes.size(); root_i++) {
+ _generate_scene_node(p_state, p_state->root_nodes[root_i], single_root, single_root);
+ }
+ }
+ // Assign the scene name and single root name to each other
+ // if one is missing, or do nothing if both are already set.
+ if (unlikely(p_state->scene_name.is_empty())) {
+ p_state->scene_name = single_root->get_name();
+ } else if (single_root->get_name() == StringName()) {
+ if (_naming_version == 0) {
+ single_root->set_name(p_state->scene_name);
+ } else {
+ single_root->set_name(_gen_unique_name(p_state, p_state->scene_name));
+ }
+ }
+ return single_root;
+}
+
Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool p_trimming, bool p_remove_immutable_tracks) {
ERR_FAIL_NULL_V(p_state, nullptr);
ERR_FAIL_INDEX_V(0, p_state->root_nodes.size(), nullptr);
Error err = OK;
- GLTFNodeIndex gltf_root = p_state->root_nodes.write[0];
- Node *gltf_root_node = p_state->get_scene_node(gltf_root);
- Node *root = gltf_root_node->get_parent();
+ Node *root = _generate_scene_node_tree(p_state);
ERR_FAIL_NULL_V(root, nullptr);
- _process_mesh_instances(p_state, root);
+ _process_mesh_instances(p_state);
if (p_state->get_create_animations() && p_state->animations.size()) {
AnimationPlayer *ap = memnew(AnimationPlayer);
root->add_child(ap, true);
@@ -7310,6 +7477,8 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint
ERR_FAIL_COND_V(p_state.is_null(), FAILED);
p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
+ p_state->force_generate_tangents = p_flags & GLTF_IMPORT_GENERATE_TANGENT_ARRAYS;
+ p_state->force_disable_compression = p_flags & GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION;
if (!p_state->buffers.size()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
@@ -7324,6 +7493,19 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint
}
}
// Add the root node(s) and their descendants to the state.
+ if (_root_node_mode == RootNodeMode::ROOT_NODE_MODE_MULTI_ROOT) {
+ const int child_count = p_node->get_child_count();
+ if (child_count > 0) {
+ for (int i = 0; i < child_count; i++) {
+ _convert_scene_node(p_state, p_node->get_child(i), -1, -1);
+ }
+ p_state->scene_name = p_node->get_name();
+ return OK;
+ }
+ }
+ if (_root_node_mode == RootNodeMode::ROOT_NODE_MODE_SINGLE_ROOT) {
+ p_state->extensions_used.append("GODOT_single_root");
+ }
_convert_scene_node(p_state, p_node, -1, -1);
return OK;
}
@@ -7334,6 +7516,8 @@ Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_pa
Error err = FAILED;
p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
+ p_state->force_generate_tangents = p_flags & GLTF_IMPORT_GENERATE_TANGENT_ARRAYS;
+ p_state->force_disable_compression = p_flags & GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION;
Ref<FileAccessMemory> file_access;
file_access.instantiate();
@@ -7427,14 +7611,6 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se
err = _determine_skeletons(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
- /* CREATE SKELETONS */
- err = _create_skeletons(p_state);
- ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
-
- /* CREATE SKINS */
- err = _create_skins(p_state);
- ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
-
/* PARSE MESHES (we have enough info now) */
err = _parse_meshes(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
@@ -7454,11 +7630,6 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se
/* ASSIGN SCENE NAMES */
_assign_node_names(p_state);
- Node3D *root = memnew(Node3D);
- for (int32_t root_i = 0; root_i < p_state->root_nodes.size(); root_i++) {
- _generate_scene_node(p_state, p_state->root_nodes[root_i], root, root);
- }
-
return OK;
}
@@ -7470,6 +7641,9 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint
p_state->filename = p_path.get_file().get_basename();
p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
+ p_state->force_generate_tangents = p_flags & GLTF_IMPORT_GENERATE_TANGENT_ARRAYS;
+ p_state->force_disable_compression = p_flags & GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION;
+
Error err;
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err);
ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN);
@@ -7521,3 +7695,11 @@ Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> p_state) {
}
return ret;
}
+
+void GLTFDocument::set_root_node_mode(GLTFDocument::RootNodeMode p_root_node_mode) {
+ _root_node_mode = p_root_node_mode;
+}
+
+GLTFDocument::RootNodeMode GLTFDocument::get_root_node_mode() const {
+ return _root_node_mode;
+}