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:
parent
15b7a8e7f1
commit
9aa46bf3f5
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue