Merge pull request #102144 from Cwazywierdo/get-euler-epsilon

Core: Fix `Basis::get_euler` incorrectly simplifying rotations in some cases.
This commit is contained in:
Thaddeus Crews
2025-02-03 08:15:42 -06:00
2 changed files with 29 additions and 18 deletions

View File

@ -455,6 +455,11 @@ void Basis::get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) cons
}
Vector3 Basis::get_euler(EulerOrder p_order) const {
// This epsilon value results in angles within a +/- 0.04 degree range being simplified/truncated.
// Based on testing, this is the largest the epsilon can be without the angle truncation becoming
// visually noticeable.
const real_t epsilon = 0.00000025;
switch (p_order) {
case EulerOrder::XYZ: {
// Euler angles in XYZ convention.
@ -466,8 +471,8 @@ Vector3 Basis::get_euler(EulerOrder p_order) const {
Vector3 euler;
real_t sy = rows[0][2];
if (sy < (1.0f - (real_t)CMP_EPSILON)) {
if (sy > -(1.0f - (real_t)CMP_EPSILON)) {
if (sy < (1.0f - epsilon)) {
if (sy > -(1.0f - epsilon)) {
// is this a pure Y rotation?
if (rows[1][0] == 0 && rows[0][1] == 0 && rows[1][2] == 0 && rows[2][1] == 0 && rows[1][1] == 1) {
// return the simplest form (human friendlier in editor and scripts)
@ -501,8 +506,8 @@ Vector3 Basis::get_euler(EulerOrder p_order) const {
Vector3 euler;
real_t sz = rows[0][1];
if (sz < (1.0f - (real_t)CMP_EPSILON)) {
if (sz > -(1.0f - (real_t)CMP_EPSILON)) {
if (sz < (1.0f - epsilon)) {
if (sz > -(1.0f - epsilon)) {
euler.x = Math::atan2(rows[2][1], rows[1][1]);
euler.y = Math::atan2(rows[0][2], rows[0][0]);
euler.z = Math::asin(-sz);
@ -532,8 +537,8 @@ Vector3 Basis::get_euler(EulerOrder p_order) const {
real_t m12 = rows[1][2];
if (m12 < (1 - (real_t)CMP_EPSILON)) {
if (m12 > -(1 - (real_t)CMP_EPSILON)) {
if (m12 < (1 - epsilon)) {
if (m12 > -(1 - epsilon)) {
// is this a pure X rotation?
if (rows[1][0] == 0 && rows[0][1] == 0 && rows[0][2] == 0 && rows[2][0] == 0 && rows[0][0] == 1) {
// return the simplest form (human friendlier in editor and scripts)
@ -568,8 +573,8 @@ Vector3 Basis::get_euler(EulerOrder p_order) const {
Vector3 euler;
real_t sz = rows[1][0];
if (sz < (1.0f - (real_t)CMP_EPSILON)) {
if (sz > -(1.0f - (real_t)CMP_EPSILON)) {
if (sz < (1.0f - epsilon)) {
if (sz > -(1.0f - epsilon)) {
euler.x = Math::atan2(-rows[1][2], rows[1][1]);
euler.y = Math::atan2(-rows[2][0], rows[0][0]);
euler.z = Math::asin(sz);
@ -596,8 +601,8 @@ Vector3 Basis::get_euler(EulerOrder p_order) const {
// -cx*sy sx cx*cy
Vector3 euler;
real_t sx = rows[2][1];
if (sx < (1.0f - (real_t)CMP_EPSILON)) {
if (sx > -(1.0f - (real_t)CMP_EPSILON)) {
if (sx < (1.0f - epsilon)) {
if (sx > -(1.0f - epsilon)) {
euler.x = Math::asin(sx);
euler.y = Math::atan2(-rows[2][0], rows[2][2]);
euler.z = Math::atan2(-rows[0][1], rows[1][1]);
@ -624,8 +629,8 @@ Vector3 Basis::get_euler(EulerOrder p_order) const {
// -sy cy*sx cy*cx
Vector3 euler;
real_t sy = rows[2][0];
if (sy < (1.0f - (real_t)CMP_EPSILON)) {
if (sy > -(1.0f - (real_t)CMP_EPSILON)) {
if (sy < (1.0f - epsilon)) {
if (sy > -(1.0f - epsilon)) {
euler.x = Math::atan2(rows[2][1], rows[2][2]);
euler.y = Math::asin(-sy);
euler.z = Math::atan2(rows[1][0], rows[0][0]);