bevy_transform/components/transform.rs
1use super::GlobalTransform;
2#[cfg(feature = "bevy-support")]
3use bevy_ecs::{component::Component, reflect::ReflectComponent};
4use bevy_math::{Affine3A, Dir3, Mat3, Mat4, Quat, Vec3};
5#[cfg(feature = "bevy-support")]
6use bevy_reflect::{prelude::*, Reflect};
7use std::ops::Mul;
8
9/// Describe the position of an entity. If the entity has a parent, the position is relative
10/// to its parent position.
11///
12/// * To place or move an entity, you should set its [`Transform`].
13/// * To get the global transform of an entity, you should get its [`GlobalTransform`].
14/// * To be displayed, an entity must have both a [`Transform`] and a [`GlobalTransform`].
15/// * You may use the [`TransformBundle`](crate::TransformBundle) to guarantee this.
16///
17/// ## [`Transform`] and [`GlobalTransform`]
18///
19/// [`Transform`] is the position of an entity relative to its parent position, or the reference
20/// frame if it doesn't have a [`Parent`](bevy_hierarchy::Parent).
21///
22/// [`GlobalTransform`] is the position of an entity relative to the reference frame.
23///
24/// [`GlobalTransform`] is updated from [`Transform`] by systems in the system set
25/// [`TransformPropagate`](crate::TransformSystem::TransformPropagate).
26///
27/// This system runs during [`PostUpdate`](bevy_app::PostUpdate). If you
28/// update the [`Transform`] of an entity during this set or after, you will notice a 1 frame lag
29/// before the [`GlobalTransform`] is updated.
30///
31/// # Examples
32///
33/// - [`transform`][transform_example]
34///
35/// [transform_example]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/transform.rs
36#[derive(Debug, PartialEq, Clone, Copy)]
37#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
38#[cfg_attr(
39 feature = "bevy-support",
40 derive(Component, Reflect),
41 reflect(Component, Default, PartialEq)
42)]
43pub struct Transform {
44 /// Position of the entity. In 2d, the last value of the `Vec3` is used for z-ordering.
45 ///
46 /// See the [`translations`] example for usage.
47 ///
48 /// [`translations`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/translation.rs
49 pub translation: Vec3,
50 /// Rotation of the entity.
51 ///
52 /// See the [`3d_rotation`] example for usage.
53 ///
54 /// [`3d_rotation`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/3d_rotation.rs
55 pub rotation: Quat,
56 /// Scale of the entity.
57 ///
58 /// See the [`scale`] example for usage.
59 ///
60 /// [`scale`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/scale.rs
61 pub scale: Vec3,
62}
63
64impl Transform {
65 /// An identity [`Transform`] with no translation, rotation, and a scale of 1 on all axes.
66 pub const IDENTITY: Self = Transform {
67 translation: Vec3::ZERO,
68 rotation: Quat::IDENTITY,
69 scale: Vec3::ONE,
70 };
71
72 /// Creates a new [`Transform`] at the position `(x, y, z)`. In 2d, the `z` component
73 /// is used for z-ordering elements: higher `z`-value will be in front of lower
74 /// `z`-value.
75 #[inline]
76 pub const fn from_xyz(x: f32, y: f32, z: f32) -> Self {
77 Self::from_translation(Vec3::new(x, y, z))
78 }
79
80 /// Extracts the translation, rotation, and scale from `matrix`. It must be a 3d affine
81 /// transformation matrix.
82 #[inline]
83 pub fn from_matrix(world_from_local: Mat4) -> Self {
84 let (scale, rotation, translation) = world_from_local.to_scale_rotation_translation();
85
86 Transform {
87 translation,
88 rotation,
89 scale,
90 }
91 }
92
93 /// Creates a new [`Transform`], with `translation`. Rotation will be 0 and scale 1 on
94 /// all axes.
95 #[inline]
96 pub const fn from_translation(translation: Vec3) -> Self {
97 Transform {
98 translation,
99 ..Self::IDENTITY
100 }
101 }
102
103 /// Creates a new [`Transform`], with `rotation`. Translation will be 0 and scale 1 on
104 /// all axes.
105 #[inline]
106 pub const fn from_rotation(rotation: Quat) -> Self {
107 Transform {
108 rotation,
109 ..Self::IDENTITY
110 }
111 }
112
113 /// Creates a new [`Transform`], with `scale`. Translation will be 0 and rotation 0 on
114 /// all axes.
115 #[inline]
116 pub const fn from_scale(scale: Vec3) -> Self {
117 Transform {
118 scale,
119 ..Self::IDENTITY
120 }
121 }
122
123 /// Returns this [`Transform`] with a new rotation so that [`Transform::forward`]
124 /// points towards the `target` position and [`Transform::up`] points towards `up`.
125 ///
126 /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
127 /// * if `target` is the same as the transform translation, `Vec3::Z` is used instead
128 /// * if `up` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::Y` is used instead
129 /// * if the resulting forward direction is parallel with `up`, an orthogonal vector is used as the "right" direction
130 #[inline]
131 #[must_use]
132 pub fn looking_at(mut self, target: Vec3, up: impl TryInto<Dir3>) -> Self {
133 self.look_at(target, up);
134 self
135 }
136
137 /// Returns this [`Transform`] with a new rotation so that [`Transform::forward`]
138 /// points in the given `direction` and [`Transform::up`] points towards `up`.
139 ///
140 /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
141 /// * if `direction` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::Z` is used instead
142 /// * if `up` fails converting to `Dir3`, `Dir3::Y` is used instead
143 /// * if `direction` is parallel with `up`, an orthogonal vector is used as the "right" direction
144 #[inline]
145 #[must_use]
146 pub fn looking_to(mut self, direction: impl TryInto<Dir3>, up: impl TryInto<Dir3>) -> Self {
147 self.look_to(direction, up);
148 self
149 }
150
151 /// Rotates this [`Transform`] so that the `main_axis` vector, reinterpreted in local coordinates, points
152 /// in the given `main_direction`, while `secondary_axis` points towards `secondary_direction`.
153 /// For example, if a spaceship model has its nose pointing in the X-direction in its own local coordinates
154 /// and its dorsal fin pointing in the Y-direction, then `align(Dir3::X, v, Dir3::Y, w)` will make the spaceship's
155 /// nose point in the direction of `v`, while the dorsal fin does its best to point in the direction `w`.
156 ///
157 ///
158 /// In some cases a rotation cannot be constructed. Another axis will be picked in those cases:
159 /// * if `main_axis` or `main_direction` fail converting to `Dir3` (e.g are zero), `Dir3::X` takes their place
160 /// * if `secondary_axis` or `secondary_direction` fail converting, `Dir3::Y` takes their place
161 /// * if `main_axis` is parallel with `secondary_axis` or `main_direction` is parallel with `secondary_direction`,
162 /// a rotation is constructed which takes `main_axis` to `main_direction` along a great circle, ignoring the secondary
163 /// counterparts
164 ///
165 /// See [`Transform::align`] for additional details.
166 #[inline]
167 #[must_use]
168 pub fn aligned_by(
169 mut self,
170 main_axis: impl TryInto<Dir3>,
171 main_direction: impl TryInto<Dir3>,
172 secondary_axis: impl TryInto<Dir3>,
173 secondary_direction: impl TryInto<Dir3>,
174 ) -> Self {
175 self.align(
176 main_axis,
177 main_direction,
178 secondary_axis,
179 secondary_direction,
180 );
181 self
182 }
183
184 /// Returns this [`Transform`] with a new translation.
185 #[inline]
186 #[must_use]
187 pub const fn with_translation(mut self, translation: Vec3) -> Self {
188 self.translation = translation;
189 self
190 }
191
192 /// Returns this [`Transform`] with a new rotation.
193 #[inline]
194 #[must_use]
195 pub const fn with_rotation(mut self, rotation: Quat) -> Self {
196 self.rotation = rotation;
197 self
198 }
199
200 /// Returns this [`Transform`] with a new scale.
201 #[inline]
202 #[must_use]
203 pub const fn with_scale(mut self, scale: Vec3) -> Self {
204 self.scale = scale;
205 self
206 }
207
208 /// Returns the 3d affine transformation matrix from this transforms translation,
209 /// rotation, and scale.
210 #[inline]
211 pub fn compute_matrix(&self) -> Mat4 {
212 Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
213 }
214
215 /// Returns the 3d affine transformation matrix from this transforms translation,
216 /// rotation, and scale.
217 #[inline]
218 pub fn compute_affine(&self) -> Affine3A {
219 Affine3A::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
220 }
221
222 /// Get the unit vector in the local `X` direction.
223 #[inline]
224 pub fn local_x(&self) -> Dir3 {
225 // Quat * unit vector is length 1
226 Dir3::new_unchecked(self.rotation * Vec3::X)
227 }
228
229 /// Equivalent to [`-local_x()`][Transform::local_x()]
230 #[inline]
231 pub fn left(&self) -> Dir3 {
232 -self.local_x()
233 }
234
235 /// Equivalent to [`local_x()`][Transform::local_x()]
236 #[inline]
237 pub fn right(&self) -> Dir3 {
238 self.local_x()
239 }
240
241 /// Get the unit vector in the local `Y` direction.
242 #[inline]
243 pub fn local_y(&self) -> Dir3 {
244 // Quat * unit vector is length 1
245 Dir3::new_unchecked(self.rotation * Vec3::Y)
246 }
247
248 /// Equivalent to [`local_y()`][Transform::local_y]
249 #[inline]
250 pub fn up(&self) -> Dir3 {
251 self.local_y()
252 }
253
254 /// Equivalent to [`-local_y()`][Transform::local_y]
255 #[inline]
256 pub fn down(&self) -> Dir3 {
257 -self.local_y()
258 }
259
260 /// Get the unit vector in the local `Z` direction.
261 #[inline]
262 pub fn local_z(&self) -> Dir3 {
263 // Quat * unit vector is length 1
264 Dir3::new_unchecked(self.rotation * Vec3::Z)
265 }
266
267 /// Equivalent to [`-local_z()`][Transform::local_z]
268 #[inline]
269 pub fn forward(&self) -> Dir3 {
270 -self.local_z()
271 }
272
273 /// Equivalent to [`local_z()`][Transform::local_z]
274 #[inline]
275 pub fn back(&self) -> Dir3 {
276 self.local_z()
277 }
278
279 /// Rotates this [`Transform`] by the given rotation.
280 ///
281 /// If this [`Transform`] has a parent, the `rotation` is relative to the rotation of the parent.
282 ///
283 /// # Examples
284 ///
285 /// - [`3d_rotation`]
286 ///
287 /// [`3d_rotation`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/3d_rotation.rs
288 #[inline]
289 pub fn rotate(&mut self, rotation: Quat) {
290 self.rotation = rotation * self.rotation;
291 }
292
293 /// Rotates this [`Transform`] around the given `axis` by `angle` (in radians).
294 ///
295 /// If this [`Transform`] has a parent, the `axis` is relative to the rotation of the parent.
296 #[inline]
297 pub fn rotate_axis(&mut self, axis: Dir3, angle: f32) {
298 self.rotate(Quat::from_axis_angle(axis.into(), angle));
299 }
300
301 /// Rotates this [`Transform`] around the `X` axis by `angle` (in radians).
302 ///
303 /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
304 #[inline]
305 pub fn rotate_x(&mut self, angle: f32) {
306 self.rotate(Quat::from_rotation_x(angle));
307 }
308
309 /// Rotates this [`Transform`] around the `Y` axis by `angle` (in radians).
310 ///
311 /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
312 #[inline]
313 pub fn rotate_y(&mut self, angle: f32) {
314 self.rotate(Quat::from_rotation_y(angle));
315 }
316
317 /// Rotates this [`Transform`] around the `Z` axis by `angle` (in radians).
318 ///
319 /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
320 #[inline]
321 pub fn rotate_z(&mut self, angle: f32) {
322 self.rotate(Quat::from_rotation_z(angle));
323 }
324
325 /// Rotates this [`Transform`] by the given `rotation`.
326 ///
327 /// The `rotation` is relative to this [`Transform`]'s current rotation.
328 #[inline]
329 pub fn rotate_local(&mut self, rotation: Quat) {
330 self.rotation *= rotation;
331 }
332
333 /// Rotates this [`Transform`] around its local `axis` by `angle` (in radians).
334 #[inline]
335 pub fn rotate_local_axis(&mut self, axis: Dir3, angle: f32) {
336 self.rotate_local(Quat::from_axis_angle(axis.into(), angle));
337 }
338
339 /// Rotates this [`Transform`] around its local `X` axis by `angle` (in radians).
340 #[inline]
341 pub fn rotate_local_x(&mut self, angle: f32) {
342 self.rotate_local(Quat::from_rotation_x(angle));
343 }
344
345 /// Rotates this [`Transform`] around its local `Y` axis by `angle` (in radians).
346 #[inline]
347 pub fn rotate_local_y(&mut self, angle: f32) {
348 self.rotate_local(Quat::from_rotation_y(angle));
349 }
350
351 /// Rotates this [`Transform`] around its local `Z` axis by `angle` (in radians).
352 #[inline]
353 pub fn rotate_local_z(&mut self, angle: f32) {
354 self.rotate_local(Quat::from_rotation_z(angle));
355 }
356
357 /// Translates this [`Transform`] around a `point` in space.
358 ///
359 /// If this [`Transform`] has a parent, the `point` is relative to the [`Transform`] of the parent.
360 #[inline]
361 pub fn translate_around(&mut self, point: Vec3, rotation: Quat) {
362 self.translation = point + rotation * (self.translation - point);
363 }
364
365 /// Rotates this [`Transform`] around a `point` in space.
366 ///
367 /// If this [`Transform`] has a parent, the `point` is relative to the [`Transform`] of the parent.
368 #[inline]
369 pub fn rotate_around(&mut self, point: Vec3, rotation: Quat) {
370 self.translate_around(point, rotation);
371 self.rotate(rotation);
372 }
373
374 /// Rotates this [`Transform`] so that [`Transform::forward`] points towards the `target` position,
375 /// and [`Transform::up`] points towards `up`.
376 ///
377 /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
378 /// * if `target` is the same as the transform translation, `Vec3::Z` is used instead
379 /// * if `up` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::Y` is used instead
380 /// * if the resulting forward direction is parallel with `up`, an orthogonal vector is used as the "right" direction
381 #[inline]
382 pub fn look_at(&mut self, target: Vec3, up: impl TryInto<Dir3>) {
383 self.look_to(target - self.translation, up);
384 }
385
386 /// Rotates this [`Transform`] so that [`Transform::forward`] points in the given `direction`
387 /// and [`Transform::up`] points towards `up`.
388 ///
389 /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
390 /// * if `direction` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::NEG_Z` is used instead
391 /// * if `up` fails converting to `Dir3`, `Dir3::Y` is used instead
392 /// * if `direction` is parallel with `up`, an orthogonal vector is used as the "right" direction
393 #[inline]
394 pub fn look_to(&mut self, direction: impl TryInto<Dir3>, up: impl TryInto<Dir3>) {
395 let back = -direction.try_into().unwrap_or(Dir3::NEG_Z);
396 let up = up.try_into().unwrap_or(Dir3::Y);
397 let right = up
398 .cross(back.into())
399 .try_normalize()
400 .unwrap_or_else(|| up.any_orthonormal_vector());
401 let up = back.cross(right);
402 self.rotation = Quat::from_mat3(&Mat3::from_cols(right, up, back.into()));
403 }
404
405 /// Rotates this [`Transform`] so that the `main_axis` vector, reinterpreted in local coordinates, points
406 /// in the given `main_direction`, while `secondary_axis` points towards `secondary_direction`.
407 ///
408 /// For example, if a spaceship model has its nose pointing in the X-direction in its own local coordinates
409 /// and its dorsal fin pointing in the Y-direction, then `align(Dir3::X, v, Dir3::Y, w)` will make the spaceship's
410 /// nose point in the direction of `v`, while the dorsal fin does its best to point in the direction `w`.
411 ///
412 /// More precisely, the [`Transform::rotation`] produced will be such that:
413 /// * applying it to `main_axis` results in `main_direction`
414 /// * applying it to `secondary_axis` produces a vector that lies in the half-plane generated by `main_direction` and
415 /// `secondary_direction` (with positive contribution by `secondary_direction`)
416 ///
417 /// [`Transform::look_to`] is recovered, for instance, when `main_axis` is `Dir3::NEG_Z` (the [`Transform::forward`]
418 /// direction in the default orientation) and `secondary_axis` is `Dir3::Y` (the [`Transform::up`] direction in the default
419 /// orientation). (Failure cases may differ somewhat.)
420 ///
421 /// In some cases a rotation cannot be constructed. Another axis will be picked in those cases:
422 /// * if `main_axis` or `main_direction` fail converting to `Dir3` (e.g are zero), `Dir3::X` takes their place
423 /// * if `secondary_axis` or `secondary_direction` fail converting, `Dir3::Y` takes their place
424 /// * if `main_axis` is parallel with `secondary_axis` or `main_direction` is parallel with `secondary_direction`,
425 /// a rotation is constructed which takes `main_axis` to `main_direction` along a great circle, ignoring the secondary
426 /// counterparts
427 ///
428 /// Example
429 /// ```
430 /// # use bevy_math::{Dir3, Vec3, Quat};
431 /// # use bevy_transform::components::Transform;
432 /// # let mut t1 = Transform::IDENTITY;
433 /// # let mut t2 = Transform::IDENTITY;
434 /// t1.align(Dir3::X, Dir3::Y, Vec3::new(1., 1., 0.), Dir3::Z);
435 /// let main_axis_image = t1.rotation * Dir3::X;
436 /// let secondary_axis_image = t1.rotation * Vec3::new(1., 1., 0.);
437 /// assert!(main_axis_image.abs_diff_eq(Vec3::Y, 1e-5));
438 /// assert!(secondary_axis_image.abs_diff_eq(Vec3::new(0., 1., 1.), 1e-5));
439 ///
440 /// t1.align(Vec3::ZERO, Dir3::Z, Vec3::ZERO, Dir3::X);
441 /// t2.align(Dir3::X, Dir3::Z, Dir3::Y, Dir3::X);
442 /// assert_eq!(t1.rotation, t2.rotation);
443 ///
444 /// t1.align(Dir3::X, Dir3::Z, Dir3::X, Dir3::Y);
445 /// assert_eq!(t1.rotation, Quat::from_rotation_arc(Vec3::X, Vec3::Z));
446 /// ```
447 #[inline]
448 pub fn align(
449 &mut self,
450 main_axis: impl TryInto<Dir3>,
451 main_direction: impl TryInto<Dir3>,
452 secondary_axis: impl TryInto<Dir3>,
453 secondary_direction: impl TryInto<Dir3>,
454 ) {
455 let main_axis = main_axis.try_into().unwrap_or(Dir3::X);
456 let main_direction = main_direction.try_into().unwrap_or(Dir3::X);
457 let secondary_axis = secondary_axis.try_into().unwrap_or(Dir3::Y);
458 let secondary_direction = secondary_direction.try_into().unwrap_or(Dir3::Y);
459
460 // The solution quaternion will be constructed in two steps.
461 // First, we start with a rotation that takes `main_axis` to `main_direction`.
462 let first_rotation = Quat::from_rotation_arc(main_axis.into(), main_direction.into());
463
464 // Let's follow by rotating about the `main_direction` axis so that the image of `secondary_axis`
465 // is taken to something that lies in the plane of `main_direction` and `secondary_direction`. Since
466 // `main_direction` is fixed by this rotation, the first criterion is still satisfied.
467 let secondary_image = first_rotation * secondary_axis;
468 let secondary_image_ortho = secondary_image
469 .reject_from_normalized(main_direction.into())
470 .try_normalize();
471 let secondary_direction_ortho = secondary_direction
472 .reject_from_normalized(main_direction.into())
473 .try_normalize();
474
475 // If one of the two weak vectors was parallel to `main_direction`, then we just do the first part
476 self.rotation = match (secondary_image_ortho, secondary_direction_ortho) {
477 (Some(secondary_img_ortho), Some(secondary_dir_ortho)) => {
478 let second_rotation =
479 Quat::from_rotation_arc(secondary_img_ortho, secondary_dir_ortho);
480 second_rotation * first_rotation
481 }
482 _ => first_rotation,
483 };
484 }
485
486 /// Multiplies `self` with `transform` component by component, returning the
487 /// resulting [`Transform`]
488 #[inline]
489 #[must_use]
490 pub fn mul_transform(&self, transform: Transform) -> Self {
491 let translation = self.transform_point(transform.translation);
492 let rotation = self.rotation * transform.rotation;
493 let scale = self.scale * transform.scale;
494 Transform {
495 translation,
496 rotation,
497 scale,
498 }
499 }
500
501 /// Transforms the given `point`, applying scale, rotation and translation.
502 ///
503 /// If this [`Transform`] has a parent, this will transform a `point` that is
504 /// relative to the parent's [`Transform`] into one relative to this [`Transform`].
505 ///
506 /// If this [`Transform`] does not have a parent, this will transform a `point`
507 /// that is in global space into one relative to this [`Transform`].
508 ///
509 /// If you want to transform a `point` in global space to the local space of this [`Transform`],
510 /// consider using [`GlobalTransform::transform_point()`] instead.
511 #[inline]
512 pub fn transform_point(&self, mut point: Vec3) -> Vec3 {
513 point = self.scale * point;
514 point = self.rotation * point;
515 point += self.translation;
516 point
517 }
518
519 /// Returns `true` if, and only if, translation, rotation and scale all are
520 /// finite. If any of them contains a `NaN`, positive or negative infinity,
521 /// this will return `false`.
522 #[inline]
523 #[must_use]
524 pub fn is_finite(&self) -> bool {
525 self.translation.is_finite() && self.rotation.is_finite() && self.scale.is_finite()
526 }
527}
528
529impl Default for Transform {
530 fn default() -> Self {
531 Self::IDENTITY
532 }
533}
534
535/// The transform is expected to be non-degenerate and without shearing, or the output
536/// will be invalid.
537impl From<GlobalTransform> for Transform {
538 fn from(transform: GlobalTransform) -> Self {
539 transform.compute_transform()
540 }
541}
542
543impl Mul<Transform> for Transform {
544 type Output = Transform;
545
546 fn mul(self, transform: Transform) -> Self::Output {
547 self.mul_transform(transform)
548 }
549}
550
551impl Mul<GlobalTransform> for Transform {
552 type Output = GlobalTransform;
553
554 #[inline]
555 fn mul(self, global_transform: GlobalTransform) -> Self::Output {
556 GlobalTransform::from(self) * global_transform
557 }
558}
559
560impl Mul<Vec3> for Transform {
561 type Output = Vec3;
562
563 fn mul(self, value: Vec3) -> Self::Output {
564 self.transform_point(value)
565 }
566}