bevy_transform/components/
transform.rs

1use super::GlobalTransform;
2use bevy_math::{Affine3A, Dir3, Isometry3d, Mat3, Mat4, Quat, Vec3};
3use core::ops::Mul;
4#[cfg(feature = "bevy-support")]
5use {
6    bevy_ecs::{component::Component, reflect::ReflectComponent},
7    bevy_reflect::prelude::*,
8};
9
10/// Describe the position of an entity. If the entity has a parent, the position is relative
11/// to its parent position.
12///
13/// * To place or move an entity, you should set its [`Transform`].
14/// * To get the global transform of an entity, you should get its [`GlobalTransform`].
15/// * To be displayed, an entity must have both a [`Transform`] and a [`GlobalTransform`].
16///   * ~You may use the [`TransformBundle`](crate::bundles::TransformBundle) to guarantee this.~
17///     [`TransformBundle`](crate::bundles::TransformBundle) is now deprecated.
18///     [`GlobalTransform`] is automatically inserted whenever [`Transform`] is inserted.
19///
20/// ## [`Transform`] and [`GlobalTransform`]
21///
22/// [`Transform`] is the position of an entity relative to its parent position, or the reference
23/// frame if it doesn't have a [`Parent`](bevy_hierarchy::Parent).
24///
25/// [`GlobalTransform`] is the position of an entity relative to the reference frame.
26///
27/// [`GlobalTransform`] is updated from [`Transform`] by systems in the system set
28/// [`TransformPropagate`](crate::TransformSystem::TransformPropagate).
29///
30/// This system runs during [`PostUpdate`](bevy_app::PostUpdate). If you
31/// update the [`Transform`] of an entity during this set or after, you will notice a 1 frame lag
32/// before the [`GlobalTransform`] is updated.
33///
34/// # Examples
35///
36/// - [`transform`][transform_example]
37///
38/// [transform_example]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/transform.rs
39#[derive(Debug, PartialEq, Clone, Copy)]
40#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
41#[cfg_attr(
42    feature = "bevy-support",
43    derive(Component, Reflect),
44    require(GlobalTransform),
45    reflect(Component, Default, PartialEq, Debug)
46)]
47#[cfg_attr(
48    all(feature = "bevy-support", feature = "serialize"),
49    reflect(Serialize, Deserialize)
50)]
51pub struct Transform {
52    /// Position of the entity. In 2d, the last value of the `Vec3` is used for z-ordering.
53    ///
54    /// See the [`translations`] example for usage.
55    ///
56    /// [`translations`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/translation.rs
57    pub translation: Vec3,
58    /// Rotation of the entity.
59    ///
60    /// See the [`3d_rotation`] example for usage.
61    ///
62    /// [`3d_rotation`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/3d_rotation.rs
63    pub rotation: Quat,
64    /// Scale of the entity.
65    ///
66    /// See the [`scale`] example for usage.
67    ///
68    /// [`scale`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/scale.rs
69    pub scale: Vec3,
70}
71
72impl Transform {
73    /// An identity [`Transform`] with no translation, rotation, and a scale of 1 on all axes.
74    pub const IDENTITY: Self = Transform {
75        translation: Vec3::ZERO,
76        rotation: Quat::IDENTITY,
77        scale: Vec3::ONE,
78    };
79
80    /// Creates a new [`Transform`] at the position `(x, y, z)`. In 2d, the `z` component
81    /// is used for z-ordering elements: higher `z`-value will be in front of lower
82    /// `z`-value.
83    #[inline]
84    pub const fn from_xyz(x: f32, y: f32, z: f32) -> Self {
85        Self::from_translation(Vec3::new(x, y, z))
86    }
87
88    /// Extracts the translation, rotation, and scale from `matrix`. It must be a 3d affine
89    /// transformation matrix.
90    #[inline]
91    pub fn from_matrix(world_from_local: Mat4) -> Self {
92        let (scale, rotation, translation) = world_from_local.to_scale_rotation_translation();
93
94        Transform {
95            translation,
96            rotation,
97            scale,
98        }
99    }
100
101    /// Creates a new [`Transform`], with `translation`. Rotation will be 0 and scale 1 on
102    /// all axes.
103    #[inline]
104    pub const fn from_translation(translation: Vec3) -> Self {
105        Transform {
106            translation,
107            ..Self::IDENTITY
108        }
109    }
110
111    /// Creates a new [`Transform`], with `rotation`. Translation will be 0 and scale 1 on
112    /// all axes.
113    #[inline]
114    pub const fn from_rotation(rotation: Quat) -> Self {
115        Transform {
116            rotation,
117            ..Self::IDENTITY
118        }
119    }
120
121    /// Creates a new [`Transform`], with `scale`. Translation will be 0 and rotation 0 on
122    /// all axes.
123    #[inline]
124    pub const fn from_scale(scale: Vec3) -> Self {
125        Transform {
126            scale,
127            ..Self::IDENTITY
128        }
129    }
130
131    /// Creates a new [`Transform`] that is equivalent to the given [isometry].
132    ///
133    /// [isometry]: Isometry3d
134    #[inline]
135    pub fn from_isometry(iso: Isometry3d) -> Self {
136        Transform {
137            translation: iso.translation.into(),
138            rotation: iso.rotation,
139            ..Self::IDENTITY
140        }
141    }
142
143    /// Returns this [`Transform`] with a new rotation so that [`Transform::forward`]
144    /// points towards the `target` position and [`Transform::up`] points towards `up`.
145    ///
146    /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
147    /// * if `target` is the same as the transform translation, `Vec3::Z` is used instead
148    /// * if `up` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::Y` is used instead
149    /// * if the resulting forward direction is parallel with `up`, an orthogonal vector is used as the "right" direction
150    #[inline]
151    #[must_use]
152    pub fn looking_at(mut self, target: Vec3, up: impl TryInto<Dir3>) -> Self {
153        self.look_at(target, up);
154        self
155    }
156
157    /// Returns this [`Transform`] with a new rotation so that [`Transform::forward`]
158    /// points in the given `direction` and [`Transform::up`] points towards `up`.
159    ///
160    /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
161    /// * if `direction` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::Z` is used instead
162    /// * if `up` fails converting to `Dir3`, `Dir3::Y` is used instead
163    /// * if `direction` is parallel with `up`, an orthogonal vector is used as the "right" direction
164    #[inline]
165    #[must_use]
166    pub fn looking_to(mut self, direction: impl TryInto<Dir3>, up: impl TryInto<Dir3>) -> Self {
167        self.look_to(direction, up);
168        self
169    }
170
171    /// Rotates this [`Transform`] so that the `main_axis` vector, reinterpreted in local coordinates, points
172    /// in the given `main_direction`, while `secondary_axis` points towards `secondary_direction`.
173    /// For example, if a spaceship model has its nose pointing in the X-direction in its own local coordinates
174    /// and its dorsal fin pointing in the Y-direction, then `align(Dir3::X, v, Dir3::Y, w)` will make the spaceship's
175    /// nose point in the direction of `v`, while the dorsal fin does its best to point in the direction `w`.
176    ///
177    ///
178    /// In some cases a rotation cannot be constructed. Another axis will be picked in those cases:
179    /// * if `main_axis` or `main_direction` fail converting to `Dir3` (e.g are zero), `Dir3::X` takes their place
180    /// * if `secondary_axis` or `secondary_direction` fail converting, `Dir3::Y` takes their place
181    /// * if `main_axis` is parallel with `secondary_axis` or `main_direction` is parallel with `secondary_direction`,
182    ///     a rotation is constructed which takes `main_axis` to `main_direction` along a great circle, ignoring the secondary
183    ///     counterparts
184    ///
185    /// See [`Transform::align`] for additional details.
186    #[inline]
187    #[must_use]
188    pub fn aligned_by(
189        mut self,
190        main_axis: impl TryInto<Dir3>,
191        main_direction: impl TryInto<Dir3>,
192        secondary_axis: impl TryInto<Dir3>,
193        secondary_direction: impl TryInto<Dir3>,
194    ) -> Self {
195        self.align(
196            main_axis,
197            main_direction,
198            secondary_axis,
199            secondary_direction,
200        );
201        self
202    }
203
204    /// Returns this [`Transform`] with a new translation.
205    #[inline]
206    #[must_use]
207    pub const fn with_translation(mut self, translation: Vec3) -> Self {
208        self.translation = translation;
209        self
210    }
211
212    /// Returns this [`Transform`] with a new rotation.
213    #[inline]
214    #[must_use]
215    pub const fn with_rotation(mut self, rotation: Quat) -> Self {
216        self.rotation = rotation;
217        self
218    }
219
220    /// Returns this [`Transform`] with a new scale.
221    #[inline]
222    #[must_use]
223    pub const fn with_scale(mut self, scale: Vec3) -> Self {
224        self.scale = scale;
225        self
226    }
227
228    /// Returns the 3d affine transformation matrix from this transforms translation,
229    /// rotation, and scale.
230    #[inline]
231    pub fn compute_matrix(&self) -> Mat4 {
232        Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
233    }
234
235    /// Returns the 3d affine transformation matrix from this transforms translation,
236    /// rotation, and scale.
237    #[inline]
238    pub fn compute_affine(&self) -> Affine3A {
239        Affine3A::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
240    }
241
242    /// Get the unit vector in the local `X` direction.
243    #[inline]
244    pub fn local_x(&self) -> Dir3 {
245        // Quat * unit vector is length 1
246        Dir3::new_unchecked(self.rotation * Vec3::X)
247    }
248
249    /// Equivalent to [`-local_x()`][Transform::local_x()]
250    #[inline]
251    pub fn left(&self) -> Dir3 {
252        -self.local_x()
253    }
254
255    /// Equivalent to [`local_x()`][Transform::local_x()]
256    #[inline]
257    pub fn right(&self) -> Dir3 {
258        self.local_x()
259    }
260
261    /// Get the unit vector in the local `Y` direction.
262    #[inline]
263    pub fn local_y(&self) -> Dir3 {
264        // Quat * unit vector is length 1
265        Dir3::new_unchecked(self.rotation * Vec3::Y)
266    }
267
268    /// Equivalent to [`local_y()`][Transform::local_y]
269    #[inline]
270    pub fn up(&self) -> Dir3 {
271        self.local_y()
272    }
273
274    /// Equivalent to [`-local_y()`][Transform::local_y]
275    #[inline]
276    pub fn down(&self) -> Dir3 {
277        -self.local_y()
278    }
279
280    /// Get the unit vector in the local `Z` direction.
281    #[inline]
282    pub fn local_z(&self) -> Dir3 {
283        // Quat * unit vector is length 1
284        Dir3::new_unchecked(self.rotation * Vec3::Z)
285    }
286
287    /// Equivalent to [`-local_z()`][Transform::local_z]
288    #[inline]
289    pub fn forward(&self) -> Dir3 {
290        -self.local_z()
291    }
292
293    /// Equivalent to [`local_z()`][Transform::local_z]
294    #[inline]
295    pub fn back(&self) -> Dir3 {
296        self.local_z()
297    }
298
299    /// Rotates this [`Transform`] by the given rotation.
300    ///
301    /// If this [`Transform`] has a parent, the `rotation` is relative to the rotation of the parent.
302    ///
303    /// # Examples
304    ///
305    /// - [`3d_rotation`]
306    ///
307    /// [`3d_rotation`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/3d_rotation.rs
308    #[inline]
309    pub fn rotate(&mut self, rotation: Quat) {
310        self.rotation = rotation * self.rotation;
311    }
312
313    /// Rotates this [`Transform`] around the given `axis` by `angle` (in radians).
314    ///
315    /// If this [`Transform`] has a parent, the `axis` is relative to the rotation of the parent.
316    #[inline]
317    pub fn rotate_axis(&mut self, axis: Dir3, angle: f32) {
318        self.rotate(Quat::from_axis_angle(axis.into(), angle));
319    }
320
321    /// Rotates this [`Transform`] around the `X` axis by `angle` (in radians).
322    ///
323    /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
324    #[inline]
325    pub fn rotate_x(&mut self, angle: f32) {
326        self.rotate(Quat::from_rotation_x(angle));
327    }
328
329    /// Rotates this [`Transform`] around the `Y` axis by `angle` (in radians).
330    ///
331    /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
332    #[inline]
333    pub fn rotate_y(&mut self, angle: f32) {
334        self.rotate(Quat::from_rotation_y(angle));
335    }
336
337    /// Rotates this [`Transform`] around the `Z` axis by `angle` (in radians).
338    ///
339    /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
340    #[inline]
341    pub fn rotate_z(&mut self, angle: f32) {
342        self.rotate(Quat::from_rotation_z(angle));
343    }
344
345    /// Rotates this [`Transform`] by the given `rotation`.
346    ///
347    /// The `rotation` is relative to this [`Transform`]'s current rotation.
348    #[inline]
349    pub fn rotate_local(&mut self, rotation: Quat) {
350        self.rotation *= rotation;
351    }
352
353    /// Rotates this [`Transform`] around its local `axis` by `angle` (in radians).
354    #[inline]
355    pub fn rotate_local_axis(&mut self, axis: Dir3, angle: f32) {
356        self.rotate_local(Quat::from_axis_angle(axis.into(), angle));
357    }
358
359    /// Rotates this [`Transform`] around its local `X` axis by `angle` (in radians).
360    #[inline]
361    pub fn rotate_local_x(&mut self, angle: f32) {
362        self.rotate_local(Quat::from_rotation_x(angle));
363    }
364
365    /// Rotates this [`Transform`] around its local `Y` axis by `angle` (in radians).
366    #[inline]
367    pub fn rotate_local_y(&mut self, angle: f32) {
368        self.rotate_local(Quat::from_rotation_y(angle));
369    }
370
371    /// Rotates this [`Transform`] around its local `Z` axis by `angle` (in radians).
372    #[inline]
373    pub fn rotate_local_z(&mut self, angle: f32) {
374        self.rotate_local(Quat::from_rotation_z(angle));
375    }
376
377    /// Translates this [`Transform`] around a `point` in space.
378    ///
379    /// If this [`Transform`] has a parent, the `point` is relative to the [`Transform`] of the parent.
380    #[inline]
381    pub fn translate_around(&mut self, point: Vec3, rotation: Quat) {
382        self.translation = point + rotation * (self.translation - point);
383    }
384
385    /// Rotates this [`Transform`] around a `point` in space.
386    ///
387    /// If this [`Transform`] has a parent, the `point` is relative to the [`Transform`] of the parent.
388    #[inline]
389    pub fn rotate_around(&mut self, point: Vec3, rotation: Quat) {
390        self.translate_around(point, rotation);
391        self.rotate(rotation);
392    }
393
394    /// Rotates this [`Transform`] so that [`Transform::forward`] points towards the `target` position,
395    /// and [`Transform::up`] points towards `up`.
396    ///
397    /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
398    /// * if `target` is the same as the transform translation, `Vec3::Z` is used instead
399    /// * if `up` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::Y` is used instead
400    /// * if the resulting forward direction is parallel with `up`, an orthogonal vector is used as the "right" direction
401    #[inline]
402    pub fn look_at(&mut self, target: Vec3, up: impl TryInto<Dir3>) {
403        self.look_to(target - self.translation, up);
404    }
405
406    /// Rotates this [`Transform`] so that [`Transform::forward`] points in the given `direction`
407    /// and [`Transform::up`] points towards `up`.
408    ///
409    /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
410    /// * if `direction` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::NEG_Z` is used instead
411    /// * if `up` fails converting to `Dir3`, `Dir3::Y` is used instead
412    /// * if `direction` is parallel with `up`, an orthogonal vector is used as the "right" direction
413    #[inline]
414    pub fn look_to(&mut self, direction: impl TryInto<Dir3>, up: impl TryInto<Dir3>) {
415        let back = -direction.try_into().unwrap_or(Dir3::NEG_Z);
416        let up = up.try_into().unwrap_or(Dir3::Y);
417        let right = up
418            .cross(back.into())
419            .try_normalize()
420            .unwrap_or_else(|| up.any_orthonormal_vector());
421        let up = back.cross(right);
422        self.rotation = Quat::from_mat3(&Mat3::from_cols(right, up, back.into()));
423    }
424
425    /// Rotates this [`Transform`] so that the `main_axis` vector, reinterpreted in local coordinates, points
426    /// in the given `main_direction`, while `secondary_axis` points towards `secondary_direction`.
427    ///
428    /// For example, if a spaceship model has its nose pointing in the X-direction in its own local coordinates
429    /// and its dorsal fin pointing in the Y-direction, then `align(Dir3::X, v, Dir3::Y, w)` will make the spaceship's
430    /// nose point in the direction of `v`, while the dorsal fin does its best to point in the direction `w`.
431    ///
432    /// More precisely, the [`Transform::rotation`] produced will be such that:
433    /// * applying it to `main_axis` results in `main_direction`
434    /// * applying it to `secondary_axis` produces a vector that lies in the half-plane generated by `main_direction` and
435    ///     `secondary_direction` (with positive contribution by `secondary_direction`)
436    ///
437    /// [`Transform::look_to`] is recovered, for instance, when `main_axis` is `Dir3::NEG_Z` (the [`Transform::forward`]
438    /// direction in the default orientation) and `secondary_axis` is `Dir3::Y` (the [`Transform::up`] direction in the default
439    /// orientation). (Failure cases may differ somewhat.)
440    ///
441    /// In some cases a rotation cannot be constructed. Another axis will be picked in those cases:
442    /// * if `main_axis` or `main_direction` fail converting to `Dir3` (e.g are zero), `Dir3::X` takes their place
443    /// * if `secondary_axis` or `secondary_direction` fail converting, `Dir3::Y` takes their place
444    /// * if `main_axis` is parallel with `secondary_axis` or `main_direction` is parallel with `secondary_direction`,
445    ///     a rotation is constructed which takes `main_axis` to `main_direction` along a great circle, ignoring the secondary
446    ///     counterparts
447    ///
448    /// Example
449    /// ```
450    /// # use bevy_math::{Dir3, Vec3, Quat};
451    /// # use bevy_transform::components::Transform;
452    /// # let mut t1 = Transform::IDENTITY;
453    /// # let mut t2 = Transform::IDENTITY;
454    /// t1.align(Dir3::X, Dir3::Y, Vec3::new(1., 1., 0.), Dir3::Z);
455    /// let main_axis_image = t1.rotation * Dir3::X;
456    /// let secondary_axis_image = t1.rotation * Vec3::new(1., 1., 0.);
457    /// assert!(main_axis_image.abs_diff_eq(Vec3::Y, 1e-5));
458    /// assert!(secondary_axis_image.abs_diff_eq(Vec3::new(0., 1., 1.), 1e-5));
459    ///
460    /// t1.align(Vec3::ZERO, Dir3::Z, Vec3::ZERO, Dir3::X);
461    /// t2.align(Dir3::X, Dir3::Z, Dir3::Y, Dir3::X);
462    /// assert_eq!(t1.rotation, t2.rotation);
463    ///
464    /// t1.align(Dir3::X, Dir3::Z, Dir3::X, Dir3::Y);
465    /// assert_eq!(t1.rotation, Quat::from_rotation_arc(Vec3::X, Vec3::Z));
466    /// ```
467    #[inline]
468    pub fn align(
469        &mut self,
470        main_axis: impl TryInto<Dir3>,
471        main_direction: impl TryInto<Dir3>,
472        secondary_axis: impl TryInto<Dir3>,
473        secondary_direction: impl TryInto<Dir3>,
474    ) {
475        let main_axis = main_axis.try_into().unwrap_or(Dir3::X);
476        let main_direction = main_direction.try_into().unwrap_or(Dir3::X);
477        let secondary_axis = secondary_axis.try_into().unwrap_or(Dir3::Y);
478        let secondary_direction = secondary_direction.try_into().unwrap_or(Dir3::Y);
479
480        // The solution quaternion will be constructed in two steps.
481        // First, we start with a rotation that takes `main_axis` to `main_direction`.
482        let first_rotation = Quat::from_rotation_arc(main_axis.into(), main_direction.into());
483
484        // Let's follow by rotating about the `main_direction` axis so that the image of `secondary_axis`
485        // is taken to something that lies in the plane of `main_direction` and `secondary_direction`. Since
486        // `main_direction` is fixed by this rotation, the first criterion is still satisfied.
487        let secondary_image = first_rotation * secondary_axis;
488        let secondary_image_ortho = secondary_image
489            .reject_from_normalized(main_direction.into())
490            .try_normalize();
491        let secondary_direction_ortho = secondary_direction
492            .reject_from_normalized(main_direction.into())
493            .try_normalize();
494
495        // If one of the two weak vectors was parallel to `main_direction`, then we just do the first part
496        self.rotation = match (secondary_image_ortho, secondary_direction_ortho) {
497            (Some(secondary_img_ortho), Some(secondary_dir_ortho)) => {
498                let second_rotation =
499                    Quat::from_rotation_arc(secondary_img_ortho, secondary_dir_ortho);
500                second_rotation * first_rotation
501            }
502            _ => first_rotation,
503        };
504    }
505
506    /// Multiplies `self` with `transform` component by component, returning the
507    /// resulting [`Transform`]
508    #[inline]
509    #[must_use]
510    pub fn mul_transform(&self, transform: Transform) -> Self {
511        let translation = self.transform_point(transform.translation);
512        let rotation = self.rotation * transform.rotation;
513        let scale = self.scale * transform.scale;
514        Transform {
515            translation,
516            rotation,
517            scale,
518        }
519    }
520
521    /// Transforms the given `point`, applying scale, rotation and translation.
522    ///
523    /// If this [`Transform`] has an ancestor entity with a [`Transform`] component,
524    /// [`Transform::transform_point`] will transform a point in local space into its
525    /// parent transform's space.
526    ///
527    /// If this [`Transform`] does not have a parent, [`Transform::transform_point`] will
528    /// transform a point in local space into worldspace coordinates.
529    ///
530    /// If you always want to transform a point in local space to worldspace, or if you need
531    /// the inverse transformations, see [`GlobalTransform::transform_point()`].
532    #[inline]
533    pub fn transform_point(&self, mut point: Vec3) -> Vec3 {
534        point = self.scale * point;
535        point = self.rotation * point;
536        point += self.translation;
537        point
538    }
539
540    /// Returns `true` if, and only if, translation, rotation and scale all are
541    /// finite. If any of them contains a `NaN`, positive or negative infinity,
542    /// this will return `false`.
543    #[inline]
544    #[must_use]
545    pub fn is_finite(&self) -> bool {
546        self.translation.is_finite() && self.rotation.is_finite() && self.scale.is_finite()
547    }
548
549    /// Get the [isometry] defined by this transform's rotation and translation, ignoring scale.
550    ///
551    /// [isometry]: Isometry3d
552    #[inline]
553    pub fn to_isometry(&self) -> Isometry3d {
554        Isometry3d::new(self.translation, self.rotation)
555    }
556}
557
558impl Default for Transform {
559    fn default() -> Self {
560        Self::IDENTITY
561    }
562}
563
564/// The transform is expected to be non-degenerate and without shearing, or the output
565/// will be invalid.
566impl From<GlobalTransform> for Transform {
567    fn from(transform: GlobalTransform) -> Self {
568        transform.compute_transform()
569    }
570}
571
572impl Mul<Transform> for Transform {
573    type Output = Transform;
574
575    fn mul(self, transform: Transform) -> Self::Output {
576        self.mul_transform(transform)
577    }
578}
579
580impl Mul<GlobalTransform> for Transform {
581    type Output = GlobalTransform;
582
583    #[inline]
584    fn mul(self, global_transform: GlobalTransform) -> Self::Output {
585        GlobalTransform::from(self) * global_transform
586    }
587}
588
589impl Mul<Vec3> for Transform {
590    type Output = Vec3;
591
592    fn mul(self, value: Vec3) -> Self::Output {
593        self.transform_point(value)
594    }
595}