bevy_transform/components/
global_transform.rs

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