mirror of
https://github.com/godotengine/godot.git
synced 2024-11-10 14:12:51 +00:00
Refactored Node3D rotation modes
* Made the Basis euler orders indexed via enum. * Node3D has a new rotation_order property to choose Euler rotation order. * Node3D has also a rotation_mode property to choose between Euler, Quaternion and Basis Exposing these modes as well as the order makes Godot a lot friendlier for animators, which can choose the best way to interpolate rotations. The new *Basis* mode makes the (exposed) transform property obsolete, so it was removed (can still be accessed by code of course).
This commit is contained in:
parent
5ff0624a07
commit
d03b7fbe09
@ -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,57 +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 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)
|
||||
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[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;
|
||||
}
|
||||
|
||||
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 {
|
||||
// It's 1
|
||||
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]);
|
||||
euler.y = -Math_PI / 2.0;
|
||||
euler.z = -Math::atan2(elements[0][1], elements[1][1]);
|
||||
}
|
||||
} else {
|
||||
euler.x = Math::atan2(elements[2][1], elements[1][1]);
|
||||
euler.y = -Math_PI / 2.0;
|
||||
euler.z = 0.0;
|
||||
return euler;
|
||||
} break;
|
||||
default: {
|
||||
ERR_FAIL_V_MSG(Vector3(), "Invalid parameter for get_euler(order)");
|
||||
}
|
||||
} else {
|
||||
euler.x = Math::atan2(elements[2][1], elements[1][1]);
|
||||
euler.y = Math_PI / 2.0;
|
||||
euler.z = 0.0;
|
||||
}
|
||||
return euler;
|
||||
return Vector3();
|
||||
}
|
||||
|
||||
// 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) {
|
||||
void Basis::set_euler(const Vector3 &p_euler, EulerOrder p_order) {
|
||||
real_t c, s;
|
||||
|
||||
c = Math::cos(p_euler.x);
|
||||
@ -489,263 +635,29 @@ void Basis::set_euler_xyz(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 = 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;
|
||||
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::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;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
} 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 {
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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));
|
||||
|
@ -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"));
|
||||
|
||||
|
@ -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)); }
|
||||
|
@ -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#.
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -580,6 +580,8 @@ void Area3D::_validate_property(PropertyInfo &property) const {
|
||||
|
||||
property.hint_string = options;
|
||||
}
|
||||
|
||||
CollisionObject3D::_validate_property(property);
|
||||
}
|
||||
|
||||
void Area3D::_bind_methods() {
|
||||
|
@ -638,6 +638,8 @@ void AudioStreamPlayer3D::_validate_property(PropertyInfo &property) const {
|
||||
|
||||
property.hint_string = options;
|
||||
}
|
||||
|
||||
Node3D::_validate_property(property);
|
||||
}
|
||||
|
||||
void AudioStreamPlayer3D::_bus_layout_changed() {
|
||||
|
@ -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) {
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -215,6 +215,7 @@ void Light3D::_validate_property(PropertyInfo &property) const {
|
||||
if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") {
|
||||
property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
VisualInstance3D::_validate_property(property);
|
||||
}
|
||||
|
||||
void Light3D::_bind_methods() {
|
||||
|
@ -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() {
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -351,6 +351,8 @@ void SkeletonIK3D::_validate_property(PropertyInfo &property) const {
|
||||
property.hint_string = "";
|
||||
}
|
||||
}
|
||||
|
||||
Node::_validate_property(property);
|
||||
}
|
||||
|
||||
void SkeletonIK3D::_bind_methods() {
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -292,6 +292,8 @@ void AudioStreamPlayer::_validate_property(PropertyInfo &property) const {
|
||||
|
||||
property.hint_string = options;
|
||||
}
|
||||
|
||||
Node::_validate_property(property);
|
||||
}
|
||||
|
||||
void AudioStreamPlayer::_bus_layout_changed() {
|
||||
|
@ -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]) :
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user