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