bevy_transform/components/
global_transform.rs

1use core::ops::Mul;
2
3use super::Transform;
4use bevy_math::{Affine3A, Dir3, Isometry3d, Mat4, Quat, Vec3, Vec3A};
5#[cfg(all(feature = "bevy-support", feature = "serialize"))]
6use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
7use derive_more::derive::From;
8#[cfg(feature = "bevy-support")]
9use {
10    bevy_ecs::{component::Component, reflect::ReflectComponent},
11    bevy_reflect::{std_traits::ReflectDefault, Reflect},
12};
13
14/// [`GlobalTransform`] is an affine transformation from entity-local coordinates to worldspace coordinates.
15///
16/// You cannot directly mutate [`GlobalTransform`]; instead, you change an entity's transform by manipulating
17/// its [`Transform`], which indirectly causes Bevy to update its [`GlobalTransform`].
18///
19/// * To get the global transform of an entity, you should get its [`GlobalTransform`].
20/// * For transform hierarchies to work correctly, you must have both a [`Transform`] and a [`GlobalTransform`].
21///   * ~You may use the [`TransformBundle`](crate::bundles::TransformBundle) to guarantee this.~
22///     [`TransformBundle`](crate::bundles::TransformBundle) is now deprecated.
23///     [`GlobalTransform`] is automatically inserted whenever [`Transform`] is inserted.
24///
25/// ## [`Transform`] and [`GlobalTransform`]
26///
27/// [`Transform`] transforms an entity relative to its parent's reference frame, or relative to world space coordinates,
28/// if it doesn't have a [`Parent`](bevy_hierarchy::Parent).
29///
30/// [`GlobalTransform`] is managed by Bevy; it is computed by successively applying the [`Transform`] of each ancestor
31/// entity which has a Transform. This is done automatically by Bevy-internal systems in the system set
32/// [`TransformPropagate`](crate::TransformSystem::TransformPropagate).
33///
34/// This system runs during [`PostUpdate`](bevy_app::PostUpdate). If you
35/// update the [`Transform`] of an entity in this schedule or after, you will notice a 1 frame lag
36/// before the [`GlobalTransform`] is updated.
37///
38/// # Examples
39///
40/// - [`transform`][transform_example]
41///
42/// [transform_example]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/transform.rs
43#[derive(Debug, PartialEq, Clone, Copy, From)]
44#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
45#[cfg_attr(
46    feature = "bevy-support",
47    derive(Component, Reflect),
48    reflect(Component, Default, PartialEq, Debug)
49)]
50#[cfg_attr(
51    all(feature = "bevy-support", feature = "serialize"),
52    reflect(Serialize, Deserialize)
53)]
54pub struct GlobalTransform(Affine3A);
55
56macro_rules! impl_local_axis {
57    ($pos_name: ident, $neg_name: ident, $axis: ident) => {
58        #[doc=std::concat!("Return the local ", std::stringify!($pos_name), " vector (", std::stringify!($axis) ,").")]
59        #[inline]
60        pub fn $pos_name(&self) -> Dir3 {
61            Dir3::new_unchecked((self.0.matrix3 * Vec3::$axis).normalize())
62        }
63
64        #[doc=std::concat!("Return the local ", std::stringify!($neg_name), " vector (-", std::stringify!($axis) ,").")]
65        #[inline]
66        pub fn $neg_name(&self) -> Dir3 {
67            -self.$pos_name()
68        }
69    };
70}
71
72impl GlobalTransform {
73    /// An identity [`GlobalTransform`] that maps all points in space to themselves.
74    pub const IDENTITY: Self = Self(Affine3A::IDENTITY);
75
76    #[doc(hidden)]
77    #[inline]
78    pub fn from_xyz(x: f32, y: f32, z: f32) -> Self {
79        Self::from_translation(Vec3::new(x, y, z))
80    }
81
82    #[doc(hidden)]
83    #[inline]
84    pub fn from_translation(translation: Vec3) -> Self {
85        GlobalTransform(Affine3A::from_translation(translation))
86    }
87
88    #[doc(hidden)]
89    #[inline]
90    pub fn from_rotation(rotation: Quat) -> Self {
91        GlobalTransform(Affine3A::from_rotation_translation(rotation, Vec3::ZERO))
92    }
93
94    #[doc(hidden)]
95    #[inline]
96    pub fn from_scale(scale: Vec3) -> Self {
97        GlobalTransform(Affine3A::from_scale(scale))
98    }
99
100    #[doc(hidden)]
101    #[inline]
102    pub fn from_isometry(iso: Isometry3d) -> Self {
103        Self(iso.into())
104    }
105
106    /// Returns the 3d affine transformation matrix as a [`Mat4`].
107    #[inline]
108    pub fn compute_matrix(&self) -> Mat4 {
109        Mat4::from(self.0)
110    }
111
112    /// Returns the 3d affine transformation matrix as an [`Affine3A`].
113    #[inline]
114    pub fn affine(&self) -> Affine3A {
115        self.0
116    }
117
118    /// Returns the transformation as a [`Transform`].
119    ///
120    /// The transform is expected to be non-degenerate and without shearing, or the output
121    /// will be invalid.
122    #[inline]
123    pub fn compute_transform(&self) -> Transform {
124        let (scale, rotation, translation) = self.0.to_scale_rotation_translation();
125        Transform {
126            translation,
127            rotation,
128            scale,
129        }
130    }
131
132    /// Returns the isometric part of the transformation as an [isometry]. Any scaling done by the
133    /// transformation will be ignored.
134    ///
135    /// The transform is expected to be non-degenerate and without shearing, or the output
136    /// will be invalid.
137    ///
138    /// [isometry]: Isometry3d
139    #[inline]
140    pub fn to_isometry(&self) -> Isometry3d {
141        let (_, rotation, translation) = self.0.to_scale_rotation_translation();
142        Isometry3d::new(translation, rotation)
143    }
144
145    /// Returns the [`Transform`] `self` would have if it was a child of an entity
146    /// with the `parent` [`GlobalTransform`].
147    ///
148    /// This is useful if you want to "reparent" an [`Entity`](bevy_ecs::entity::Entity).
149    /// Say you have an entity `e1` that you want to turn into a child of `e2`,
150    /// but you want `e1` to keep the same global transform, even after re-parenting. You would use:
151    ///
152    /// ```
153    /// # use bevy_transform::prelude::{GlobalTransform, Transform};
154    /// # use bevy_ecs::prelude::{Entity, Query, Component, Commands};
155    /// # use bevy_hierarchy::{prelude::Parent, BuildChildren};
156    /// #[derive(Component)]
157    /// struct ToReparent {
158    ///     new_parent: Entity,
159    /// }
160    /// fn reparent_system(
161    ///     mut commands: Commands,
162    ///     mut targets: Query<(&mut Transform, Entity, &GlobalTransform, &ToReparent)>,
163    ///     transforms: Query<&GlobalTransform>,
164    /// ) {
165    ///     for (mut transform, entity, initial, to_reparent) in targets.iter_mut() {
166    ///         if let Ok(parent_transform) = transforms.get(to_reparent.new_parent) {
167    ///             *transform = initial.reparented_to(parent_transform);
168    ///             commands.entity(entity)
169    ///                 .remove::<ToReparent>()
170    ///                 .set_parent(to_reparent.new_parent);
171    ///         }
172    ///     }
173    /// }
174    /// ```
175    ///
176    /// The transform is expected to be non-degenerate and without shearing, or the output
177    /// will be invalid.
178    #[inline]
179    pub fn reparented_to(&self, parent: &GlobalTransform) -> Transform {
180        let relative_affine = parent.affine().inverse() * self.affine();
181        let (scale, rotation, translation) = relative_affine.to_scale_rotation_translation();
182        Transform {
183            translation,
184            rotation,
185            scale,
186        }
187    }
188
189    /// Extracts `scale`, `rotation` and `translation` from `self`.
190    ///
191    /// The transform is expected to be non-degenerate and without shearing, or the output
192    /// will be invalid.
193    #[inline]
194    pub fn to_scale_rotation_translation(&self) -> (Vec3, Quat, Vec3) {
195        self.0.to_scale_rotation_translation()
196    }
197
198    impl_local_axis!(right, left, X);
199    impl_local_axis!(up, down, Y);
200    impl_local_axis!(back, forward, Z);
201
202    /// Get the translation as a [`Vec3`].
203    #[inline]
204    pub fn translation(&self) -> Vec3 {
205        self.0.translation.into()
206    }
207
208    /// Get the translation as a [`Vec3A`].
209    #[inline]
210    pub fn translation_vec3a(&self) -> Vec3A {
211        self.0.translation
212    }
213
214    /// Get the rotation as a [`Quat`].
215    ///
216    /// The transform is expected to be non-degenerate and without shearing, or the output will be invalid.
217    ///
218    /// # Warning
219    ///
220    /// This is calculated using `to_scale_rotation_translation`, meaning that you
221    /// should probably use it directly if you also need translation or scale.
222    #[inline]
223    pub fn rotation(&self) -> Quat {
224        self.to_scale_rotation_translation().1
225    }
226
227    /// Get the scale as a [`Vec3`].
228    ///
229    /// The transform is expected to be non-degenerate and without shearing, or the output will be invalid.
230    ///
231    /// Some of the computations overlap with `to_scale_rotation_translation`, which means you should use
232    /// it instead if you also need rotation.
233    #[inline]
234    pub fn scale(&self) -> Vec3 {
235        //Formula based on glam's implementation https://github.com/bitshifter/glam-rs/blob/2e4443e70c709710dfb25958d866d29b11ed3e2b/src/f32/affine3a.rs#L290
236        let det = self.0.matrix3.determinant();
237        Vec3::new(
238            self.0.matrix3.x_axis.length() * det.signum(),
239            self.0.matrix3.y_axis.length(),
240            self.0.matrix3.z_axis.length(),
241        )
242    }
243
244    /// Get an upper bound of the radius from the given `extents`.
245    #[inline]
246    pub fn radius_vec3a(&self, extents: Vec3A) -> f32 {
247        (self.0.matrix3 * extents).length()
248    }
249
250    /// Transforms the given point from local space to global space, applying shear, scale, rotation and translation.
251    ///
252    /// It can be used like this:
253    ///
254    /// ```
255    /// # use bevy_transform::prelude::{GlobalTransform};
256    /// # use bevy_math::prelude::Vec3;
257    /// let global_transform = GlobalTransform::from_xyz(1., 2., 3.);
258    /// let local_point = Vec3::new(1., 2., 3.);
259    /// let global_point = global_transform.transform_point(local_point);
260    /// assert_eq!(global_point, Vec3::new(2., 4., 6.));
261    /// ```
262    ///
263    /// ```
264    /// # use bevy_transform::prelude::{GlobalTransform};
265    /// # use bevy_math::Vec3;
266    /// let global_point = Vec3::new(2., 4., 6.);
267    /// let global_transform = GlobalTransform::from_xyz(1., 2., 3.);
268    /// let local_point = global_transform.affine().inverse().transform_point3(global_point);
269    /// assert_eq!(local_point, Vec3::new(1., 2., 3.))
270    /// ```
271    ///
272    /// To apply shear, scale, and rotation *without* applying translation, different functions are available:
273    /// ```
274    /// # use bevy_transform::prelude::{GlobalTransform};
275    /// # use bevy_math::prelude::Vec3;
276    /// let global_transform = GlobalTransform::from_xyz(1., 2., 3.);
277    /// let local_direction = Vec3::new(1., 2., 3.);
278    /// let global_direction = global_transform.affine().transform_vector3(local_direction);
279    /// assert_eq!(global_direction, Vec3::new(1., 2., 3.));
280    /// let roundtripped_local_direction = global_transform.affine().inverse().transform_vector3(global_direction);
281    /// assert_eq!(roundtripped_local_direction, local_direction);
282    /// ```
283    #[inline]
284    pub fn transform_point(&self, point: Vec3) -> Vec3 {
285        self.0.transform_point3(point)
286    }
287
288    /// Multiplies `self` with `transform` component by component, returning the
289    /// resulting [`GlobalTransform`]
290    #[inline]
291    pub fn mul_transform(&self, transform: Transform) -> Self {
292        Self(self.0 * transform.compute_affine())
293    }
294}
295
296impl Default for GlobalTransform {
297    fn default() -> Self {
298        Self::IDENTITY
299    }
300}
301
302impl From<Transform> for GlobalTransform {
303    fn from(transform: Transform) -> Self {
304        Self(transform.compute_affine())
305    }
306}
307
308impl From<Mat4> for GlobalTransform {
309    fn from(world_from_local: Mat4) -> Self {
310        Self(Affine3A::from_mat4(world_from_local))
311    }
312}
313
314impl Mul<GlobalTransform> for GlobalTransform {
315    type Output = GlobalTransform;
316
317    #[inline]
318    fn mul(self, global_transform: GlobalTransform) -> Self::Output {
319        GlobalTransform(self.0 * global_transform.0)
320    }
321}
322
323impl Mul<Transform> for GlobalTransform {
324    type Output = GlobalTransform;
325
326    #[inline]
327    fn mul(self, transform: Transform) -> Self::Output {
328        self.mul_transform(transform)
329    }
330}
331
332impl Mul<Vec3> for GlobalTransform {
333    type Output = Vec3;
334
335    #[inline]
336    fn mul(self, value: Vec3) -> Self::Output {
337        self.transform_point(value)
338    }
339}
340
341#[cfg(test)]
342mod test {
343    use super::*;
344
345    use bevy_math::EulerRot::XYZ;
346
347    fn transform_equal(left: GlobalTransform, right: Transform) -> bool {
348        left.0.abs_diff_eq(right.compute_affine(), 0.01)
349    }
350
351    #[test]
352    fn reparented_to_transform_identity() {
353        fn reparent_to_same(t1: GlobalTransform, t2: GlobalTransform) -> Transform {
354            t2.mul_transform(t1.into()).reparented_to(&t2)
355        }
356        let t1 = GlobalTransform::from(Transform {
357            translation: Vec3::new(1034.0, 34.0, -1324.34),
358            rotation: Quat::from_euler(XYZ, 1.0, 0.9, 2.1),
359            scale: Vec3::new(1.0, 1.0, 1.0),
360        });
361        let t2 = GlobalTransform::from(Transform {
362            translation: Vec3::new(0.0, -54.493, 324.34),
363            rotation: Quat::from_euler(XYZ, 1.9, 0.3, 3.0),
364            scale: Vec3::new(1.345, 1.345, 1.345),
365        });
366        let retransformed = reparent_to_same(t1, t2);
367        assert!(
368            transform_equal(t1, retransformed),
369            "t1:{:#?} retransformed:{:#?}",
370            t1.compute_transform(),
371            retransformed,
372        );
373    }
374    #[test]
375    fn reparented_usecase() {
376        let t1 = GlobalTransform::from(Transform {
377            translation: Vec3::new(1034.0, 34.0, -1324.34),
378            rotation: Quat::from_euler(XYZ, 0.8, 1.9, 2.1),
379            scale: Vec3::new(10.9, 10.9, 10.9),
380        });
381        let t2 = GlobalTransform::from(Transform {
382            translation: Vec3::new(28.0, -54.493, 324.34),
383            rotation: Quat::from_euler(XYZ, 0.0, 3.1, 0.1),
384            scale: Vec3::new(0.9, 0.9, 0.9),
385        });
386        // goal: find `X` such as `t2 * X = t1`
387        let reparented = t1.reparented_to(&t2);
388        let t1_prime = t2 * reparented;
389        assert!(
390            transform_equal(t1, t1_prime.into()),
391            "t1:{:#?} t1_prime:{:#?}",
392            t1.compute_transform(),
393            t1_prime.compute_transform(),
394        );
395    }
396
397    #[test]
398    fn scale() {
399        let test_values = [-42.42, 0., 42.42];
400        for x in test_values {
401            for y in test_values {
402                for z in test_values {
403                    let scale = Vec3::new(x, y, z);
404                    let gt = GlobalTransform::from_scale(scale);
405                    assert_eq!(gt.scale(), gt.to_scale_rotation_translation().0);
406                }
407            }
408        }
409    }
410}