Merge pull request #10190 from tagcup/euler_yxz

Use YXZ convention for Euler angles.
This commit is contained in:
Rémi Verschelde 2017-08-11 10:38:46 +02:00 committed by GitHub
commit 5c6e41cc13
5 changed files with 145 additions and 20 deletions

View File

@ -338,7 +338,7 @@ void Basis::set_rotation_axis_angle(const Vector3 &p_axis, real_t p_angle) {
rotate(p_axis, p_angle); rotate(p_axis, p_angle);
} }
// get_euler returns a vector containing the Euler angles in the format // get_euler_xyz returns a vector containing the Euler angles in the format
// (a1,a2,a3), where a3 is the angle of the first rotation, and a1 is the last // (a1,a2,a3), where a3 is the angle of the first rotation, and a1 is the last
// (following the convention they are commonly defined in the literature). // (following the convention they are commonly defined in the literature).
// //
@ -348,7 +348,7 @@ void Basis::set_rotation_axis_angle(const Vector3 &p_axis, real_t p_angle) {
// And thus, assuming the matrix is a rotation matrix, this function returns // And thus, assuming the matrix is a rotation matrix, this function returns
// the angles in the decomposition R = X(a1).Y(a2).Z(a3) where Z(a) rotates // the angles in the decomposition R = X(a1).Y(a2).Z(a3) where Z(a) rotates
// around the z-axis by a and so on. // around the z-axis by a and so on.
Vector3 Basis::get_euler() const { Vector3 Basis::get_euler_xyz() const {
// Euler angles in XYZ convention. // Euler angles in XYZ convention.
// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
@ -366,6 +366,9 @@ Vector3 Basis::get_euler() const {
if (euler.y > -Math_PI * 0.5) { if (euler.y > -Math_PI * 0.5) {
//if rotation is Y-only, return a proper -pi,pi range like in x or z for the same case. //if rotation is Y-only, return a proper -pi,pi range like in x or z for the same case.
if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[0][0] < 0.0) { if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[0][0] < 0.0) {
euler.x = 0;
euler.z = 0;
if (euler.y > 0.0) if (euler.y > 0.0)
euler.y = Math_PI - euler.y; euler.y = Math_PI - euler.y;
else else
@ -389,10 +392,11 @@ Vector3 Basis::get_euler() const {
return euler; return euler;
} }
// set_euler expects a vector containing the Euler angles in the format // set_euler_xyz expects a vector containing the Euler angles in the format
// (c,b,a), where a is the angle of the first rotation, and c is the last. // (ax,ay,az), where ax is the angle of rotation around x axis,
// and similar for other axes.
// The current implementation uses XYZ convention (Z is the first rotation). // The current implementation uses XYZ convention (Z is the first rotation).
void Basis::set_euler(const Vector3 &p_euler) { void Basis::set_euler_xyz(const Vector3 &p_euler) {
real_t c, s; real_t c, s;
@ -412,6 +416,78 @@ void Basis::set_euler(const Vector3 &p_euler) {
*this = xmat * (ymat * zmat); *this = xmat * (ymat * zmat);
} }
// get_euler_yxz returns a vector containing the Euler angles in the YXZ convention,
// as in first-Z, then-X, last-Y. The angles for X, Y, and Z rotations are returned
// as the x, y, and z components of a Vector3 respectively.
Vector3 Basis::get_euler_yxz() const {
// Euler angles in YXZ convention.
// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
//
// rot = cy*cz+sy*sx*sz cz*sy*sx-cy*sz cx*sy
// cx*sz cx*cz -sx
// cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx
Vector3 euler;
#ifdef MATH_CHECKS
ERR_FAIL_COND_V(is_rotation() == false, euler);
#endif
real_t m12 = elements[1][2];
if (m12 < 1) {
if (m12 > -1) {
if (elements[1][0] == 0 && elements[0][1] == 0 && elements[2][2] < 0) { // use pure x rotation
real_t x = asin(-m12);
euler.y = 0;
euler.z = 0;
if (x > 0.0)
euler.x = Math_PI - x;
else
euler.x = -(Math_PI + x);
} else {
euler.x = asin(-m12);
euler.y = atan2(elements[0][2], elements[2][2]);
euler.z = atan2(elements[1][0], elements[1][1]);
}
} else { // m12 == -1
euler.x = Math_PI * 0.5;
euler.y = -atan2(-elements[0][1], elements[0][0]);
euler.z = 0;
}
} else { // m12 == 1
euler.x = -Math_PI * 0.5;
euler.y = -atan2(-elements[0][1], elements[0][0]);
euler.z = 0;
}
return euler;
}
// set_euler_yxz expects a vector containing the Euler angles in the format
// (ax,ay,az), where ax is the angle of rotation around x axis,
// and similar for other axes.
// The current implementation uses YXZ convention (Z is the first rotation).
void Basis::set_euler_yxz(const Vector3 &p_euler) {
real_t c, s;
c = Math::cos(p_euler.x);
s = Math::sin(p_euler.x);
Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c);
c = Math::cos(p_euler.y);
s = Math::sin(p_euler.y);
Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
c = Math::cos(p_euler.z);
s = Math::sin(p_euler.z);
Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
//optimizer will optimize away all this anyway
*this = ymat * xmat * zmat;
}
bool Basis::is_equal_approx(const Basis &a, const Basis &b) const { bool Basis::is_equal_approx(const Basis &a, const Basis &b) const {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {

View File

@ -84,8 +84,13 @@ public:
void set_rotation_euler(const Vector3 &p_euler); void set_rotation_euler(const Vector3 &p_euler);
void set_rotation_axis_angle(const Vector3 &p_axis, real_t p_angle); void set_rotation_axis_angle(const Vector3 &p_axis, real_t p_angle);
Vector3 get_euler() const; Vector3 get_euler_xyz() const;
void set_euler(const Vector3 &p_euler); void set_euler_xyz(const Vector3 &p_euler);
Vector3 get_euler_yxz() const;
void set_euler_yxz(const Vector3 &p_euler);
Vector3 get_euler() const { return get_euler_yxz(); };
void set_euler(const Vector3 &p_euler) { set_euler_yxz(p_euler); };
void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const; void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const;
void set_axis_angle(const Vector3 &p_axis, real_t p_phi); void set_axis_angle(const Vector3 &p_axis, real_t p_phi);

View File

@ -31,10 +31,11 @@
#include "matrix3.h" #include "matrix3.h"
#include "print_string.h" #include "print_string.h"
// set_euler expects a vector containing the Euler angles in the format // set_euler_xyz expects a vector containing the Euler angles in the format
// (c,b,a), where a is the angle of the first rotation, and c is the last. // (ax,ay,az), where ax is the angle of rotation around x axis,
// The current implementation uses XYZ convention (Z is the first rotation). // and similar for other axes.
void Quat::set_euler(const Vector3 &p_euler) { // This implementation uses XYZ convention (Z is the first rotation).
void Quat::set_euler_xyz(const Vector3 &p_euler) {
real_t half_a1 = p_euler.x * 0.5; real_t half_a1 = p_euler.x * 0.5;
real_t half_a2 = p_euler.y * 0.5; real_t half_a2 = p_euler.y * 0.5;
real_t half_a3 = p_euler.z * 0.5; real_t half_a3 = p_euler.z * 0.5;
@ -56,12 +57,48 @@ void Quat::set_euler(const Vector3 &p_euler) {
-sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3); -sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3);
} }
// get_euler returns a vector containing the Euler angles in the format // get_euler_xyz returns a vector containing the Euler angles in the format
// (a1,a2,a3), where a3 is the angle of the first rotation, and a1 is the last. // (ax,ay,az), where ax is the angle of rotation around x axis,
// The current implementation uses XYZ convention (Z is the first rotation). // and similar for other axes.
Vector3 Quat::get_euler() const { // This implementation uses XYZ convention (Z is the first rotation).
Vector3 Quat::get_euler_xyz() const {
Basis m(*this); Basis m(*this);
return m.get_euler(); return m.get_euler_xyz();
}
// set_euler_yxz expects a vector containing the Euler angles in the format
// (ax,ay,az), where ax is the angle of rotation around x axis,
// and similar for other axes.
// This implementation uses YXZ convention (Z is the first rotation).
void Quat::set_euler_yxz(const Vector3 &p_euler) {
real_t half_a1 = p_euler.y * 0.5;
real_t half_a2 = p_euler.x * 0.5;
real_t half_a3 = p_euler.z * 0.5;
// R = Y(a1).X(a2).Z(a3) convention for Euler angles.
// Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
// a3 is the angle of the first rotation, following the notation in this reference.
real_t cos_a1 = Math::cos(half_a1);
real_t sin_a1 = Math::sin(half_a1);
real_t cos_a2 = Math::cos(half_a2);
real_t sin_a2 = Math::sin(half_a2);
real_t cos_a3 = Math::cos(half_a3);
real_t sin_a3 = Math::sin(half_a3);
set(sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3,
sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3,
-sin_a1 * sin_a2 * cos_a3 + cos_a1 * sin_a2 * sin_a3,
sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3);
}
// get_euler_yxz returns a vector containing the Euler angles in the format
// (ax,ay,az), where ax is the angle of rotation around x axis,
// and similar for other axes.
// This implementation uses YXZ convention (Z is the first rotation).
Vector3 Quat::get_euler_yxz() const {
Basis m(*this);
return m.get_euler_yxz();
} }
void Quat::operator*=(const Quat &q) { void Quat::operator*=(const Quat &q) {

View File

@ -51,8 +51,15 @@ public:
bool is_normalized() const; bool is_normalized() const;
Quat inverse() const; Quat inverse() const;
_FORCE_INLINE_ real_t dot(const Quat &q) const; _FORCE_INLINE_ real_t dot(const Quat &q) const;
void set_euler(const Vector3 &p_euler);
Vector3 get_euler() const; void set_euler_xyz(const Vector3 &p_euler);
Vector3 get_euler_xyz() const;
void set_euler_yxz(const Vector3 &p_euler);
Vector3 get_euler_yxz() const;
void set_euler(const Vector3 &p_euler) { set_euler_yxz(p_euler); };
Vector3 get_euler() const { return get_euler_yxz(); };
Quat slerp(const Quat &q, const real_t &t) const; Quat slerp(const Quat &q, const real_t &t) const;
Quat slerpni(const Quat &q, const real_t &t) const; Quat slerpni(const Quat &q, const real_t &t) const;
Quat cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const; Quat cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const;

View File

@ -7652,7 +7652,7 @@
<argument index="0" name="euler" type="Vector3"> <argument index="0" name="euler" type="Vector3">
</argument> </argument>
<description> <description>
Create a rotation matrix (in the XYZ convention: first Z, then Y, and X last) from the specified Euler angles, given in the vector format as (third, second, first). Create a rotation matrix (in the YXZ convention: first Z, then X, and Y last) from the specified Euler angles, given in the vector format as (X-angle, Y-angle, Z-angle).
</description> </description>
</method> </method>
<method name="Basis"> <method name="Basis">
@ -7690,7 +7690,7 @@
<return type="Vector3"> <return type="Vector3">
</return> </return>
<description> <description>
Assuming that the matrix is a proper rotation matrix (orthonormal matrix with determinant +1), return Euler angles (in the XYZ convention: first Z, then Y, and X last). Returned vector contains the rotation angles in the format (third,second,first). Assuming that the matrix is a proper rotation matrix (orthonormal matrix with determinant +1), return Euler angles (in the YXZ convention: first Z, then X, and Y last). Returned vector contains the rotation angles in the format (X-angle, Y-angle, Z-angle).
</description> </description>
</method> </method>
<method name="get_orthogonal_index"> <method name="get_orthogonal_index">