From 7c7cd8c024c23b7483f746a759290af3fa610f88 Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Tue, 4 May 2021 06:50:55 -0700 Subject: [PATCH 1/5] Support bone animation export and bone attachment export --- modules/gltf/gltf_document.cpp | 325 +++++++++++++++++++++++---------- modules/gltf/gltf_document.h | 21 +-- 2 files changed, 233 insertions(+), 113 deletions(-) diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index ff0579a11c6d..38b7d8df7529 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -29,7 +29,6 @@ /*************************************************************************/ #include "gltf_document.h" - #include "gltf_accessor.h" #include "gltf_animation.h" #include "gltf_camera.h" @@ -72,33 +71,141 @@ #include #include +void GLTFDocument::_convert_skins(Ref state) { + for (int32_t skin_i = 0; skin_i < state->skins.size(); skin_i++) { + ERR_FAIL_NULL(state->skins[skin_i]); + Ref gltf_skin = state->skins[skin_i]; + ERR_FAIL_NULL(gltf_skin); + Ref skin = gltf_skin->godot_skin; + if (skin.is_null()) { + GLTFSkeletonIndex skeleton_i = gltf_skin->skeleton; + Ref skeleton_gltf = state->skeletons[skeleton_i]; + skin = skeleton_gltf->godot_skeleton->register_skin(nullptr)->get_skin(); + gltf_skin->godot_skin = skin; + } + GLTFSkeletonIndex skeleton_gltf_i = -1; + for (int32_t skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) { + Ref gltf_skeleton = state->skeletons.write[skeleton_i]; + Skeleton3D *skeleton = gltf_skeleton->godot_skeleton; + if (skeleton != gltf_skeleton->godot_skeleton) { + continue; + } + skeleton_gltf_i = skeleton_i; + break; + } + ERR_FAIL_INDEX(skeleton_gltf_i, state->skeletons.size()); + Ref gltf_skeleton = state->skeletons.write[skeleton_gltf_i]; + Skeleton3D *skeleton = gltf_skeleton->godot_skeleton; + for (int32_t bind_i = 0; bind_i < skin->get_bind_count(); bind_i++) { + Transform3D bind_pose = skin->get_bind_pose(bind_i); + String bone_name = skin->get_bind_name(bind_i); + int32_t bone = skeleton->find_bone(bone_name); + if (bone == -1) { + continue; + } + GLTFNodeIndex joint_node_i = gltf_skeleton->godot_bone_node[bone]; + if (joint_node_i == -1) { + continue; + } + BoneId bone_i = skeleton->find_bone(bone_name); + ERR_CONTINUE(bone_i == -1); + gltf_skin->inverse_binds.push_back(bind_pose); + gltf_skin->joint_i_to_bone_i.insert(joint_node_i, bone_i); + gltf_skin->joints.push_back(joint_node_i); + gltf_skin->joints_original.push_back(joint_node_i); + if (skeleton->get_bone_parent(bone_i) == -1 && !gltf_skeleton->roots.has(joint_node_i)) { + gltf_skeleton->roots.push_back(joint_node_i); + } + bool is_root = gltf_skeleton->roots.find(joint_node_i) != -1; + if (!is_root) { + BoneId bone_parent = skeleton->get_bone_parent(bone_i); + ERR_CONTINUE(bone_parent == -1); + ERR_CONTINUE(!gltf_skeleton->godot_bone_node.has(bone_parent)); + GLTFNodeIndex parent_joint_gltf_node = gltf_skeleton->godot_bone_node[bone_parent]; + Ref parent_joint_node = state->nodes.write[parent_joint_gltf_node]; + if (!parent_joint_node->children.has(joint_node_i)) { + parent_joint_node->children.push_back(joint_node_i); + } + } + } + } + + // reparent bone root node to the skeleton's parent + for (int32_t skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) { + Ref gltf_skeleton = state->skeletons[skeleton_i]; + Node *parent = gltf_skeleton->godot_skeleton->get_parent(); + for (int32_t root_i = 0; root_i < gltf_skeleton->roots.size(); root_i++) { + GLTFNodeIndex bone_root_node_gltf_i = gltf_skeleton->roots[root_i]; + for (Map::Element *E = state->scene_nodes.front(); E; E = E->next()) { + if (E->get() != parent) { + continue; + } + GLTFNodeIndex parent_node_gltf = E->key(); + ERR_FAIL_INDEX(parent_node_gltf, state->nodes.size()); + Ref parent_node = state->nodes.write[parent_node_gltf]; + if (parent_node->children.has(bone_root_node_gltf_i)) { + continue; + } + parent_node->children.push_back(bone_root_node_gltf_i); + break; + } + } + } + _remove_duplicate_skins(state); + // save json skins + { + Array json_skins; + for (int32_t skin_i = 0; skin_i < state->skins.size(); skin_i++) { + if (state->skins[skin_i].is_null()) { + continue; + } + GLTFSkeletonIndex skeleton_gltf_i = state->skins[skin_i]->skeleton; + if (skeleton_gltf_i < 0) { + continue; + } + Ref gltf_skin = state->skins[skin_i]; + if (gltf_skin.is_valid()) { + ERR_FAIL_COND(_expand_skin(state, gltf_skin)); + ERR_FAIL_COND(_verify_skin(state, gltf_skin)); + } + Ref gltf_skeleton = state->skeletons.write[skeleton_gltf_i]; + Dictionary json_skin; + json_skin["inverseBindMatrices"] = _encode_accessor_as_xform(state, gltf_skin->inverse_binds, false); + Array json_joints; + for (int32_t joint_i = 0; joint_i < gltf_skin->joints.size(); joint_i++) { + json_joints.push_back(gltf_skin->joints[joint_i]); + } + json_skin["joints"] = json_joints; + // Do not set skeleton + // json_skin["skeleton"]; + json_skin["name"] = gltf_skin->get_name(); + json_skins.push_back(json_skin); + } + state->json["skins"] = json_skins; + } +} + Error GLTFDocument::serialize(Ref state, Node *p_root, const String &p_path) { uint64_t begin_time = OS::get_singleton()->get_ticks_usec(); + /* STEP 1 CREATE SCENE NODES */ _convert_scene_node(state, p_root, p_root, -1, -1); if (!state->buffers.size()) { state->buffers.push_back(Vector()); } - /* STEP 1 CONVERT MESH INSTANCES */ - _convert_mesh_instances(state); + /* STEP 2 CREATE MESH SKINS */ + _convert_mesh_skins(state); - /* STEP 2 SERIALIZE CAMERAS */ - Error err = _serialize_cameras(state); - if (err != OK) { - return Error::FAILED; - } + /* STEP 3 CONVERT SKINS */ + _convert_skins(state); - /* STEP 3 CREATE SKINS */ - err = _serialize_skins(state); - if (err != OK) { - return Error::FAILED; - } - /* STEP 4 CREATE BONE ATTACHMENTS */ - err = _serialize_bone_attachment(state); + /* STEP 4 SERIALIZE CAMERAS */ + int32_t err = _serialize_cameras(state); if (err != OK) { return Error::FAILED; } + /* STEP 5 SERIALIZE MESHES (we have enough info now) */ err = _serialize_meshes(state); if (err != OK) { @@ -123,7 +230,7 @@ Error GLTFDocument::serialize(Ref state, Node *p_root, const String & return Error::FAILED; } - // /* STEP 9 SERIALIZE ANIMATIONS */ + /* STEP 9 SERIALIZE ANIMATIONS */ err = _serialize_animations(state); if (err != OK) { return Error::FAILED; @@ -246,30 +353,6 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref state) { return OK; } -Error GLTFDocument::_serialize_bone_attachment(Ref state) { - for (int skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) { - for (int attachment_i = 0; attachment_i < state->skeletons[skeleton_i]->bone_attachments.size(); attachment_i++) { - BoneAttachment3D *bone_attachment = state->skeletons[skeleton_i]->bone_attachments[attachment_i]; - String bone_name = bone_attachment->get_bone_name(); - bone_name = _sanitize_bone_name(bone_name); - int32_t bone = state->skeletons[skeleton_i]->godot_skeleton->find_bone(bone_name); - ERR_CONTINUE(bone == -1); - for (int skin_i = 0; skin_i < state->skins.size(); skin_i++) { - if (state->skins[skin_i]->skeleton != skeleton_i) { - continue; - } - - for (int node_i = 0; node_i < bone_attachment->get_child_count(); node_i++) { - ERR_CONTINUE(bone >= state->skins[skin_i]->joints.size()); - _convert_scene_node(state, bone_attachment->get_child(node_i), bone_attachment->get_owner(), state->skins[skin_i]->joints[bone], 0); - } - break; - } - } - } - return OK; -} - Error GLTFDocument::_parse_glb(const String &p_path, Ref state) { Error err; FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); @@ -4257,11 +4340,6 @@ Error GLTFDocument::_map_skin_joints_indices_to_skeleton_bone_indices(Ref state) { - _remove_duplicate_skins(state); - return OK; -} - Error GLTFDocument::_create_skins(Ref state) { for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) { Ref gltf_skin = state->skins.write[skin_i]; @@ -5060,13 +5138,27 @@ GLTFLightIndex GLTFDocument::_convert_light(Ref state, Light3D *p_lig return light_index; } -GLTFSkeletonIndex GLTFDocument::_convert_skeleton(Ref state, Skeleton3D *p_skeleton) { +GLTFSkeletonIndex GLTFDocument::_convert_skeleton(Ref state, Skeleton3D *p_skeleton, Ref p_node, MeshInstance3D *p_mi) { print_verbose("glTF: Converting skeleton: " + p_skeleton->get_name()); Ref gltf_skeleton; gltf_skeleton.instantiate(); gltf_skeleton->set_name(_gen_unique_name(state, p_skeleton->get_name())); gltf_skeleton->godot_skeleton = p_skeleton; GLTFSkeletonIndex skeleton_i = state->skeletons.size(); + for (int32_t bone_i = 0; bone_i < p_skeleton->get_bone_count(); bone_i++) { + String bone_name = p_skeleton->get_bone_name(bone_i); + GLTFNodeIndex joint_node_i = state->nodes.size(); + Ref joint_node; + joint_node.instantiate(); + joint_node->set_name(bone_name); + Transform3D bone_rest_xform = p_skeleton->get_bone_rest(bone_i); + joint_node->scale = bone_rest_xform.basis.get_scale(); + joint_node->rotation = bone_rest_xform.basis.get_rotation_quaternion(); + joint_node->translation = bone_rest_xform.origin; + joint_node->joint = true; + state->nodes.push_back(joint_node); + gltf_skeleton->godot_bone_node.insert(bone_i, joint_node_i); + } state->skeletons.push_back(gltf_skeleton); return skeleton_i; } @@ -5086,6 +5178,7 @@ Node3D *GLTFDocument::_generate_spatial(Ref state, Node *scene_parent return spatial; } + void GLTFDocument::_convert_scene_node(Ref state, Node *p_current, Node *p_root, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) { bool retflag = true; _check_visibility(p_current, retflag); @@ -5099,19 +5192,22 @@ void GLTFDocument::_convert_scene_node(Ref state, Node *p_current, No Node3D *spatial = cast_to(p_current); _convert_spatial(state, spatial, gltf_node); } + GLTFNodeIndex current_node_i = state->nodes.size(); if (cast_to(p_current)) { - Node3D *spatial = cast_to(p_current); + MeshInstance3D *spatial = cast_to(p_current); _convert_mesh_to_gltf(p_current, state, spatial, gltf_node); + if (spatial && gltf_node->mesh >= 0) { + _create_mesh_skin(state, spatial, gltf_node); + } } else if (cast_to(p_current)) { - _convert_bone_attachment_to_gltf(p_current, state, gltf_node, retflag); - // TODO 2020-12-21 iFire Handle the case of objects under the bone attachment. return; } else if (cast_to(p_current)) { - _convert_skeleton_to_gltf(p_current, state, p_gltf_parent, p_gltf_root, gltf_node, p_root); - // We ignore the Godot Engine node that is the skeleton. + for (int node_i = 0; node_i < p_current->get_child_count(); node_i++) { + _convert_scene_node(state, p_current->get_child(node_i), p_current->get_owner(), p_gltf_parent, p_gltf_root); + } return; } else if (cast_to(p_current)) { - _convert_mult_mesh_instance_to_gltf(p_current, p_gltf_parent, p_gltf_root, gltf_node, state, p_root); + _convert_multi_mesh_instance_to_gltf(p_current, p_gltf_parent, p_gltf_root, gltf_node, state, p_root); #ifdef MODULE_CSG_ENABLED } else if (cast_to(p_current)) { if (p_current->get_parent() && cast_to(p_current)->is_root_shape()) { @@ -5132,7 +5228,6 @@ void GLTFDocument::_convert_scene_node(Ref state, Node *p_current, No AnimationPlayer *animation_player = Object::cast_to(p_current); _convert_animation_player_to_gltf(animation_player, state, p_gltf_parent, p_gltf_root, gltf_node, p_current, p_root); } - GLTFNodeIndex current_node_i = state->nodes.size(); GLTFNodeIndex gltf_root = p_gltf_root; if (gltf_root == -1) { gltf_root = current_node_i; @@ -5257,7 +5352,7 @@ void GLTFDocument::_convert_grid_map_to_gltf(Node *p_scene_parent, const GLTFNod } #endif // MODULE_GRIDMAP_ENABLED -void GLTFDocument::_convert_mult_mesh_instance_to_gltf(Node *p_scene_parent, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref gltf_node, Ref state, Node *p_root_node) { +void GLTFDocument::_convert_multi_mesh_instance_to_gltf(Node *p_scene_parent, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref gltf_node, Ref state, Node *p_root_node) { MultiMeshInstance3D *multi_mesh_instance = Object::cast_to(p_scene_parent); ERR_FAIL_COND(!multi_mesh_instance); Ref multi_mesh = multi_mesh_instance->get_multimesh(); @@ -5305,42 +5400,6 @@ void GLTFDocument::_convert_mult_mesh_instance_to_gltf(Node *p_scene_parent, con } } -void GLTFDocument::_convert_skeleton_to_gltf(Node *p_scene_parent, Ref state, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref gltf_node, Node *p_root_node) { - Skeleton3D *skeleton = Object::cast_to(p_scene_parent); - if (skeleton) { - // Remove placeholder skeleton3d node by not creating the gltf node - // Skins are per mesh - for (int node_i = 0; node_i < skeleton->get_child_count(); node_i++) { - _convert_scene_node(state, skeleton->get_child(node_i), p_root_node, p_parent_node_index, p_root_node_index); - } - } -} - -void GLTFDocument::_convert_bone_attachment_to_gltf(Node *p_scene_parent, Ref state, Ref gltf_node, bool &retflag) { - retflag = true; - BoneAttachment3D *bone_attachment = Object::cast_to(p_scene_parent); - if (bone_attachment) { - Node *node = bone_attachment->get_parent(); - while (node) { - Skeleton3D *bone_attachment_skeleton = Object::cast_to(node); - if (bone_attachment_skeleton) { - for (GLTFSkeletonIndex skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) { - if (state->skeletons[skeleton_i]->godot_skeleton != bone_attachment_skeleton) { - continue; - } - state->skeletons.write[skeleton_i]->bone_attachments.push_back(bone_attachment); - break; - } - break; - } - node = node->get_parent(); - } - gltf_node.unref(); - return; - } - retflag = false; -} - void GLTFDocument::_convert_mesh_to_gltf(Node *p_scene_parent, Ref state, Node3D *spatial, Ref gltf_node) { MeshInstance3D *mi = Object::cast_to(p_scene_parent); if (mi) { @@ -5476,7 +5535,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref state, Node *scen if (current_node != scene_root) { current_node->set_owner(scene_root); } - // Do not set transform here. Transform is already applied to our bone. + // Do not set transform here. Transform3D is already applied to our bone. current_node->set_name(gltf_node->get_name()); } @@ -5854,7 +5913,7 @@ void GLTFDocument::_convert_mesh_instances(Ref state) { if (!is_unique) { continue; } - GLTFSkeletonIndex skeleton_i = _convert_skeleton(state, skeleton); + GLTFSkeletonIndex skeleton_i = _convert_skeleton(state, skeleton, node, mi); skeleton_gltf_i = skeleton_i; ERR_CONTINUE(skeleton_gltf_i == -1); gltf_skin->skeleton = skeleton_gltf_i; @@ -5979,26 +6038,24 @@ void GLTFDocument::_process_mesh_instances(Ref state, Node *scene_roo Ref node = state->nodes[node_i]; if (node->skin >= 0 && node->mesh >= 0) { - const GLTFSkinIndex skin_i = node->skin; - Map::Element *mi_element = state->scene_nodes.find(node_i); ERR_CONTINUE_MSG(mi_element == nullptr, vformat("Unable to find node %d", node_i)); EditorSceneImporterMeshNode3D *mi = Object::cast_to(mi_element->get()); ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to EditorSceneImporterMeshNode3D", node_i, mi_element->get()->get_class_name())); - + ERR_CONTINUE_MSG(node->skin >= state->skins.size(), vformat("Unable to find Skin for node %d skin %d", node_i, node->skin)); const GLTFSkeletonIndex skel_i = state->skins.write[node->skin]->skeleton; + ERR_FAIL_INDEX(skel_i, state->skeletons.size()); Ref gltf_skeleton = state->skeletons.write[skel_i]; Skeleton3D *skeleton = gltf_skeleton->godot_skeleton; - ERR_CONTINUE_MSG(skeleton == nullptr, vformat("Unable to find Skeleton for node %d skin %d", node_i, skin_i)); + ERR_CONTINUE_MSG(skeleton == nullptr, vformat("Unable to find Skeleton for node %d skin %d", node_i, node->skin)); mi->get_parent()->remove_child(mi); skeleton->add_child(mi); mi->set_owner(skeleton->get_owner()); - - mi->set_skin(state->skins.write[skin_i]->godot_skin); mi->set_skeleton_path(mi->get_path_to(skeleton)); mi->set_transform(Transform3D()); + mi->set_skin(state->skins.write[node->skin]->godot_skin); } } } @@ -6630,3 +6687,75 @@ Error GLTFDocument::_serialize_file(Ref state, const String p_path) { } return err; } + +void GLTFDocument::_create_mesh_skin(Ref state, MeshInstance3D *p_mi, Ref p_gltf_node) { + if (p_gltf_node->mesh < 0) { + return; + } + Array json_skins; + if (state->json.has("skins")) { + json_skins = state->json["skins"]; + } + MeshInstance3D *mi = p_mi; + if (!mi) { + return; + } + Transform3D mi_xform = mi->get_transform(); + p_gltf_node->scale = mi_xform.basis.get_scale(); + p_gltf_node->rotation = mi_xform.basis.get_rotation_quaternion(); + p_gltf_node->translation = mi_xform.origin; + Node *node = mi->get_node_or_null(mi->get_skeleton_path()); + if (!node) { + return; + } + Skeleton3D *skeleton = cast_to(node); + GLTFSkeletonIndex skeleton_gltf_i = -1; + for (int32_t skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) { + if (state->skeletons[skeleton_i]->godot_skeleton != skeleton) { + continue; + } + skeleton_gltf_i = skeleton_i; + break; + } + if (skeleton_gltf_i == -1) { + skeleton_gltf_i = _convert_skeleton(state, skeleton, p_gltf_node, mi); + for (int32_t node_i = 0; node_i < skeleton->get_child_count(); node_i++) { + BoneAttachment3D *bone_attachment = cast_to(skeleton->get_child(node_i)); + if (!bone_attachment) { + continue; + } + String bone_name = bone_attachment->get_bone_name(); + BoneId bone = skeleton->find_bone(bone_name); + ERR_CONTINUE(bone == -1); + GLTFNodeIndex gltf_node_i = state->skeletons[skeleton_gltf_i]->godot_bone_node[bone]; + ERR_CONTINUE(gltf_node_i == -1); + state->nodes.write[gltf_node_i]->set_name(bone_name); + for (int skin_i = 0; skin_i < state->skins.size(); skin_i++) { + if (state->skins[skin_i]->skeleton != skeleton_gltf_i) { + continue; + } + for (int32_t bone_attachment_i = 0; bone_attachment_i < bone_attachment->get_child_count(); bone_attachment_i++) { + _convert_scene_node(state, bone_attachment->get_child(bone_attachment_i), bone_attachment->get_owner(), gltf_node_i, 0); + } + } + } + } + p_gltf_node->skeleton = skeleton_gltf_i; + Ref gltf_skin; + gltf_skin.instantiate(); + gltf_skin->skeleton = skeleton_gltf_i; + Ref godot_skin = p_mi->get_skin(); + if (godot_skin.is_null()) { + godot_skin = skeleton->register_skin(godot_skin)->get_skin(); + } + gltf_skin->godot_skin = godot_skin; + p_gltf_node->skin = state->skins.size(); + if (godot_skin->get_name().is_empty()) { + gltf_skin->set_name(vformat("skin_%s", itos(p_gltf_node->skin))); + } else { + gltf_skin->set_name(godot_skin->get_name()); + } + state->skins.push_back(gltf_skin); +} +void GLTFDocument::_convert_mesh_skins(Ref state) { +} diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 3e8ea1904577..d40ba586c6f7 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -155,6 +155,7 @@ class GLTFDocument : public Resource { r_out[keys[i]] = p_inp[keys[i]]; } } + void _convert_mesh_instances(Ref state); double _filter_number(double p_float); String _get_component_type_name(const uint32_t p_component); int _get_component_type_size(const int component_type); @@ -249,7 +250,6 @@ class GLTFDocument : public Resource { const GLTFSkeletonIndex skel_i); Error _create_skeletons(Ref state); Error _map_skin_joints_indices_to_skeleton_bone_indices(Ref state); - Error _serialize_skins(Ref state); Error _create_skins(Ref state); bool _skins_are_same(const Ref skin_a, const Ref skin_b); void _remove_duplicate_skins(Ref state); @@ -338,7 +338,6 @@ class GLTFDocument : public Resource { GLTFNodeIndex p_node_i); Error _encode_buffer_bins(Ref state, const String &p_path); Error _encode_buffer_glb(Ref state, const String &p_path); - Error _serialize_bone_attachment(Ref state); Dictionary _serialize_texture_transform_uv1(Ref p_material); Dictionary _serialize_texture_transform_uv2(Ref p_material); Error _serialize_version(Ref state); @@ -371,20 +370,21 @@ class GLTFDocument : public Resource { const GLTFAnimationIndex index, const int bake_fps); GLTFMeshIndex _convert_mesh_instance(Ref state, MeshInstance3D *p_mesh_instance); - void _convert_mesh_instances(Ref state); + void _convert_skins(Ref state); GLTFCameraIndex _convert_camera(Ref state, Camera3D *p_camera); void _convert_light_to_gltf(Light3D *light, Ref state, Node3D *spatial, Ref gltf_node); GLTFLightIndex _convert_light(Ref state, Light3D *p_light); - GLTFSkeletonIndex _convert_skeleton(Ref state, Skeleton3D *p_skeleton); + GLTFSkeletonIndex _convert_skeleton(Ref state, Skeleton3D *p_skeleton, Ref p_node, MeshInstance3D *p_mi); void _convert_spatial(Ref state, Node3D *p_spatial, Ref p_node); void _convert_scene_node(Ref state, Node *p_current, Node *p_root, const GLTFNodeIndex p_gltf_current, const GLTFNodeIndex p_gltf_root); + void _create_mesh_skin(Ref state, MeshInstance3D *p_mi, Ref p_gltf_node); + void _convert_mesh_skins(Ref state); #ifdef MODULE_CSG_ENABLED void _convert_csg_shape_to_gltf(Node *p_current, GLTFNodeIndex p_gltf_parent, Ref gltf_node, Ref state); #endif // MODULE_CSG_ENABLED - void _create_gltf_node(Ref state, Node *p_scene_parent, GLTFNodeIndex current_node_i, @@ -409,21 +409,12 @@ class GLTFDocument : public Resource { Ref gltf_node, Ref state, Node *p_root_node); #endif // MODULE_GRIDMAP_ENABLED - void _convert_mult_mesh_instance_to_gltf( + void _convert_multi_mesh_instance_to_gltf( Node *p_scene_parent, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref gltf_node, Ref state, Node *p_root_node); - void _convert_skeleton_to_gltf( - Node *p_scene_parent, Ref state, - const GLTFNodeIndex &p_parent_node_index, - const GLTFNodeIndex &p_root_node_index, - Ref gltf_node, Node *p_root_node); - void _convert_bone_attachment_to_gltf(Node *p_scene_parent, - Ref state, - Ref gltf_node, - bool &retflag); void _convert_mesh_to_gltf(Node *p_scene_parent, Ref state, Node3D *spatial, Ref gltf_node); From f1b5b4f076e632bde4a1f49521aeaedd82f35866 Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Sun, 1 Aug 2021 18:58:05 -0700 Subject: [PATCH 2/5] Remove tangents. --- modules/gltf/gltf_document.cpp | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 38b7d8df7529..3c6d752a3e91 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -2259,23 +2259,6 @@ Error GLTFDocument::_serialize_meshes(Ref state) { ERR_FAIL_COND_V(!a.size(), ERR_INVALID_DATA); attributes["POSITION"] = _encode_accessor_as_vec3(state, a, true); } - { - Vector a = array[Mesh::ARRAY_TANGENT]; - if (a.size()) { - const int ret_size = a.size() / 4; - Vector attribs; - attribs.resize(ret_size); - for (int i = 0; i < ret_size; i++) { - Color out; - out.r = a[(i * 4) + 0]; - out.g = a[(i * 4) + 1]; - out.b = a[(i * 4) + 2]; - out.a = a[(i * 4) + 3]; - attribs.write[i] = out; - } - attributes["TANGENT"] = _encode_accessor_as_color(state, attribs, true); - } - } { Vector a = array[Mesh::ARRAY_NORMAL]; if (a.size()) { @@ -2457,20 +2440,6 @@ Error GLTFDocument::_serialize_meshes(Ref state) { if (varr.size()) { t["NORMAL"] = _encode_accessor_as_vec3(state, narr, true); } - Vector tarr = array_morph[Mesh::ARRAY_TANGENT]; - if (tarr.size()) { - const int ret_size = tarr.size() / 4; - Vector attribs; - attribs.resize(ret_size); - for (int i = 0; i < ret_size; i++) { - Color tangent; - tangent.r = tarr[(i * 4) + 0]; - tangent.g = tarr[(i * 4) + 1]; - tangent.b = tarr[(i * 4) + 2]; - tangent.a = tarr[(i * 4) + 3]; - } - t["TANGENT"] = _encode_accessor_as_color(state, attribs, true); - } targets.push_back(t); } } From c9f16c54312a568155f614c6e9eda40d50fcbbd6 Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Sun, 1 Aug 2021 19:03:03 -0700 Subject: [PATCH 3/5] Normalize the glTF2 weights on export. Remove max and min on weights because it was difficult and it was allowed. --- modules/gltf/gltf_document.cpp | 255 ++++++++++++++++++--------------- modules/gltf/gltf_document.h | 1 + 2 files changed, 144 insertions(+), 112 deletions(-) diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 3c6d752a3e91..a448575b5261 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -945,8 +945,10 @@ Error GLTFDocument::_encode_accessors(Ref state) { d["type"] = _get_accessor_type_name(accessor->type); d["byteOffset"] = accessor->byte_offset; d["normalized"] = accessor->normalized; - d["max"] = accessor->max; - d["min"] = accessor->min; + if (accessor->max.size() && accessor->min.size()) { + d["max"] = accessor->max; + d["min"] = accessor->min; + } d["bufferView"] = accessor->buffer_view; //optional because it may be sparse... // Dictionary s; @@ -1769,18 +1771,12 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref state const int element_count = 4; - Vector type_max; - type_max.resize(element_count); - Vector type_min; - type_min.resize(element_count); for (int i = 0; i < p_attribs.size(); i++) { Color attrib = p_attribs[i]; - attribs.write[(i * element_count) + 0] = Math::snapped(attrib.r, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 1] = Math::snapped(attrib.g, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 2] = Math::snapped(attrib.b, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 3] = Math::snapped(attrib.a, CMP_NORMALIZE_TOLERANCE); - - _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); + attribs.write[(i * element_count) + 0] = attrib.r; + attribs.write[(i * element_count) + 1] = attrib.g; + attribs.write[(i * element_count) + 2] = attrib.b; + attribs.write[(i * element_count) + 3] = attrib.a; } ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); @@ -1792,8 +1788,6 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref state const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; - accessor->max = type_max; - accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); accessor->type = type; @@ -2300,118 +2294,147 @@ Error GLTFDocument::_serialize_meshes(Ref state) { break; } } - { - const Array &a = array[Mesh::ARRAY_BONES]; - const Vector &vertex_array = array[Mesh::ARRAY_VERTEX]; - if ((a.size() / JOINT_GROUP_SIZE) == vertex_array.size()) { - const int ret_size = a.size() / JOINT_GROUP_SIZE; - Vector attribs; - attribs.resize(ret_size); + const Array &array_weights = array[Mesh::ARRAY_WEIGHTS]; + const Array &array_bones = array[Mesh::ARRAY_BONES]; + const Vector &vertex_array = array[Mesh::ARRAY_VERTEX]; + if ((array_weights.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) { + int32_t vertex_count = vertex_array.size(); + Vector weights_0; + weights_0.resize(vertex_count); + Vector weights_1; + weights_1.resize(vertex_count); + int32_t WEIGHT_8_COUNT = JOINT_GROUP_SIZE * 2; + const int ret_size = array_weights.size() / JOINT_GROUP_SIZE; + Vector attribs; + attribs.resize(ret_size); + Vector joints_0; + joints_0.resize(vertex_count); + Vector joints_1; + joints_1.resize(vertex_count); + for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { + Color weight_0; + Color weight_1; + for (int bone_i = 0; bone_i < WEIGHT_8_COUNT; bone_i++) { + weight_0.r = array_weights[vertex_i * WEIGHT_8_COUNT + bone_i]; + } { - for (int array_i = 0; array_i < attribs.size(); array_i++) { - int32_t joint_0 = a[(array_i * JOINT_GROUP_SIZE) + 0]; - int32_t joint_1 = a[(array_i * JOINT_GROUP_SIZE) + 1]; - int32_t joint_2 = a[(array_i * JOINT_GROUP_SIZE) + 2]; - int32_t joint_3 = a[(array_i * JOINT_GROUP_SIZE) + 3]; - attribs.write[array_i] = Color(joint_0, joint_1, joint_2, joint_3); + float total = 0.0; + for (int bone_i = 0; bone_i < JOINT_GROUP_SIZE; bone_i++) { + total += weight_0[bone_i]; + } + for (int bone_i = 0; bone_i < JOINT_GROUP_SIZE; bone_i++) { + total += weight_1[bone_i]; + } + if (total > 0.0) { + weight_0 /= Color(total, total, total, total); + weight_1 /= Color(total, total, total, total); } } - attributes["JOINTS_0"] = _encode_accessor_as_joints(state, attribs, true); - } else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) { - int32_t vertex_count = vertex_array.size(); - Vector joints_0; - joints_0.resize(vertex_count); - Vector joints_1; - joints_1.resize(vertex_count); - int32_t weights_8_count = JOINT_GROUP_SIZE * 2; - for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { - Color joint_0; - joint_0.r = a[vertex_i * weights_8_count + 0]; - joint_0.g = a[vertex_i * weights_8_count + 1]; - joint_0.b = a[vertex_i * weights_8_count + 2]; - joint_0.a = a[vertex_i * weights_8_count + 3]; - joints_0.write[vertex_i] = joint_0; - Color joint_1; - joint_1.r = a[vertex_i * weights_8_count + 4]; - joint_1.g = a[vertex_i * weights_8_count + 5]; - joint_1.b = a[vertex_i * weights_8_count + 6]; - joint_1.a = a[vertex_i * weights_8_count + 7]; - joints_1.write[vertex_i] = joint_1; + Set set_bones; + { + Color joint = { 0.0f, 0.0f, 0.0f, 0.0f }; + for (int bone_i = 0; bone_i < JOINT_GROUP_SIZE; bone_i++) { + _normalize_bone(array_bones[vertex_i * WEIGHT_8_COUNT + bone_i], set_bones, joint[bone_i], weight_0[bone_i]); + } + joints_0.write[vertex_i] = joint; } - attributes["JOINTS_0"] = _encode_accessor_as_joints(state, joints_0, true); - attributes["JOINTS_1"] = _encode_accessor_as_joints(state, joints_1, true); - } - } - { - const Array &a = array[Mesh::ARRAY_WEIGHTS]; - const Vector &vertex_array = array[Mesh::ARRAY_VERTEX]; - if ((a.size() / JOINT_GROUP_SIZE) == vertex_array.size()) { - const int ret_size = a.size() / JOINT_GROUP_SIZE; - Vector attribs; - attribs.resize(ret_size); - for (int i = 0; i < ret_size; i++) { - attribs.write[i] = Color(a[(i * JOINT_GROUP_SIZE) + 0], a[(i * JOINT_GROUP_SIZE) + 1], a[(i * JOINT_GROUP_SIZE) + 2], a[(i * JOINT_GROUP_SIZE) + 3]); + { + Color joint = { 0.0f, 0.0f, 0.0f, 0.0f }; + for (int bone_i = 0; bone_i < JOINT_GROUP_SIZE; bone_i++) { + _normalize_bone(array_bones[vertex_i * WEIGHT_8_COUNT + JOINT_GROUP_SIZE + bone_i], set_bones, joint[bone_i], weight_1[bone_i]); + } + joints_1.write[vertex_i] = joint; } - attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, attribs, true); - } else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) { - int32_t vertex_count = vertex_array.size(); - Vector weights_0; - weights_0.resize(vertex_count); - Vector weights_1; - weights_1.resize(vertex_count); - int32_t weights_8_count = JOINT_GROUP_SIZE * 2; - for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { - Color weight_0; - weight_0.r = a[vertex_i * weights_8_count + 0]; - weight_0.g = a[vertex_i * weights_8_count + 1]; - weight_0.b = a[vertex_i * weights_8_count + 2]; - weight_0.a = a[vertex_i * weights_8_count + 3]; - weights_0.write[vertex_i] = weight_0; - Color weight_1; - weight_1.r = a[vertex_i * weights_8_count + 4]; - weight_1.g = a[vertex_i * weights_8_count + 5]; - weight_1.b = a[vertex_i * weights_8_count + 6]; - weight_1.a = a[vertex_i * weights_8_count + 7]; - weights_1.write[vertex_i] = weight_1; + { + float total = 0.0; + for (int bone_i = 0; bone_i < JOINT_GROUP_SIZE; bone_i++) { + total += weight_0[bone_i]; + } + for (int bone_i = 0; bone_i < JOINT_GROUP_SIZE; bone_i++) { + total += weight_1[bone_i]; + } + if (total > 0.0) { + weight_0 /= Color(total, total, total, total); + weight_1 /= Color(total, total, total, total); + } } - attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, weights_0, true); - attributes["WEIGHTS_1"] = _encode_accessor_as_weights(state, weights_1, true); + weights_0.write[vertex_i] = weight_0; + weights_1.write[vertex_i] = weight_1; } - } - { - Vector mesh_indices = array[Mesh::ARRAY_INDEX]; - if (mesh_indices.size()) { - if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) { - //swap around indices, convert ccw to cw for front face - const int is = mesh_indices.size(); - for (int k = 0; k < is; k += 3) { - SWAP(mesh_indices.write[k + 0], mesh_indices.write[k + 2]); + attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, weights_0, true); + attributes["WEIGHTS_1"] = _encode_accessor_as_weights(state, weights_1, true); + attributes["JOINTS_0"] = _encode_accessor_as_joints(state, joints_0, true); + attributes["JOINTS_1"] = _encode_accessor_as_joints(state, joints_1, true); + } else if ((array_weights.size() / JOINT_GROUP_SIZE) == vertex_array.size()) { + const int ret_size = array_weights.size() / JOINT_GROUP_SIZE; + Vector weight_attribs; + Vector joint_attribs; + weight_attribs.resize(ret_size); + joint_attribs.resize(ret_size); + for (int i = 0; i < ret_size; i++) { + Color weight_0; + for (int bone_i = 0; bone_i < JOINT_GROUP_SIZE; bone_i++) { + weight_0[bone_i] = array_weights[(i * JOINT_GROUP_SIZE) + bone_i]; + } + { + float total = 0.0; + for (int bone_i = 0; bone_i < JOINT_GROUP_SIZE; bone_i++) { + total += weight_0[bone_i]; + } + if (total > 0.0) { + weight_0 /= Color(total, total, total, total); } } - primitive["indices"] = _encode_accessor_as_ints(state, mesh_indices, true); - } else { - if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) { - //generate indices because they need to be swapped for CW/CCW - const Vector &vertices = array[Mesh::ARRAY_VERTEX]; - Ref st; - st.instantiate(); - st->create_from_triangle_arrays(array); - st->index(); - Vector generated_indices = st->commit_to_arrays()[Mesh::ARRAY_INDEX]; - const int vs = vertices.size(); - generated_indices.resize(vs); - { - for (int k = 0; k < vs; k += 3) { - generated_indices.write[k] = k; - generated_indices.write[k + 1] = k + 2; - generated_indices.write[k + 2] = k + 1; - } + Color joint_0 = { 0.0f, 0.0f, 0.0f, 0.0f }; + Set set_bones; + for (int bone_i = 0; bone_i < JOINT_GROUP_SIZE; bone_i++) { + _normalize_bone(array_bones[(i * JOINT_GROUP_SIZE) + 0], set_bones, joint_0[bone_i], weight_0[bone_i]); + } + joint_attribs.write[i] = joint_0; + { + float total = 0.0; + total += weight_0.r + weight_0.g + weight_0.b + weight_0.a; + if (total > 0.0) { + weight_0 /= Color(total, total, total, total); } - primitive["indices"] = _encode_accessor_as_ints(state, generated_indices, true); } + weight_attribs.write[i] = weight_0; } + attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, weight_attribs, true); + attributes["JOINTS_0"] = _encode_accessor_as_joints(state, joint_attribs, true); } + Vector mesh_indices = array[Mesh::ARRAY_INDEX]; + if (mesh_indices.size()) { + if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) { + //swap around indices, convert ccw to cw for front face + const int is = mesh_indices.size(); + for (int k = 0; k < is; k += 3) { + SWAP(mesh_indices.write[k + 0], mesh_indices.write[k + 2]); + } + } + primitive["indices"] = _encode_accessor_as_ints(state, mesh_indices, true); + } else { + if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) { + //generate indices because they need to be swapped for CW/CCW + const Vector &vertices = array[Mesh::ARRAY_VERTEX]; + Ref st; + st.instantiate(); + st->create_from_triangle_arrays(array); + st->index(); + Array mesh_arrays = st->commit_to_arrays(); + ERR_FAIL_INDEX_V(Mesh::ARRAY_INDEX, mesh_arrays.size(), ERR_PARSE_ERROR); + Vector generated_indices = mesh_arrays[Mesh::ARRAY_INDEX]; + const int vs = vertices.size(); + generated_indices.resize(vs); + for (int k = 0; k < vs; k += 3) { + generated_indices.write[k] = k; + generated_indices.write[k + 1] = k + 2; + generated_indices.write[k + 2] = k + 1; + } + primitive["indices"] = _encode_accessor_as_ints(state, generated_indices, true); + } + } primitive["attributes"] = attributes; //blend shapes @@ -6656,6 +6679,14 @@ Error GLTFDocument::_serialize_file(Ref state, const String p_path) { } return err; } +void GLTFDocument::_normalize_bone(int32_t p_bone, Set &r_bones, float &r_joint, float &r_weight) { + if (!Math::is_zero_approx(r_weight) && !r_bones.has(p_bone)) { + r_joint = p_bone; + r_bones.insert(r_joint); + } else { + r_weight = 0.0f; + } +} void GLTFDocument::_create_mesh_skin(Ref state, MeshInstance3D *p_mi, Ref p_gltf_node) { if (p_gltf_node->mesh < 0) { diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index d40ba586c6f7..efb4c0f0249a 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -379,6 +379,7 @@ class GLTFDocument : public Resource { void _convert_scene_node(Ref state, Node *p_current, Node *p_root, const GLTFNodeIndex p_gltf_current, const GLTFNodeIndex p_gltf_root); + void _normalize_bone(int32_t p_bone, Set &r_bones, float &r_joint, float &r_weight); void _create_mesh_skin(Ref state, MeshInstance3D *p_mi, Ref p_gltf_node); void _convert_mesh_skins(Ref state); From 70fd73f5f305565d1a6f38dedf5b1fe029b0e73a Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Mon, 9 Aug 2021 01:18:40 -0700 Subject: [PATCH 4/5] Better blend shape animation export. Export blend shape animations. Better output on blend shape tangent arrays. --- modules/gltf/gltf_document.cpp | 213 ++++++++++++++++++++------------- modules/gltf/gltf_document.h | 1 + 2 files changed, 130 insertions(+), 84 deletions(-) diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index a448575b5261..d52473fa9e29 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -2439,7 +2439,7 @@ Error GLTFDocument::_serialize_meshes(Ref state) { //blend shapes print_verbose("glTF: Mesh has targets"); - if (import_mesh->get_blend_shape_count()) { + if (import_mesh->get_blend_shape_count() && surface_i == 0) { ArrayMesh::BlendShapeMode shape_mode = import_mesh->get_blend_shape_mode(); for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) { Array array_morph = import_mesh->get_surface_blend_shape_arrays(surface_i, morph_i); @@ -2463,6 +2463,20 @@ Error GLTFDocument::_serialize_meshes(Ref state) { if (varr.size()) { t["NORMAL"] = _encode_accessor_as_vec3(state, narr, true); } + Vector tarr = array_morph[Mesh::ARRAY_TANGENT]; + if (tarr.size()) { + const int ret_size = tarr.size() / 4; + Vector attribs; + attribs.resize(ret_size); + for (int i = 0; i < ret_size; i++) { + Vector3 vec3; + vec3.x = tarr[(i * 4) + 0]; + vec3.y = tarr[(i * 4) + 1]; + vec3.z = tarr[(i * 4) + 2]; + vec3 *= tarr[(i * 4) + 3] * -1; + } + t["TANGENT"] = _encode_accessor_as_vec3(state, attribs, true); + } targets.push_back(t); } } @@ -2490,12 +2504,13 @@ Error GLTFDocument::_serialize_meshes(Ref state) { Dictionary e; e["targetNames"] = target_names; - for (int j = 0; j < target_names.size(); j++) { + weights.resize(target_names.size()); + for (int name_i = 0; name_i < target_names.size(); name_i++) { real_t weight = 0.0; - if (j < state->meshes.write[gltf_mesh_i]->get_blend_weights().size()) { - weight = state->meshes.write[gltf_mesh_i]->get_blend_weights()[j]; + if (name_i < state->meshes.write[gltf_mesh_i]->get_blend_weights().size()) { + weight = state->meshes.write[gltf_mesh_i]->get_blend_weights()[name_i]; } - weights.push_back(weight); + weights[name_i] = weight; } if (weights.size()) { gltf_mesh["weights"] = weights; @@ -4644,8 +4659,8 @@ Error GLTFDocument::_serialize_animations(Ref state) { Array channels; Array samplers; - for (Map::Element *track_i = gltf_animation->get_tracks().front(); track_i; track_i = track_i->next()) { - GLTFAnimation::Track track = track_i->get(); + for (Map::Element *anim_track_i = gltf_animation->get_tracks().front(); anim_track_i; anim_track_i = anim_track_i->next()) { + GLTFAnimation::Track track = anim_track_i->get(); if (track.translation_track.times.size()) { Dictionary t; t["sampler"] = samplers.size(); @@ -4661,7 +4676,7 @@ Error GLTFDocument::_serialize_animations(Ref state) { Dictionary target; target["path"] = "translation"; - target["node"] = track_i->key(); + target["node"] = anim_track_i->key(); t["target"] = target; channels.push_back(t); @@ -4681,7 +4696,7 @@ Error GLTFDocument::_serialize_animations(Ref state) { Dictionary target; target["path"] = "rotation"; - target["node"] = track_i->key(); + target["node"] = anim_track_i->key(); t["target"] = target; channels.push_back(t); @@ -4701,42 +4716,86 @@ Error GLTFDocument::_serialize_animations(Ref state) { Dictionary target; target["path"] = "scale"; - target["node"] = track_i->key(); + target["node"] = anim_track_i->key(); t["target"] = target; channels.push_back(t); } if (track.weight_tracks.size()) { + double length = 0.0f; + + for (int32_t weight_track_i = 0; weight_track_i < track.weight_tracks.size(); weight_track_i++) { + int32_t last_time_index = track.weight_tracks[weight_track_i].times.size() - 1; + length = MAX(length, track.weight_tracks[weight_track_i].times[last_time_index]); + } + Dictionary t; t["sampler"] = samplers.size(); Dictionary s; - Vector times; - Vector values; + const double increment = 1.0 / BAKE_FPS; + { + double time = 0.0; + bool last = false; + while (true) { + times.push_back(time); + if (last) { + break; + } + time += increment; + if (time >= length) { + last = true; + time = length; + } + } + } - for (int32_t times_i = 0; times_i < track.weight_tracks[0].times.size(); times_i++) { - real_t time = track.weight_tracks[0].times[times_i]; - times.push_back(time); + for (int32_t interp_weight_track_i = 0; interp_weight_track_i < track.weight_tracks.size(); interp_weight_track_i++) { + double time = 0.0; + bool last = false; + Vector weight_track; + while (true) { + float weight = _interpolate_track(track.weight_tracks[interp_weight_track_i].times, + track.weight_tracks[interp_weight_track_i].values, + time, + track.weight_tracks[interp_weight_track_i].interpolation); + weight_track.push_back(weight); + if (last) { + break; + } + time += increment; + if (time >= length) { + last = true; + time = length; + } + } + track.weight_tracks.write[interp_weight_track_i].times = times; + track.weight_tracks.write[interp_weight_track_i].values = weight_track; } - values.resize(times.size() * track.weight_tracks.size()); - // TODO Sort by order in blend shapes + Vector all_track_times = times; + Vector all_track_values; + int32_t values_size = track.weight_tracks[0].values.size(); + int32_t weight_tracks_size = track.weight_tracks.size(); + all_track_values.resize(weight_tracks_size * values_size); for (int k = 0; k < track.weight_tracks.size(); k++) { Vector wdata = track.weight_tracks[k].values; for (int l = 0; l < wdata.size(); l++) { - values.write[l * track.weight_tracks.size() + k] = wdata.write[l]; + int32_t index = k * weight_tracks_size + l; + ERR_BREAK(index >= all_track_values.size()); + all_track_values.write[index] = wdata.write[l]; } } s["interpolation"] = interpolation_to_string(track.weight_tracks[track.weight_tracks.size() - 1].interpolation); - s["input"] = _encode_accessor_as_floats(state, times, false); - s["output"] = _encode_accessor_as_floats(state, values, false); + s["input"] = _encode_accessor_as_floats(state, all_track_times, false); + s["output"] = _encode_accessor_as_floats(state, all_track_values, false); samplers.push_back(s); Dictionary target; target["path"] = "weights"; - target["node"] = track_i->key(); + target["node"] = anim_track_i->key(); t["target"] = target; channels.push_back(t); @@ -4864,7 +4923,7 @@ Error GLTFDocument::_parse_animations(Ref state) { track->weight_tracks.resize(wc); const int expected_value_count = times.size() * output_count * wc; - ERR_FAIL_COND_V_MSG(weights.size() != expected_value_count, ERR_PARSE_ERROR, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead."); + ERR_CONTINUE_MSG(weights.size() != expected_value_count, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead."); const int wlen = weights.size() / wc; for (int k = 0; k < wc; k++) { //separate tracks, having them together is not such a good idea @@ -6071,7 +6130,6 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref state for (int32_t key_i = 0; key_i < key_count; key_i++) { times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i); } - const float BAKE_FPS = 30.0f; if (track_type == Animation::TYPE_TRANSFORM3D) { p_track.translation_track.times = times; p_track.translation_track.interpolation = gltf_interpolation; @@ -6309,69 +6367,56 @@ void GLTFDocument::_convert_animation(Ref state, AnimationPlayer *ap, const Vector node_suffix = String(orig_track_path).split(":blend_shapes/"); const NodePath path = node_suffix[0]; const String suffix = node_suffix[1]; - const Node *node = ap->get_parent()->get_node_or_null(path); - for (Map::Element *transform_track_i = state->scene_nodes.front(); transform_track_i; transform_track_i = transform_track_i->next()) { - if (transform_track_i->get() == node) { - const MeshInstance3D *mi = Object::cast_to(node); - if (!mi) { - continue; - } - Ref array_mesh = mi->get_mesh(); - if (array_mesh.is_null()) { - continue; - } - if (node_suffix.size() != 2) { - continue; - } - GLTFNodeIndex mesh_index = -1; - for (GLTFNodeIndex node_i = 0; node_i < state->scene_nodes.size(); node_i++) { - if (state->scene_nodes[node_i] == node) { - mesh_index = node_i; - break; - } - } - ERR_CONTINUE(mesh_index == -1); - Ref mesh = mi->get_mesh(); - ERR_CONTINUE(mesh.is_null()); - for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) { - if (mesh->get_blend_shape_name(shape_i) != suffix) { - continue; - } - GLTFAnimation::Track track; - Map::Element *blend_shape_track_i = gltf_animation->get_tracks().find(mesh_index); - if (blend_shape_track_i) { - track = blend_shape_track_i->get(); - } - Animation::InterpolationType interpolation = animation->track_get_interpolation_type(track_i); - - GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR; - if (interpolation == Animation::InterpolationType::INTERPOLATION_LINEAR) { - gltf_interpolation = GLTFAnimation::INTERP_LINEAR; - } else if (interpolation == Animation::InterpolationType::INTERPOLATION_NEAREST) { - gltf_interpolation = GLTFAnimation::INTERP_STEP; - } else if (interpolation == Animation::InterpolationType::INTERPOLATION_CUBIC) { - gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE; - } - Animation::TrackType track_type = animation->track_get_type(track_i); - if (track_type == Animation::TYPE_VALUE) { - int32_t key_count = animation->track_get_key_count(track_i); - GLTFAnimation::Channel weight; - weight.interpolation = gltf_interpolation; - weight.times.resize(key_count); - for (int32_t time_i = 0; time_i < key_count; time_i++) { - weight.times.write[time_i] = animation->track_get_key_time(track_i, time_i); - } - weight.values.resize(key_count); - for (int32_t value_i = 0; value_i < key_count; value_i++) { - weight.values.write[value_i] = animation->track_get_key_value(track_i, value_i); - } - track.weight_tracks.push_back(weight); - } - gltf_animation->get_tracks()[mesh_index] = track; - } + Node *node = ap->get_parent()->get_node_or_null(path); + MeshInstance3D *mi = cast_to(node); + Ref mesh = mi->get_mesh(); + ERR_CONTINUE(mesh.is_null()); + int32_t mesh_index = -1; + for (Map::Element *mesh_track_i = state->scene_nodes.front(); mesh_track_i; mesh_track_i = mesh_track_i->next()) { + if (mesh_track_i->get() == node) { + mesh_index = mesh_track_i->key(); } } - + ERR_CONTINUE(mesh_index == -1); + GLTFAnimation::Track track = gltf_animation->get_tracks().has(mesh_index) ? gltf_animation->get_tracks()[mesh_index] : GLTFAnimation::Track(); + for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) { + String shape_name = mesh->get_blend_shape_name(shape_i); + NodePath shape_path = String(path) + ":blend_shapes/" + shape_name; + int32_t shape_track_i = animation->find_track(shape_path); + if (shape_track_i == -1) { + GLTFAnimation::Channel weight; + weight.interpolation = GLTFAnimation::INTERP_LINEAR; + weight.times.push_back(0.0f); + weight.values.push_back(0.0f); + track.weight_tracks.push_back(weight); + continue; + } + Animation::InterpolationType interpolation = animation->track_get_interpolation_type(track_i); + GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR; + if (interpolation == Animation::InterpolationType::INTERPOLATION_LINEAR) { + gltf_interpolation = GLTFAnimation::INTERP_LINEAR; + } else if (interpolation == Animation::InterpolationType::INTERPOLATION_NEAREST) { + gltf_interpolation = GLTFAnimation::INTERP_STEP; + } else if (interpolation == Animation::InterpolationType::INTERPOLATION_CUBIC) { + gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE; + } + int32_t key_count = animation->track_get_key_count(shape_track_i); + GLTFAnimation::Channel weight; + weight.interpolation = gltf_interpolation; + weight.times.resize(key_count); + for (int32_t time_i = 0; time_i < key_count; time_i++) { + weight.times.write[time_i] = animation->track_get_key_time(shape_track_i, time_i); + } + weight.values.resize(key_count); + for (int32_t value_i = 0; value_i < key_count; value_i++) { + weight.values.write[value_i] = animation->track_get_key_value(shape_track_i, value_i); + } + weight.times.push_back(0.0f); + weight.values.push_back(0.0f); + track.weight_tracks.push_back(weight); + } + Map &tracks = gltf_animation->get_tracks(); + tracks[mesh_index] = track; } else if (String(orig_track_path).find(":") != -1) { //Process skeleton const Vector node_suffix = String(orig_track_path).split(":"); diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index efb4c0f0249a..5bb4d135b49d 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -103,6 +103,7 @@ class GLTFDocument : public Resource { }; private: + const float BAKE_FPS = 30.0f; template static Array to_array(const Vector &p_inp) { Array ret; From 960e9e149986d1aee32377f04995fe135d059cdd Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Mon, 9 Aug 2021 01:39:05 -0700 Subject: [PATCH 5/5] gltf2 normalize normals and quaternions for export. --- modules/gltf/gltf_document.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index d52473fa9e29..0e0a2d265b82 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -2455,15 +2455,19 @@ Error GLTFDocument::_serialize_meshes(Ref state) { varr.write[blend_i] = Vector3(varr[blend_i]) - src_varr[blend_i]; } } - - t["POSITION"] = _encode_accessor_as_vec3(state, varr, true); } - + t["POSITION"] = _encode_accessor_as_vec3(state, varr, true); Vector narr = array_morph[Mesh::ARRAY_NORMAL]; - if (varr.size()) { + if (narr.size()) { + for (int i = 0; i < narr.size(); i++) { + narr.write[i] = Vector3(narr[i]).normalized(); + } t["NORMAL"] = _encode_accessor_as_vec3(state, narr, true); } Vector tarr = array_morph[Mesh::ARRAY_TANGENT]; + const int ret_size = tarr.size() / 4; + Vector attribs; + attribs.resize(ret_size); if (tarr.size()) { const int ret_size = tarr.size() / 4; Vector attribs; @@ -4690,6 +4694,9 @@ Error GLTFDocument::_serialize_animations(Ref state) { Vector times = Variant(track.rotation_track.times); s["input"] = _encode_accessor_as_floats(state, times, false); Vector values = track.rotation_track.values; + for (int i = 0; i < values.size(); i++) { + values.write[i].normalize(); + } s["output"] = _encode_accessor_as_quaternions(state, values, false); samplers.push_back(s);