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