Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_transform/src/components/global_transform.rs
6598 views
1
use core::ops::Mul;
2
3
use super::Transform;
4
use bevy_math::{ops, Affine3A, Dir3, Isometry3d, Mat4, Quat, Vec3, Vec3A};
5
use derive_more::derive::From;
6
7
#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
8
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
9
10
#[cfg(feature = "bevy-support")]
11
use bevy_ecs::{component::Component, hierarchy::validate_parent_has_component};
12
13
#[cfg(feature = "bevy_reflect")]
14
use {
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
)]
64
pub struct GlobalTransform(Affine3A);
65
66
macro_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
82
impl 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
306
impl Default for GlobalTransform {
307
fn default() -> Self {
308
Self::IDENTITY
309
}
310
}
311
312
impl From<Transform> for GlobalTransform {
313
fn from(transform: Transform) -> Self {
314
Self(transform.compute_affine())
315
}
316
}
317
318
impl From<Mat4> for GlobalTransform {
319
fn from(world_from_local: Mat4) -> Self {
320
Self(Affine3A::from_mat4(world_from_local))
321
}
322
}
323
324
impl 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
333
impl 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
342
impl 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)]
352
mod 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
}
421
422