Skip to main content

glam/f32/
affine3.rs

1// Generated from affine.rs.tera template. Edit the template, not the generated file.
2
3use crate::{Affine3A, Mat3, Mat4, Quat, Vec3};
4use core::ops::{Deref, DerefMut, Mul, MulAssign};
5
6#[cfg(all(feature = "zerocopy-08", not(feature = "core-simd")))]
7use zerocopy_derive_08::*;
8
9/// A 3D affine transform, which can represent translation, rotation, scaling and shear.
10#[derive(Copy, Clone)]
11#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
12#[cfg_attr(
13    all(feature = "zerocopy-08", not(feature = "core-simd")),
14    derive(FromBytes, Immutable, IntoBytes, KnownLayout)
15)]
16#[repr(C)]
17pub struct Affine3 {
18    pub matrix3: Mat3,
19    pub translation: Vec3,
20}
21
22impl Affine3 {
23    /// The degenerate zero transform.
24    ///
25    /// This transforms any finite vector and point to zero.
26    /// The zero transform is non-invertible.
27    pub const ZERO: Self = Self {
28        matrix3: Mat3::ZERO,
29        translation: Vec3::ZERO,
30    };
31
32    /// The identity transform.
33    ///
34    /// Multiplying a vector with this returns the same vector.
35    pub const IDENTITY: Self = Self {
36        matrix3: Mat3::IDENTITY,
37        translation: Vec3::ZERO,
38    };
39
40    /// All NAN:s.
41    pub const NAN: Self = Self {
42        matrix3: Mat3::NAN,
43        translation: Vec3::NAN,
44    };
45
46    /// Creates an affine transform from three column vectors.
47    #[inline(always)]
48    #[must_use]
49    pub const fn from_cols(x_axis: Vec3, y_axis: Vec3, z_axis: Vec3, w_axis: Vec3) -> Self {
50        Self {
51            matrix3: Mat3::from_cols(x_axis, y_axis, z_axis),
52            translation: w_axis,
53        }
54    }
55
56    /// Creates an affine transform from a `[f32; 12]` array stored in column major order.
57    #[inline]
58    #[must_use]
59    pub fn from_cols_array(m: &[f32; 12]) -> Self {
60        Self {
61            matrix3: Mat3::from_cols_array(&[m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]]),
62            translation: Vec3::from_array([m[9], m[10], m[11]]),
63        }
64    }
65
66    /// Creates a `[f32; 12]` array storing data in column major order.
67    #[inline]
68    #[must_use]
69    pub fn to_cols_array(&self) -> [f32; 12] {
70        let x = &self.matrix3.x_axis;
71        let y = &self.matrix3.y_axis;
72        let z = &self.matrix3.z_axis;
73        let w = &self.translation;
74        [x.x, x.y, x.z, y.x, y.y, y.z, z.x, z.y, z.z, w.x, w.y, w.z]
75    }
76
77    /// Creates an affine transform from a `[[f32; 3]; 4]`
78    /// 3D array stored in column major order.
79    /// If your data is in row major order you will need to `transpose` the returned
80    /// matrix.
81    #[inline]
82    #[must_use]
83    pub fn from_cols_array_2d(m: &[[f32; 3]; 4]) -> Self {
84        Self {
85            matrix3: Mat3::from_cols(m[0].into(), m[1].into(), m[2].into()),
86            translation: m[3].into(),
87        }
88    }
89
90    /// Creates a `[[f32; 3]; 4]` 3D array storing data in
91    /// column major order.
92    /// If you require data in row major order `transpose` the matrix first.
93    #[inline]
94    #[must_use]
95    pub fn to_cols_array_2d(&self) -> [[f32; 3]; 4] {
96        [
97            self.matrix3.x_axis.into(),
98            self.matrix3.y_axis.into(),
99            self.matrix3.z_axis.into(),
100            self.translation.into(),
101        ]
102    }
103
104    /// Creates an affine transform from the first 12 values in `slice`.
105    ///
106    /// # Panics
107    ///
108    /// Panics if `slice` is less than 12 elements long.
109    #[inline]
110    #[must_use]
111    pub fn from_cols_slice(slice: &[f32]) -> Self {
112        Self {
113            matrix3: Mat3::from_cols_slice(&slice[0..9]),
114            translation: Vec3::from_slice(&slice[9..12]),
115        }
116    }
117
118    /// Writes the columns of `self` to the first 12 elements in `slice`.
119    ///
120    /// # Panics
121    ///
122    /// Panics if `slice` is less than 12 elements long.
123    #[inline]
124    pub fn write_cols_to_slice(&self, slice: &mut [f32]) {
125        self.matrix3.write_cols_to_slice(&mut slice[0..9]);
126        self.translation.write_to_slice(&mut slice[9..12]);
127    }
128
129    /// Creates an affine transform that changes scale.
130    /// Note that if any scale is zero the transform will be non-invertible.
131    #[inline]
132    #[must_use]
133    pub fn from_scale(scale: Vec3) -> Self {
134        Self {
135            matrix3: Mat3::from_diagonal(scale),
136            translation: Vec3::ZERO,
137        }
138    }
139    /// Creates an affine transform from the given `rotation` quaternion.
140    ///
141    /// # Panics
142    ///
143    /// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
144    #[inline]
145    #[must_use]
146    #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
147    pub fn from_quat(rotation: Quat) -> Self {
148        Self {
149            matrix3: Mat3::from_quat(rotation),
150            translation: Vec3::ZERO,
151        }
152    }
153
154    /// Creates an affine transform containing a 3D rotation around a normalized
155    /// rotation `axis` of `angle` (in radians).
156    ///
157    /// # Panics
158    ///
159    /// Will panic if `axis` is not normalized when `glam_assert` is enabled.
160    #[inline]
161    #[must_use]
162    #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
163    pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
164        Self {
165            matrix3: Mat3::from_axis_angle(axis, angle),
166            translation: Vec3::ZERO,
167        }
168    }
169
170    /// Creates an affine transform containing a 3D rotation around the x axis of
171    /// `angle` (in radians).
172    #[inline]
173    #[must_use]
174    pub fn from_rotation_x(angle: f32) -> Self {
175        Self {
176            matrix3: Mat3::from_rotation_x(angle),
177            translation: Vec3::ZERO,
178        }
179    }
180
181    /// Creates an affine transform containing a 3D rotation around the y axis of
182    /// `angle` (in radians).
183    #[inline]
184    #[must_use]
185    pub fn from_rotation_y(angle: f32) -> Self {
186        Self {
187            matrix3: Mat3::from_rotation_y(angle),
188            translation: Vec3::ZERO,
189        }
190    }
191
192    /// Creates an affine transform containing a 3D rotation around the z axis of
193    /// `angle` (in radians).
194    #[inline]
195    #[must_use]
196    pub fn from_rotation_z(angle: f32) -> Self {
197        Self {
198            matrix3: Mat3::from_rotation_z(angle),
199            translation: Vec3::ZERO,
200        }
201    }
202
203    /// Creates an affine transformation from the given 3D `translation`.
204    #[inline]
205    #[must_use]
206    pub fn from_translation(translation: Vec3) -> Self {
207        #[allow(clippy::useless_conversion)]
208        Self {
209            matrix3: Mat3::IDENTITY,
210            translation: translation.into(),
211        }
212    }
213
214    /// Creates an affine transform from a 3x3 matrix (expressing scale, shear and
215    /// rotation)
216    #[inline]
217    #[must_use]
218    pub fn from_mat3(mat3: Mat3) -> Self {
219        #[allow(clippy::useless_conversion)]
220        Self {
221            matrix3: mat3.into(),
222            translation: Vec3::ZERO,
223        }
224    }
225
226    /// Creates an affine transform from a 3x3 matrix (expressing scale, shear and rotation)
227    /// and a translation vector.
228    ///
229    /// Equivalent to `Affine3::from_translation(translation) * Affine3::from_mat3(mat3)`
230    #[inline]
231    #[must_use]
232    pub fn from_mat3_translation(mat3: Mat3, translation: Vec3) -> Self {
233        #[allow(clippy::useless_conversion)]
234        Self {
235            matrix3: mat3.into(),
236            translation: translation.into(),
237        }
238    }
239
240    /// Creates an affine transform from the given 3D `scale`, `rotation` and
241    /// `translation`.
242    ///
243    /// Equivalent to `Affine3::from_translation(translation) *
244    /// Affine3::from_quat(rotation) * Affine3::from_scale(scale)`
245    ///
246    /// # Panics
247    ///
248    /// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
249    #[inline]
250    #[must_use]
251    #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
252    pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
253        let rotation = Mat3::from_quat(rotation);
254        #[allow(clippy::useless_conversion)]
255        Self {
256            matrix3: Mat3::from_cols(
257                rotation.x_axis * scale.x,
258                rotation.y_axis * scale.y,
259                rotation.z_axis * scale.z,
260            ),
261            translation: translation.into(),
262        }
263    }
264
265    /// Creates an affine transform from the given 3D `rotation` and `translation`.
266    ///
267    /// Equivalent to `Affine3::from_translation(translation) * Affine3::from_quat(rotation)`
268    ///
269    /// # Panics
270    ///
271    /// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
272    #[inline]
273    #[must_use]
274    #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
275    pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
276        #[allow(clippy::useless_conversion)]
277        Self {
278            matrix3: Mat3::from_quat(rotation),
279            translation: translation.into(),
280        }
281    }
282
283    /// The given `Mat4` must be an affine transform,
284    /// i.e. contain no perspective transform.
285    #[inline]
286    #[must_use]
287    pub fn from_mat4(m: Mat4) -> Self {
288        Self {
289            matrix3: Mat3::from_cols(
290                Vec3::from_vec4(m.x_axis),
291                Vec3::from_vec4(m.y_axis),
292                Vec3::from_vec4(m.z_axis),
293            ),
294            translation: Vec3::from_vec4(m.w_axis),
295        }
296    }
297
298    /// Extracts `scale`, `rotation` and `translation` from `self`.
299    ///
300    /// The transform is expected to be non-degenerate and without shearing, or the output
301    /// will be invalid.
302    ///
303    /// # Panics
304    ///
305    /// Will panic if the determinant `self.matrix3` is zero or if the resulting scale
306    /// vector contains any zero elements when `glam_assert` is enabled.
307    #[inline]
308    #[must_use]
309    #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
310    pub fn to_scale_rotation_translation(&self) -> (Vec3, Quat, Vec3) {
311        use crate::f32::math;
312        let det = self.matrix3.determinant();
313        glam_assert!(det != 0.0);
314
315        let scale = Vec3::new(
316            self.matrix3.x_axis.length() * math::signum(det),
317            self.matrix3.y_axis.length(),
318            self.matrix3.z_axis.length(),
319        );
320
321        glam_assert!(scale.cmpne(Vec3::ZERO).all());
322
323        let inv_scale = scale.recip();
324
325        #[allow(clippy::useless_conversion)]
326        let rotation = Quat::from_mat3(&Mat3::from_cols(
327            (self.matrix3.x_axis * inv_scale.x).into(),
328            (self.matrix3.y_axis * inv_scale.y).into(),
329            (self.matrix3.z_axis * inv_scale.z).into(),
330        ));
331
332        #[allow(clippy::useless_conversion)]
333        (scale, rotation, self.translation.into())
334    }
335
336    /// Creates a left-handed view transform using a camera position, an up direction, and a facing
337    /// direction.
338    ///
339    /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
340    ///
341    /// # Panics
342    ///
343    /// Will panic if `dir` or `up` is zero length or not finite, or if `dir` and `up` are parallel,
344    /// when `glam_assert` is enabled.
345    #[deprecated(
346        since = "0.33.1",
347        note = "use the `glam::camera::lh::view::look_to_affine3` function instead"
348    )]
349    #[inline]
350    #[must_use]
351    #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
352    pub fn look_to_lh(eye: Vec3, dir: Vec3, up: Vec3) -> Self {
353        #[allow(deprecated)]
354        Self::look_to_rh(eye, -dir, up)
355    }
356
357    /// Creates a right-handed view transform using a camera position, an up direction, and a facing
358    /// direction.
359    ///
360    /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
361    ///
362    /// # Panics
363    ///
364    /// Will panic if `dir` or `up` is zero length or not finite, or if `dir` and `up` are parallel,
365    /// when `glam_assert` is enabled.
366    #[deprecated(
367        since = "0.33.1",
368        note = "use the `glam::camera::rh::view::look_to_affine3` function instead"
369    )]
370    #[inline]
371    #[must_use]
372    #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
373    pub fn look_to_rh(eye: Vec3, dir: Vec3, up: Vec3) -> Self {
374        let f = dir.normalize();
375        let s = f.cross(up).normalize();
376        let u = s.cross(f);
377
378        Self {
379            matrix3: Mat3::from_cols(
380                Vec3::new(s.x, u.x, -f.x),
381                Vec3::new(s.y, u.y, -f.y),
382                Vec3::new(s.z, u.z, -f.z),
383            ),
384            translation: Vec3::new(-eye.dot(s), -eye.dot(u), eye.dot(f)),
385        }
386    }
387
388    /// Creates a left-handed view transform using a camera position, an up direction, and a focal
389    /// point.
390    /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
391    ///
392    /// # Panics
393    ///
394    /// Will panic if `up` is not normalized, if `center` is equal to `eye`, or if the view
395    /// direction is parallel to `up`, when `glam_assert` is enabled.
396    #[deprecated(
397        since = "0.33.1",
398        note = "use the `glam::camera::lh::view::look_at_affine3` function instead"
399    )]
400    #[inline]
401    #[must_use]
402    #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
403    pub fn look_at_lh(eye: Vec3, center: Vec3, up: Vec3) -> Self {
404        glam_assert!(up.is_normalized());
405        #[allow(deprecated)]
406        Self::look_to_lh(eye, center - eye, up)
407    }
408
409    /// Creates a right-handed view transform using a camera position, an up direction, and a focal
410    /// point.
411    /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
412    ///
413    /// # Panics
414    ///
415    /// Will panic if `up` is not normalized, if `center` is equal to `eye`, or if the view
416    /// direction is parallel to `up`, when `glam_assert` is enabled.
417    #[deprecated(
418        since = "0.33.1",
419        note = "use the `glam::camera::rh::view::look_at_affine3` function instead"
420    )]
421    #[inline]
422    #[must_use]
423    #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
424    pub fn look_at_rh(eye: Vec3, center: Vec3, up: Vec3) -> Self {
425        glam_assert!(up.is_normalized());
426        #[allow(deprecated)]
427        Self::look_to_rh(eye, center - eye, up)
428    }
429
430    /// Transforms the given 3D points, applying shear, scale, rotation and translation.
431    #[inline]
432    pub fn transform_point3(&self, rhs: Vec3) -> Vec3 {
433        #[allow(clippy::useless_conversion)]
434        ((self.matrix3.x_axis * rhs.x)
435            + (self.matrix3.y_axis * rhs.y)
436            + (self.matrix3.z_axis * rhs.z)
437            + self.translation)
438            .into()
439    }
440
441    /// Transforms the given 3D vector, applying shear, scale and rotation (but NOT
442    /// translation).
443    ///
444    /// To also apply translation, use [`Self::transform_point3()`] instead.
445    #[inline]
446    #[must_use]
447    pub fn transform_vector3(&self, rhs: Vec3) -> Vec3 {
448        #[allow(clippy::useless_conversion)]
449        ((self.matrix3.x_axis * rhs.x)
450            + (self.matrix3.y_axis * rhs.y)
451            + (self.matrix3.z_axis * rhs.z))
452            .into()
453    }
454
455    /// Returns `true` if, and only if, all elements are finite.
456    ///
457    /// If any element is either `NaN`, positive or negative infinity, this will return
458    /// `false`.
459    #[inline]
460    #[must_use]
461    pub fn is_finite(&self) -> bool {
462        self.matrix3.is_finite() && self.translation.is_finite()
463    }
464
465    /// Returns `true` if any elements are `NaN`.
466    #[inline]
467    #[must_use]
468    pub fn is_nan(&self) -> bool {
469        self.matrix3.is_nan() || self.translation.is_nan()
470    }
471
472    /// Returns true if the absolute difference of all elements between `self` and `rhs`
473    /// is less than or equal to `max_abs_diff`.
474    ///
475    /// This can be used to compare if two 3x4 matrices contain similar elements. It works
476    /// best when comparing with a known value. The `max_abs_diff` that should be used used
477    /// depends on the values being compared against.
478    ///
479    /// For more see
480    /// [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
481    #[inline]
482    #[must_use]
483    pub fn abs_diff_eq(&self, rhs: Self, max_abs_diff: f32) -> bool {
484        self.matrix3.abs_diff_eq(rhs.matrix3, max_abs_diff)
485            && self.translation.abs_diff_eq(rhs.translation, max_abs_diff)
486    }
487
488    /// Return the inverse of this transform.
489    ///
490    /// Note that if the transform is not invertible the result will be invalid.
491    ///
492    /// # Panics
493    ///
494    /// Will panic if the resulting inverted matrix is not finite when `glam_assert` is enabled.
495    #[inline]
496    #[must_use]
497    #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
498    pub fn inverse(&self) -> Self {
499        let matrix3 = self.matrix3.inverse();
500        // transform negative translation by the matrix inverse:
501        let translation = -(matrix3 * self.translation);
502
503        Self {
504            matrix3,
505            translation,
506        }
507    }
508
509    /// Casts all elements of `self` to `f64`.
510    #[cfg(feature = "f64")]
511    #[inline]
512    #[must_use]
513    pub fn as_daffine3(&self) -> crate::DAffine3 {
514        crate::DAffine3::from_mat3_translation(self.matrix3.as_dmat3(), self.translation.as_dvec3())
515    }
516}
517
518impl Default for Affine3 {
519    #[inline(always)]
520    fn default() -> Self {
521        Self::IDENTITY
522    }
523}
524
525impl Deref for Affine3 {
526    type Target = crate::deref::Cols4<Vec3>;
527    #[inline(always)]
528    fn deref(&self) -> &Self::Target {
529        unsafe { &*(self as *const Self as *const Self::Target) }
530    }
531}
532
533impl DerefMut for Affine3 {
534    #[inline(always)]
535    fn deref_mut(&mut self) -> &mut Self::Target {
536        unsafe { &mut *(self as *mut Self as *mut Self::Target) }
537    }
538}
539
540impl PartialEq for Affine3 {
541    #[inline]
542    fn eq(&self, rhs: &Self) -> bool {
543        self.matrix3.eq(&rhs.matrix3) && self.translation.eq(&rhs.translation)
544    }
545}
546
547impl core::fmt::Debug for Affine3 {
548    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
549        fmt.debug_struct(stringify!(Affine3))
550            .field("matrix3", &self.matrix3)
551            .field("translation", &self.translation)
552            .finish()
553    }
554}
555
556impl core::fmt::Display for Affine3 {
557    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
558        if let Some(p) = f.precision() {
559            write!(
560                f,
561                "[{:.*}, {:.*}, {:.*}, {:.*}]",
562                p,
563                self.matrix3.x_axis,
564                p,
565                self.matrix3.y_axis,
566                p,
567                self.matrix3.z_axis,
568                p,
569                self.translation
570            )
571        } else {
572            write!(
573                f,
574                "[{}, {}, {}, {}]",
575                self.matrix3.x_axis, self.matrix3.y_axis, self.matrix3.z_axis, self.translation
576            )
577        }
578    }
579}
580
581impl<'a> core::iter::Product<&'a Self> for Affine3 {
582    fn product<I>(iter: I) -> Self
583    where
584        I: Iterator<Item = &'a Self>,
585    {
586        iter.fold(Self::IDENTITY, |a, &b| a * b)
587    }
588}
589
590impl Mul for Affine3 {
591    type Output = Self;
592
593    #[inline]
594    fn mul(self, rhs: Self) -> Self {
595        Self {
596            matrix3: self.matrix3 * rhs.matrix3,
597            translation: self.matrix3 * rhs.translation + self.translation,
598        }
599    }
600}
601
602impl Mul<&Self> for Affine3 {
603    type Output = Self;
604    #[inline]
605    fn mul(self, rhs: &Self) -> Self {
606        self.mul(*rhs)
607    }
608}
609
610impl Mul<&Affine3> for &Affine3 {
611    type Output = Affine3;
612    #[inline]
613    fn mul(self, rhs: &Affine3) -> Affine3 {
614        (*self).mul(*rhs)
615    }
616}
617
618impl Mul<Affine3> for &Affine3 {
619    type Output = Affine3;
620    #[inline]
621    fn mul(self, rhs: Affine3) -> Affine3 {
622        (*self).mul(rhs)
623    }
624}
625
626impl MulAssign for Affine3 {
627    #[inline]
628    fn mul_assign(&mut self, rhs: Self) {
629        *self = self.mul(rhs);
630    }
631}
632
633impl MulAssign<&Self> for Affine3 {
634    #[inline]
635    fn mul_assign(&mut self, rhs: &Self) {
636        self.mul_assign(*rhs);
637    }
638}
639
640impl Mul<Mat4> for Affine3 {
641    type Output = Mat4;
642
643    #[inline]
644    fn mul(self, rhs: Mat4) -> Self::Output {
645        Mat4::from(self) * rhs
646    }
647}
648
649impl Mul<&Mat4> for Affine3 {
650    type Output = Mat4;
651    #[inline]
652    fn mul(self, rhs: &Mat4) -> Mat4 {
653        self.mul(*rhs)
654    }
655}
656
657impl Mul<&Mat4> for &Affine3 {
658    type Output = Mat4;
659    #[inline]
660    fn mul(self, rhs: &Mat4) -> Mat4 {
661        (*self).mul(*rhs)
662    }
663}
664
665impl Mul<Mat4> for &Affine3 {
666    type Output = Mat4;
667    #[inline]
668    fn mul(self, rhs: Mat4) -> Mat4 {
669        (*self).mul(rhs)
670    }
671}
672
673impl Mul<Affine3> for Mat4 {
674    type Output = Self;
675
676    #[inline]
677    fn mul(self, rhs: Affine3) -> Self {
678        self * Self::from(rhs)
679    }
680}
681
682impl Mul<&Affine3> for Mat4 {
683    type Output = Self;
684    #[inline]
685    fn mul(self, rhs: &Affine3) -> Self {
686        self.mul(*rhs)
687    }
688}
689
690impl Mul<&Affine3> for &Mat4 {
691    type Output = Mat4;
692    #[inline]
693    fn mul(self, rhs: &Affine3) -> Mat4 {
694        (*self).mul(*rhs)
695    }
696}
697
698impl Mul<Affine3> for &Mat4 {
699    type Output = Mat4;
700    #[inline]
701    fn mul(self, rhs: Affine3) -> Mat4 {
702        (*self).mul(rhs)
703    }
704}
705
706impl MulAssign<Affine3> for Mat4 {
707    #[inline]
708    fn mul_assign(&mut self, rhs: Affine3) {
709        *self = self.mul(rhs);
710    }
711}
712
713impl MulAssign<&Affine3> for Mat4 {
714    #[inline]
715    fn mul_assign(&mut self, rhs: &Affine3) {
716        self.mul_assign(*rhs);
717    }
718}
719
720impl From<Affine3> for Mat4 {
721    #[inline]
722    fn from(m: Affine3) -> Self {
723        Self::from_cols(
724            m.matrix3.x_axis.extend(0.0),
725            m.matrix3.y_axis.extend(0.0),
726            m.matrix3.z_axis.extend(0.0),
727            m.translation.extend(1.0),
728        )
729    }
730}
731
732impl From<Affine3A> for Affine3 {
733    #[inline]
734    fn from(a: Affine3A) -> Self {
735        Self::from_cols(
736            a.matrix3.x_axis.into(),
737            a.matrix3.y_axis.into(),
738            a.matrix3.z_axis.into(),
739            a.translation.into(),
740        )
741    }
742}