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