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