1- use super :: IsEqualApprox ;
1+ use super :: { Basis , IsEqualApprox , Vector3 , CMP_EPSILON } ;
2+ use glam:: EulerRot ;
3+ use std:: ops:: { Mul , Neg } ;
24
35#[ derive( Copy , Clone , Debug , PartialEq ) ]
46#[ repr( C ) ]
@@ -9,23 +11,300 @@ pub struct Quat {
911 pub w : f32 ,
1012}
1113
14+ /// Helper methods for `Quat`.
15+ ///
16+ /// See the official [`Godot documentation`](https://docs.godotengine.org/en/stable/classes/class_quat.html).
1217impl 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.
1323 #[ 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 {
1525 Self { x, y, z, w }
1626 }
1727
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
1887 #[ inline]
19- pub fn is_equal_approx ( self , to : & Self ) -> bool {
88+ pub fn is_equal_approx ( self , to : Self ) -> bool {
2089 self . x . is_equal_approx ( to. x )
2190 && self . y . is_equal_approx ( to. y )
2291 && self . z . is_equal_approx ( to. z )
2392 && self . w . is_equal_approx ( to. w )
2493 }
2594
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.
26188 #[ 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 {
29200 glam:: Quat :: from_xyzw ( self . x , self . y , self . z , self . w )
30201 }
31202}
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