diff options
-rw-r--r-- | editor/import/editor_scene_importer_gltf.cpp | 176 | ||||
-rw-r--r-- | editor/import/editor_scene_importer_gltf.h | 9 |
2 files changed, 160 insertions, 25 deletions
diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 2f29d72ce4..6f609a8ab2 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -35,6 +35,7 @@ #include "core/math/math_defs.h" #include "core/os/file_access.h" #include "core/os/os.h" +#include "modules/regex/regex.h" #include "scene/3d/bone_attachment.h" #include "scene/3d/camera.h" #include "scene/3d/mesh_instance.h" @@ -154,14 +155,21 @@ static Transform _arr_to_xform(const Array &p_array) { return xform; } +String EditorSceneImporterGLTF::_sanitize_scene_name(const String &name) { + RegEx regex("([^a-zA-Z0-9_ ]+)"); + String p_name = regex.sub(name, "", true); + return p_name; +} + String EditorSceneImporterGLTF::_gen_unique_name(GLTFState &state, const String &p_name) { - int index = 1; + const String s_name = _sanitize_scene_name(p_name); String name; + int index = 1; while (true) { + name = s_name; - name = p_name; if (index > 1) { name += " " + itos(index); } @@ -176,6 +184,47 @@ String EditorSceneImporterGLTF::_gen_unique_name(GLTFState &state, const String return name; } +String EditorSceneImporterGLTF::_sanitize_bone_name(const String &name) { + String p_name = name.camelcase_to_underscore(true); + + RegEx pattern_del("([^a-zA-Z0-9_ ])+"); + p_name = pattern_del.sub(p_name, "", true); + + RegEx pattern_nospace(" +"); + p_name = pattern_nospace.sub(p_name, "_", true); + + RegEx pattern_multiple("_+"); + p_name = pattern_multiple.sub(p_name, "_", true); + + RegEx pattern_padded("0+(\\d+)"); + p_name = pattern_padded.sub(p_name, "$1", true); + + return p_name; +} + +String EditorSceneImporterGLTF::_gen_unique_bone_name(GLTFState &state, const GLTFSkeletonIndex skel_i, const String &p_name) { + + const String s_name = _sanitize_bone_name(p_name); + + String name; + int index = 1; + while (true) { + name = s_name; + + if (index > 1) { + name += "_" + itos(index); + } + if (!state.skeletons[skel_i].unique_names.has(name)) { + break; + } + index++; + } + + state.skeletons.write[skel_i].unique_names.insert(name); + + return name; +} + Error EditorSceneImporterGLTF::_parse_scenes(GLTFState &state) { ERR_FAIL_COND_V(!state.json.has("scenes"), ERR_FILE_CORRUPT); @@ -188,7 +237,7 @@ Error EditorSceneImporterGLTF::_parse_scenes(GLTFState &state) { state.root_nodes.push_back(nodes[j]); } - if (s.has("name")) { + if (s.has("name") && s["name"] != "") { state.scene_name = _gen_unique_name(state, s["name"]); } else { state.scene_name = _gen_unique_name(state, "Scene"); @@ -1425,6 +1474,7 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { const String &am = d["alphaMode"]; if (am != "OPAQUE") { material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + material->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); } } @@ -1749,17 +1799,22 @@ Error EditorSceneImporterGLTF::_determine_skeletons(GLTFState &state) { const GLTFNodeIndex skeleton_owner = skeleton_owners[skel_i]; GLTFSkeleton skeleton; + Vector<GLTFNodeIndex> skeleton_nodes; + skeleton_sets.get_members(skeleton_nodes, skeleton_owner); + for (GLTFSkinIndex skin_i = 0; skin_i < state.skins.size(); ++skin_i) { GLTFSkin &skin = state.skins.write[skin_i]; - if (skin.joints.find(skeleton_owner) >= 0 || skin.non_joints.find(skeleton_owner) >= 0) { - skin.skeleton = skel_i; + // If any of the the skeletons nodes exist in a skin, that skin now maps to the skeleton + for (int i = 0; i < skeleton_nodes.size(); ++i) { + GLTFNodeIndex skel_node_i = skeleton_nodes[i]; + if (skin.joints.find(skel_node_i) >= 0 || skin.non_joints.find(skel_node_i) >= 0) { + skin.skeleton = skel_i; + continue; + } } } - Vector<GLTFNodeIndex> skeleton_nodes; - skeleton_sets.get_members(skeleton_nodes, skeleton_owner); - Vector<GLTFNodeIndex> non_joints; for (int i = 0; i < skeleton_nodes.size(); ++i) { const GLTFNodeIndex node_i = skeleton_nodes[i]; @@ -1864,8 +1919,8 @@ Error EditorSceneImporterGLTF::_reparent_to_fake_joint(GLTFState &state, GLTFSke fake_joint->xform = node->xform; fake_joint->joint = true; - // Create a new name - fake_joint->name = node->name + "_fake_joint"; + // We can use the exact same name here, because the joint will be inside a skeleton and not the scene + fake_joint->name = node->name; // Clear the nodes transforms, since it will be parented to the fake joint node->translation = Vector3(0, 0, 0); @@ -1971,7 +2026,7 @@ Error EditorSceneImporterGLTF::_create_skeletons(GLTFState &state) { Skeleton *skeleton = memnew(Skeleton); gltf_skeleton.godot_skeleton = skeleton; - //skeleton->set_use_bones_in_world_transform(true); + // Make a unique name, no gltf node represents this skeleton skeleton->set_name(_gen_unique_name(state, "Skeleton")); List<GLTFNodeIndex> bones; @@ -1980,26 +2035,41 @@ Error EditorSceneImporterGLTF::_create_skeletons(GLTFState &state) { bones.push_back(gltf_skeleton.roots[i]); } + // Make the skeleton creation deterministic by going through the roots in + // a sorted order, and DEPTH FIRST + bones.sort(); + while (!bones.empty()) { const GLTFNodeIndex node_i = bones.front()->get(); bones.pop_front(); GLTFNode *node = state.nodes[node_i]; + ERR_FAIL_COND_V(node->skeleton != skel_i, FAILED); + + { // Add all child nodes to the stack (deterministically) + Vector<GLTFNodeIndex> child_nodes; + for (int i = 0; i < node->children.size(); ++i) { + const GLTFNodeIndex child_i = node->children[i]; + if (state.nodes[child_i]->skeleton == skel_i) { + child_nodes.push_back(child_i); + } + } - // Add all child nodes to the stack - for (int i = 0; i < node->children.size(); ++i) { - const GLTFNodeIndex child_i = node->children[i]; - if (state.nodes[child_i]->skeleton == skel_i) { - bones.push_back(child_i); + // Depth first insertion + child_nodes.sort(); + for (int i = child_nodes.size() - 1; i >= 0; --i) { + bones.push_front(child_nodes[i]); } } const int bone_index = skeleton->get_bone_count(); if (node->name.empty()) { - node->name = "Bone " + itos(bone_index); + node->name = "bone"; } + node->name = _gen_unique_bone_name(state, skel_i, node->name); + skeleton->add_bone(node->name); skeleton->set_bone_rest(bone_index, node->xform); skeleton->set_bone_pose(bone_index, node->xform); @@ -2087,8 +2157,6 @@ Error EditorSceneImporterGLTF::_create_skins(GLTFState &state) { Ref<Skin> skin; skin.instance(); - skin->set_name(_gen_unique_name(state, "Skin")); - for (int joint_i = 0; joint_i < gltf_skin.joints_original.size(); ++joint_i) { int bone_i = gltf_skin.joint_i_to_bone_i[joint_i]; @@ -2098,9 +2166,57 @@ Error EditorSceneImporterGLTF::_create_skins(GLTFState &state) { gltf_skin.godot_skin = skin; } + // Purge the duplicates! + _remove_duplicate_skins(state); + + // Create unique names now, after removing duplicates + for (GLTFSkinIndex skin_i = 0; skin_i < state.skins.size(); ++skin_i) { + Ref<Skin> skin = state.skins[skin_i].godot_skin; + if (skin->get_name().empty()) { + // Make a unique name, no gltf node represents this skin + skin->set_name(_gen_unique_name(state, "Skin")); + } + } + return OK; } +bool EditorSceneImporterGLTF::_skins_are_same(const Ref<Skin> &skin_a, const Ref<Skin> &skin_b) { + if (skin_a->get_bind_count() != skin_b->get_bind_count()) { + return false; + } + + for (int i = 0; i < skin_a->get_bind_count(); ++i) { + + if (skin_a->get_bind_bone(i) != skin_b->get_bind_bone(i)) { + return false; + } + + Transform a_xform = skin_a->get_bind_pose(i); + Transform b_xform = skin_b->get_bind_pose(i); + + if (a_xform != b_xform) { + return false; + } + } + + return true; +} + +void EditorSceneImporterGLTF::_remove_duplicate_skins(GLTFState &state) { + for (int i = 0; i < state.skins.size(); ++i) { + for (int j = i + 1; j < state.skins.size(); ++j) { + const Ref<Skin>& skin_i = state.skins[i].godot_skin; + const Ref<Skin>& skin_j = state.skins[j].godot_skin; + + if (_skins_are_same(skin_i, skin_j)) { + // replace it and delete the old + state.skins.write[j].godot_skin = skin_i; + } + } + } +} + Error EditorSceneImporterGLTF::_parse_cameras(GLTFState &state) { if (!state.json.has("cameras")) @@ -2282,11 +2398,16 @@ void EditorSceneImporterGLTF::_assign_scene_names(GLTFState &state) { for (int i = 0; i < state.nodes.size(); i++) { GLTFNode *n = state.nodes[i]; - if (n->name == "") { + + // Any joints get unique names generated when the skeleton is made, unique to the skeleton + if (n->skeleton >= 0) + continue; + + if (n->name.empty()) { if (n->mesh >= 0) { n->name = "Mesh"; - } else if (n->joint) { - n->name = "Bone"; + } else if (n->camera >= 0) { + n->name = "Camera"; } else { n->name = "Node"; } @@ -2378,7 +2499,8 @@ void EditorSceneImporterGLTF::_generate_scene_node(GLTFState &state, Node *scene scene_parent->add_child(bone_attachment); bone_attachment->set_owner(scene_root); - bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment " + gltf_node->name)); + // There is no gltf_node that represent this, so just directly create a unique name + bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment")); // 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 @@ -2549,6 +2671,7 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye String name = anim.name; if (name.empty()) { + // No node represent these, and they are not in the hierarchy, so just make a unique name name = _gen_unique_name(state, "Animation"); } @@ -2677,11 +2800,12 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye ERR_CONTINUE(node->mesh < 0 || node->mesh >= state.meshes.size()); const GLTFMesh &mesh = state.meshes[node->mesh]; const String prop = "blend_shapes/" + mesh.mesh->get_blend_shape_name(i); - node_path = String(node_path) + ":" + prop; + + const String blend_path = String(node_path) + ":" + prop; const int track_idx = animation->get_track_count(); animation->add_track(Animation::TYPE_VALUE); - animation->track_set_path(track_idx, node_path); + animation->track_set_path(track_idx, blend_path); // Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation, // the other modes have to be baked. @@ -2748,6 +2872,8 @@ void EditorSceneImporterGLTF::_process_mesh_instances(GLTFState &state, Spatial Spatial *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, const int p_bake_fps) { Spatial *root = memnew(Spatial); + + // scene_name is already unique root->set_name(state.scene_name); for (int i = 0; i < state.root_nodes.size(); ++i) { diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h index 711064f2ec..e0331adf6c 100644 --- a/editor/import/editor_scene_importer_gltf.h +++ b/editor/import/editor_scene_importer_gltf.h @@ -191,6 +191,9 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { // The created Skeleton for the scene Skeleton *godot_skeleton; + // Set of unique bone names for the skeleton + Set<String> unique_names; + GLTFSkeleton() : godot_skeleton(nullptr) { } @@ -326,8 +329,12 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { } }; + String _sanitize_scene_name(const String &name); String _gen_unique_name(GLTFState &state, const String &p_name); + String _sanitize_bone_name(const String &name); + String _gen_unique_bone_name(GLTFState &state, const GLTFSkeletonIndex skel_i, const String &p_name); + Ref<Texture> _get_texture(GLTFState &state, const GLTFTextureIndex p_texture); Error _parse_json(const String &p_path, GLTFState &state); @@ -381,6 +388,8 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Transform _compute_skin_to_skeleton_transform(const GLTFState &state, const GLTFNodeIndex skin_parent, const GLTFNodeIndex skeleton_parent); void _compute_skeleton_rooted_skin_inverse_binds(GLTFState &state, const GLTFSkinIndex skin_i); Error _create_skins(GLTFState &state); + bool _skins_are_same(const Ref<Skin>& skin_a, const Ref<Skin>& skin_b); + void _remove_duplicate_skins(GLTFState& state); Error _parse_cameras(GLTFState &state); |