@@ -116,12 +116,9 @@ impl Transform {
116
116
/// affine_inverse for transforms with scaling).
117
117
#[ inline]
118
118
pub fn inverse ( & self ) -> Self {
119
- let basis_inv = self . basis . transposed ( ) ;
120
- let origin_inv = basis_inv. xform ( -self . origin ) ;
121
-
122
- Self {
123
- basis : basis_inv,
124
- origin : origin_inv,
119
+ Transform {
120
+ basis : self . basis . transposed ( ) ,
121
+ origin : self . basis . xform ( -self . origin ) ,
125
122
}
126
123
}
127
124
@@ -138,6 +135,25 @@ impl Transform {
138
135
}
139
136
}
140
137
138
+ /// In-place rotation of the transform around the given axis by the given
139
+ /// angle (in radians), using matrix multiplication. The axis must be a
140
+ /// normalized vector.
141
+ /// Due to nature of the operation, a new transform is created first.
142
+ #[ inline]
143
+ pub fn rotated ( & self , axis : Vector3 , phi : f32 ) -> Self {
144
+ Transform {
145
+ basis : Basis :: from_axis_angle ( axis, phi) ,
146
+ origin : Vector3 :: default ( ) ,
147
+ } * ( * self )
148
+ }
149
+
150
+ /// Returns the rotated transform around the given axis by the given angle (in radians),
151
+ /// using matrix multiplication. The axis must be a normalized vector.
152
+ #[ inline]
153
+ pub fn rotate ( & mut self , axis : Vector3 , phi : f32 ) {
154
+ * self = self . rotated ( axis, phi) ;
155
+ }
156
+
141
157
/// Returns a copy of the transform rotated such that its -Z axis points
142
158
/// towards the target position.
143
159
///
@@ -157,6 +173,92 @@ impl Transform {
157
173
}
158
174
}
159
175
176
+ /// Scales basis and origin of the transform by the given scale factor,
177
+ /// using matrix multiplication.
178
+ #[ inline]
179
+ pub fn scaled ( & self , scale : Vector3 ) -> Self {
180
+ Transform {
181
+ basis : self . basis . scaled ( scale) ,
182
+ origin : self . origin * scale,
183
+ }
184
+ }
185
+
186
+ /// In-place translates the transform by the given offset, relative to
187
+ /// the transform's basis vectors.
188
+ #[ inline]
189
+ fn translate_withbasis ( & mut self , translation : Vector3 ) {
190
+ // Note: Godot source uses origin + basis dot translation,
191
+ // but self.translate() uses only origin + translation
192
+ self . origin . x += self . basis . elements [ 0 ] . dot ( translation) ;
193
+ self . origin . y += self . basis . elements [ 1 ] . dot ( translation) ;
194
+ self . origin . z += self . basis . elements [ 2 ] . dot ( translation) ;
195
+ }
196
+
197
+ /// Translates the transform by the given offset, relative to
198
+ /// the transform's basis vectors.
199
+ #[ inline]
200
+ pub fn translated_withbasis ( & self , translation : Vector3 ) -> Self {
201
+ let mut copy = * self ;
202
+ copy. translate_withbasis ( translation) ;
203
+ copy
204
+ }
205
+
206
+ /// Returns the transform with the basis orthogonal (90 degrees),
207
+ /// and normalized axis vectors.
208
+ #[ inline]
209
+ pub fn orthonormalized ( & self ) -> Self {
210
+ Transform {
211
+ basis : self . basis . orthonormalized ( ) ,
212
+ origin : self . origin ,
213
+ }
214
+ }
215
+
216
+ /// Returns the transform with the basis orthogonal (90 degrees),
217
+ /// but without normalizing the axis vectors.
218
+ #[ inline]
219
+ pub fn orthogonalized ( & self ) -> Self {
220
+ Transform {
221
+ basis : self . basis . orthogonalized ( ) ,
222
+ origin : self . origin ,
223
+ }
224
+ }
225
+
226
+ #[ inline]
227
+ pub fn is_equal_approx ( & self , other : Transform ) -> bool {
228
+ self . basis . is_equal_approx ( & other. basis ) && self . origin . is_equal_approx ( other. origin )
229
+ }
230
+
231
+ /// Interpolates the transform to other Transform by
232
+ /// weight amount (on the range of 0.0 to 1.0).
233
+ /// Assuming the two transforms are located on a sphere surface.
234
+ #[ inline]
235
+ pub fn sphere_interpolate_with ( & self , other : Transform , weight : f32 ) -> Self {
236
+ let src_scale = self . basis . scale ( ) ;
237
+ let src_rot = self . basis . to_quat ( ) ;
238
+ let src_loc = self . origin ;
239
+
240
+ let dst_scale = other. basis . scale ( ) ;
241
+ let dst_rot = other. basis . to_quat ( ) ;
242
+ let dst_loc = other. origin ;
243
+
244
+ let new_basis = Basis :: from_quat ( src_rot. slerp ( dst_rot, weight) . normalized ( ) ) ;
245
+ let new_basis = new_basis. scaled ( src_scale. linear_interpolate ( dst_scale, weight) ) ;
246
+ Transform {
247
+ basis : new_basis,
248
+ origin : src_loc. linear_interpolate ( dst_loc, weight) ,
249
+ }
250
+ }
251
+
252
+ /// Interpolates the transform to other Transform by
253
+ /// weight amount (on the range of 0.0 to 1.0).
254
+ #[ inline]
255
+ pub fn interpolate_with ( & self , other : Transform , weight : f32 ) -> Self {
256
+ Transform {
257
+ basis : self . basis . lerp ( other. basis , weight) ,
258
+ origin : self . origin . linear_interpolate ( other. origin , weight) ,
259
+ }
260
+ }
261
+
160
262
#[ doc( hidden) ]
161
263
#[ inline]
162
264
pub fn sys ( & self ) -> * const sys:: godot_transform {
@@ -182,3 +284,120 @@ impl Mul<Transform> for Transform {
182
284
Self { origin, basis }
183
285
}
184
286
}
287
+
288
+ #[ cfg( test) ]
289
+ mod tests {
290
+ use super :: * ;
291
+
292
+ fn test_inputs ( ) -> ( Transform , Transform ) {
293
+ let basis = Basis :: from_euler ( Vector3 :: new ( 37.51756 , 20.39467 , 49.96816 ) ) ;
294
+ let mut t = Transform :: from_basis_origin (
295
+ basis. a ( ) ,
296
+ basis. b ( ) ,
297
+ basis. c ( ) ,
298
+ Vector3 :: new ( 0.0 , 0.0 , 0.0 ) ,
299
+ ) ;
300
+ t = t. translated_withbasis ( Vector3 :: new ( 0.5 , -1.0 , 0.25 ) ) ;
301
+ t = t. scaled ( Vector3 :: new ( 0.25 , 0.5 , 2.0 ) ) ;
302
+
303
+ let basis = Basis :: from_euler ( Vector3 :: new ( 12.23 , 50.46 , 93.94 ) ) ;
304
+ let mut t2 = Transform :: from_basis_origin (
305
+ basis. a ( ) ,
306
+ basis. b ( ) ,
307
+ basis. c ( ) ,
308
+ Vector3 :: new ( 0.0 , 0.0 , 0.0 ) ,
309
+ ) ;
310
+ t2 = t2. translated_withbasis ( Vector3 :: new ( 1.5 , -2.0 , 1.25 ) ) ;
311
+ t2 = t2. scaled ( Vector3 :: new ( 0.5 , 0.58 , 1.0 ) ) ;
312
+ // Godot reports:
313
+ // t = 0.019358, -0.041264, 0.24581, -0.144074, 0.470205, 0.090279, -1.908901, -0.594598, 0.050514 - 0.112395, -0.519672, -0.347224
314
+ // t2 = 0.477182, 0.118214, 0.09123, -0.165859, 0.521769, 0.191437, -0.086105, -0.367178, 0.926157 - 0.593383, -1.05303, 1.762894
315
+
316
+ ( t, t2)
317
+ }
318
+
319
+ #[ test]
320
+ fn translation_is_sane ( ) {
321
+ let translation = Vector3 :: new ( 1.0 , 2.0 , 3.0 ) ;
322
+ let t = Transform :: default ( ) . translated ( translation) ;
323
+ assert ! ( t. basis. elements[ 0 ] == Vector3 :: new( 1.0 , 0.0 , 0.0 ) ) ;
324
+ assert ! ( t. basis. elements[ 1 ] == Vector3 :: new( 0.0 , 1.0 , 0.0 ) ) ;
325
+ assert ! ( t. basis. elements[ 2 ] == Vector3 :: new( 0.0 , 0.0 , 1.0 ) ) ;
326
+ assert ! ( t. origin == translation) ;
327
+ }
328
+
329
+ #[ test]
330
+ fn scale_is_sane ( ) {
331
+ let scale = Vector3 :: new ( 1.0 , 2.0 , 3.0 ) ;
332
+ let t = Transform :: default ( ) . scaled ( scale) ;
333
+ assert ! ( t. basis. elements[ 0 ] == Vector3 :: new( 1.0 , 0.0 , 0.0 ) ) ;
334
+ assert ! ( t. basis. elements[ 1 ] == Vector3 :: new( 0.0 , 2.0 , 0.0 ) ) ;
335
+ assert ! ( t. basis. elements[ 2 ] == Vector3 :: new( 0.0 , 0.0 , 3.0 ) ) ;
336
+ assert ! ( t. origin == Vector3 :: new( 0.0 , 0.0 , 0.0 ) ) ;
337
+ }
338
+
339
+ #[ test]
340
+ fn affine_inverse_is_sane ( ) {
341
+ // Godot reports:
342
+ // From 0.019358, -0.041264, 0.24581, -0.144074, 0.470205, 0.090279, -1.908901, -0.594598, 0.050514 - 0.112395, -0.519672, -0.347224
343
+ // To 0.309725, -0.576295, -0.477225, -0.66022, 1.880819, -0.148649, 3.932961, 0.361114, 0.012628 - -0.5, 1, -0.25
344
+ let t = test_inputs ( ) . 0 . affine_inverse ( ) ;
345
+ let expected = Transform :: from_basis_origin (
346
+ Vector3 :: new ( 0.309725 , -0.66022015 , 3.9329607 ) ,
347
+ Vector3 :: new ( -0.57629496 , 1.8808193 , 0.3611141 ) ,
348
+ Vector3 :: new ( -0.47722515 , -0.14864945 , 0.012628445 ) ,
349
+ Vector3 :: new ( -0.5 , 1.0 , -0.25 ) ,
350
+ ) ;
351
+ assert ! ( expected. is_equal_approx( t) )
352
+ }
353
+
354
+ #[ test]
355
+ fn orthonormalization_is_sane ( ) {
356
+ // Godot reports:
357
+ // From 0.019358, -0.041264, 0.24581, -0.144074, 0.470205, 0.090279, -1.908901, -0.594598, 0.050514 - 0.112395, -0.519672, -0.347224
358
+ // To 0.010112, -0.090928, 0.995806, -0.075257, 0.992963, 0.091432, -0.997113, -0.075866, 0.003197 - 0.112395, -0.519672, -0.347224
359
+ let t = test_inputs ( ) . 0 . orthonormalized ( ) ;
360
+ let expected = Transform :: from_basis_origin (
361
+ Vector3 :: new ( 0.010111539 , -0.0752568 , -0.99711293 ) ,
362
+ Vector3 :: new ( -0.090927705 , 0.9929635 , -0.075865656 ) ,
363
+ Vector3 :: new ( 0.99580616 , 0.0914323 , 0.0031974507 ) ,
364
+ Vector3 :: new ( 0.11239518 , -0.519672 , -0.34722406 ) ,
365
+ ) ;
366
+ assert ! ( expected. is_equal_approx( t) )
367
+ }
368
+
369
+ #[ test]
370
+ fn linear_interpolation_is_sane ( ) {
371
+ // Godot reports:
372
+ // t = 0.019358, -0.041264, 0.24581, -0.144074, 0.470205, 0.090279, -1.908901, -0.594598, 0.050514 - 0.112395, -0.519672, -0.347224
373
+ // t2 = 0.477182, 0.118214, 0.09123, -0.165859, 0.521769, 0.191437, -0.086105, -0.367178, 0.926157 - 0.593383, -1.05303, 1.762894
374
+ // TODO: Get new godot result. https://github.com/godotengine/godot/commit/61759da5b35e44003ab3ffe3d4024dd611d17eff changed how Transform3D.linear_interpolate works
375
+ // For now assuming this is sane - examined the new implementation manually.
376
+ let ( t, t2) = test_inputs ( ) ;
377
+ let result = t. interpolate_with ( t2, 0.5 ) ;
378
+ let expected = Transform :: from_basis_origin (
379
+ Vector3 :: new ( 0.24826992 , -0.15496635 , -0.997503 ) ,
380
+ Vector3 :: new ( 0.038474888 , 0.49598676 , -0.4808879 ) ,
381
+ Vector3 :: new ( 0.16852 , 0.14085774 , 0.48833522 ) ,
382
+ Vector3 :: new ( 0.352889 , -0.786351 , 0.707835 ) ,
383
+ ) ;
384
+ assert ! ( expected. is_equal_approx( result) )
385
+ }
386
+
387
+ #[ test]
388
+ fn sphere_linear_interpolation_is_sane ( ) {
389
+ // Godot reports:
390
+ // t = 0.019358, -0.041264, 0.24581, -0.144074, 0.470205, 0.090279, -1.908901, -0.594598, 0.050514 - 0.112395, -0.519672, -0.347224
391
+ // t2 = 0.477182, 0.118214, 0.09123, -0.165859, 0.521769, 0.191437, -0.086105, -0.367178, 0.926157 - 0.593383, -1.05303, 1.762894
392
+ // result = 0.727909, -0.029075, 0.486138, -0.338385, 0.6514, 0.156468, -0.910002, -0.265481, 0.330678 - 0.352889, -0.786351, 0.707835
393
+ let ( t, t2) = test_inputs ( ) ;
394
+ let result = t. sphere_interpolate_with ( t2, 0.5 ) ;
395
+ let expected = Transform :: from_basis_origin (
396
+ Vector3 :: new ( 0.7279087 , -0.19632529 , -0.45626357 ) ,
397
+ Vector3 :: new ( -0.05011323 , 0.65140045 , -0.22942543 ) ,
398
+ Vector3 :: new ( 0.9695858 , 0.18105738 , 0.33067825 ) ,
399
+ Vector3 :: new ( 0.3528893 , -0.78635097 , 0.7078349 ) ,
400
+ ) ;
401
+ assert ! ( expected. is_equal_approx( result) )
402
+ }
403
+ }
0 commit comments