1
- use super :: IsEqualApprox ;
1
+ use super :: { Basis , IsEqualApprox , Vector3 , CMP_EPSILON } ;
2
+ use glam:: EulerRot ;
3
+ use std:: ops:: { Mul , Neg } ;
2
4
3
5
#[ derive( Copy , Clone , Debug , PartialEq ) ]
4
6
#[ repr( C ) ]
@@ -9,23 +11,300 @@ pub struct Quat {
9
11
pub w : f32 ,
10
12
}
11
13
14
+ /// Helper methods for `Quat`.
15
+ ///
16
+ /// See the official [`Godot documentation`](https://docs.godotengine.org/en/stable/classes/class_quat.html).
12
17
impl Quat {
18
+ /// The identity quaternion, representing no rotation. Equivalent to an identity [`Basis`] matrix.
19
+ /// If a vector is transformed by an identity quaternion, it will not change.
20
+ pub const IDENTITY : Self = Self :: new ( 0.0 , 0.0 , 0.0 , 1.0 ) ;
21
+
22
+ /// Constructs a quaternion defined by the given values.
13
23
#[ inline]
14
- pub fn new ( x : f32 , y : f32 , z : f32 , w : f32 ) -> Self {
24
+ pub const fn new ( x : f32 , y : f32 , z : f32 , w : f32 ) -> Self {
15
25
Self { x, y, z, w }
16
26
}
17
27
28
+ /// Constructs a quaternion from the given [Basis]
29
+ #[ inline]
30
+ pub fn from_basis ( basis : & Basis ) -> Self {
31
+ basis. to_quat ( )
32
+ }
33
+
34
+ /// Constructs a quaternion that will perform a rotation specified by Euler angles (in the YXZ
35
+ /// convention: when decomposing, first Z, then X, and Y last), given in the vector format as
36
+ /// (X angle, Y angle, Z angle).
37
+ #[ inline]
38
+ pub fn from_euler ( euler : Vector3 ) -> Self {
39
+ Self :: gd ( glam:: Quat :: from_euler (
40
+ EulerRot :: YXZ ,
41
+ euler. y ,
42
+ euler. x ,
43
+ euler. z ,
44
+ ) )
45
+ }
46
+
47
+ /// Constructs a quaternion that will rotate around the given axis by the specified angle. The
48
+ /// axis must be a normalized vector.
49
+ #[ inline]
50
+ pub fn from_axis_angle ( axis : Vector3 , angle : f32 ) -> Self {
51
+ debug_assert ! ( axis. is_normalized( ) , "Axis is not normalized" ) ;
52
+ Self :: gd ( glam:: Quat :: from_axis_angle ( axis. glam ( ) . into ( ) , angle) )
53
+ }
54
+
55
+ /// Performs a cubic spherical interpolation between quaternions `pre_a`, this quaternion, `b`,
56
+ /// and `post_b`, by the given amount `t`.
57
+ #[ inline]
58
+ pub fn cubic_slerp ( self , b : Self , pre_a : Self , post_b : Self , t : f32 ) -> Self {
59
+ let t2 = ( 1.0 - t) * t * 2.0 ;
60
+ let sp = self . slerp ( b, t) ;
61
+ let sq = pre_a. slerpni ( post_b, t) ;
62
+ sp. slerpni ( sq, t2)
63
+ }
64
+
65
+ /// Returns the dot product of two quaternions.
66
+ #[ inline]
67
+ pub fn dot ( self , b : Self ) -> f32 {
68
+ self . glam ( ) . dot ( b. glam ( ) )
69
+ }
70
+
71
+ /// Returns Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last)
72
+ /// corresponding to the rotation represented by the unit quaternion. Returned vector contains
73
+ /// the rotation angles in the format (X angle, Y angle, Z angle).
74
+ #[ inline]
75
+ pub fn to_euler ( self ) -> Vector3 {
76
+ Basis :: from_quat ( self ) . to_euler ( )
77
+ }
78
+
79
+ /// Returns the inverse of the quaternion.
80
+ #[ inline]
81
+ pub fn inverse ( self ) -> Self {
82
+ Self :: gd ( self . glam ( ) . inverse ( ) )
83
+ }
84
+
85
+ /// Returns `true` if this quaternion and `quat` are approximately equal, by running
86
+ /// `is_equal_approx` on each component
18
87
#[ inline]
19
- pub fn is_equal_approx ( self , to : & Self ) -> bool {
88
+ pub fn is_equal_approx ( self , to : Self ) -> bool {
20
89
self . x . is_equal_approx ( to. x )
21
90
&& self . y . is_equal_approx ( to. y )
22
91
&& self . z . is_equal_approx ( to. z )
23
92
&& self . w . is_equal_approx ( to. w )
24
93
}
25
94
95
+ /// Returns whether the quaternion is normalized or not.
96
+ #[ inline]
97
+ pub fn is_normalized ( self ) -> bool {
98
+ self . glam ( ) . is_normalized ( )
99
+ }
100
+
101
+ /// Returns the length of the quaternion.
102
+ #[ inline]
103
+ pub fn length ( self ) -> f32 {
104
+ self . glam ( ) . length ( )
105
+ }
106
+
107
+ /// Returns the length of the quaternion, squared.
108
+ #[ inline]
109
+ pub fn length_squared ( self ) -> f32 {
110
+ self . glam ( ) . length_squared ( )
111
+ }
112
+
113
+ /// Returns a copy of the quaternion, normalized to unit length.
114
+ ///
115
+ /// Normalization is necessary before transforming vectors through `xform()` or `*`.
116
+ #[ inline]
117
+ pub fn normalized ( self ) -> Self {
118
+ Self :: gd ( self . glam ( ) . normalize ( ) )
119
+ }
120
+
121
+ /// Returns the result of the spherical linear interpolation between this quaternion and to by
122
+ /// amount weight.
123
+ ///
124
+ /// **Note:** Both quaternions must be normalized.
125
+ #[ inline]
126
+ pub fn slerp ( self , b : Self , t : f32 ) -> Self {
127
+ debug_assert ! ( self . is_normalized( ) , "Quaternion `self` is not normalized" ) ;
128
+ debug_assert ! ( b. is_normalized( ) , "Quaternion `b` is not normalized" ) ;
129
+
130
+ // Copied from Godot's Quat::slerp as glam::lerp version diverges too much
131
+
132
+ // calc cosine
133
+ let cos = self . dot ( b) ;
134
+
135
+ // adjust signs (if necessary)
136
+ let ( cos, b) = if cos < 0.0 { ( -cos, -b) } else { ( cos, b) } ;
137
+
138
+ // calculate coefficients
139
+ let scale = if 1.0 - cos > CMP_EPSILON as f32 {
140
+ // standard case (slerp)
141
+ let omega = cos. acos ( ) ;
142
+ let sin = omega. sin ( ) ;
143
+ ( ( ( 1.0 - t) * omega) . sin ( ) / sin, ( t * omega) . sin ( ) / sin)
144
+ } else {
145
+ // "from" and "to" quaternions are very close
146
+ // ... so we can do a linear interpolation
147
+ ( 1.0 - t, t)
148
+ } ;
149
+
150
+ // calculate final values
151
+ Self :: new (
152
+ scale. 0 * self . x + scale. 1 * b. x ,
153
+ scale. 0 * self . y + scale. 1 * b. y ,
154
+ scale. 0 * self . z + scale. 1 * b. z ,
155
+ scale. 0 * self . w + scale. 1 * b. w ,
156
+ )
157
+ }
158
+
159
+ /// Returns the result of the spherical linear interpolation between this quaternion and `t` by
160
+ /// amount `t`, but without checking if the rotation path is not bigger than 90 degrees.
161
+ #[ inline]
162
+ pub fn slerpni ( self , b : Self , t : f32 ) -> Self {
163
+ debug_assert ! ( self . is_normalized( ) , "Quaternion `self` is not normalized" ) ;
164
+ debug_assert ! ( b. is_normalized( ) , "Quaternion `b` is not normalized" ) ;
165
+
166
+ // Copied from Godot's Quat::slerpni as glam::slerp version diverges too much
167
+
168
+ let dot = self . dot ( b) ;
169
+ if dot. abs ( ) > 0.9999 {
170
+ self
171
+ } else {
172
+ let theta = dot. acos ( ) ;
173
+ let sin_t = 1.0 / theta. sin ( ) ;
174
+ let new_f = ( t * theta) . sin ( ) * sin_t;
175
+ let inv_f = ( ( 1.0 - t) * theta) . sin ( ) * sin_t;
176
+ Self :: new (
177
+ inv_f * self . x + new_f * b. x ,
178
+ inv_f * self . y + new_f * b. y ,
179
+ inv_f * self . z + new_f * b. z ,
180
+ inv_f * self . w + new_f * b. w ,
181
+ )
182
+ }
183
+ }
184
+
185
+ /// Returns a vector transformed (multiplied) by this quaternion. This is the same as `mul`
186
+ ///
187
+ /// **Note:** The quaternion must be normalized.
26
188
#[ inline]
27
- #[ allow( unused) ]
28
- fn glam ( self ) -> glam:: Quat {
189
+ pub fn xform ( self , v : Vector3 ) -> Vector3 {
190
+ self * v
191
+ }
192
+
193
+ #[ inline]
194
+ pub ( super ) fn gd ( quat : glam:: Quat ) -> Self {
195
+ Self :: new ( quat. x , quat. y , quat. z , quat. w )
196
+ }
197
+
198
+ #[ inline]
199
+ pub ( super ) fn glam ( self ) -> glam:: Quat {
29
200
glam:: Quat :: from_xyzw ( self . x , self . y , self . z , self . w )
30
201
}
31
202
}
203
+
204
+ impl Mul < Vector3 > for Quat {
205
+ type Output = Vector3 ;
206
+
207
+ #[ inline]
208
+ /// Returns a vector transformed (multiplied) by this quaternion. This is the same as `xform`
209
+ ///
210
+ /// **Note:** The quaternion must be normalized.
211
+ fn mul ( self , with : Vector3 ) -> Vector3 {
212
+ debug_assert ! ( self . is_normalized( ) , "Quaternion is not normalized" ) ;
213
+ Vector3 :: gd ( self . glam ( ) * with. glam ( ) )
214
+ }
215
+ }
216
+
217
+ impl Mul < Self > for Quat {
218
+ type Output = Self ;
219
+
220
+ #[ inline]
221
+ /// Returns another quaternion transformed (multiplied) by this quaternion.
222
+ fn mul ( self , with : Self ) -> Self {
223
+ Self :: gd ( self . glam ( ) * with. glam ( ) )
224
+ }
225
+ }
226
+
227
+ impl Neg for Quat {
228
+ type Output = Quat ;
229
+
230
+ #[ inline]
231
+ fn neg ( self ) -> Self {
232
+ Self :: gd ( -self . glam ( ) )
233
+ }
234
+ }
235
+
236
+ #[ cfg( test) ]
237
+ mod test {
238
+ use super :: * ;
239
+
240
+ #[ test]
241
+ fn from_euler ( ) {
242
+ let euler = Vector3 :: new ( 0.25 , 5.24 , 3.0 ) ;
243
+ let expect = Quat :: new ( 0.485489 , 0.142796 , -0.862501 , 0.001113 ) ;
244
+ assert ! ( Quat :: from_euler( euler) . is_equal_approx( expect) ) ;
245
+ }
246
+
247
+ #[ test]
248
+ fn to_basis ( ) {
249
+ let quat = Quat :: new ( 0.485489 , 0.142796 , -0.862501 , 0.001113 ) ;
250
+ let expect = Basis :: from_elements ( [
251
+ Vector3 :: new ( -0.528598 , 0.140572 , -0.837152 ) ,
252
+ Vector3 :: new ( 0.136732 , -0.959216 , -0.247404 ) ,
253
+ Vector3 :: new ( -0.837788 , -0.245243 , 0.487819 ) ,
254
+ ] ) ;
255
+ let basis = Basis :: from_quat ( quat) ;
256
+ assert ! ( basis. is_equal_approx( & expect) ) ;
257
+ }
258
+
259
+ #[ test]
260
+ fn to_euler ( ) {
261
+ let quat = Quat :: new ( 0.485489 , 0.142796 , -0.862501 , 0.001113 ) ;
262
+ let expect = Vector3 :: new ( 0.25 , -1.043185 , 3.00001 ) ;
263
+ assert ! ( quat. to_euler( ) . is_equal_approx( expect) ) ;
264
+ }
265
+
266
+ #[ test]
267
+ fn mul_vec ( ) {
268
+ let quat = Quat :: new ( 0.485489 , 0.142796 , -0.862501 , 0.001113 ) ;
269
+ let vec = Vector3 :: new ( 2.2 , 0.8 , 1.65 ) ;
270
+ let expect = Vector3 :: new ( -2.43176 , -0.874777 , -1.234427 ) ;
271
+ assert ! ( expect. is_equal_approx( quat * vec) ) ;
272
+ }
273
+
274
+ #[ test]
275
+ fn mul_quat ( ) {
276
+ let a = Quat :: new ( -0.635115 , -0.705592 , 0.314052 , 0.011812 ) ;
277
+ let b = Quat :: new ( 0.485489 , 0.142796 , -0.862501 , 0.001113 ) ;
278
+ let e = Quat :: new ( 0.568756 , -0.394417 , 0.242027 , 0.67998 ) ;
279
+ assert ! ( e. is_equal_approx( a * b) ) ;
280
+ }
281
+
282
+ #[ test]
283
+ fn slerp ( ) {
284
+ let q = Quat :: new ( -0.635115 , -0.705592 , 0.314052 , 0.011812 ) ;
285
+ let p = Quat :: new ( 0.485489 , 0.142796 , -0.862501 , 0.001113 ) ;
286
+ let t = 0.2 ;
287
+ let e = Quat :: new ( -0.638517 , -0.620742 , 0.454844 , 0.009609 ) ;
288
+ assert ! ( e. is_equal_approx( q. slerp( p, t) ) ) ;
289
+ }
290
+
291
+ #[ test]
292
+ fn slerpni ( ) {
293
+ let q = Quat :: new ( -0.635115 , -0.705592 , 0.314052 , 0.011812 ) ;
294
+ let p = Quat :: new ( 0.485489 , 0.142796 , -0.862501 , 0.001113 ) ;
295
+ let t = 0.2 ;
296
+ let e = Quat :: new ( -0.535331 , -0.836627 , -0.114954 , 0.016143 ) ;
297
+ assert ! ( e. is_equal_approx( q. slerpni( p, t) ) ) ;
298
+ }
299
+
300
+ #[ test]
301
+ fn cubic_slerp ( ) {
302
+ let a = Quat :: new ( -0.635115 , -0.705592 , 0.314052 , 0.011812 ) ;
303
+ let b = Quat :: new ( 0.485489 , 0.142796 , -0.862501 , 0.001113 ) ;
304
+ let c = Quat :: new ( -0.666276 , 0.03859 , 0.083527 , -0.740007 ) ;
305
+ let d = Quat :: new ( -0.856633 , -0.430228 , -0.284017 , 0.020464 ) ;
306
+ let t = 0.2 ;
307
+ let e = Quat :: new ( -0.768253 , -0.490687 , 0.341836 , -0.22839 ) ;
308
+ assert ! ( e. is_equal_approx( a. cubic_slerp( b, c, d, t) ) ) ;
309
+ }
310
+ }
0 commit comments