bevy_transform/components/
global_transform.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
use core::ops::Mul;

use super::Transform;
use bevy_math::{Affine3A, Dir3, Isometry3d, Mat4, Quat, Vec3, Vec3A};
#[cfg(all(feature = "bevy-support", feature = "serialize"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
use derive_more::derive::From;
#[cfg(feature = "bevy-support")]
use {
    bevy_ecs::{component::Component, reflect::ReflectComponent},
    bevy_reflect::{std_traits::ReflectDefault, Reflect},
};

/// [`GlobalTransform`] is an affine transformation from entity-local coordinates to worldspace coordinates.
///
/// You cannot directly mutate [`GlobalTransform`]; instead, you change an entity's transform by manipulating
/// its [`Transform`], which indirectly causes Bevy to update its [`GlobalTransform`].
///
/// * To get the global transform of an entity, you should get its [`GlobalTransform`].
/// * For transform hierarchies to work correctly, you must have both a [`Transform`] and a [`GlobalTransform`].
///   * ~You may use the [`TransformBundle`](crate::bundles::TransformBundle) to guarantee this.~
///     [`TransformBundle`](crate::bundles::TransformBundle) is now deprecated.
///     [`GlobalTransform`] is automatically inserted whenever [`Transform`] is inserted.
///
/// ## [`Transform`] and [`GlobalTransform`]
///
/// [`Transform`] transforms an entity relative to its parent's reference frame, or relative to world space coordinates,
/// if it doesn't have a [`Parent`](bevy_hierarchy::Parent).
///
/// [`GlobalTransform`] is managed by Bevy; it is computed by successively applying the [`Transform`] of each ancestor
/// entity which has a Transform. This is done automatically by Bevy-internal systems in the system set
/// [`TransformPropagate`](crate::TransformSystem::TransformPropagate).
///
/// This system runs during [`PostUpdate`](bevy_app::PostUpdate). If you
/// update the [`Transform`] of an entity in this schedule or after, you will notice a 1 frame lag
/// before the [`GlobalTransform`] is updated.
///
/// # Examples
///
/// - [`transform`][transform_example]
///
/// [transform_example]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/transform.rs
#[derive(Debug, PartialEq, Clone, Copy, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
    feature = "bevy-support",
    derive(Component, Reflect),
    reflect(Component, Default, PartialEq, Debug)
)]
#[cfg_attr(
    all(feature = "bevy-support", feature = "serialize"),
    reflect(Serialize, Deserialize)
)]
pub struct GlobalTransform(Affine3A);

macro_rules! impl_local_axis {
    ($pos_name: ident, $neg_name: ident, $axis: ident) => {
        #[doc=std::concat!("Return the local ", std::stringify!($pos_name), " vector (", std::stringify!($axis) ,").")]
        #[inline]
        pub fn $pos_name(&self) -> Dir3 {
            Dir3::new_unchecked((self.0.matrix3 * Vec3::$axis).normalize())
        }

        #[doc=std::concat!("Return the local ", std::stringify!($neg_name), " vector (-", std::stringify!($axis) ,").")]
        #[inline]
        pub fn $neg_name(&self) -> Dir3 {
            -self.$pos_name()
        }
    };
}

impl GlobalTransform {
    /// An identity [`GlobalTransform`] that maps all points in space to themselves.
    pub const IDENTITY: Self = Self(Affine3A::IDENTITY);

    #[doc(hidden)]
    #[inline]
    pub fn from_xyz(x: f32, y: f32, z: f32) -> Self {
        Self::from_translation(Vec3::new(x, y, z))
    }

    #[doc(hidden)]
    #[inline]
    pub fn from_translation(translation: Vec3) -> Self {
        GlobalTransform(Affine3A::from_translation(translation))
    }

    #[doc(hidden)]
    #[inline]
    pub fn from_rotation(rotation: Quat) -> Self {
        GlobalTransform(Affine3A::from_rotation_translation(rotation, Vec3::ZERO))
    }

    #[doc(hidden)]
    #[inline]
    pub fn from_scale(scale: Vec3) -> Self {
        GlobalTransform(Affine3A::from_scale(scale))
    }

    #[doc(hidden)]
    #[inline]
    pub fn from_isometry(iso: Isometry3d) -> Self {
        Self(iso.into())
    }

    /// Returns the 3d affine transformation matrix as a [`Mat4`].
    #[inline]
    pub fn compute_matrix(&self) -> Mat4 {
        Mat4::from(self.0)
    }

    /// Returns the 3d affine transformation matrix as an [`Affine3A`].
    #[inline]
    pub fn affine(&self) -> Affine3A {
        self.0
    }

    /// Returns the transformation as a [`Transform`].
    ///
    /// The transform is expected to be non-degenerate and without shearing, or the output
    /// will be invalid.
    #[inline]
    pub fn compute_transform(&self) -> Transform {
        let (scale, rotation, translation) = self.0.to_scale_rotation_translation();
        Transform {
            translation,
            rotation,
            scale,
        }
    }

    /// Returns the isometric part of the transformation as an [isometry]. Any scaling done by the
    /// transformation will be ignored.
    ///
    /// The transform is expected to be non-degenerate and without shearing, or the output
    /// will be invalid.
    ///
    /// [isometry]: Isometry3d
    #[inline]
    pub fn to_isometry(&self) -> Isometry3d {
        let (_, rotation, translation) = self.0.to_scale_rotation_translation();
        Isometry3d::new(translation, rotation)
    }

    /// Returns the [`Transform`] `self` would have if it was a child of an entity
    /// with the `parent` [`GlobalTransform`].
    ///
    /// This is useful if you want to "reparent" an [`Entity`](bevy_ecs::entity::Entity).
    /// Say you have an entity `e1` that you want to turn into a child of `e2`,
    /// but you want `e1` to keep the same global transform, even after re-parenting. You would use:
    ///
    /// ```
    /// # use bevy_transform::prelude::{GlobalTransform, Transform};
    /// # use bevy_ecs::prelude::{Entity, Query, Component, Commands};
    /// # use bevy_hierarchy::{prelude::Parent, BuildChildren};
    /// #[derive(Component)]
    /// struct ToReparent {
    ///     new_parent: Entity,
    /// }
    /// fn reparent_system(
    ///     mut commands: Commands,
    ///     mut targets: Query<(&mut Transform, Entity, &GlobalTransform, &ToReparent)>,
    ///     transforms: Query<&GlobalTransform>,
    /// ) {
    ///     for (mut transform, entity, initial, to_reparent) in targets.iter_mut() {
    ///         if let Ok(parent_transform) = transforms.get(to_reparent.new_parent) {
    ///             *transform = initial.reparented_to(parent_transform);
    ///             commands.entity(entity)
    ///                 .remove::<ToReparent>()
    ///                 .set_parent(to_reparent.new_parent);
    ///         }
    ///     }
    /// }
    /// ```
    ///
    /// The transform is expected to be non-degenerate and without shearing, or the output
    /// will be invalid.
    #[inline]
    pub fn reparented_to(&self, parent: &GlobalTransform) -> Transform {
        let relative_affine = parent.affine().inverse() * self.affine();
        let (scale, rotation, translation) = relative_affine.to_scale_rotation_translation();
        Transform {
            translation,
            rotation,
            scale,
        }
    }

    /// Extracts `scale`, `rotation` and `translation` from `self`.
    ///
    /// The transform is expected to be non-degenerate and without shearing, or the output
    /// will be invalid.
    #[inline]
    pub fn to_scale_rotation_translation(&self) -> (Vec3, Quat, Vec3) {
        self.0.to_scale_rotation_translation()
    }

    impl_local_axis!(right, left, X);
    impl_local_axis!(up, down, Y);
    impl_local_axis!(back, forward, Z);

    /// Get the translation as a [`Vec3`].
    #[inline]
    pub fn translation(&self) -> Vec3 {
        self.0.translation.into()
    }

    /// Get the translation as a [`Vec3A`].
    #[inline]
    pub fn translation_vec3a(&self) -> Vec3A {
        self.0.translation
    }

    /// Get the rotation as a [`Quat`].
    ///
    /// The transform is expected to be non-degenerate and without shearing, or the output will be invalid.
    ///
    /// # Warning
    ///
    /// This is calculated using `to_scale_rotation_translation`, meaning that you
    /// should probably use it directly if you also need translation or scale.
    #[inline]
    pub fn rotation(&self) -> Quat {
        self.to_scale_rotation_translation().1
    }

    /// Get the scale as a [`Vec3`].
    ///
    /// The transform is expected to be non-degenerate and without shearing, or the output will be invalid.
    ///
    /// Some of the computations overlap with `to_scale_rotation_translation`, which means you should use
    /// it instead if you also need rotation.
    #[inline]
    pub fn scale(&self) -> Vec3 {
        //Formula based on glam's implementation https://github.com/bitshifter/glam-rs/blob/2e4443e70c709710dfb25958d866d29b11ed3e2b/src/f32/affine3a.rs#L290
        let det = self.0.matrix3.determinant();
        Vec3::new(
            self.0.matrix3.x_axis.length() * det.signum(),
            self.0.matrix3.y_axis.length(),
            self.0.matrix3.z_axis.length(),
        )
    }

    /// Get an upper bound of the radius from the given `extents`.
    #[inline]
    pub fn radius_vec3a(&self, extents: Vec3A) -> f32 {
        (self.0.matrix3 * extents).length()
    }

    /// Transforms the given point from local space to global space, applying shear, scale, rotation and translation.
    ///
    /// It can be used like this:
    ///
    /// ```
    /// # use bevy_transform::prelude::{GlobalTransform};
    /// # use bevy_math::prelude::Vec3;
    /// let global_transform = GlobalTransform::from_xyz(1., 2., 3.);
    /// let local_point = Vec3::new(1., 2., 3.);
    /// let global_point = global_transform.transform_point(local_point);
    /// assert_eq!(global_point, Vec3::new(2., 4., 6.));
    /// ```
    ///
    /// ```
    /// # use bevy_transform::prelude::{GlobalTransform};
    /// # use bevy_math::Vec3;
    /// let global_point = Vec3::new(2., 4., 6.);
    /// let global_transform = GlobalTransform::from_xyz(1., 2., 3.);
    /// let local_point = global_transform.affine().inverse().transform_point3(global_point);
    /// assert_eq!(local_point, Vec3::new(1., 2., 3.))
    /// ```
    ///
    /// To apply shear, scale, and rotation *without* applying translation, different functions are available:
    /// ```
    /// # use bevy_transform::prelude::{GlobalTransform};
    /// # use bevy_math::prelude::Vec3;
    /// let global_transform = GlobalTransform::from_xyz(1., 2., 3.);
    /// let local_direction = Vec3::new(1., 2., 3.);
    /// let global_direction = global_transform.affine().transform_vector3(local_direction);
    /// assert_eq!(global_direction, Vec3::new(1., 2., 3.));
    /// let roundtripped_local_direction = global_transform.affine().inverse().transform_vector3(global_direction);
    /// assert_eq!(roundtripped_local_direction, local_direction);
    /// ```
    #[inline]
    pub fn transform_point(&self, point: Vec3) -> Vec3 {
        self.0.transform_point3(point)
    }

    /// Multiplies `self` with `transform` component by component, returning the
    /// resulting [`GlobalTransform`]
    #[inline]
    pub fn mul_transform(&self, transform: Transform) -> Self {
        Self(self.0 * transform.compute_affine())
    }
}

impl Default for GlobalTransform {
    fn default() -> Self {
        Self::IDENTITY
    }
}

impl From<Transform> for GlobalTransform {
    fn from(transform: Transform) -> Self {
        Self(transform.compute_affine())
    }
}

impl From<Mat4> for GlobalTransform {
    fn from(world_from_local: Mat4) -> Self {
        Self(Affine3A::from_mat4(world_from_local))
    }
}

impl Mul<GlobalTransform> for GlobalTransform {
    type Output = GlobalTransform;

    #[inline]
    fn mul(self, global_transform: GlobalTransform) -> Self::Output {
        GlobalTransform(self.0 * global_transform.0)
    }
}

impl Mul<Transform> for GlobalTransform {
    type Output = GlobalTransform;

    #[inline]
    fn mul(self, transform: Transform) -> Self::Output {
        self.mul_transform(transform)
    }
}

impl Mul<Vec3> for GlobalTransform {
    type Output = Vec3;

    #[inline]
    fn mul(self, value: Vec3) -> Self::Output {
        self.transform_point(value)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    use bevy_math::EulerRot::XYZ;

    fn transform_equal(left: GlobalTransform, right: Transform) -> bool {
        left.0.abs_diff_eq(right.compute_affine(), 0.01)
    }

    #[test]
    fn reparented_to_transform_identity() {
        fn reparent_to_same(t1: GlobalTransform, t2: GlobalTransform) -> Transform {
            t2.mul_transform(t1.into()).reparented_to(&t2)
        }
        let t1 = GlobalTransform::from(Transform {
            translation: Vec3::new(1034.0, 34.0, -1324.34),
            rotation: Quat::from_euler(XYZ, 1.0, 0.9, 2.1),
            scale: Vec3::new(1.0, 1.0, 1.0),
        });
        let t2 = GlobalTransform::from(Transform {
            translation: Vec3::new(0.0, -54.493, 324.34),
            rotation: Quat::from_euler(XYZ, 1.9, 0.3, 3.0),
            scale: Vec3::new(1.345, 1.345, 1.345),
        });
        let retransformed = reparent_to_same(t1, t2);
        assert!(
            transform_equal(t1, retransformed),
            "t1:{:#?} retransformed:{:#?}",
            t1.compute_transform(),
            retransformed,
        );
    }
    #[test]
    fn reparented_usecase() {
        let t1 = GlobalTransform::from(Transform {
            translation: Vec3::new(1034.0, 34.0, -1324.34),
            rotation: Quat::from_euler(XYZ, 0.8, 1.9, 2.1),
            scale: Vec3::new(10.9, 10.9, 10.9),
        });
        let t2 = GlobalTransform::from(Transform {
            translation: Vec3::new(28.0, -54.493, 324.34),
            rotation: Quat::from_euler(XYZ, 0.0, 3.1, 0.1),
            scale: Vec3::new(0.9, 0.9, 0.9),
        });
        // goal: find `X` such as `t2 * X = t1`
        let reparented = t1.reparented_to(&t2);
        let t1_prime = t2 * reparented;
        assert!(
            transform_equal(t1, t1_prime.into()),
            "t1:{:#?} t1_prime:{:#?}",
            t1.compute_transform(),
            t1_prime.compute_transform(),
        );
    }

    #[test]
    fn scale() {
        let test_values = [-42.42, 0., 42.42];
        for x in test_values {
            for y in test_values {
                for z in test_values {
                    let scale = Vec3::new(x, y, z);
                    let gt = GlobalTransform::from_scale(scale);
                    assert_eq!(gt.scale(), gt.to_scale_rotation_translation().0);
                }
            }
        }
    }
}