bevy_transform/components/
transform.rs

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