diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 2f9135c52c7..4c22ff1a989 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -2179,6 +2179,8 @@ Error EditorSceneImporterGLTF::_map_skin_joints_indices_to_skeleton_bone_indices const GLTFNodeIndex node_i = skin.joints_original[joint_index]; const GLTFNode *node = state.nodes[node_i]; + skin.joint_i_to_name.insert(joint_index, node->name); + const int bone_index = skeleton.godot_skeleton->find_bone(node->name); ERR_FAIL_COND_V(bone_index < 0, FAILED); @@ -2200,12 +2202,18 @@ Error EditorSceneImporterGLTF::_create_skins(GLTFState &state) { const bool has_ibms = !gltf_skin.inverse_binds.empty(); 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]; + Transform xform; if (has_ibms) { - skin->add_bind(bone_i, gltf_skin.inverse_binds[joint_i]); + xform = gltf_skin.inverse_binds[joint_i]; + } + + if (state.use_named_skin_binds) { + StringName name = gltf_skin.joint_i_to_name[joint_i]; + skin->add_named_bind(name, xform); } else { - skin->add_bind(bone_i, Transform()); + int bone_i = gltf_skin.joint_i_to_bone_i[joint_i]; + skin->add_bind(bone_i, xform); } } @@ -2991,6 +2999,7 @@ Node *EditorSceneImporterGLTF::import_scene(const String &p_path, uint32_t p_fla state.major_version = version.get_slice(".", 0).to_int(); state.minor_version = version.get_slice(".", 1).to_int(); + state.use_named_skin_binds = p_flags & IMPORT_USE_NAMED_SKIN_BINDS; /* STEP 0 PARSE SCENE */ Error err = _parse_scenes(state); diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h index 78d7106b0d7..8e9c44db935 100644 --- a/editor/import/editor_scene_importer_gltf.h +++ b/editor/import/editor_scene_importer_gltf.h @@ -231,6 +231,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { // A mapping from the joint indices (in the order of joints_original) to the // Godot Skeleton's bone_indices Map joint_i_to_bone_i; + Map joint_i_to_name; // The Actual Skin that will be created as a mapping between the IBM's of this skin // to the generated skeleton for the mesh instances. @@ -298,6 +299,8 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { int minor_version; Vector glb_data; + bool use_named_skin_binds; + Vector nodes; Vector > buffers; Vector buffer_views; diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 0774d0b5dcc..0a612c64b20 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1171,6 +1171,7 @@ void ResourceImporterScene::get_import_options(List *r_options, in r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/storage", PROPERTY_HINT_ENUM, "Built-In,Files (.mesh),Files (.tres)"), meshes_out ? 1 : 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable,Gen Lightmaps", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "external_files/store_in_subdir"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15)); @@ -1313,6 +1314,9 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p if (int(p_options["materials/location"]) == 0) import_flags |= EditorSceneImporter::IMPORT_MATERIALS_IN_INSTANCES; + if (bool(p_options["skins/use_named_skins"])) + import_flags |= EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS; + Error err = OK; List missing_deps; // for now, not much will be done with this Node *scene = importer->import_scene(src_path, import_flags, fps, &missing_deps, &err); diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 2691b224eb5..20e7af15b5d 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -59,7 +59,8 @@ public: IMPORT_GENERATE_TANGENT_ARRAYS = 256, IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 512, IMPORT_MATERIALS_IN_INSTANCES = 1024, - IMPORT_USE_COMPRESSION = 2048 + IMPORT_USE_COMPRESSION = 2048, + IMPORT_USE_NAMED_SKIN_BINDS = 4096, }; diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 5edce284b5f..be9bd93e621 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -40,6 +40,7 @@ void SkinReference::_skin_changed() { if (skeleton_node) { skeleton_node->_make_dirty(); } + skeleton_version = 0; } void SkinReference::_bind_methods() { @@ -321,10 +322,49 @@ void Skeleton::_notification(int p_what) { if (E->get()->bind_count != bind_count) { VS::get_singleton()->skeleton_allocate(skeleton, bind_count); E->get()->bind_count = bind_count; + E->get()->skin_bone_indices.resize(bind_count); + E->get()->skin_bone_indices_ptrs = E->get()->skin_bone_indices.ptrw(); + } + + if (E->get()->skeleton_version != version) { + + for (uint32_t i = 0; i < bind_count; i++) { + StringName bind_name = skin->get_bind_name(i); + + if (bind_name != StringName()) { + //bind name used, use this + bool found = false; + for (int j = 0; j < len; j++) { + if (bonesptr[j].name == bind_name) { + E->get()->skin_bone_indices_ptrs[i] = j; + found = true; + break; + } + } + + if (!found) { + ERR_PRINT("Skin bind #" + itos(i) + " contains named bind '" + String(bind_name) + "' but Skeleton has no bone by that name."); + E->get()->skin_bone_indices_ptrs[i] = 0; + } + } else if (skin->get_bind_bone(i) >= 0) { + int bind_index = skin->get_bind_bone(i); + if (bind_index >= len) { + ERR_PRINT("Skin bind #" + itos(i) + " contains bone index bind: " + itos(bind_index) + " , which is greater than the skeleton bone count: " + itos(len) + "."); + E->get()->skin_bone_indices_ptrs[i] = 0; + } else { + E->get()->skin_bone_indices_ptrs[i] = bind_index; + } + } else { + ERR_PRINT("Skin bind #" + itos(i) + " does not contain a name nor a bone index."); + E->get()->skin_bone_indices_ptrs[i] = 0; + } + } + + E->get()->skeleton_version = version; } for (uint32_t i = 0; i < bind_count; i++) { - uint32_t bone_index = skin->get_bind_bone(i); + uint32_t bone_index = E->get()->skin_bone_indices_ptrs[i]; ERR_CONTINUE(bone_index >= (uint32_t)len); vs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i)); } @@ -366,6 +406,7 @@ void Skeleton::add_bone(const String &p_name) { b.name = p_name; bones.push_back(b); process_order_dirty = true; + version++; _make_dirty(); update_gizmo(); } @@ -517,7 +558,7 @@ void Skeleton::clear_bones() { bones.clear(); process_order_dirty = true; - + version++; _make_dirty(); } @@ -849,6 +890,7 @@ void Skeleton::_bind_methods() { Skeleton::Skeleton() { dirty = false; + version = 1; process_order_dirty = true; } diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index 056f70e22be..6a69fbdb93f 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -51,6 +51,9 @@ class SkinReference : public Reference { RID skeleton; Ref skin; uint32_t bind_count = 0; + uint64_t skeleton_version = 0; + Vector skin_bone_indices; + uint32_t *skin_bone_indices_ptrs; void _skin_changed(); protected: @@ -122,6 +125,8 @@ private: void _make_dirty(); bool dirty; + uint64_t version; + // bind helpers Array _get_bound_child_nodes_to_bone(int p_bone) const { diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp index 9c8710a59ca..68b9bafaec8 100644 --- a/scene/resources/skin.cpp +++ b/scene/resources/skin.cpp @@ -45,6 +45,24 @@ void Skin::add_bind(int p_bone, const Transform &p_pose) { set_bind_pose(index, p_pose); } +void Skin::add_named_bind(const String &p_name, const Transform &p_pose) { + + uint32_t index = bind_count; + set_bind_count(bind_count + 1); + set_bind_name(index, p_name); + set_bind_pose(index, p_pose); +} + +void Skin::set_bind_name(int p_index, const StringName &p_name) { + ERR_FAIL_INDEX(p_index, bind_count); + bool notify_change = (binds_ptr[p_index].name != StringName()) != (p_name != StringName()); + binds_ptr[p_index].name = p_name; + emit_changed(); + if (notify_change) { + _change_notify(); + } +} + void Skin::set_bind_bone(int p_index, int p_bone) { ERR_FAIL_INDEX(p_index, bind_count); binds_ptr[p_index].bone = p_bone; @@ -75,6 +93,9 @@ bool Skin::_set(const StringName &p_name, const Variant &p_value) { if (what == "bone") { set_bind_bone(index, p_value); return true; + } else if (what == "name") { + set_bind_name(index, p_value); + return true; } else if (what == "pose") { set_bind_pose(index, p_value); return true; @@ -95,6 +116,9 @@ bool Skin::_get(const StringName &p_name, Variant &r_ret) const { if (what == "bone") { r_ret = get_bind_bone(index); return true; + } else if (what == "name") { + r_ret = get_bind_name(index); + return true; } else if (what == "pose") { r_ret = get_bind_pose(index); return true; @@ -105,7 +129,8 @@ bool Skin::_get(const StringName &p_name, Variant &r_ret) const { void Skin::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::INT, "bind_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater")); for (int i = 0; i < get_bind_count(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater")); + p_list->push_back(PropertyInfo(Variant::STRING, "bind/" + itos(i) + "/name")); + p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NOEDITOR : PROPERTY_USAGE_DEFAULT)); p_list->push_back(PropertyInfo(Variant::TRANSFORM, "bind/" + itos(i) + "/pose")); } } @@ -120,6 +145,9 @@ void Skin::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bind_pose", "bind_index", "pose"), &Skin::set_bind_pose); ClassDB::bind_method(D_METHOD("get_bind_pose", "bind_index"), &Skin::get_bind_pose); + ClassDB::bind_method(D_METHOD("set_bind_name", "bind_index", "name"), &Skin::set_bind_name); + ClassDB::bind_method(D_METHOD("get_bind_name", "bind_index"), &Skin::get_bind_name); + ClassDB::bind_method(D_METHOD("set_bind_bone", "bind_index", "bone"), &Skin::set_bind_bone); ClassDB::bind_method(D_METHOD("get_bind_bone", "bind_index"), &Skin::get_bind_bone); diff --git a/scene/resources/skin.h b/scene/resources/skin.h index ddc7c655f5a..57aaf1afd41 100644 --- a/scene/resources/skin.h +++ b/scene/resources/skin.h @@ -37,7 +37,8 @@ class Skin : public Resource { GDCLASS(Skin, Resource) struct Bind { - int bone; + int bone = -1; + StringName name; Transform pose; }; @@ -58,9 +59,11 @@ public: inline int get_bind_count() const { return bind_count; } void add_bind(int p_bone, const Transform &p_pose); + void add_named_bind(const String &p_name, const Transform &p_pose); void set_bind_bone(int p_index, int p_bone); void set_bind_pose(int p_index, const Transform &p_pose); + void set_bind_name(int p_index, const StringName &p_name); inline int get_bind_bone(int p_index) const { #ifdef DEBUG_ENABLED @@ -69,6 +72,13 @@ public: return binds_ptr[p_index].bone; } + inline StringName get_bind_name(int p_index) const { +#ifdef DEBUG_ENABLED + ERR_FAIL_INDEX_V(p_index, bind_count, StringName()); +#endif + return binds_ptr[p_index].name; + } + inline Transform get_bind_pose(int p_index) const { #ifdef DEBUG_ENABLED ERR_FAIL_INDEX_V(p_index, bind_count, Transform());