Adjust BoneAttachment3D children/meshes during rest fixer

Also simplifies equivalent matrix math which previously used ibm_diffs to calculate skinned mesh offsets.

(cherry picked from commit 7b71061b3e)
This commit is contained in:
Lyuma 2023-07-20 12:45:54 +02:00 committed by Yuri Sizov
parent 1fcf58d72e
commit a18fe83298
1 changed files with 35 additions and 47 deletions

View File

@ -31,6 +31,7 @@
#include "post_import_plugin_skeleton_rest_fixer.h" #include "post_import_plugin_skeleton_rest_fixer.h"
#include "editor/import/scene_import_settings.h" #include "editor/import/scene_import_settings.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/skeleton_3d.h" #include "scene/3d/skeleton_3d.h"
#include "scene/animation/animation_player.h" #include "scene/animation/animation_player.h"
@ -105,42 +106,6 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
global_transform.origin = Vector3(); // Translation by a Node is not a bone animation, so the retargeted model should be at the origin. global_transform.origin = Vector3(); // Translation by a Node is not a bone animation, so the retargeted model should be at the origin.
} }
// Calc IBM difference.
LocalVector<Vector<Transform3D>> ibm_diffs;
{
TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D");
while (nodes.size()) {
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back());
ERR_CONTINUE(!mi);
Ref<Skin> skin = mi->get_skin();
ERR_CONTINUE(!skin.is_valid());
Node *node = mi->get_node(mi->get_skeleton_path());
ERR_CONTINUE(!node);
Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node);
if (!mesh_skeleton || mesh_skeleton != src_skeleton) {
continue;
}
Vector<Transform3D> ibm_diff;
ibm_diff.resize(src_skeleton->get_bone_count());
Transform3D *ibm_diff_w = ibm_diff.ptrw();
int skin_len = skin->get_bind_count();
for (int i = 0; i < skin_len; i++) {
StringName bn = skin->get_bind_name(i);
int bone_idx = src_skeleton->find_bone(bn);
if (bone_idx >= 0) {
ibm_diff_w[bone_idx] = global_transform * src_skeleton->get_bone_global_rest(bone_idx) * skin->get_bind_pose(i);
}
}
ibm_diffs.push_back(ibm_diff);
}
}
// Apply node transforms. // Apply node transforms.
if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) { if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) {
Vector3 scl = global_transform.basis.get_scale_local(); Vector3 scl = global_transform.basis.get_scale_local();
@ -288,12 +253,11 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
Vector<Transform3D> silhouette_diff; // Transform values to be ignored when overwrite axis. Vector<Transform3D> silhouette_diff; // Transform values to be ignored when overwrite axis.
silhouette_diff.resize(src_skeleton->get_bone_count()); silhouette_diff.resize(src_skeleton->get_bone_count());
Transform3D *silhouette_diff_w = silhouette_diff.ptrw(); Transform3D *silhouette_diff_w = silhouette_diff.ptrw();
LocalVector<Transform3D> pre_silhouette_skeleton_global_rest;
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
pre_silhouette_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
}
if (bool(p_options["retarget/rest_fixer/fix_silhouette/enable"])) { if (bool(p_options["retarget/rest_fixer/fix_silhouette/enable"])) {
LocalVector<Transform3D> old_skeleton_global_rest;
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
}
Vector<int> bones_to_process = prof_skeleton->get_parentless_bones(); Vector<int> bones_to_process = prof_skeleton->get_parentless_bones();
while (bones_to_process.size() > 0) { while (bones_to_process.size() > 0) {
int prof_idx = bones_to_process[0]; int prof_idx = bones_to_process[0];
@ -450,7 +414,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
// For skin modification in overwrite rest. // For skin modification in overwrite rest.
for (int i = 0; i < src_skeleton->get_bone_count(); i++) { for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
silhouette_diff_w[i] = old_skeleton_global_rest[i] * src_skeleton->get_bone_global_rest(i).inverse(); silhouette_diff_w[i] = pre_silhouette_skeleton_global_rest[i] * src_skeleton->get_bone_global_rest(i).affine_inverse();
} }
is_rest_changed = true; is_rest_changed = true;
@ -652,7 +616,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
ERR_CONTINUE(!mi); ERR_CONTINUE(!mi);
Ref<Skin> skin = mi->get_skin(); Ref<Skin> skin = mi->get_skin();
ERR_CONTINUE(!skin.is_valid()); if (skin.is_null()) {
continue;
}
Node *node = mi->get_node(mi->get_skeleton_path()); Node *node = mi->get_node(mi->get_skeleton_path());
ERR_CONTINUE(!node); ERR_CONTINUE(!node);
@ -662,20 +628,42 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
continue; continue;
} }
Vector<Transform3D> ibm_diff = ibm_diffs[skin_idx];
int skin_len = skin->get_bind_count(); int skin_len = skin->get_bind_count();
for (int i = 0; i < skin_len; i++) { for (int i = 0; i < skin_len; i++) {
StringName bn = skin->get_bind_name(i); StringName bn = skin->get_bind_name(i);
int bone_idx = src_skeleton->find_bone(bn); int bone_idx = src_skeleton->find_bone(bn);
if (bone_idx >= 0) { if (bone_idx >= 0) {
Transform3D new_rest = silhouette_diff[bone_idx] * src_skeleton->get_bone_global_rest(bone_idx); Transform3D adjust_transform = src_skeleton->get_bone_global_rest(bone_idx).affine_inverse() * silhouette_diff[bone_idx].affine_inverse() * pre_silhouette_skeleton_global_rest[bone_idx];
skin->set_bind_pose(i, new_rest.inverse() * ibm_diff[bone_idx]); adjust_transform.scale(global_transform.basis.get_scale_local());
skin->set_bind_pose(i, adjust_transform * skin->get_bind_pose(i));
} }
} }
skin_idx++; skin_idx++;
} }
nodes = src_skeleton->get_children();
while (nodes.size()) {
BoneAttachment3D *attachment = Object::cast_to<BoneAttachment3D>(nodes.pop_back());
if (attachment == nullptr) {
continue;
}
int bone_idx = attachment->get_bone_idx();
if (bone_idx == -1) {
bone_idx = src_skeleton->find_bone(attachment->get_bone_name());
}
ERR_CONTINUE(bone_idx < 0 || bone_idx >= src_skeleton->get_bone_count());
Transform3D adjust_transform = src_skeleton->get_bone_global_rest(bone_idx).affine_inverse() * silhouette_diff[bone_idx].affine_inverse() * pre_silhouette_skeleton_global_rest[bone_idx];
adjust_transform.scale(global_transform.basis.get_scale_local());
TypedArray<Node> child_nodes = attachment->get_children();
while (child_nodes.size()) {
Node3D *child = Object::cast_to<Node3D>(child_nodes.pop_back());
if (child == nullptr) {
continue;
}
child->set_transform(adjust_transform * child->get_transform());
}
}
} }
// Init skeleton pose to new rest. // Init skeleton pose to new rest.