Fix LookAtModifier3D / AimModifier3D forward vector

This commit is contained in:
Silc Lizard (Tokage) Renew
2026-02-01 05:01:33 +09:00
parent d48f9d45e2
commit 381ad1e4b5
4 changed files with 13 additions and 11 deletions

View File

@@ -91,8 +91,9 @@
<member name="primary_rotation_axis" type="int" setter="set_primary_rotation_axis" getter="get_primary_rotation_axis" enum="Vector3.Axis" default="1">
The axis of the first rotation. This [SkeletonModifier3D] works by compositing the rotation by Euler angles to prevent to rotate the [member forward_axis].
</member>
<member name="relative" type="bool" setter="set_relative" getter="is_relative" default="true">
<member name="relative" type="bool" setter="set_relative" getter="is_relative" default="false">
The relative option. If [code]true[/code], the rotation is applied relative to the pose. If [code]false[/code], the rotation is applied relative to the rest. It means to replace the current pose with the [LookAtModifier3D]'s result.
[b]Note:[/b] This option affects the base angle for [member use_angle_limitation] unlike [IterateIK3D]'s [JointLimitation3D]. Since the [LookAtModifier3D] relies strongly on Euler rotation, the axis that determines the limitation and the actual rotation are strongly tied together.
</member>
<member name="secondary_damp_threshold" type="float" setter="set_secondary_damp_threshold" getter="get_secondary_damp_threshold">
The threshold to start damping for [member secondary_limit_angle].
@@ -124,7 +125,7 @@
</member>
<member name="use_angle_limitation" type="bool" setter="set_use_angle_limitation" getter="is_using_angle_limitation" default="false">
If [code]true[/code], limits the amount of rotation. For example, this helps to prevent a character's neck from rotating 360 degrees.
[b]Note:[/b] As with [AnimationTree] blending, interpolation is provided that favors [method Skeleton3D.get_bone_rest]. This means that interpolation does not select the shortest path in some cases.
[b]Note:[/b] As with [AnimationTree] blending, interpolation is provided that favors [method Skeleton3D.get_bone_rest] or [method Skeleton3D.get_bone_pose] depends on the [member relative] option. This means that interpolation does not select the shortest path in some cases.
[b]Note:[/b] Some values for [member transition_type] (such as [constant Tween.TRANS_BACK], [constant Tween.TRANS_ELASTIC], and [constant Tween.TRANS_SPRING]) may exceed the limitations. If interpolation occurs while overshooting the limitations, the result might not respect the bone rest.
</member>
<member name="use_secondary_rotation" type="bool" setter="set_use_secondary_rotation" getter="is_using_secondary_rotation" default="true">

View File

@@ -204,8 +204,9 @@ void AimModifier3D::_process_constraint_by_node(int p_index, Skeleton3D *p_skele
if (!nd) {
return;
}
Vector3 reference_origin = nd->get_global_transform_interpolated().origin - p_skeleton->get_global_transform_interpolated().origin;
_process_aim(p_index, p_skeleton, p_apply_bone, reference_origin, p_amount);
Transform3D skel_tr = p_skeleton->get_global_transform_interpolated();
Vector3 reference_origin = nd->get_global_transform_interpolated().origin - skel_tr.origin;
_process_aim(p_index, p_skeleton, p_apply_bone, skel_tr.basis.get_rotation_quaternion().xform_inv(reference_origin), p_amount);
}
void AimModifier3D::_process_aim(int p_index, Skeleton3D *p_skeleton, int p_apply_bone, Vector3 p_target, float p_amount) {

View File

@@ -559,6 +559,7 @@ void LookAtModifier3D::_process_modification(double p_delta) {
forward_vector = Vector3(0, 0, 0); // The zero-vector to be used for checking in the line immediately below to avoid animation glitch.
} else {
destination = look_at_with_axes(bone_rest).basis.get_rotation_quaternion();
forward_vector = bone_rest.basis.xform_inv(forward_vector_nrm); // Forward vector in the "current" bone rest.
}
}
@@ -574,18 +575,17 @@ void LookAtModifier3D::_process_modification(double p_delta) {
init_transition();
} else if (is_flippable && std::signbit(prev_forward_vector[secondary_rotation_axis]) != std::signbit(forward_vector[secondary_rotation_axis])) {
// Flipping by angle_limitation can be detected by sign of secondary rotation axes during forward_vector is rotated more than 90 degree from forward_axis (means dot production is negative).
Vector3 prev_forward_vector_nrm = forward_vector.normalized();
Vector3 rest_forward_vector = get_vector_from_bone_axis(forward_axis);
if (symmetry_limitation) {
if ((is_not_max_influence || !Math::is_equal_approx(primary_limit_angle, (float)Math::TAU)) &&
prev_forward_vector_nrm.dot(rest_forward_vector) < 0 &&
forward_vector_nrm.dot(rest_forward_vector) < 0) {
prev_forward_vector.dot(rest_forward_vector) < 0 &&
forward_vector.dot(rest_forward_vector) < 0) {
init_transition();
}
} else {
if ((is_not_max_influence || !Math::is_equal_approx(primary_positive_limit_angle + primary_negative_limit_angle, (float)Math::TAU)) &&
prev_forward_vector_nrm.dot(rest_forward_vector) < 0 &&
forward_vector_nrm.dot(rest_forward_vector) < 0) {
prev_forward_vector.dot(rest_forward_vector) < 0 &&
forward_vector.dot(rest_forward_vector) < 0) {
init_transition();
}
}
@@ -596,7 +596,7 @@ void LookAtModifier3D::_process_modification(double p_delta) {
remaining = MAX(0, remaining - time_step * p_delta);
if (is_flippable) {
// Interpolate through the rest same as AnimationTree blending for preventing to penetrate the bone into the body.
Quaternion rest = skeleton->get_bone_rest(bone).basis.get_rotation_quaternion();
Quaternion rest = bone_rest.basis.get_rotation_quaternion();
float weight = Tween::run_equation(transition_type, ease_type, 1 - remaining, 0.0, 1.0, 1.0);
destination = Animation::interpolate_via_rest(Animation::interpolate_via_rest(rest, from_q, 1 - weight, rest), destination, weight, rest);
} else {

View File

@@ -53,7 +53,7 @@ private:
Vector3::Axis primary_rotation_axis = Vector3::AXIS_Y;
Vector3::Axis secondary_rotation_axis = Vector3::AXIS_X;
bool use_secondary_rotation = true;
bool relative = true;
bool relative = false;
OriginFrom origin_from = ORIGIN_FROM_SELF;
String origin_bone_name;