Fix for SkeletonIK3D interpolation and bone roll

Fix bug in internal Basis::rotate_to_align function (also used with identity Basis in scene/resources/curve.cpp)
Use ChainItem children rather than local bone rest to determine IK bone roll to match Godot 3.x behavior
This commit is contained in:
Lyuma 2023-05-25 02:58:17 -07:00
parent 15b7a8e7f1
commit 9aa46bf3f5
2 changed files with 2 additions and 22 deletions

View File

@ -397,7 +397,7 @@ void Basis::rotate_to_align(Vector3 p_start_direction, Vector3 p_end_direction)
real_t dot = p_start_direction.dot(p_end_direction); real_t dot = p_start_direction.dot(p_end_direction);
dot = CLAMP(dot, -1.0f, 1.0f); dot = CLAMP(dot, -1.0f, 1.0f);
const real_t angle_rads = Math::acos(dot); const real_t angle_rads = Math::acos(dot);
set_axis_angle(axis, angle_rads); *this = Basis(axis, angle_rads) * (*this);
} }
} }

View File

@ -249,26 +249,6 @@ void FabrikInverseKinematic::make_goal(Task *p_task, const Transform3D &p_invers
} }
} }
static Vector3 get_bone_axis_forward_vector(Skeleton3D *skeleton, int p_bone) {
// If it is a child/leaf bone...
if (skeleton->get_bone_parent(p_bone) > 0) {
return skeleton->get_bone_rest(p_bone).origin.normalized();
}
// If it has children...
Vector<int> child_bones = skeleton->get_bone_children(p_bone);
if (child_bones.size() == 0) {
WARN_PRINT_ONCE("Cannot calculate forward direction for bone " + itos(p_bone));
WARN_PRINT_ONCE("Assuming direction of (0, 1, 0) for bone");
return Vector3(0, 1, 0);
}
Vector3 combined_child_dir = Vector3(0, 0, 0);
for (int i = 0; i < child_bones.size(); i++) {
combined_child_dir += skeleton->get_bone_rest(child_bones[i]).origin.normalized();
}
combined_child_dir = combined_child_dir / child_bones.size();
return combined_child_dir.normalized();
}
void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) { void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) {
if (blending_delta <= 0.01f) { if (blending_delta <= 0.01f) {
// Before skipping, make sure we undo the global pose overrides // Before skipping, make sure we undo the global pose overrides
@ -307,7 +287,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
new_bone_pose.origin = ci->current_pos; new_bone_pose.origin = ci->current_pos;
if (!ci->children.is_empty()) { if (!ci->children.is_empty()) {
Vector3 forward_vector = get_bone_axis_forward_vector(p_task->skeleton, ci->bone); Vector3 forward_vector = (ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized();
// Rotate the bone towards the next bone in the chain: // Rotate the bone towards the next bone in the chain:
new_bone_pose.basis.rotate_to_align(forward_vector, new_bone_pose.origin.direction_to(ci->children[0].current_pos)); new_bone_pose.basis.rotate_to_align(forward_vector, new_bone_pose.origin.direction_to(ci->children[0].current_pos));