Skip to content

Commit dfdc047

Browse files
authored
Merge pull request #1630 from dsnopek/sync-quaternion
Sync `Quaternion` with the version in Godot
2 parents a98d41f + 2004af6 commit dfdc047

File tree

2 files changed

+70
-84
lines changed

2 files changed

+70
-84
lines changed

include/godot_cpp/variant/quaternion.hpp

Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#ifndef GODOT_QUATERNION_HPP
3232
#define GODOT_QUATERNION_HPP
3333

34+
#include <godot_cpp/classes/global_constants.hpp>
3435
#include <godot_cpp/core/math.hpp>
3536
#include <godot_cpp/variant/vector3.hpp>
3637

@@ -47,11 +48,11 @@ struct _NO_DISCARD_ Quaternion {
4748
real_t components[4] = { 0, 0, 0, 1.0 };
4849
};
4950

50-
_FORCE_INLINE_ real_t &operator[](int idx) {
51-
return components[idx];
51+
_FORCE_INLINE_ real_t &operator[](int p_idx) {
52+
return components[p_idx];
5253
}
53-
_FORCE_INLINE_ const real_t &operator[](int idx) const {
54-
return components[idx];
54+
_FORCE_INLINE_ const real_t &operator[](int p_idx) const {
55+
return components[p_idx];
5556
}
5657
_FORCE_INLINE_ real_t length_squared() const;
5758
bool is_equal_approx(const Quaternion &p_quaternion) const;
@@ -66,14 +67,13 @@ struct _NO_DISCARD_ Quaternion {
6667
_FORCE_INLINE_ real_t dot(const Quaternion &p_q) const;
6768
real_t angle_to(const Quaternion &p_to) const;
6869

69-
Vector3 get_euler_xyz() const;
70-
Vector3 get_euler_yxz() const;
71-
Vector3 get_euler() const { return get_euler_yxz(); }
70+
Vector3 get_euler(EulerOrder p_order = EulerOrder::EULER_ORDER_YXZ) const;
71+
static Quaternion from_euler(const Vector3 &p_euler);
7272

73-
Quaternion slerp(const Quaternion &p_to, const real_t &p_weight) const;
74-
Quaternion slerpni(const Quaternion &p_to, const real_t &p_weight) const;
75-
Quaternion spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const;
76-
Quaternion spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight, const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const;
73+
Quaternion slerp(const Quaternion &p_to, real_t p_weight) const;
74+
Quaternion slerpni(const Quaternion &p_to, real_t p_weight) const;
75+
Quaternion spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight) const;
76+
Quaternion spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight, real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const;
7777

7878
Vector3 get_axis() const;
7979
real_t get_angle() const;
@@ -89,28 +89,28 @@ struct _NO_DISCARD_ Quaternion {
8989
void operator*=(const Quaternion &p_q);
9090
Quaternion operator*(const Quaternion &p_q) const;
9191

92-
_FORCE_INLINE_ Vector3 xform(const Vector3 &v) const {
92+
_FORCE_INLINE_ Vector3 xform(const Vector3 &p_v) const {
9393
#ifdef MATH_CHECKS
94-
ERR_FAIL_COND_V_MSG(!is_normalized(), v, "The quaternion must be normalized.");
94+
ERR_FAIL_COND_V_MSG(!is_normalized(), p_v, "The quaternion " + operator String() + " must be normalized.");
9595
#endif
9696
Vector3 u(x, y, z);
97-
Vector3 uv = u.cross(v);
98-
return v + ((uv * w) + u.cross(uv)) * ((real_t)2);
97+
Vector3 uv = u.cross(p_v);
98+
return p_v + ((uv * w) + u.cross(uv)) * ((real_t)2);
9999
}
100100

101-
_FORCE_INLINE_ Vector3 xform_inv(const Vector3 &v) const {
102-
return inverse().xform(v);
101+
_FORCE_INLINE_ Vector3 xform_inv(const Vector3 &p_v) const {
102+
return inverse().xform(p_v);
103103
}
104104

105105
_FORCE_INLINE_ void operator+=(const Quaternion &p_q);
106106
_FORCE_INLINE_ void operator-=(const Quaternion &p_q);
107-
_FORCE_INLINE_ void operator*=(const real_t &s);
108-
_FORCE_INLINE_ void operator/=(const real_t &s);
109-
_FORCE_INLINE_ Quaternion operator+(const Quaternion &q2) const;
110-
_FORCE_INLINE_ Quaternion operator-(const Quaternion &q2) const;
107+
_FORCE_INLINE_ void operator*=(real_t p_s);
108+
_FORCE_INLINE_ void operator/=(real_t p_s);
109+
_FORCE_INLINE_ Quaternion operator+(const Quaternion &p_q2) const;
110+
_FORCE_INLINE_ Quaternion operator-(const Quaternion &p_q2) const;
111111
_FORCE_INLINE_ Quaternion operator-() const;
112-
_FORCE_INLINE_ Quaternion operator*(const real_t &s) const;
113-
_FORCE_INLINE_ Quaternion operator/(const real_t &s) const;
112+
_FORCE_INLINE_ Quaternion operator*(real_t p_s) const;
113+
_FORCE_INLINE_ Quaternion operator/(real_t p_s) const;
114114

115115
_FORCE_INLINE_ bool operator==(const Quaternion &p_quaternion) const;
116116
_FORCE_INLINE_ bool operator!=(const Quaternion &p_quaternion) const;
@@ -128,8 +128,6 @@ struct _NO_DISCARD_ Quaternion {
128128

129129
Quaternion(const Vector3 &p_axis, real_t p_angle);
130130

131-
Quaternion(const Vector3 &p_euler);
132-
133131
Quaternion(const Quaternion &p_q) :
134132
x(p_q.x),
135133
y(p_q.y),
@@ -144,9 +142,9 @@ struct _NO_DISCARD_ Quaternion {
144142
w = p_q.w;
145143
}
146144

147-
Quaternion(const Vector3 &v0, const Vector3 &v1) { // Shortest arc.
148-
Vector3 c = v0.cross(v1);
149-
real_t d = v0.dot(v1);
145+
Quaternion(const Vector3 &p_v0, const Vector3 &p_v1) { // Shortest arc.
146+
Vector3 c = p_v0.cross(p_v1);
147+
real_t d = p_v0.dot(p_v1);
150148

151149
if (d < -1.0f + (real_t)CMP_EPSILON) {
152150
x = 0;
@@ -187,38 +185,38 @@ void Quaternion::operator-=(const Quaternion &p_q) {
187185
w -= p_q.w;
188186
}
189187

190-
void Quaternion::operator*=(const real_t &s) {
191-
x *= s;
192-
y *= s;
193-
z *= s;
194-
w *= s;
188+
void Quaternion::operator*=(real_t p_s) {
189+
x *= p_s;
190+
y *= p_s;
191+
z *= p_s;
192+
w *= p_s;
195193
}
196194

197-
void Quaternion::operator/=(const real_t &s) {
198-
*this *= 1.0f / s;
195+
void Quaternion::operator/=(real_t p_s) {
196+
*this *= 1.0f / p_s;
199197
}
200198

201-
Quaternion Quaternion::operator+(const Quaternion &q2) const {
199+
Quaternion Quaternion::operator+(const Quaternion &p_q2) const {
202200
const Quaternion &q1 = *this;
203-
return Quaternion(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w);
201+
return Quaternion(q1.x + p_q2.x, q1.y + p_q2.y, q1.z + p_q2.z, q1.w + p_q2.w);
204202
}
205203

206-
Quaternion Quaternion::operator-(const Quaternion &q2) const {
204+
Quaternion Quaternion::operator-(const Quaternion &p_q2) const {
207205
const Quaternion &q1 = *this;
208-
return Quaternion(q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w);
206+
return Quaternion(q1.x - p_q2.x, q1.y - p_q2.y, q1.z - p_q2.z, q1.w - p_q2.w);
209207
}
210208

211209
Quaternion Quaternion::operator-() const {
212210
const Quaternion &q2 = *this;
213211
return Quaternion(-q2.x, -q2.y, -q2.z, -q2.w);
214212
}
215213

216-
Quaternion Quaternion::operator*(const real_t &s) const {
217-
return Quaternion(x * s, y * s, z * s, w * s);
214+
Quaternion Quaternion::operator*(real_t p_s) const {
215+
return Quaternion(x * p_s, y * p_s, z * p_s, w * p_s);
218216
}
219217

220-
Quaternion Quaternion::operator/(const real_t &s) const {
221-
return *this * (1.0f / s);
218+
Quaternion Quaternion::operator/(real_t p_s) const {
219+
return *this * (1.0f / p_s);
222220
}
223221

224222
bool Quaternion::operator==(const Quaternion &p_quaternion) const {
@@ -229,7 +227,7 @@ bool Quaternion::operator!=(const Quaternion &p_quaternion) const {
229227
return x != p_quaternion.x || y != p_quaternion.y || z != p_quaternion.z || w != p_quaternion.w;
230228
}
231229

232-
_FORCE_INLINE_ Quaternion operator*(const real_t &p_real, const Quaternion &p_quaternion) {
230+
_FORCE_INLINE_ Quaternion operator*(real_t p_real, const Quaternion &p_quaternion) {
233231
return p_quaternion * p_real;
234232
}
235233

src/variant/quaternion.cpp

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,15 @@ namespace godot {
3737

3838
real_t Quaternion::angle_to(const Quaternion &p_to) const {
3939
real_t d = dot(p_to);
40-
return Math::acos(CLAMP(d * d * 2 - 1, -1, 1));
40+
// acos does clamping.
41+
return Math::acos(d * d * 2 - 1);
4142
}
4243

43-
// get_euler_xyz returns a vector containing the Euler angles in the format
44-
// (ax,ay,az), where ax is the angle of rotation around x axis,
45-
// and similar for other axes.
46-
// This implementation uses XYZ convention (Z is the first rotation).
47-
Vector3 Quaternion::get_euler_xyz() const {
48-
Basis m(*this);
49-
return m.get_euler(EULER_ORDER_XYZ);
50-
}
51-
52-
// get_euler_yxz returns a vector containing the Euler angles in the format
53-
// (ax,ay,az), where ax is the angle of rotation around x axis,
54-
// and similar for other axes.
55-
// This implementation uses YXZ convention (Z is the first rotation).
56-
Vector3 Quaternion::get_euler_yxz() const {
44+
Vector3 Quaternion::get_euler(EulerOrder p_order) const {
5745
#ifdef MATH_CHECKS
58-
ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion must be normalized.");
46+
ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion " + operator String() + " must be normalized.");
5947
#endif
60-
Basis m(*this);
61-
return m.get_euler(EULER_ORDER_YXZ);
48+
return Basis(*this).get_euler(p_order);
6249
}
6350

6451
void Quaternion::operator*=(const Quaternion &p_q) {
@@ -103,7 +90,7 @@ bool Quaternion::is_normalized() const {
10390

10491
Quaternion Quaternion::inverse() const {
10592
#ifdef MATH_CHECKS
106-
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The quaternion must be normalized.");
93+
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The quaternion " + operator String() + " must be normalized.");
10794
#endif
10895
return Quaternion(-x, -y, -z, w);
10996
}
@@ -125,10 +112,10 @@ Quaternion Quaternion::exp() const {
125112
return Quaternion(src_v, theta);
126113
}
127114

128-
Quaternion Quaternion::slerp(const Quaternion &p_to, const real_t &p_weight) const {
115+
Quaternion Quaternion::slerp(const Quaternion &p_to, real_t p_weight) const {
129116
#ifdef MATH_CHECKS
130-
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
131-
ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
117+
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
118+
ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion " + p_to.operator String() + " must be normalized.");
132119
#endif
133120
Quaternion to1;
134121
real_t omega, cosom, sinom, scale0, scale1;
@@ -166,10 +153,10 @@ Quaternion Quaternion::slerp(const Quaternion &p_to, const real_t &p_weight) con
166153
scale0 * w + scale1 * to1.w);
167154
}
168155

169-
Quaternion Quaternion::slerpni(const Quaternion &p_to, const real_t &p_weight) const {
156+
Quaternion Quaternion::slerpni(const Quaternion &p_to, real_t p_weight) const {
170157
#ifdef MATH_CHECKS
171-
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
172-
ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
158+
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
159+
ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion " + p_to.operator String() + " must be normalized.");
173160
#endif
174161
const Quaternion &from = *this;
175162

@@ -190,10 +177,10 @@ Quaternion Quaternion::slerpni(const Quaternion &p_to, const real_t &p_weight) c
190177
invFactor * from.w + newFactor * p_to.w);
191178
}
192179

193-
Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const {
180+
Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight) const {
194181
#ifdef MATH_CHECKS
195-
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
196-
ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
182+
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
183+
ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion " + p_b.operator String() + " must be normalized.");
197184
#endif
198185
Quaternion from_q = *this;
199186
Quaternion pre_q = p_pre_a;
@@ -236,15 +223,15 @@ Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const
236223
ln.z = Math::cubic_interpolate(ln_from.z, ln_to.z, ln_pre.z, ln_post.z, p_weight);
237224
Quaternion q2 = to_q * ln.exp();
238225

239-
// To cancel error made by Expmap ambiguity, do blends.
226+
// To cancel error made by Expmap ambiguity, do blending.
240227
return q1.slerp(q2, p_weight);
241228
}
242229

243-
Quaternion Quaternion::spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight,
244-
const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const {
230+
Quaternion Quaternion::spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight,
231+
real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const {
245232
#ifdef MATH_CHECKS
246-
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
247-
ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
233+
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
234+
ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion " + p_b.operator String() + " must be normalized.");
248235
#endif
249236
Quaternion from_q = *this;
250237
Quaternion pre_q = p_pre_a;
@@ -287,7 +274,7 @@ Quaternion Quaternion::spherical_cubic_interpolate_in_time(const Quaternion &p_b
287274
ln.z = Math::cubic_interpolate_in_time(ln_from.z, ln_to.z, ln_pre.z, ln_post.z, p_weight, p_b_t, p_pre_a_t, p_post_b_t);
288275
Quaternion q2 = to_q * ln.exp();
289276

290-
// To cancel error made by Expmap ambiguity, do blends.
277+
// To cancel error made by Expmap ambiguity, do blending.
291278
return q1.slerp(q2, p_weight);
292279
}
293280

@@ -309,7 +296,7 @@ real_t Quaternion::get_angle() const {
309296

310297
Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) {
311298
#ifdef MATH_CHECKS
312-
ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 must be normalized.");
299+
ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 " + p_axis.operator String() + " must be normalized.");
313300
#endif
314301
real_t d = p_axis.length();
315302
if (d == 0) {
@@ -332,7 +319,7 @@ Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) {
332319
// (ax, ay, az), where ax is the angle of rotation around x axis,
333320
// and similar for other axes.
334321
// This implementation uses YXZ convention (Z is the first rotation).
335-
Quaternion::Quaternion(const Vector3 &p_euler) {
322+
Quaternion Quaternion::from_euler(const Vector3 &p_euler) {
336323
real_t half_a1 = p_euler.y * 0.5f;
337324
real_t half_a2 = p_euler.x * 0.5f;
338325
real_t half_a3 = p_euler.z * 0.5f;
@@ -348,10 +335,11 @@ Quaternion::Quaternion(const Vector3 &p_euler) {
348335
real_t cos_a3 = Math::cos(half_a3);
349336
real_t sin_a3 = Math::sin(half_a3);
350337

351-
x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3;
352-
y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3;
353-
z = -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3;
354-
w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3;
338+
return Quaternion(
339+
sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3,
340+
sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3,
341+
-sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3,
342+
sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3);
355343
}
356344

357345
} // namespace godot

0 commit comments

Comments
 (0)