Browse Source

Merge pull request #54084 from reduz/node3d-rotation-options

Rémi Verschelde 4 years ago
parent
commit
d98a6363fa

+ 210 - 298
core/math/basis.cpp

@@ -354,7 +354,7 @@ void Basis::rotate(const Quaternion &p_quaternion) {
 	*this = rotated(p_quaternion);
 }
 
-Vector3 Basis::get_rotation_euler() const {
+Vector3 Basis::get_euler_normalized(EulerOrder p_order) const {
 	// Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
 	// and returns the Euler angles corresponding to the rotation part, complementing get_scale().
 	// See the comment in get_scale() for further information.
@@ -365,7 +365,7 @@ Vector3 Basis::get_rotation_euler() const {
 		m.scale(Vector3(-1, -1, -1));
 	}
 
-	return m.get_euler();
+	return m.get_euler(p_order);
 }
 
 Quaternion Basis::get_rotation_quaternion() const {
@@ -424,218 +424,203 @@ void Basis::get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) cons
 	p_angle = -p_angle;
 }
 
-// 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
-// (following the convention they are commonly defined in the literature).
-//
-// The current implementation uses XYZ convention (Z is the first rotation),
-// so euler.z is the angle of the (first) rotation around Z axis and so on,
-//
-// 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
-// around the z-axis by a and so on.
-Vector3 Basis::get_euler_xyz() const {
-	// Euler angles in XYZ convention.
-	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
-	//
-	// rot =  cy*cz          -cy*sz           sy
-	//        cz*sx*sy+cx*sz  cx*cz-sx*sy*sz -cy*sx
-	//       -cx*cz*sy+sx*sz  cz*sx+cx*sy*sz  cx*cy
-
-	Vector3 euler;
-	real_t sy = elements[0][2];
-	if (sy < (1.0 - CMP_EPSILON)) {
-		if (sy > -(1.0 - CMP_EPSILON)) {
-			// is this a pure Y rotation?
-			if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[1][2] == 0 && elements[2][1] == 0 && elements[1][1] == 1) {
-				// return the simplest form (human friendlier in editor and scripts)
-				euler.x = 0;
-				euler.y = atan2(elements[0][2], elements[0][0]);
-				euler.z = 0;
+Vector3 Basis::get_euler(EulerOrder p_order) const {
+	switch (p_order) {
+		case EULER_ORDER_XYZ: {
+			// Euler angles in XYZ convention.
+			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+			//
+			// rot =  cy*cz          -cy*sz           sy
+			//        cz*sx*sy+cx*sz  cx*cz-sx*sy*sz -cy*sx
+			//       -cx*cz*sy+sx*sz  cz*sx+cx*sy*sz  cx*cy
+
+			Vector3 euler;
+			real_t sy = elements[0][2];
+			if (sy < (1.0 - CMP_EPSILON)) {
+				if (sy > -(1.0 - CMP_EPSILON)) {
+					// is this a pure Y rotation?
+					if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[1][2] == 0 && elements[2][1] == 0 && elements[1][1] == 1) {
+						// return the simplest form (human friendlier in editor and scripts)
+						euler.x = 0;
+						euler.y = atan2(elements[0][2], elements[0][0]);
+						euler.z = 0;
+					} else {
+						euler.x = Math::atan2(-elements[1][2], elements[2][2]);
+						euler.y = Math::asin(sy);
+						euler.z = Math::atan2(-elements[0][1], elements[0][0]);
+					}
+				} else {
+					euler.x = Math::atan2(elements[2][1], elements[1][1]);
+					euler.y = -Math_PI / 2.0;
+					euler.z = 0.0;
+				}
 			} else {
-				euler.x = Math::atan2(-elements[1][2], elements[2][2]);
-				euler.y = Math::asin(sy);
-				euler.z = Math::atan2(-elements[0][1], elements[0][0]);
+				euler.x = Math::atan2(elements[2][1], elements[1][1]);
+				euler.y = Math_PI / 2.0;
+				euler.z = 0.0;
+			}
+			return euler;
+		} break;
+		case EULER_ORDER_XZY: {
+			// Euler angles in XZY convention.
+			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+			//
+			// rot =  cz*cy             -sz             cz*sy
+			//        sx*sy+cx*cy*sz    cx*cz           cx*sz*sy-cy*sx
+			//        cy*sx*sz          cz*sx           cx*cy+sx*sz*sy
+
+			Vector3 euler;
+			real_t sz = elements[0][1];
+			if (sz < (1.0 - CMP_EPSILON)) {
+				if (sz > -(1.0 - CMP_EPSILON)) {
+					euler.x = Math::atan2(elements[2][1], elements[1][1]);
+					euler.y = Math::atan2(elements[0][2], elements[0][0]);
+					euler.z = Math::asin(-sz);
+				} else {
+					// It's -1
+					euler.x = -Math::atan2(elements[1][2], elements[2][2]);
+					euler.y = 0.0;
+					euler.z = Math_PI / 2.0;
+				}
+			} else {
+				// It's 1
+				euler.x = -Math::atan2(elements[1][2], elements[2][2]);
+				euler.y = 0.0;
+				euler.z = -Math_PI / 2.0;
+			}
+			return euler;
+		} break;
+		case EULER_ORDER_YXZ: {
+			// 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;
+
+			real_t m12 = elements[1][2];
+
+			if (m12 < (1 - CMP_EPSILON)) {
+				if (m12 > -(1 - CMP_EPSILON)) {
+					// is this a pure X rotation?
+					if (elements[1][0] == 0 && elements[0][1] == 0 && elements[0][2] == 0 && elements[2][0] == 0 && elements[0][0] == 1) {
+						// return the simplest form (human friendlier in editor and scripts)
+						euler.x = atan2(-m12, elements[1][1]);
+						euler.y = 0;
+						euler.z = 0;
+					} 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;
 			}
-		} else {
-			euler.x = Math::atan2(elements[2][1], elements[1][1]);
-			euler.y = -Math_PI / 2.0;
-			euler.z = 0.0;
-		}
-	} else {
-		euler.x = Math::atan2(elements[2][1], elements[1][1]);
-		euler.y = Math_PI / 2.0;
-		euler.z = 0.0;
-	}
-	return euler;
-}
-
-// set_euler_xyz 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 XYZ convention (Z is the first rotation).
-void Basis::set_euler_xyz(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 = xmat * (ymat * zmat);
-}
-
-Vector3 Basis::get_euler_xzy() const {
-	// Euler angles in XZY convention.
-	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
-	//
-	// rot =  cz*cy             -sz             cz*sy
-	//        sx*sy+cx*cy*sz    cx*cz           cx*sz*sy-cy*sx
-	//        cy*sx*sz          cz*sx           cx*cy+sx*sz*sy
-
-	Vector3 euler;
-	real_t sz = elements[0][1];
-	if (sz < (1.0 - CMP_EPSILON)) {
-		if (sz > -(1.0 - CMP_EPSILON)) {
-			euler.x = Math::atan2(elements[2][1], elements[1][1]);
-			euler.y = Math::atan2(elements[0][2], elements[0][0]);
-			euler.z = Math::asin(-sz);
-		} else {
-			// It's -1
-			euler.x = -Math::atan2(elements[1][2], elements[2][2]);
-			euler.y = 0.0;
-			euler.z = Math_PI / 2.0;
-		}
-	} else {
-		// It's 1
-		euler.x = -Math::atan2(elements[1][2], elements[2][2]);
-		euler.y = 0.0;
-		euler.z = -Math_PI / 2.0;
-	}
-	return euler;
-}
-
-void Basis::set_euler_xzy(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);
-
-	*this = xmat * zmat * ymat;
-}
-
-Vector3 Basis::get_euler_yzx() const {
-	// Euler angles in YZX convention.
-	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
-	//
-	// rot =  cy*cz             sy*sx-cy*cx*sz     cx*sy+cy*sz*sx
-	//        sz                cz*cx              -cz*sx
-	//        -cz*sy            cy*sx+cx*sy*sz     cy*cx-sy*sz*sx
-
-	Vector3 euler;
-	real_t sz = elements[1][0];
-	if (sz < (1.0 - CMP_EPSILON)) {
-		if (sz > -(1.0 - CMP_EPSILON)) {
-			euler.x = Math::atan2(-elements[1][2], elements[1][1]);
-			euler.y = Math::atan2(-elements[2][0], elements[0][0]);
-			euler.z = Math::asin(sz);
-		} else {
-			// It's -1
-			euler.x = Math::atan2(elements[2][1], elements[2][2]);
-			euler.y = 0.0;
-			euler.z = -Math_PI / 2.0;
-		}
-	} else {
-		// It's 1
-		euler.x = Math::atan2(elements[2][1], elements[2][2]);
-		euler.y = 0.0;
-		euler.z = Math_PI / 2.0;
-	}
-	return euler;
-}
-
-void Basis::set_euler_yzx(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);
-
-	*this = ymat * zmat * xmat;
-}
-
-// 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;
-
-	real_t m12 = elements[1][2];
 
-	if (m12 < (1 - CMP_EPSILON)) {
-		if (m12 > -(1 - CMP_EPSILON)) {
-			// is this a pure X rotation?
-			if (elements[1][0] == 0 && elements[0][1] == 0 && elements[0][2] == 0 && elements[2][0] == 0 && elements[0][0] == 1) {
-				// return the simplest form (human friendlier in editor and scripts)
-				euler.x = atan2(-m12, elements[1][1]);
-				euler.y = 0;
+			return euler;
+		} break;
+		case EULER_ORDER_YZX: {
+			// Euler angles in YZX convention.
+			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+			//
+			// rot =  cy*cz             sy*sx-cy*cx*sz     cx*sy+cy*sz*sx
+			//        sz                cz*cx              -cz*sx
+			//        -cz*sy            cy*sx+cx*sy*sz     cy*cx-sy*sz*sx
+
+			Vector3 euler;
+			real_t sz = elements[1][0];
+			if (sz < (1.0 - CMP_EPSILON)) {
+				if (sz > -(1.0 - CMP_EPSILON)) {
+					euler.x = Math::atan2(-elements[1][2], elements[1][1]);
+					euler.y = Math::atan2(-elements[2][0], elements[0][0]);
+					euler.z = Math::asin(sz);
+				} else {
+					// It's -1
+					euler.x = Math::atan2(elements[2][1], elements[2][2]);
+					euler.y = 0.0;
+					euler.z = -Math_PI / 2.0;
+				}
+			} else {
+				// It's 1
+				euler.x = Math::atan2(elements[2][1], elements[2][2]);
+				euler.y = 0.0;
+				euler.z = Math_PI / 2.0;
+			}
+			return euler;
+		} break;
+		case EULER_ORDER_ZXY: {
+			// Euler angles in ZXY convention.
+			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+			//
+			// rot =  cz*cy-sz*sx*sy    -cx*sz                cz*sy+cy*sz*sx
+			//        cy*sz+cz*sx*sy    cz*cx                 sz*sy-cz*cy*sx
+			//        -cx*sy            sx                    cx*cy
+			Vector3 euler;
+			real_t sx = elements[2][1];
+			if (sx < (1.0 - CMP_EPSILON)) {
+				if (sx > -(1.0 - CMP_EPSILON)) {
+					euler.x = Math::asin(sx);
+					euler.y = Math::atan2(-elements[2][0], elements[2][2]);
+					euler.z = Math::atan2(-elements[0][1], elements[1][1]);
+				} else {
+					// It's -1
+					euler.x = -Math_PI / 2.0;
+					euler.y = Math::atan2(elements[0][2], elements[0][0]);
+					euler.z = 0;
+				}
+			} else {
+				// It's 1
+				euler.x = Math_PI / 2.0;
+				euler.y = Math::atan2(elements[0][2], elements[0][0]);
 				euler.z = 0;
+			}
+			return euler;
+		} break;
+		case EULER_ORDER_ZYX: {
+			// Euler angles in ZYX convention.
+			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+			//
+			// rot =  cz*cy             cz*sy*sx-cx*sz        sz*sx+cz*cx*cy
+			//        cy*sz             cz*cx+sz*sy*sx        cx*sz*sy-cz*sx
+			//        -sy               cy*sx                 cy*cx
+			Vector3 euler;
+			real_t sy = elements[2][0];
+			if (sy < (1.0 - CMP_EPSILON)) {
+				if (sy > -(1.0 - CMP_EPSILON)) {
+					euler.x = Math::atan2(elements[2][1], elements[2][2]);
+					euler.y = Math::asin(-sy);
+					euler.z = Math::atan2(elements[1][0], elements[0][0]);
+				} else {
+					// It's -1
+					euler.x = 0;
+					euler.y = Math_PI / 2.0;
+					euler.z = -Math::atan2(elements[0][1], elements[1][1]);
+				}
 			} else {
-				euler.x = asin(-m12);
-				euler.y = atan2(elements[0][2], elements[2][2]);
-				euler.z = atan2(elements[1][0], elements[1][1]);
+				// It's 1
+				euler.x = 0;
+				euler.y = -Math_PI / 2.0;
+				euler.z = -Math::atan2(elements[0][1], elements[1][1]);
 			}
-		} else { // m12 == -1
-			euler.x = Math_PI * 0.5;
-			euler.y = atan2(elements[0][1], elements[0][0]);
-			euler.z = 0;
+			return euler;
+		} break;
+		default: {
+			ERR_FAIL_V_MSG(Vector3(), "Invalid parameter for get_euler(order)");
 		}
-	} else { // m12 == 1
-		euler.x = -Math_PI * 0.5;
-		euler.y = -atan2(elements[0][1], elements[0][0]);
-		euler.z = 0;
 	}
-
-	return euler;
+	return Vector3();
 }
 
-// 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) {
+void Basis::set_euler(const Vector3 &p_euler, EulerOrder p_order) {
 	real_t c, s;
 
 	c = Math::cos(p_euler.x);
@@ -650,102 +635,29 @@ void Basis::set_euler_yxz(const Vector3 &p_euler) {
 	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;
-}
-
-Vector3 Basis::get_euler_zxy() const {
-	// Euler angles in ZXY convention.
-	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
-	//
-	// rot =  cz*cy-sz*sx*sy    -cx*sz                cz*sy+cy*sz*sx
-	//        cy*sz+cz*sx*sy    cz*cx                 sz*sy-cz*cy*sx
-	//        -cx*sy            sx                    cx*cy
-	Vector3 euler;
-	real_t sx = elements[2][1];
-	if (sx < (1.0 - CMP_EPSILON)) {
-		if (sx > -(1.0 - CMP_EPSILON)) {
-			euler.x = Math::asin(sx);
-			euler.y = Math::atan2(-elements[2][0], elements[2][2]);
-			euler.z = Math::atan2(-elements[0][1], elements[1][1]);
-		} else {
-			// It's -1
-			euler.x = -Math_PI / 2.0;
-			euler.y = Math::atan2(elements[0][2], elements[0][0]);
-			euler.z = 0;
+	switch (p_order) {
+		case EULER_ORDER_XYZ: {
+			*this = xmat * (ymat * zmat);
+		} break;
+		case EULER_ORDER_XZY: {
+			*this = xmat * zmat * ymat;
+		} break;
+		case EULER_ORDER_YXZ: {
+			*this = ymat * xmat * zmat;
+		} break;
+		case EULER_ORDER_YZX: {
+			*this = ymat * zmat * xmat;
+		} break;
+		case EULER_ORDER_ZXY: {
+			*this = zmat * xmat * ymat;
+		} break;
+		case EULER_ORDER_ZYX: {
+			*this = zmat * ymat * xmat;
+		} break;
+		default: {
+			ERR_FAIL_MSG("Invalid order parameter for set_euler(vec3,order)");
 		}
-	} else {
-		// It's 1
-		euler.x = Math_PI / 2.0;
-		euler.y = Math::atan2(elements[0][2], elements[0][0]);
-		euler.z = 0;
 	}
-	return euler;
-}
-
-void Basis::set_euler_zxy(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);
-
-	*this = zmat * xmat * ymat;
-}
-
-Vector3 Basis::get_euler_zyx() const {
-	// Euler angles in ZYX convention.
-	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
-	//
-	// rot =  cz*cy             cz*sy*sx-cx*sz        sz*sx+cz*cx*cy
-	//        cy*sz             cz*cx+sz*sy*sx        cx*sz*sy-cz*sx
-	//        -sy               cy*sx                 cy*cx
-	Vector3 euler;
-	real_t sy = elements[2][0];
-	if (sy < (1.0 - CMP_EPSILON)) {
-		if (sy > -(1.0 - CMP_EPSILON)) {
-			euler.x = Math::atan2(elements[2][1], elements[2][2]);
-			euler.y = Math::asin(-sy);
-			euler.z = Math::atan2(elements[1][0], elements[0][0]);
-		} else {
-			// It's -1
-			euler.x = 0;
-			euler.y = Math_PI / 2.0;
-			euler.z = -Math::atan2(elements[0][1], elements[1][1]);
-		}
-	} else {
-		// It's 1
-		euler.x = 0;
-		euler.y = -Math_PI / 2.0;
-		euler.z = -Math::atan2(elements[0][1], elements[1][1]);
-	}
-	return euler;
-}
-
-void Basis::set_euler_zyx(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);
-
-	*this = zmat * ymat * xmat;
 }
 
 bool Basis::is_equal_approx(const Basis &p_basis) const {

+ 17 - 25
core/math/basis.h

@@ -85,40 +85,35 @@ public:
 	void rotate(const Quaternion &p_quaternion);
 	Basis rotated(const Quaternion &p_quaternion) const;
 
-	Vector3 get_rotation_euler() const;
+	enum EulerOrder {
+		EULER_ORDER_XYZ,
+		EULER_ORDER_XZY,
+		EULER_ORDER_YXZ,
+		EULER_ORDER_YZX,
+		EULER_ORDER_ZXY,
+		EULER_ORDER_ZYX
+	};
+
+	Vector3 get_euler_normalized(EulerOrder p_order = EULER_ORDER_YXZ) const;
 	void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const;
 	void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const;
 	Quaternion get_rotation_quaternion() const;
-	Vector3 get_rotation() const { return get_rotation_euler(); };
 
 	void rotate_to_align(Vector3 p_start_direction, Vector3 p_end_direction);
 
 	Vector3 rotref_posscale_decomposition(Basis &rotref) const;
 
-	Vector3 get_euler_xyz() const;
-	void set_euler_xyz(const Vector3 &p_euler);
-
-	Vector3 get_euler_xzy() const;
-	void set_euler_xzy(const Vector3 &p_euler);
-
-	Vector3 get_euler_yzx() const;
-	void set_euler_yzx(const Vector3 &p_euler);
-
-	Vector3 get_euler_yxz() const;
-	void set_euler_yxz(const Vector3 &p_euler);
-
-	Vector3 get_euler_zxy() const;
-	void set_euler_zxy(const Vector3 &p_euler);
-
-	Vector3 get_euler_zyx() const;
-	void set_euler_zyx(const Vector3 &p_euler);
+	Vector3 get_euler(EulerOrder p_order = EULER_ORDER_YXZ) const;
+	void set_euler(const Vector3 &p_euler, EulerOrder p_order = EULER_ORDER_YXZ);
+	static Basis from_euler(const Vector3 &p_euler, EulerOrder p_order = EULER_ORDER_YXZ) {
+		Basis b;
+		b.set_euler(p_euler, p_order);
+		return b;
+	}
 
 	Quaternion get_quaternion() const;
 	void set_quaternion(const Quaternion &p_quaternion);
 
-	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 set_axis_angle(const Vector3 &p_axis, real_t p_phi);
 
@@ -250,9 +245,6 @@ public:
 	Basis(const Quaternion &p_quaternion) { set_quaternion(p_quaternion); };
 	Basis(const Quaternion &p_quaternion, const Vector3 &p_scale) { set_quaternion_scale(p_quaternion, p_scale); }
 
-	Basis(const Vector3 &p_euler) { set_euler(p_euler); }
-	Basis(const Vector3 &p_euler, const Vector3 &p_scale) { set_euler_scale(p_euler, p_scale); }
-
 	Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); }
 	Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); }
 	static Basis from_scale(const Vector3 &p_scale);

+ 2 - 2
core/math/quaternion.cpp

@@ -44,7 +44,7 @@ real_t Quaternion::angle_to(const Quaternion &p_to) const {
 // This implementation uses XYZ convention (Z is the first rotation).
 Vector3 Quaternion::get_euler_xyz() const {
 	Basis m(*this);
-	return m.get_euler_xyz();
+	return m.get_euler(Basis::EULER_ORDER_XYZ);
 }
 
 // get_euler_yxz returns a vector containing the Euler angles in the format
@@ -56,7 +56,7 @@ Vector3 Quaternion::get_euler_yxz() const {
 	ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion must be normalized.");
 #endif
 	Basis m(*this);
-	return m.get_euler_yxz();
+	return m.get_euler(Basis::EULER_ORDER_YXZ);
 }
 
 void Quaternion::operator*=(const Quaternion &p_q) {

+ 1 - 0
core/variant/binder_common.h

@@ -88,6 +88,7 @@ struct VariantCaster<const T &> {
 VARIANT_ENUM_CAST(Object::ConnectFlags);
 
 VARIANT_ENUM_CAST(Vector3::Axis);
+VARIANT_ENUM_CAST(Basis::EulerOrder);
 
 VARIANT_ENUM_CAST(Error);
 VARIANT_ENUM_CAST(Side);

+ 0 - 4
core/variant/variant.cpp

@@ -313,7 +313,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
 		case BASIS: {
 			static const Type valid[] = {
 				QUATERNION,
-				VECTOR3,
 				NIL
 			};
 
@@ -620,7 +619,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
 		case BASIS: {
 			static const Type valid[] = {
 				QUATERNION,
-				VECTOR3,
 				NIL
 			};
 
@@ -1889,8 +1887,6 @@ Variant::operator Basis() const {
 		return *_data._basis;
 	} else if (type == QUATERNION) {
 		return *reinterpret_cast<const Quaternion *>(_data._mem);
-	} else if (type == VECTOR3) {
-		return Basis(*reinterpret_cast<const Vector3 *>(_data._mem));
 	} else if (type == TRANSFORM3D) { // unexposed in Variant::can_convert?
 		return _data._transform3d->basis;
 	} else {

+ 9 - 1
core/variant/variant_call.cpp

@@ -1731,7 +1731,7 @@ static void _register_variant_builtin_methods() {
 	bind_methodv(Basis, rotated, static_cast<Basis (Basis::*)(const Vector3 &, real_t) const>(&Basis::rotated), sarray("axis", "phi"), varray());
 	bind_method(Basis, scaled, sarray("scale"), varray());
 	bind_method(Basis, get_scale, sarray(), varray());
-	bind_method(Basis, get_euler, sarray(), varray());
+	bind_method(Basis, get_euler, sarray("order"), varray(Basis::EULER_ORDER_YXZ));
 	bind_method(Basis, tdotx, sarray("with"), varray());
 	bind_method(Basis, tdoty, sarray("with"), varray());
 	bind_method(Basis, tdotz, sarray("with"), varray());
@@ -1741,6 +1741,7 @@ static void _register_variant_builtin_methods() {
 	bind_method(Basis, get_rotation_quaternion, sarray(), varray());
 	bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
 	bind_static_method(Basis, from_scale, sarray("scale"), varray());
+	bind_static_method(Basis, from_euler, sarray("euler", "order"), varray(Basis::EULER_ORDER_YXZ));
 
 	/* AABB */
 
@@ -2109,6 +2110,13 @@ static void _register_variant_builtin_methods() {
 	_VariantCall::add_variant_constant(Variant::VECTOR2I, "UP", Vector2i(0, -1));
 	_VariantCall::add_variant_constant(Variant::VECTOR2I, "DOWN", Vector2i(0, 1));
 
+	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_XYZ", Basis::EULER_ORDER_XYZ);
+	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_XZY", Basis::EULER_ORDER_XZY);
+	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_YXZ", Basis::EULER_ORDER_YXZ);
+	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_YZX", Basis::EULER_ORDER_YZX);
+	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_ZXY", Basis::EULER_ORDER_ZXY);
+	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_ZYX", Basis::EULER_ORDER_ZYX);
+
 	_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D());
 	_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0));
 	_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0));

+ 1 - 2
core/variant/variant_construct.cpp

@@ -128,10 +128,10 @@ void Variant::_register_variant_constructors() {
 	add_constructor<VariantConstructNoArgs<Quaternion>>(sarray());
 	add_constructor<VariantConstructor<Quaternion, Quaternion>>(sarray("from"));
 	add_constructor<VariantConstructor<Quaternion, Basis>>(sarray("from"));
-	add_constructor<VariantConstructor<Quaternion, Vector3>>(sarray("euler"));
 	add_constructor<VariantConstructor<Quaternion, Vector3, double>>(sarray("axis", "angle"));
 	add_constructor<VariantConstructor<Quaternion, Vector3, Vector3>>(sarray("arc_from", "arc_to"));
 	add_constructor<VariantConstructor<Quaternion, double, double, double, double>>(sarray("x", "y", "z", "w"));
+	add_constructor<VariantConstructor<Quaternion, Vector3>>(sarray("euler_yxz"));
 
 	add_constructor<VariantConstructNoArgs<::AABB>>(sarray());
 	add_constructor<VariantConstructor<::AABB, ::AABB>>(sarray("from"));
@@ -140,7 +140,6 @@ void Variant::_register_variant_constructors() {
 	add_constructor<VariantConstructNoArgs<Basis>>(sarray());
 	add_constructor<VariantConstructor<Basis, Basis>>(sarray("from"));
 	add_constructor<VariantConstructor<Basis, Quaternion>>(sarray("from"));
-	add_constructor<VariantConstructor<Basis, Vector3>>(sarray("euler"));
 	add_constructor<VariantConstructor<Basis, Vector3, double>>(sarray("axis", "phi"));
 	add_constructor<VariantConstructor<Basis, Vector3, Vector3, Vector3>>(sarray("x_axis", "y_axis", "z_axis"));
 

+ 6 - 0
core/variant/variant_internal.h

@@ -756,6 +756,12 @@ VARIANT_ACCESSOR_NUMBER(char32_t)
 VARIANT_ACCESSOR_NUMBER(Error)
 VARIANT_ACCESSOR_NUMBER(Side)
 
+template <>
+struct VariantInternalAccessor<Basis::EulerOrder> {
+	static _FORCE_INLINE_ Basis::EulerOrder get(const Variant *v) { return Basis::EulerOrder(*VariantInternal::get_int(v)); }
+	static _FORCE_INLINE_ void set(Variant *v, Basis::EulerOrder p_value) { *VariantInternal::get_int(v) = p_value; }
+};
+
 template <>
 struct VariantInternalAccessor<ObjectID> {
 	static _FORCE_INLINE_ ObjectID get(const Variant *v) { return ObjectID(*VariantInternal::get_int(v)); }

+ 20 - 8
doc/classes/Basis.xml

@@ -40,14 +40,6 @@
 				Constructs a pure rotation basis matrix, rotated around the given [code]axis[/code] by [code]phi[/code], in radians. The axis must be a normalized vector.
 			</description>
 		</method>
-		<method name="Basis" qualifiers="constructor">
-			<return type="Basis" />
-			<argument index="0" name="euler" type="Vector3" />
-			<description>
-				Constructs a pure rotation basis matrix from the given Euler angles (in the YXZ convention: when *composing*, first Y, then X, and Z last), given in the vector format as (X angle, Y angle, Z angle).
-				Consider using the [Quaternion] constructor instead, which uses a quaternion instead of Euler angles.
-			</description>
-		</method>
 		<method name="Basis" qualifiers="constructor">
 			<return type="Basis" />
 			<argument index="0" name="from" type="Quaternion" />
@@ -71,6 +63,13 @@
 				A negative determinant means the basis has a negative scale. A zero determinant means the basis isn't invertible, and is usually considered invalid.
 			</description>
 		</method>
+		<method name="from_euler" qualifiers="static">
+			<return type="Basis" />
+			<argument index="0" name="euler" type="Vector3" />
+			<argument index="1" name="order" type="int" default="2" />
+			<description>
+			</description>
+		</method>
 		<method name="from_scale" qualifiers="static">
 			<return type="Basis" />
 			<argument index="0" name="scale" type="Vector3" />
@@ -80,6 +79,7 @@
 		</method>
 		<method name="get_euler" qualifiers="const">
 			<return type="Vector3" />
+			<argument index="0" name="order" type="int" default="2" />
 			<description>
 				Returns the basis's rotation in the form of Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last). The returned vector contains the rotation angles in the format (X angle, Y angle, Z angle).
 				Consider using the [method get_rotation_quaternion] method instead, which returns a [Quaternion] quaternion instead of Euler angles.
@@ -248,6 +248,18 @@
 		</member>
 	</members>
 	<constants>
+		<constant name="EULER_ORDER_XYZ" value="0">
+		</constant>
+		<constant name="EULER_ORDER_XZY" value="1">
+		</constant>
+		<constant name="EULER_ORDER_YXZ" value="2">
+		</constant>
+		<constant name="EULER_ORDER_YZX" value="3">
+		</constant>
+		<constant name="EULER_ORDER_ZXY" value="4">
+		</constant>
+		<constant name="EULER_ORDER_ZYX" value="5">
+		</constant>
 		<constant name="IDENTITY" value="Basis(1, 0, 0, 0, 1, 0, 0, 0, 1)">
 			The identity basis, with no rotation or scaling applied.
 			This is identical to calling [code]Basis()[/code] without any parameters. This constant can be used to make your code clearer, and for consistency with C#.

+ 31 - 1
doc/classes/Node3D.xml

@@ -264,16 +264,28 @@
 		</method>
 	</methods>
 	<members>
+		<member name="basis" type="Basis" setter="set_basis" getter="get_basis">
+			Direct access to the 3x3 basis of the [Transform3D] property.
+		</member>
 		<member name="global_transform" type="Transform3D" setter="set_global_transform" getter="get_global_transform">
 			World3D space (global) [Transform3D] of this node.
 		</member>
 		<member name="position" type="Vector3" setter="set_position" getter="get_position" default="Vector3(0, 0, 0)">
 			Local position or translation of this node relative to the parent. This is equivalent to [code]transform.origin[/code].
 		</member>
+		<member name="quaternion" type="Quaternion" setter="set_quaternion" getter="get_quaternion">
+			Access to the node rotation as a [Quaternion]. This property is ideal for tweening complex rotations.
+		</member>
 		<member name="rotation" type="Vector3" setter="set_rotation" getter="get_rotation" default="Vector3(0, 0, 0)">
-			Rotation part of the local transformation in radians, specified in terms of YXZ-Euler angles in the format (X angle, Y angle, Z angle).
+			Rotation part of the local transformation in radians, specified in terms of Euler angles. The angles construct a rotaton in the order specified by the [member rotation_order] property.
 			[b]Note:[/b] In the mathematical sense, rotation is a matrix and not a vector. The three Euler angles, which are the three independent parameters of the Euler-angle parametrization of the rotation matrix, are stored in a [Vector3] data structure not because the rotation is a vector, but only because [Vector3] exists as a convenient data-structure to store 3 floating-point numbers. Therefore, applying affine operations on the rotation "vector" is not meaningful.
 		</member>
+		<member name="rotation_edit_mode" type="int" setter="set_rotation_edit_mode" getter="get_rotation_edit_mode" enum="Node3D.RotationEditMode" default="0">
+			Specify how rotation (and scale) will be presented in the editor.
+		</member>
+		<member name="rotation_order" type="int" setter="set_rotation_order" getter="get_rotation_order" enum="Node3D.RotationOrder" default="2">
+			Specify the axis rotation order of the [member rotation] property. The final orientation is constructed by rotating the Euler angles in the order specified by this property.
+		</member>
 		<member name="scale" type="Vector3" setter="set_scale" getter="get_scale" default="Vector3(1, 1, 1)">
 			Scale part of the local transformation.
 		</member>
@@ -311,5 +323,23 @@
 		<constant name="NOTIFICATION_VISIBILITY_CHANGED" value="43">
 			Node3D nodes receives this notification when their visibility changes.
 		</constant>
+		<constant name="ROTATION_EDIT_MODE_EULER" value="0" enum="RotationEditMode">
+		</constant>
+		<constant name="ROTATION_EDIT_MODE_QUATERNION" value="1" enum="RotationEditMode">
+		</constant>
+		<constant name="ROTATION_EDIT_MODE_BASIS" value="2" enum="RotationEditMode">
+		</constant>
+		<constant name="ROTATION_ORDER_XYZ" value="0" enum="RotationOrder">
+		</constant>
+		<constant name="ROTATION_ORDER_XZY" value="1" enum="RotationOrder">
+		</constant>
+		<constant name="ROTATION_ORDER_YXZ" value="2" enum="RotationOrder">
+		</constant>
+		<constant name="ROTATION_ORDER_YZX" value="3" enum="RotationOrder">
+		</constant>
+		<constant name="ROTATION_ORDER_ZXY" value="4" enum="RotationOrder">
+		</constant>
+		<constant name="ROTATION_ORDER_ZYX" value="5" enum="RotationOrder">
+		</constant>
 	</constants>
 </class>

+ 1 - 2
doc/classes/Quaternion.xml

@@ -43,9 +43,8 @@
 		</method>
 		<method name="Quaternion" qualifiers="constructor">
 			<return type="Quaternion" />
-			<argument index="0" name="euler" type="Vector3" />
+			<argument index="0" name="euler_yxz" type="Vector3" />
 			<description>
-				Constructs a quaternion that will perform a rotation specified by Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last), given in the vector format as (X angle, Y angle, Z angle).
 			</description>
 		</method>
 		<method name="Quaternion" qualifiers="constructor">

+ 1 - 1
editor/plugins/node_3d_editor_plugin.cpp

@@ -3253,7 +3253,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
 					continue;
 				}
 
-				undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_rotation());
+				undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_euler_normalized());
 				undo_redo->add_undo_method(sp, "set_rotation", sp->get_rotation());
 			}
 			undo_redo->commit_action();

+ 1 - 0
modules/csg/csg_shape.cpp

@@ -576,6 +576,7 @@ void CSGShape3D::_validate_property(PropertyInfo &property) const {
 	} else if (is_collision_prefixed && !bool(get("use_collision"))) {
 		property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
 	}
+	GeometryInstance3D::_validate_property(property);
 }
 
 Array CSGShape3D::get_meshes() const {

+ 12 - 12
modules/fbx/tools/import_utils.cpp

@@ -45,27 +45,27 @@ Basis ImportUtils::EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector
 	// by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
 	switch (mode) {
 		case FBXDocParser::Model::RotOrder_EulerXYZ:
-			ret.set_euler_zyx(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_XYZ);
 			break;
 
 		case FBXDocParser::Model::RotOrder_EulerXZY:
-			ret.set_euler_yzx(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_XZY);
 			break;
 
 		case FBXDocParser::Model::RotOrder_EulerYZX:
-			ret.set_euler_xzy(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_YZX);
 			break;
 
 		case FBXDocParser::Model::RotOrder_EulerYXZ:
-			ret.set_euler_zxy(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_YXZ);
 			break;
 
 		case FBXDocParser::Model::RotOrder_EulerZXY:
-			ret.set_euler_yxz(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_ZXY);
 			break;
 
 		case FBXDocParser::Model::RotOrder_EulerZYX:
-			ret.set_euler_xyz(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_ZYX);
 			break;
 
 		case FBXDocParser::Model::RotOrder_SphericXYZ:
@@ -89,22 +89,22 @@ Vector3 ImportUtils::BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basi
 	// by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
 	switch (mode) {
 		case FBXDocParser::Model::RotOrder_EulerXYZ:
-			return p_rotation.get_euler_zyx();
+			return p_rotation.get_euler(Basis::EULER_ORDER_XYZ);
 
 		case FBXDocParser::Model::RotOrder_EulerXZY:
-			return p_rotation.get_euler_yzx();
+			return p_rotation.get_euler(Basis::EULER_ORDER_XZY);
 
 		case FBXDocParser::Model::RotOrder_EulerYZX:
-			return p_rotation.get_euler_xzy();
+			return p_rotation.get_euler(Basis::EULER_ORDER_YZX);
 
 		case FBXDocParser::Model::RotOrder_EulerYXZ:
-			return p_rotation.get_euler_zxy();
+			return p_rotation.get_euler(Basis::EULER_ORDER_YXZ);
 
 		case FBXDocParser::Model::RotOrder_EulerZXY:
-			return p_rotation.get_euler_yxz();
+			return p_rotation.get_euler(Basis::EULER_ORDER_ZXY);
 
 		case FBXDocParser::Model::RotOrder_EulerZYX:
-			return p_rotation.get_euler_xyz();
+			return p_rotation.get_euler(Basis::EULER_ORDER_ZYX);
 
 		case FBXDocParser::Model::RotOrder_SphericXYZ:
 			// TODO

+ 1 - 1
modules/gdscript/tests/scripts/runtime/features/stringify.gd

@@ -17,7 +17,7 @@ func test():
 	print(Plane(1, 2, 3, 4))
 	print(Quaternion(1, 2, 3, 4))
 	print(AABB(Vector3.ZERO, Vector3.ONE))
-	print(Basis(Vector3(0, 0, 0)))
+	print(Basis.from_euler(Vector3(0, 0, 0)))
 	print(Transform3D.IDENTITY)
 
 	print(Color(1, 2, 3, 4))

+ 2 - 0
scene/3d/area_3d.cpp

@@ -580,6 +580,8 @@ void Area3D::_validate_property(PropertyInfo &property) const {
 
 		property.hint_string = options;
 	}
+
+	CollisionObject3D::_validate_property(property);
 }
 
 void Area3D::_bind_methods() {

+ 2 - 0
scene/3d/audio_stream_player_3d.cpp

@@ -638,6 +638,8 @@ void AudioStreamPlayer3D::_validate_property(PropertyInfo &property) const {
 
 		property.hint_string = options;
 	}
+
+	Node3D::_validate_property(property);
 }
 
 void AudioStreamPlayer3D::_bus_layout_changed() {

+ 2 - 0
scene/3d/bone_attachment_3d.cpp

@@ -58,6 +58,8 @@ void BoneAttachment3D::_validate_property(PropertyInfo &property) const {
 			property.hint_string = "";
 		}
 	}
+
+	Node3D::_validate_property(property);
 }
 
 bool BoneAttachment3D::_set(const StringName &p_path, const Variant &p_value) {

+ 2 - 0
scene/3d/camera_3d.cpp

@@ -71,6 +71,8 @@ void Camera3D::_validate_property(PropertyInfo &p_property) const {
 			p_property.usage = PROPERTY_USAGE_NOEDITOR;
 		}
 	}
+
+	Node3D::_validate_property(p_property);
 }
 
 void Camera3D::_update_camera() {

+ 2 - 0
scene/3d/cpu_particles_3d.cpp

@@ -532,6 +532,8 @@ void CPUParticles3D::_validate_property(PropertyInfo &property) const {
 	if (property.name.begins_with("scale_curve_") && !split_scale) {
 		property.usage = PROPERTY_USAGE_NONE;
 	}
+
+	Node3D::_validate_property(property);
 }
 
 static uint32_t idhash(uint32_t x) {

+ 1 - 0
scene/3d/decal.cpp

@@ -160,6 +160,7 @@ void Decal::_validate_property(PropertyInfo &property) const {
 	if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_length")) {
 		property.usage = PROPERTY_USAGE_NOEDITOR;
 	}
+	VisualInstance3D::_validate_property(property);
 }
 
 TypedArray<String> Decal::get_configuration_warnings() const {

+ 2 - 0
scene/3d/gpu_particles_3d.cpp

@@ -388,6 +388,8 @@ void GPUParticles3D::_validate_property(PropertyInfo &property) const {
 			return;
 		}
 	}
+
+	GeometryInstance3D::_validate_property(property);
 }
 
 void GPUParticles3D::emit_particle(const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {

+ 1 - 0
scene/3d/light_3d.cpp

@@ -204,6 +204,7 @@ void Light3D::_validate_property(PropertyInfo &property) const {
 		// Angular distance is only used in DirectionalLight3D.
 		property.usage = PROPERTY_USAGE_NONE;
 	}
+	VisualInstance3D::_validate_property(property);
 }
 
 void Light3D::_bind_methods() {

+ 1 - 0
scene/3d/lightmap_gi.cpp

@@ -1370,6 +1370,7 @@ void LightmapGI::_validate_property(PropertyInfo &property) const {
 	if (property.name == "environment_custom_energy" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) {
 		property.usage = PROPERTY_USAGE_NONE;
 	}
+	VisualInstance3D::_validate_property(property);
 }
 
 void LightmapGI::_bind_methods() {

+ 98 - 5
scene/3d/node_3d.cpp

@@ -220,6 +220,13 @@ void Node3D::_notification(int p_what) {
 	}
 }
 
+void Node3D::set_basis(const Basis &p_basis) {
+	set_transform(Transform3D(p_basis, data.local_transform.origin));
+}
+void Node3D::set_quaternion(const Quaternion &p_quaternion) {
+	set_transform(Transform3D(Basis(p_quaternion), data.local_transform.origin));
+}
+
 void Node3D::set_transform(const Transform3D &p_transform) {
 	data.local_transform = p_transform;
 	data.dirty |= DIRTY_VECTORS;
@@ -229,6 +236,13 @@ void Node3D::set_transform(const Transform3D &p_transform) {
 	}
 }
 
+Basis Node3D::get_basis() const {
+	return get_transform().basis;
+}
+Quaternion Node3D::get_quaternion() const {
+	return Quaternion(get_transform().basis);
+}
+
 void Node3D::set_global_transform(const Transform3D &p_transform) {
 	Transform3D xform =
 			(data.parent && !data.top_level_active) ?
@@ -308,6 +322,45 @@ void Node3D::set_position(const Vector3 &p_position) {
 	}
 }
 
+void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) {
+	if (data.rotation_edit_mode == p_mode) {
+		return;
+	}
+	data.rotation_edit_mode = p_mode;
+	notify_property_list_changed();
+}
+
+Node3D::RotationEditMode Node3D::get_rotation_edit_mode() const {
+	return data.rotation_edit_mode;
+}
+
+void Node3D::set_rotation_order(RotationOrder p_order) {
+	Basis::EulerOrder order = Basis::EulerOrder(p_order);
+
+	if (data.rotation_order == order) {
+		return;
+	}
+
+	ERR_FAIL_INDEX(int32_t(order), 6);
+
+	if (data.dirty & DIRTY_VECTORS) {
+		data.rotation = data.local_transform.basis.get_euler_normalized(order);
+		data.scale = data.local_transform.basis.get_scale();
+		data.dirty &= ~DIRTY_VECTORS;
+	} else {
+		data.rotation = Basis::from_euler(data.rotation, data.rotation_order).get_euler_normalized(order);
+	}
+
+	data.rotation_order = order;
+	//changing rotation order should not affect transform
+
+	notify_property_list_changed(); //will change rotation
+}
+
+Node3D::RotationOrder Node3D::get_rotation_order() const {
+	return RotationOrder(data.rotation_order);
+}
+
 void Node3D::set_rotation(const Vector3 &p_euler_rad) {
 	if (data.dirty & DIRTY_VECTORS) {
 		data.scale = data.local_transform.basis.get_scale();
@@ -324,7 +377,7 @@ void Node3D::set_rotation(const Vector3 &p_euler_rad) {
 
 void Node3D::set_scale(const Vector3 &p_scale) {
 	if (data.dirty & DIRTY_VECTORS) {
-		data.rotation = data.local_transform.basis.get_rotation();
+		data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order);
 		data.dirty &= ~DIRTY_VECTORS;
 	}
 
@@ -343,7 +396,7 @@ Vector3 Node3D::get_position() const {
 Vector3 Node3D::get_rotation() const {
 	if (data.dirty & DIRTY_VECTORS) {
 		data.scale = data.local_transform.basis.get_scale();
-		data.rotation = data.local_transform.basis.get_rotation();
+		data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order);
 
 		data.dirty &= ~DIRTY_VECTORS;
 	}
@@ -354,7 +407,7 @@ Vector3 Node3D::get_rotation() const {
 Vector3 Node3D::get_scale() const {
 	if (data.dirty & DIRTY_VECTORS) {
 		data.scale = data.local_transform.basis.get_scale();
-		data.rotation = data.local_transform.basis.get_rotation();
+		data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order);
 
 		data.dirty &= ~DIRTY_VECTORS;
 	}
@@ -780,6 +833,24 @@ NodePath Node3D::get_visibility_parent() const {
 	return visibility_parent_path;
 }
 
+void Node3D::_validate_property(PropertyInfo &property) const {
+	if (data.rotation_edit_mode != ROTATION_EDIT_MODE_BASIS && property.name == "basis") {
+		property.usage = 0;
+	}
+	if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && property.name == "scale") {
+		property.usage = 0;
+	}
+	if (data.rotation_edit_mode != ROTATION_EDIT_MODE_QUATERNION && property.name == "quaternion") {
+		property.usage = 0;
+	}
+	if (data.rotation_edit_mode != ROTATION_EDIT_MODE_EULER && property.name == "rotation") {
+		property.usage = 0;
+	}
+	if (data.rotation_edit_mode != ROTATION_EDIT_MODE_EULER && property.name == "rotation_order") {
+		property.usage = 0;
+	}
+}
+
 void Node3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_transform", "local"), &Node3D::set_transform);
 	ClassDB::bind_method(D_METHOD("get_transform"), &Node3D::get_transform);
@@ -787,8 +858,16 @@ void Node3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_position"), &Node3D::get_position);
 	ClassDB::bind_method(D_METHOD("set_rotation", "euler"), &Node3D::set_rotation);
 	ClassDB::bind_method(D_METHOD("get_rotation"), &Node3D::get_rotation);
+	ClassDB::bind_method(D_METHOD("set_rotation_order", "order"), &Node3D::set_rotation_order);
+	ClassDB::bind_method(D_METHOD("get_rotation_order"), &Node3D::get_rotation_order);
+	ClassDB::bind_method(D_METHOD("set_rotation_edit_mode", "edit_mode"), &Node3D::set_rotation_edit_mode);
+	ClassDB::bind_method(D_METHOD("get_rotation_edit_mode"), &Node3D::get_rotation_edit_mode);
 	ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Node3D::set_scale);
 	ClassDB::bind_method(D_METHOD("get_scale"), &Node3D::get_scale);
+	ClassDB::bind_method(D_METHOD("set_quaternion", "quaternion"), &Node3D::set_quaternion);
+	ClassDB::bind_method(D_METHOD("get_quaternion"), &Node3D::get_quaternion);
+	ClassDB::bind_method(D_METHOD("set_basis", "basis"), &Node3D::set_basis);
+	ClassDB::bind_method(D_METHOD("get_basis"), &Node3D::get_basis);
 	ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform);
 	ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform);
 	ClassDB::bind_method(D_METHOD("get_parent_node_3d"), &Node3D::get_parent_node_3d);
@@ -848,15 +927,29 @@ void Node3D::_bind_methods() {
 	BIND_CONSTANT(NOTIFICATION_EXIT_WORLD);
 	BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED);
 
+	BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_EULER);
+	BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_QUATERNION);
+	BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_BASIS);
+
+	BIND_ENUM_CONSTANT(ROTATION_ORDER_XYZ);
+	BIND_ENUM_CONSTANT(ROTATION_ORDER_XZY);
+	BIND_ENUM_CONSTANT(ROTATION_ORDER_YXZ);
+	BIND_ENUM_CONSTANT(ROTATION_ORDER_YZX);
+	BIND_ENUM_CONSTANT(ROTATION_ORDER_ZXY);
+	BIND_ENUM_CONSTANT(ROTATION_ORDER_ZYX);
+
 	//ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM3D,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), "set_global_transform", "get_global_transform") ;
 	ADD_GROUP("Transform", "");
 	ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0,or_greater,or_lesser,noslider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation");
+	ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "quaternion", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_quaternion", "get_quaternion");
+	ADD_PROPERTY(PropertyInfo(Variant::BASIS, "basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_basis", "get_basis");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_edit_mode", PROPERTY_HINT_ENUM, "Euler,Quaternion,Basis"), "set_rotation_edit_mode", "get_rotation_edit_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_order", PROPERTY_HINT_ENUM, "XYZ,XZY,YXZ,YZX,ZXY,ZYX"), "set_rotation_order", "get_rotation_order");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
-	ADD_GROUP("Matrix", "");
-	ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform");
+	ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_transform", "get_transform");
 	ADD_GROUP("Visibility", "");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
 	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent");

+ 35 - 0
scene/3d/node_3d.h

@@ -51,6 +51,23 @@ class Node3D : public Node {
 	GDCLASS(Node3D, Node);
 	OBJ_CATEGORY("3D");
 
+public:
+	enum RotationEditMode {
+		ROTATION_EDIT_MODE_EULER,
+		ROTATION_EDIT_MODE_QUATERNION,
+		ROTATION_EDIT_MODE_BASIS,
+	};
+
+	enum RotationOrder {
+		ROTATION_ORDER_XYZ,
+		ROTATION_ORDER_XZY,
+		ROTATION_ORDER_YXZ,
+		ROTATION_ORDER_YZX,
+		ROTATION_ORDER_ZXY,
+		ROTATION_ORDER_ZYX
+	};
+
+private:
 	enum TransformDirty {
 		DIRTY_NONE = 0,
 		DIRTY_VECTORS = 1,
@@ -63,8 +80,10 @@ class Node3D : public Node {
 	struct Data {
 		mutable Transform3D global_transform;
 		mutable Transform3D local_transform;
+		mutable Basis::EulerOrder rotation_order = Basis::EULER_ORDER_YXZ;
 		mutable Vector3 rotation;
 		mutable Vector3 scale = Vector3(1, 1, 1);
+		mutable RotationEditMode rotation_edit_mode = ROTATION_EDIT_MODE_EULER;
 
 		mutable int dirty = DIRTY_NONE;
 
@@ -116,6 +135,8 @@ protected:
 	void _notification(int p_what);
 	static void _bind_methods();
 
+	virtual void _validate_property(PropertyInfo &property) const override;
+
 public:
 	enum {
 		NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED,
@@ -130,17 +151,28 @@ public:
 	Ref<World3D> get_world_3d() const;
 
 	void set_position(const Vector3 &p_position);
+
+	void set_rotation_edit_mode(RotationEditMode p_mode);
+	RotationEditMode get_rotation_edit_mode() const;
+
+	void set_rotation_order(RotationOrder p_order);
 	void set_rotation(const Vector3 &p_euler_rad);
 	void set_scale(const Vector3 &p_scale);
 
 	Vector3 get_position() const;
+
+	RotationOrder get_rotation_order() const;
 	Vector3 get_rotation() const;
 	Vector3 get_scale() const;
 
 	void set_transform(const Transform3D &p_transform);
+	void set_basis(const Basis &p_basis);
+	void set_quaternion(const Quaternion &p_quaternion);
 	void set_global_transform(const Transform3D &p_transform);
 
 	Transform3D get_transform() const;
+	Basis get_basis() const;
+	Quaternion get_quaternion() const;
 	Transform3D get_global_transform() const;
 
 #ifdef TOOLS_ENABLED
@@ -214,4 +246,7 @@ public:
 	Node3D();
 };
 
+VARIANT_ENUM_CAST(Node3D::RotationEditMode)
+VARIANT_ENUM_CAST(Node3D::RotationOrder)
+
 #endif // NODE_3D_H

+ 1 - 0
scene/3d/path_3d.cpp

@@ -248,6 +248,7 @@ void PathFollow3D::_validate_property(PropertyInfo &property) const {
 
 		property.hint_string = "0," + rtos(max) + ",0.01,or_lesser,or_greater";
 	}
+	Node3D::_validate_property(property);
 }
 
 TypedArray<String> PathFollow3D::get_configuration_warnings() const {

+ 3 - 1
scene/3d/physics_body_3d.cpp

@@ -1059,6 +1059,7 @@ void RigidDynamicBody3D::_validate_property(PropertyInfo &property) const {
 			property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
 		}
 	}
+	PhysicsBody3D::_validate_property(property);
 }
 
 RigidDynamicBody3D::RigidDynamicBody3D() :
@@ -1933,6 +1934,7 @@ void CharacterBody3D::_validate_property(PropertyInfo &property) const {
 			property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
 		}
 	}
+	PhysicsBody3D::_validate_property(property);
 }
 
 CharacterBody3D::CharacterBody3D() :
@@ -3060,7 +3062,7 @@ void PhysicalBone3D::set_joint_rotation(const Vector3 &p_euler_rad) {
 }
 
 Vector3 PhysicalBone3D::get_joint_rotation() const {
-	return joint_offset.basis.get_rotation();
+	return joint_offset.basis.get_euler_normalized();
 }
 
 const Transform3D &PhysicalBone3D::get_body_offset() const {

+ 1 - 0
scene/3d/reflection_probe.cpp

@@ -188,6 +188,7 @@ void ReflectionProbe::_validate_property(PropertyInfo &property) const {
 			property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
 		}
 	}
+	VisualInstance3D::_validate_property(property);
 }
 
 void ReflectionProbe::_bind_methods() {

+ 2 - 2
scene/3d/remote_transform_3d.cpp

@@ -68,7 +68,7 @@ void RemoteTransform3D::_update_remote() {
 			Transform3D our_trans = get_global_transform();
 
 			if (update_remote_rotation) {
-				n->set_rotation(our_trans.basis.get_rotation());
+				n->set_rotation(our_trans.basis.get_euler_normalized(Basis::EulerOrder(n->get_rotation_order())));
 			}
 
 			if (update_remote_scale) {
@@ -90,7 +90,7 @@ void RemoteTransform3D::_update_remote() {
 			Transform3D our_trans = get_transform();
 
 			if (update_remote_rotation) {
-				n->set_rotation(our_trans.basis.get_rotation());
+				n->set_rotation(our_trans.basis.get_euler_normalized(Basis::EulerOrder(n->get_rotation_order())));
 			}
 
 			if (update_remote_scale) {

+ 2 - 0
scene/3d/skeleton_ik_3d.cpp

@@ -351,6 +351,8 @@ void SkeletonIK3D::_validate_property(PropertyInfo &property) const {
 			property.hint_string = "";
 		}
 	}
+
+	Node::_validate_property(property);
 }
 
 void SkeletonIK3D::_bind_methods() {

+ 1 - 2
scene/3d/skeleton_ik_3d.h

@@ -137,8 +137,7 @@ class SkeletonIK3D : public Node {
 	FabrikInverseKinematic::Task *task = nullptr;
 
 protected:
-	virtual void
-	_validate_property(PropertyInfo &property) const override;
+	virtual void _validate_property(PropertyInfo &property) const override;
 
 	static void _bind_methods();
 	virtual void _notification(int p_what);

+ 4 - 0
scene/3d/sprite_3d.cpp

@@ -743,6 +743,8 @@ void Sprite3D::_validate_property(PropertyInfo &property) const {
 	if (property.name == "frame_coords") {
 		property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
 	}
+
+	SpriteBase3D::_validate_property(property);
 }
 
 void Sprite3D::_bind_methods() {
@@ -1015,6 +1017,8 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &property) const {
 		}
 		property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
 	}
+
+	SpriteBase3D::_validate_property(property);
 }
 
 void AnimatedSprite3D::_notification(int p_what) {

+ 2 - 0
scene/3d/xr_nodes.cpp

@@ -261,6 +261,8 @@ void XRNode3D::_validate_property(PropertyInfo &property) const {
 		}
 		property.hint_string = hint_string;
 	}
+
+	Node3D::_validate_property(property);
 }
 
 void XRNode3D::set_tracker(const StringName p_tracker_name) {

+ 2 - 0
scene/animation/animation_player.cpp

@@ -166,6 +166,8 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const {
 
 		property.hint_string = hint;
 	}
+
+	Node::_validate_property(property);
 }
 
 void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {

+ 2 - 0
scene/audio/audio_stream_player.cpp

@@ -292,6 +292,8 @@ void AudioStreamPlayer::_validate_property(PropertyInfo &property) const {
 
 		property.hint_string = options;
 	}
+
+	Node::_validate_property(property);
 }
 
 void AudioStreamPlayer::_bus_layout_changed() {

+ 1 - 1
servers/physics_3d/joints/godot_generic_6dof_joint_3d.cpp

@@ -232,7 +232,7 @@ GodotGeneric6DOFJoint3D::GodotGeneric6DOFJoint3D(GodotBody3D *rbA, GodotBody3D *
 void GodotGeneric6DOFJoint3D::calculateAngleInfo() {
 	Basis relative_frame = m_calculatedTransformB.basis.inverse() * m_calculatedTransformA.basis;
 
-	m_calculatedAxisAngleDiff = relative_frame.get_euler_xyz();
+	m_calculatedAxisAngleDiff = relative_frame.get_euler(Basis::EULER_ORDER_XYZ);
 
 	// in euler angle mode we do not actually constrain the angular velocity
 	// along the axes axis[0] and axis[2] (although we do use axis[1]) :

+ 14 - 14
tests/test_basis.h

@@ -60,27 +60,27 @@ Basis EulerToBasis(RotOrder mode, const Vector3 &p_rotation) {
 	Basis ret;
 	switch (mode) {
 		case EulerXYZ:
-			ret.set_euler_xyz(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_XYZ);
 			break;
 
 		case EulerXZY:
-			ret.set_euler_xzy(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_XZY);
 			break;
 
 		case EulerYZX:
-			ret.set_euler_yzx(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_YZX);
 			break;
 
 		case EulerYXZ:
-			ret.set_euler_yxz(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_YXZ);
 			break;
 
 		case EulerZXY:
-			ret.set_euler_zxy(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_ZXY);
 			break;
 
 		case EulerZYX:
-			ret.set_euler_zyx(p_rotation);
+			ret.set_euler(p_rotation, Basis::EULER_ORDER_ZYX);
 			break;
 
 		default:
@@ -94,22 +94,22 @@ Basis EulerToBasis(RotOrder mode, const Vector3 &p_rotation) {
 Vector3 BasisToEuler(RotOrder mode, const Basis &p_rotation) {
 	switch (mode) {
 		case EulerXYZ:
-			return p_rotation.get_euler_xyz();
+			return p_rotation.get_euler(Basis::EULER_ORDER_XYZ);
 
 		case EulerXZY:
-			return p_rotation.get_euler_xzy();
+			return p_rotation.get_euler(Basis::EULER_ORDER_XZY);
 
 		case EulerYZX:
-			return p_rotation.get_euler_yzx();
+			return p_rotation.get_euler(Basis::EULER_ORDER_YZX);
 
 		case EulerYXZ:
-			return p_rotation.get_euler_yxz();
+			return p_rotation.get_euler(Basis::EULER_ORDER_YXZ);
 
 		case EulerZXY:
-			return p_rotation.get_euler_zxy();
+			return p_rotation.get_euler(Basis::EULER_ORDER_ZXY);
 
 		case EulerZYX:
-			return p_rotation.get_euler_zyx();
+			return p_rotation.get_euler(Basis::EULER_ORDER_ZYX);
 
 		default:
 			// If you land here, Please integrate all rotation orders.
@@ -170,9 +170,9 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
 	CHECK_MESSAGE((res.get_axis(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Fail due to Z %s\n", String(res.get_axis(2))).utf8().ptr());
 
 	// Double check `to_rotation` decomposing with XYZ rotation order.
-	const Vector3 euler_xyz_from_rotation = to_rotation.get_euler_xyz();
+	const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(Basis::EULER_ORDER_XYZ);
 	Basis rotation_from_xyz_computed_euler;
-	rotation_from_xyz_computed_euler.set_euler_xyz(euler_xyz_from_rotation);
+	rotation_from_xyz_computed_euler.set_euler(euler_xyz_from_rotation, Basis::EULER_ORDER_XYZ);
 
 	res = to_rotation.inverse() * rotation_from_xyz_computed_euler;