From 03bd1da32b4704c041c2a912ba173a373a8b90c0 Mon Sep 17 00:00:00 2001 From: Lyuma Date: Thu, 26 Jan 2023 22:41:58 -0800 Subject: Avoid nested skeletons, and handle skinned meshes with children. Recursively adds child nodes into each skeleton. This should prevent nested skeletons and avoid bone attachments for leaf bones. In cases where a skinned mesh has children, creates two scene nodes with the same name, which both will represent this single gltf node. Because blend shape animations must target the mesh, adds a separate mapping for ImporterMeshInstance3D node references. This change will break existing imported scenes with bone attachments and more than one skeleton. Co-authored-by: K. S. Ernest (iFire) Lee --- modules/gltf/gltf_document.cpp | 81 ++++++++++++++++++++++++++++++------------ modules/gltf/gltf_document.h | 2 ++ modules/gltf/gltf_state.h | 2 ++ 3 files changed, 62 insertions(+), 23 deletions(-) (limited to 'modules/gltf') diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index b243ba933d..7a3ba6ceb1 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -4233,6 +4233,21 @@ Error GLTFDocument::_parse_skins(Ref p_state) { return OK; } +void GLTFDocument::_recurse_children(Ref p_state, const GLTFNodeIndex p_node_index, + RBSet &p_all_skin_nodes, HashSet &p_child_visited_set) { + if (p_child_visited_set.has(p_node_index)) { + return; + } + p_child_visited_set.insert(p_node_index); + for (int i = 0; i < p_state->nodes[p_node_index]->children.size(); ++i) { + _recurse_children(p_state, p_state->nodes[p_node_index]->children[i], p_all_skin_nodes, p_child_visited_set); + } + + if (p_state->nodes[p_node_index]->skin < 0 || p_state->nodes[p_node_index]->mesh < 0 || !p_state->nodes[p_node_index]->children.is_empty()) { + p_all_skin_nodes.insert(p_node_index); + } +} + Error GLTFDocument::_determine_skeletons(Ref p_state) { // Using a disjoint set, we are going to potentially combine all skins that are actually branches // of a main skeleton, or treat skins defining the same set of nodes as ONE skeleton. @@ -4243,16 +4258,21 @@ Error GLTFDocument::_determine_skeletons(Ref p_state) { for (GLTFSkinIndex skin_i = 0; skin_i < p_state->skins.size(); ++skin_i) { const Ref skin = p_state->skins[skin_i]; - Vector all_skin_nodes; - all_skin_nodes.append_array(skin->joints); - all_skin_nodes.append_array(skin->non_joints); - - for (int i = 0; i < all_skin_nodes.size(); ++i) { - const GLTFNodeIndex node_index = all_skin_nodes[i]; + HashSet child_visited_set; + RBSet all_skin_nodes; + for (int i = 0; i < skin->joints.size(); ++i) { + all_skin_nodes.insert(skin->joints[i]); + _recurse_children(p_state, skin->joints[i], all_skin_nodes, child_visited_set); + } + for (int i = 0; i < skin->non_joints.size(); ++i) { + all_skin_nodes.insert(skin->non_joints[i]); + _recurse_children(p_state, skin->non_joints[i], all_skin_nodes, child_visited_set); + } + for (GLTFNodeIndex node_index : all_skin_nodes) { const GLTFNodeIndex parent = p_state->nodes[node_index]->parent; skeleton_sets.insert(node_index); - if (all_skin_nodes.find(parent) >= 0) { + if (all_skin_nodes.has(parent)) { skeleton_sets.create_union(parent, node_index); } } @@ -5163,6 +5183,7 @@ ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref p_s ImporterMeshInstance3D *mi = memnew(ImporterMeshInstance3D); print_verbose("glTF: Creating mesh for: " + gltf_node->get_name()); + p_state->scene_mesh_instances.insert(p_node_index, mi); Ref mesh = p_state->meshes.write[gltf_node->mesh]; if (mesh.is_null()) { return mi; @@ -5619,7 +5640,13 @@ void GLTFDocument::_generate_scene_node(Ref p_state, Node *scene_pare } // If none of our GLTFDocumentExtension classes generated us a node, we generate one. if (!current_node) { - if (gltf_node->mesh >= 0) { + if (gltf_node->skin >= 0 && gltf_node->mesh >= 0 && !gltf_node->children.is_empty()) { + current_node = _generate_spatial(p_state, node_index); + Node3D *mesh_inst = _generate_mesh_instance(p_state, node_index); + mesh_inst->set_name(gltf_node->get_name()); + + current_node->add_child(mesh_inst, true); + } else if (gltf_node->mesh >= 0) { current_node = _generate_mesh_instance(p_state, node_index); } else if (gltf_node->camera >= 0) { current_node = _generate_camera(p_state, node_index); @@ -5640,7 +5667,6 @@ void GLTFDocument::_generate_scene_node(Ref p_state, Node *scene_pare current_node->set_name(gltf_node->get_name()); p_state->scene_nodes.insert(node_index, current_node); - for (int i = 0; i < gltf_node->children.size(); ++i) { _generate_scene_node(p_state, current_node, scene_root, gltf_node->children[i]); } @@ -5659,22 +5685,17 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref p_state, Node *p_ Skeleton3D *active_skeleton = Object::cast_to(p_scene_parent); if (active_skeleton != skeleton) { if (active_skeleton) { - // Bone Attachment - Direct Parented Skeleton Case + // Should no longer be possible. + ERR_PRINT(vformat("glTF: Generating scene detected direct parented Skeletons at node %d", p_node_index)); BoneAttachment3D *bone_attachment = _generate_bone_attachment(p_state, active_skeleton, p_node_index, gltf_node->parent); - p_scene_parent->add_child(bone_attachment, true); bone_attachment->set_owner(p_scene_root); - // There is no gltf_node that represent this, so just directly create a unique name bone_attachment->set_name(_gen_unique_name(p_state, "BoneAttachment3D")); - // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node // and attach it to the bone_attachment p_scene_parent = bone_attachment; - WARN_PRINT(vformat("glTF: Generating scene detected direct parented Skeletons at node %d", p_node_index)); } - - // Add it to the scene if it has not already been added if (skeleton->get_parent() == nullptr) { p_scene_parent->add_child(skeleton, true); skeleton->set_owner(p_scene_root); @@ -5682,9 +5703,10 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref p_state, Node *p_ } active_skeleton = skeleton; - current_node = skeleton; + current_node = active_skeleton; if (requires_extra_node) { + current_node = nullptr; // skinned meshes must not be placed in a bone attachment. if (!is_skinned_mesh) { // Bone Attachment - Same Node Case @@ -5885,6 +5907,8 @@ void GLTFDocument::_import_animation(Ref p_state, AnimationPlayer *p_ NodePath node_path; //for skeletons, transform tracks always affect bones NodePath transform_node_path; + //for meshes, especially skinned meshes, there are cases where it will be added as a child + NodePath mesh_instance_node_path; GLTFNodeIndex node_index = track_i.key; @@ -5895,6 +5919,12 @@ void GLTFDocument::_import_animation(Ref p_state, AnimationPlayer *p_ HashMap::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); + HashMap::Iterator mesh_instance_element = p_state->scene_mesh_instances.find(node_index); + if (mesh_instance_element) { + mesh_instance_node_path = root->get_path_to(mesh_instance_element->value); + } else { + mesh_instance_node_path = node_path; + } if (gltf_node->skeleton >= 0) { const Skeleton3D *sk = p_state->skeletons[gltf_node->skeleton]->godot_skeleton; @@ -6067,7 +6097,7 @@ void GLTFDocument::_import_animation(Ref p_state, AnimationPlayer *p_ ERR_CONTINUE(mesh->get_mesh().is_null()); ERR_CONTINUE(mesh->get_mesh()->get_mesh().is_null()); - const String blend_path = String(node_path) + ":" + String(mesh->get_mesh()->get_blend_shape_name(i)); + const String blend_path = String(mesh_instance_node_path) + ":" + String(mesh->get_mesh()->get_blend_shape_name(i)); const int track_idx = animation->get_track_count(); animation->add_track(Animation::TYPE_BLEND_SHAPE); @@ -6258,11 +6288,16 @@ void GLTFDocument::_process_mesh_instances(Ref p_state, Node *p_scene if (node->skin >= 0 && node->mesh >= 0) { const GLTFSkinIndex skin_i = node->skin; - HashMap::Iterator mi_element = p_state->scene_nodes.find(node_i); - ERR_CONTINUE_MSG(!mi_element, vformat("Unable to find node %d", node_i)); - - ImporterMeshInstance3D *mi = Object::cast_to(mi_element->value); - ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, mi_element->value->get_class_name())); + ImporterMeshInstance3D *mi = nullptr; + HashMap::Iterator mi_element = p_state->scene_mesh_instances.find(node_i); + if (mi_element) { + mi = mi_element->value; + } else { + HashMap::Iterator si_element = p_state->scene_nodes.find(node_i); + ERR_CONTINUE_MSG(!si_element, vformat("Unable to find node %d", node_i)); + mi = Object::cast_to(si_element->value); + ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, si_element->value->get_class_name())); + } const GLTFSkeletonIndex skel_i = p_state->skins.write[node->skin]->skeleton; Ref gltf_skeleton = p_state->skeletons.write[skel_i]; diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 164c63c53c..81ac91ba03 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -160,6 +160,8 @@ private: float &r_metallic); GLTFNodeIndex _find_highest_node(Ref p_state, const Vector &p_subset); + void _recurse_children(Ref p_state, const GLTFNodeIndex p_node_index, + RBSet &p_all_skin_nodes, HashSet &p_child_visited_set); bool _capture_nodes_in_skin(Ref p_state, Ref p_skin, const GLTFNodeIndex p_node_index); void _capture_nodes_for_multirooted_skin(Ref p_state, Ref p_skin); diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index 488ad038aa..abb597b6b3 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -89,6 +89,7 @@ class GLTFState : public Resource { HashMap skeleton_to_node; Vector> animations; HashMap scene_nodes; + HashMap scene_mesh_instances; HashMap skeleton3d_to_gltf_skeleton; HashMap> skin_and_skeleton3d_to_gltf_skin; @@ -182,6 +183,7 @@ public: void set_animations(TypedArray p_animations); Node *get_scene_node(GLTFNodeIndex idx); + ImporterMeshInstance3D *get_scene_mesh_instance(GLTFNodeIndex idx); int get_animation_players_count(int idx); -- cgit v1.2.3