Thanks to visit codestin.com
Credit goes to docs.rs

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