Replace Quaternion Euler constructor with from_euler
method
This commit is contained in:
parent
e6751549cf
commit
83634119d4
@ -330,7 +330,7 @@ Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) {
|
||||
// (ax, ay, az), where ax is the angle of rotation around x axis,
|
||||
// and similar for other axes.
|
||||
// This implementation uses YXZ convention (Z is the first rotation).
|
||||
Quaternion::Quaternion(const Vector3 &p_euler) {
|
||||
Quaternion Quaternion::from_euler(const Vector3 &p_euler) {
|
||||
real_t half_a1 = p_euler.y * 0.5f;
|
||||
real_t half_a2 = p_euler.x * 0.5f;
|
||||
real_t half_a3 = p_euler.z * 0.5f;
|
||||
@ -346,8 +346,9 @@ Quaternion::Quaternion(const Vector3 &p_euler) {
|
||||
real_t cos_a3 = Math::cos(half_a3);
|
||||
real_t sin_a3 = Math::sin(half_a3);
|
||||
|
||||
x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3;
|
||||
y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3;
|
||||
z = -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3;
|
||||
w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3;
|
||||
return Quaternion(
|
||||
sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3,
|
||||
sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3,
|
||||
-sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3,
|
||||
sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3);
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ struct _NO_DISCARD_ Quaternion {
|
||||
Vector3 get_euler_xyz() const;
|
||||
Vector3 get_euler_yxz() const;
|
||||
Vector3 get_euler() const { return get_euler_yxz(); };
|
||||
static Quaternion from_euler(const Vector3 &p_euler);
|
||||
|
||||
Quaternion slerp(const Quaternion &p_to, const real_t &p_weight) const;
|
||||
Quaternion slerpni(const Quaternion &p_to, const real_t &p_weight) const;
|
||||
@ -128,8 +129,6 @@ struct _NO_DISCARD_ Quaternion {
|
||||
|
||||
Quaternion(const Vector3 &p_axis, real_t p_angle);
|
||||
|
||||
Quaternion(const Vector3 &p_euler);
|
||||
|
||||
Quaternion(const Quaternion &p_q) :
|
||||
x(p_q.x),
|
||||
y(p_q.y),
|
||||
|
@ -1806,6 +1806,7 @@ static void _register_variant_builtin_methods() {
|
||||
bind_method(Quaternion, spherical_cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray());
|
||||
bind_method(Quaternion, spherical_cubic_interpolate_in_time, sarray("b", "pre_a", "post_b", "weight", "b_t", "pre_a_t", "post_b_t"), varray());
|
||||
bind_method(Quaternion, get_euler, sarray(), varray());
|
||||
bind_static_method(Quaternion, from_euler, sarray("euler"), varray());
|
||||
bind_method(Quaternion, get_axis, sarray(), varray());
|
||||
bind_method(Quaternion, get_angle, sarray(), varray());
|
||||
|
||||
|
@ -141,7 +141,6 @@ void Variant::_register_variant_constructors() {
|
||||
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"));
|
||||
|
@ -41,12 +41,6 @@
|
||||
Constructs a quaternion that will rotate around the given axis by the specified angle. The axis must be a normalized vector.
|
||||
</description>
|
||||
</constructor>
|
||||
<constructor name="Quaternion">
|
||||
<return type="Quaternion" />
|
||||
<param index="0" name="euler_yxz" type="Vector3" />
|
||||
<description>
|
||||
</description>
|
||||
</constructor>
|
||||
<constructor name="Quaternion">
|
||||
<return type="Quaternion" />
|
||||
<param index="0" name="from" type="Basis" />
|
||||
@ -86,6 +80,13 @@
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="from_euler" qualifiers="static">
|
||||
<return type="Quaternion" />
|
||||
<param index="0" name="euler" type="Vector3" />
|
||||
<description>
|
||||
Constructs a Quaternion from Euler angles in YXZ rotation order.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_angle" qualifiers="const">
|
||||
<return type="float" />
|
||||
<description>
|
||||
|
@ -2688,7 +2688,7 @@ void EditorPropertyQuaternion::_custom_value_changed(double val) {
|
||||
v.y = Math::deg_to_rad(edit_euler.y);
|
||||
v.z = Math::deg_to_rad(edit_euler.z);
|
||||
|
||||
Quaternion temp_q = Quaternion(v);
|
||||
Quaternion temp_q = Quaternion::from_euler(v);
|
||||
spin[0]->set_value(temp_q.x);
|
||||
spin[1]->set_value(temp_q.y);
|
||||
spin[2]->set_value(temp_q.z);
|
||||
|
@ -6289,7 +6289,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
|
||||
|
||||
for (int32_t key_i = 0; key_i < key_count; key_i++) {
|
||||
Vector3 rotation_radian = p_animation->track_get_key_value(p_track_i, key_i);
|
||||
p_track.rotation_track.values.write[key_i] = Quaternion(rotation_radian);
|
||||
p_track.rotation_track.values.write[key_i] = Quaternion::from_euler(rotation_radian);
|
||||
}
|
||||
} else if (path.contains(":scale")) {
|
||||
p_track.scale_track.times = times;
|
||||
|
@ -506,35 +506,6 @@ namespace Godot
|
||||
this = basis.GetQuaternion();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="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).
|
||||
/// </summary>
|
||||
/// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param>
|
||||
public Quaternion(Vector3 eulerYXZ)
|
||||
{
|
||||
real_t halfA1 = eulerYXZ.y * 0.5f;
|
||||
real_t halfA2 = eulerYXZ.x * 0.5f;
|
||||
real_t halfA3 = eulerYXZ.z * 0.5f;
|
||||
|
||||
// R = Y(a1).X(a2).Z(a3) convention for Euler angles.
|
||||
// Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
|
||||
// a3 is the angle of the first rotation, following the notation in this reference.
|
||||
|
||||
real_t cosA1 = Mathf.Cos(halfA1);
|
||||
real_t sinA1 = Mathf.Sin(halfA1);
|
||||
real_t cosA2 = Mathf.Cos(halfA2);
|
||||
real_t sinA2 = Mathf.Sin(halfA2);
|
||||
real_t cosA3 = Mathf.Cos(halfA3);
|
||||
real_t sinA3 = Mathf.Sin(halfA3);
|
||||
|
||||
x = (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3);
|
||||
y = (sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3);
|
||||
z = (cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3);
|
||||
w = (sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Quaternion"/> that will rotate around the given axis
|
||||
/// by the specified angle. The axis must be a normalized vector.
|
||||
@ -572,6 +543,37 @@ namespace Godot
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="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).
|
||||
/// </summary>
|
||||
/// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param>
|
||||
public static Quaternion FromEuler(Vector3 eulerYXZ)
|
||||
{
|
||||
real_t halfA1 = eulerYXZ.y * 0.5f;
|
||||
real_t halfA2 = eulerYXZ.x * 0.5f;
|
||||
real_t halfA3 = eulerYXZ.z * 0.5f;
|
||||
|
||||
// R = Y(a1).X(a2).Z(a3) convention for Euler angles.
|
||||
// Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
|
||||
// a3 is the angle of the first rotation, following the notation in this reference.
|
||||
|
||||
real_t cosA1 = Mathf.Cos(halfA1);
|
||||
real_t sinA1 = Mathf.Sin(halfA1);
|
||||
real_t cosA2 = Mathf.Cos(halfA2);
|
||||
real_t sinA2 = Mathf.Sin(halfA2);
|
||||
real_t cosA3 = Mathf.Cos(halfA3);
|
||||
real_t sinA3 = Mathf.Sin(halfA3);
|
||||
|
||||
return new Quaternion(
|
||||
(sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3),
|
||||
(sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3),
|
||||
(cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3),
|
||||
(sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Composes these two quaternions by multiplying them together.
|
||||
/// This has the effect of rotating the second quaternion
|
||||
|
@ -47,9 +47,9 @@ Quaternion quat_euler_yxz_deg(Vector3 angle) {
|
||||
|
||||
// Generate YXZ (Z-then-X-then-Y) Quaternion using single-axis Euler
|
||||
// constructor and quaternion product, both tested separately.
|
||||
Quaternion q_y(Vector3(0.0, yaw, 0.0));
|
||||
Quaternion q_p(Vector3(pitch, 0.0, 0.0));
|
||||
Quaternion q_r(Vector3(0.0, 0.0, roll));
|
||||
Quaternion q_y = Quaternion::from_euler(Vector3(0.0, yaw, 0.0));
|
||||
Quaternion q_p = Quaternion::from_euler(Vector3(pitch, 0.0, 0.0));
|
||||
Quaternion q_r = Quaternion::from_euler(Vector3(0.0, 0.0, roll));
|
||||
// Roll-Z is followed by Pitch-X, then Yaw-Y.
|
||||
Quaternion q_yxz = q_y * q_p * q_r;
|
||||
|
||||
@ -134,21 +134,21 @@ TEST_CASE("[Quaternion] Construct Euler SingleAxis") {
|
||||
double roll = Math::deg_to_rad(10.0);
|
||||
|
||||
Vector3 euler_y(0.0, yaw, 0.0);
|
||||
Quaternion q_y(euler_y);
|
||||
Quaternion q_y = Quaternion::from_euler(euler_y);
|
||||
CHECK(q_y[0] == doctest::Approx(0.0));
|
||||
CHECK(q_y[1] == doctest::Approx(0.382684));
|
||||
CHECK(q_y[2] == doctest::Approx(0.0));
|
||||
CHECK(q_y[3] == doctest::Approx(0.923879));
|
||||
|
||||
Vector3 euler_p(pitch, 0.0, 0.0);
|
||||
Quaternion q_p(euler_p);
|
||||
Quaternion q_p = Quaternion::from_euler(euler_p);
|
||||
CHECK(q_p[0] == doctest::Approx(0.258819));
|
||||
CHECK(q_p[1] == doctest::Approx(0.0));
|
||||
CHECK(q_p[2] == doctest::Approx(0.0));
|
||||
CHECK(q_p[3] == doctest::Approx(0.965926));
|
||||
|
||||
Vector3 euler_r(0.0, 0.0, roll);
|
||||
Quaternion q_r(euler_r);
|
||||
Quaternion q_r = Quaternion::from_euler(euler_r);
|
||||
CHECK(q_r[0] == doctest::Approx(0.0));
|
||||
CHECK(q_r[1] == doctest::Approx(0.0));
|
||||
CHECK(q_r[2] == doctest::Approx(0.0871558));
|
||||
@ -163,11 +163,11 @@ TEST_CASE("[Quaternion] Construct Euler YXZ dynamic axes") {
|
||||
// Generate YXZ comparison data (Z-then-X-then-Y) using single-axis Euler
|
||||
// constructor and quaternion product, both tested separately.
|
||||
Vector3 euler_y(0.0, yaw, 0.0);
|
||||
Quaternion q_y(euler_y);
|
||||
Quaternion q_y = Quaternion::from_euler(euler_y);
|
||||
Vector3 euler_p(pitch, 0.0, 0.0);
|
||||
Quaternion q_p(euler_p);
|
||||
Quaternion q_p = Quaternion::from_euler(euler_p);
|
||||
Vector3 euler_r(0.0, 0.0, roll);
|
||||
Quaternion q_r(euler_r);
|
||||
Quaternion q_r = Quaternion::from_euler(euler_r);
|
||||
|
||||
// Roll-Z is followed by Pitch-X.
|
||||
Quaternion check_xz = q_p * q_r;
|
||||
@ -176,7 +176,7 @@ TEST_CASE("[Quaternion] Construct Euler YXZ dynamic axes") {
|
||||
|
||||
// Test construction from YXZ Euler angles.
|
||||
Vector3 euler_yxz(pitch, yaw, roll);
|
||||
Quaternion q(euler_yxz);
|
||||
Quaternion q = Quaternion::from_euler(euler_yxz);
|
||||
CHECK(q[0] == doctest::Approx(check_yxz[0]));
|
||||
CHECK(q[1] == doctest::Approx(check_yxz[1]));
|
||||
CHECK(q[2] == doctest::Approx(check_yxz[2]));
|
||||
@ -191,7 +191,7 @@ TEST_CASE("[Quaternion] Construct Basis Euler") {
|
||||
double pitch = Math::deg_to_rad(30.0);
|
||||
double roll = Math::deg_to_rad(10.0);
|
||||
Vector3 euler_yxz(pitch, yaw, roll);
|
||||
Quaternion q_yxz(euler_yxz);
|
||||
Quaternion q_yxz = Quaternion::from_euler(euler_yxz);
|
||||
Basis basis_axes = Basis::from_euler(euler_yxz);
|
||||
Quaternion q(basis_axes);
|
||||
CHECK(q.is_equal_approx(q_yxz));
|
||||
@ -209,7 +209,7 @@ TEST_CASE("[Quaternion] Construct Basis Axes") {
|
||||
// Quaternion from local calculation.
|
||||
Quaternion q_local = quat_euler_yxz_deg(Vector3(31.41, -49.16, 12.34));
|
||||
// Quaternion from Euler angles constructor.
|
||||
Quaternion q_euler(euler_yxz);
|
||||
Quaternion q_euler = Quaternion::from_euler(euler_yxz);
|
||||
CHECK(q_calc.is_equal_approx(q_local));
|
||||
CHECK(q_local.is_equal_approx(q_euler));
|
||||
|
||||
@ -253,21 +253,21 @@ TEST_CASE("[Quaternion] Product") {
|
||||
double roll = Math::deg_to_rad(10.0);
|
||||
|
||||
Vector3 euler_y(0.0, yaw, 0.0);
|
||||
Quaternion q_y(euler_y);
|
||||
Quaternion q_y = Quaternion::from_euler(euler_y);
|
||||
CHECK(q_y[0] == doctest::Approx(0.0));
|
||||
CHECK(q_y[1] == doctest::Approx(0.382684));
|
||||
CHECK(q_y[2] == doctest::Approx(0.0));
|
||||
CHECK(q_y[3] == doctest::Approx(0.923879));
|
||||
|
||||
Vector3 euler_p(pitch, 0.0, 0.0);
|
||||
Quaternion q_p(euler_p);
|
||||
Quaternion q_p = Quaternion::from_euler(euler_p);
|
||||
CHECK(q_p[0] == doctest::Approx(0.258819));
|
||||
CHECK(q_p[1] == doctest::Approx(0.0));
|
||||
CHECK(q_p[2] == doctest::Approx(0.0));
|
||||
CHECK(q_p[3] == doctest::Approx(0.965926));
|
||||
|
||||
Vector3 euler_r(0.0, 0.0, roll);
|
||||
Quaternion q_r(euler_r);
|
||||
Quaternion q_r = Quaternion::from_euler(euler_r);
|
||||
CHECK(q_r[0] == doctest::Approx(0.0));
|
||||
CHECK(q_r[1] == doctest::Approx(0.0));
|
||||
CHECK(q_r[2] == doctest::Approx(0.0871558));
|
||||
|
@ -140,13 +140,13 @@ TEST_CASE("[Animation] Create 3D rotation track") {
|
||||
Ref<Animation> animation = memnew(Animation);
|
||||
const int track_index = animation->add_track(Animation::TYPE_ROTATION_3D);
|
||||
animation->track_set_path(track_index, NodePath("Enemy:rotation"));
|
||||
animation->rotation_track_insert_key(track_index, 0.0, Quaternion(Vector3(0, 1, 2)));
|
||||
animation->rotation_track_insert_key(track_index, 0.5, Quaternion(Vector3(3.5, 4, 5)));
|
||||
animation->rotation_track_insert_key(track_index, 0.0, Quaternion::from_euler(Vector3(0, 1, 2)));
|
||||
animation->rotation_track_insert_key(track_index, 0.5, Quaternion::from_euler(Vector3(3.5, 4, 5)));
|
||||
|
||||
CHECK(animation->get_track_count() == 1);
|
||||
CHECK(!animation->track_is_compressed(0));
|
||||
CHECK(Quaternion(animation->track_get_key_value(0, 0)).is_equal_approx(Quaternion(Vector3(0, 1, 2))));
|
||||
CHECK(Quaternion(animation->track_get_key_value(0, 1)).is_equal_approx(Quaternion(Vector3(3.5, 4, 5))));
|
||||
CHECK(Quaternion(animation->track_get_key_value(0, 0)).is_equal_approx(Quaternion::from_euler(Vector3(0, 1, 2))));
|
||||
CHECK(Quaternion(animation->track_get_key_value(0, 1)).is_equal_approx(Quaternion::from_euler(Vector3(3.5, 4, 5))));
|
||||
|
||||
Quaternion r_interpolation;
|
||||
|
||||
|
@ -86,7 +86,7 @@ TEST_SUITE("Validate tests") {
|
||||
Plane plane(Vector3(1, 1, 1), 1.0);
|
||||
INFO(plane);
|
||||
|
||||
Quaternion quat(Vector3(0.5, 1.0, 2.0));
|
||||
Quaternion quat = Quaternion::from_euler(Vector3(0.5, 1.0, 2.0));
|
||||
INFO(quat);
|
||||
|
||||
AABB aabb(Vector3(), Vector3(100, 100, 100));
|
||||
|
Loading…
Reference in New Issue
Block a user