Fixed Skeleton2D TwoBoneIK and LookAt mirroring

Co-authored-by: thiagola92 <thiagola92@gmail.com>
This commit is contained in:
cafebeef 2025-09-06 16:19:49 -05:00
parent 3c7f9b9372
commit bc0eba5296
2 changed files with 42 additions and 30 deletions

View file

@ -141,37 +141,32 @@ void SkeletonModification2DLookAt::_execute(float p_delta) {
return;
}
Transform2D operation_transform = operation_bone->get_global_transform();
Transform2D target_trans = target_node_reference->get_global_transform();
real_t angle_to_target = operation_bone->get_angle_to(target_node_reference->get_global_position());
// Look at the target!
operation_transform = operation_transform.looking_at(target_trans.get_origin());
// Apply whatever scale it had prior to looking_at
operation_transform.set_scale(operation_bone->get_global_scale());
// Account for the direction the bone faces in:
operation_transform.set_rotation(operation_transform.get_rotation() - operation_bone->get_bone_angle());
// Account for the direction the bone faces in
angle_to_target -= operation_bone->get_bone_angle();
// Apply additional rotation
operation_transform.set_rotation(operation_transform.get_rotation() + additional_rotation);
angle_to_target += additional_rotation;
// Apply constraints in globalspace:
if (enable_constraint && !constraint_in_localspace) {
operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert));
if (enable_constraint) {
real_t new_angle = angle_to_target;
if (constraint_in_localspace) {
new_angle += operation_bone->get_rotation();
new_angle = clamp_angle(new_angle, constraint_angle_min, constraint_angle_max, constraint_angle_invert);
operation_bone->set_rotation(new_angle);
} else {
new_angle += operation_bone->get_global_rotation();
new_angle = clamp_angle(new_angle, constraint_angle_min, constraint_angle_max, constraint_angle_invert);
operation_bone->set_global_rotation(new_angle);
}
// Convert from a global transform to a local transform via the Bone2D node
operation_bone->set_global_transform(operation_transform);
operation_transform = operation_bone->get_transform();
// Apply constraints in localspace:
if (enable_constraint && constraint_in_localspace) {
operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert));
} else {
operation_bone->rotate(angle_to_target);
}
// Set the local pose override, and to make sure child bones are also updated, set the transform of the bone.
stack->skeleton->set_bone_local_pose_override(bone_idx, operation_transform, stack->strength, true);
operation_bone->set_transform(operation_transform);
stack->skeleton->set_bone_local_pose_override(bone_idx, operation_bone->get_transform(), stack->strength, true);
}
void SkeletonModification2DLookAt::_setup_modification(SkeletonModificationStack2D *p_stack) {

View file

@ -142,7 +142,7 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) {
return;
}
// Adopted from the links below:
// Adapted from the links below:
// http://theorangeduck.com/page/simple-two-joint
// https://www.alanzucconi.com/2018/05/02/ik-2d-2/
// With modifications by TwistedTwigleg
@ -150,9 +150,10 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) {
float joint_one_to_target = target_difference.length();
float angle_atan = target_difference.angle();
float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y);
float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y);
float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().abs().x, joint_one_bone->get_global_scale().abs().y);
float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().abs().x, joint_two_bone->get_global_scale().abs().y);
bool override_angles_due_to_out_of_range = false;
bool same_scale_sign = true;
if (joint_one_to_target < target_minimum_distance) {
joint_one_to_target = target_minimum_distance;
@ -165,6 +166,10 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) {
override_angles_due_to_out_of_range = true;
}
if (joint_one_bone->get_global_scale().sign().x != joint_one_bone->get_global_scale().sign().y) {
same_scale_sign = false;
}
if (!override_angles_due_to_out_of_range) {
float angle_0 = Math::acos(((joint_one_to_target * joint_one_to_target) + (bone_one_length * bone_one_length) - (bone_two_length * bone_two_length)) / (2.0 * joint_one_to_target * bone_one_length));
float angle_1 = Math::acos(((bone_two_length * bone_two_length) + (bone_one_length * bone_one_length) - (joint_one_to_target * joint_one_to_target)) / (2.0 * bone_two_length * bone_one_length));
@ -177,12 +182,23 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) {
if (std::isnan(angle_0) || std::isnan(angle_1)) {
// We cannot solve for this angle! Do nothing to avoid setting the rotation (and scale) to NaN.
} else {
if (same_scale_sign) {
joint_one_bone->set_global_rotation(angle_atan - angle_0 - joint_one_bone->get_bone_angle());
} else {
joint_one_bone->set_global_rotation(angle_atan + angle_0 + joint_one_bone->get_bone_angle());
}
joint_two_bone->set_rotation(-Math::PI - angle_1 - joint_two_bone->get_bone_angle() + joint_one_bone->get_bone_angle());
}
} else {
if (same_scale_sign) {
joint_one_bone->set_global_rotation(angle_atan - joint_one_bone->get_bone_angle());
joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle());
} else {
joint_one_bone->set_global_rotation(angle_atan + joint_one_bone->get_bone_angle());
joint_two_bone->set_global_rotation(angle_atan + joint_two_bone->get_bone_angle());
}
}
stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, joint_one_bone->get_transform(), stack->strength, true);
@ -211,7 +227,8 @@ void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() {
}
stack->skeleton->draw_set_transform(
stack->skeleton->to_local(operation_bone_one->get_global_position()),
operation_bone_one->get_global_rotation() - stack->skeleton->get_global_rotation());
operation_bone_one->get_global_rotation() - stack->skeleton->get_global_rotation(),
operation_bone_one->get_global_scale());
Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4);
#ifdef TOOLS_ENABLED