-
-
Notifications
You must be signed in to change notification settings - Fork 19.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
look_at() can't look up #57457
Comments
I believe this is gimbal lock. You are providing two directions, both of which are the same, relatively to the node. So yeah, it could look up, but there is an infinite amount of possible rotations around that axis that fulfill that direction... so it cannot work because the node has no idea which one to take. Usually it can figure it out because the two directions are not the same, but here they are. It should even tell you in the debugger:
Maybe you need to specify another vector instead of
Usually I really just don't use |
We discussed a solution for the Quaternion 180° rotation and this issue was mentioned. For me, the solution looks simple. If the cross product between Up and looking-at direction is 0, it should re-use the X-direction basis-vector from the node's transform where the Basis::looking_at() function was called. (EDIT: it requires a negation if the target and the original looking direction are opposite.) Otherwise, the code behaves like it does at the moment: normalizing the cross product and use it as X-Axis. You shouldn't need to manipulate the up vector because the nodes current ground direction does not depend on where it wants to look at. The singularity in this case is caused by a loss of information in the Basis::looking_at() function arguments. The up vector is used to represent a linear mapping to a perpendicular vector but there is no linear mapping which produces a non-zero perpendicular vector for all input vectors. With the given arguments it's therefore impossible to produce situation-appropriate expected behaviour for every case. The idea behind reusing the previous vector in the edge case is, because it minimizes the change between the previous and the next vector while preserving the original angles between the basis vectors. Option 1: // choose a sensible default value for all cases and only use this function where it doesn't matter
Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front)
{
return Basis::looking_at(p_target, p_up, p_use_model_front, std::nullopt);
}
// the p_base_x value is the x column of the previous basis
Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front, const Vector3 &p_lateral, std::optional<Vector3> p_base_x) {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(p_target.is_zero_approx(), Basis(), "The target vector can't be zero.");
ERR_FAIL_COND_V_MSG(p_up.is_zero_approx(), Basis(), "The up vector can't be zero.");
#endif
Vector3 v_z = p_target.normalized();
if (!p_use_model_front) {
v_z = -v_z;
}
Vector3 v_x = p_up.cross(v_z);
if (v_x.is_zero_approx()) {
if (p_base_x) v_x = p_base_x * sign(p_up.x * v_z.x);
else v_x = ::default_perpendicular_vector(v_z); // explanation below
}
else {
v_x.normalize();
}
Vector3 v_y = v_z.cross(v_x);
Basis basis;
basis.set_columns(v_x, v_y, v_z);
return basis;
} The Option 2: Do not change The original bug is actually, that the Basis::looking_at() function is used for implementing Node3D::look_at() or Node3D::look_at_from_position() because the static Basis produces a fresh node-independent basis, throwing away the old Basis information. |
Another problem is, if the basis was distorted or non-uniformly scaled. The scale and the skewness will be lost after applying this function. Ideally, the scale and angles would be preserved in the basis. The typical and efficient way of handling these looking_at operations is a matrix multiplication with the basis to obtain a new basis. Computing a rotation matrix on the fly is not that expensive actually if efficiently computed. The corner case is just the 0° or 180° rotation which can be handled specifically by using a defined default rotation axis, such as Y axis. |
Godot version
3.4.2
System information
windows 10
Issue description
b.look_at( b.transform.origin+Vector3.UP, Vector3.UP )
When I try to make my object look upwards, it fails to rotate at all.
Based on the documentation, it should end up with the -z towards the target position.
Steps to reproduce
b.look_at( b.transform.origin+Vector3.UP, Vector3.UP )
Minimal reproduction project
No response
The text was updated successfully, but these errors were encountered: