Add support for named binds in Skin.

Helps better reutilization of skeletons from Maya exported files.
This commit is contained in:
Juan Linietsky 2020-02-21 07:27:48 -03:00
parent 851cb42963
commit 9a34f39d32
8 changed files with 110 additions and 8 deletions

View file

@ -2183,6 +2183,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);
@ -2204,12 +2206,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);
}
}
@ -2995,6 +3003,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);

View file

@ -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<int, int> joint_i_to_bone_i;
Map<int, StringName> 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<uint8_t> glb_data;
bool use_named_skin_binds;
Vector<GLTFNode *> nodes;
Vector<Vector<uint8_t> > buffers;
Vector<GLTFBufferView> buffer_views;

View file

@ -1171,6 +1171,7 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *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<String> missing_deps; // for now, not much will be done with this
Node *scene = importer->import_scene(src_path, import_flags, fps, &missing_deps, &err);

View file

@ -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,
};

View file

@ -41,6 +41,7 @@ void SkinReference::_skin_changed() {
if (skeleton_node) {
skeleton_node->_make_dirty();
}
skeleton_version = 0;
}
void SkinReference::_bind_methods() {
@ -322,10 +323,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));
}
@ -388,6 +428,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();
}
@ -539,7 +580,7 @@ void Skeleton::clear_bones() {
bones.clear();
process_order_dirty = true;
version++;
_make_dirty();
}
@ -894,6 +935,7 @@ Skeleton::Skeleton() {
animate_physical_bones = true;
dirty = false;
version = 1;
process_order_dirty = true;
}

View file

@ -51,6 +51,9 @@ class SkinReference : public Reference {
RID skeleton;
Ref<Skin> skin;
uint32_t bind_count = 0;
uint64_t skeleton_version = 0;
Vector<uint32_t> skin_bone_indices;
uint32_t *skin_bone_indices_ptrs;
void _skin_changed();
protected:
@ -123,6 +126,8 @@ private:
void _make_dirty();
bool dirty;
uint64_t version;
// bind helpers
Array _get_bound_child_nodes_to_bone(int p_bone) const {

View file

@ -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<PropertyInfo> *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", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"));
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);

View file

@ -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());