bevy_transform/components/
transform.rs

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