Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_transform/src/components/transform.rs
6598 views
1
use super::GlobalTransform;
2
use bevy_math::{Affine3A, Dir3, Isometry3d, Mat3, Mat4, Quat, Vec3};
3
use core::ops::Mul;
4
5
#[cfg(feature = "bevy-support")]
6
use bevy_ecs::component::Component;
7
8
#[cfg(feature = "bevy_reflect")]
9
use {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)]
16
fn 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
)]
82
pub 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
103
impl 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
615
impl 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.
623
impl From<GlobalTransform> for Transform {
624
fn from(transform: GlobalTransform) -> Self {
625
transform.compute_transform()
626
}
627
}
628
629
impl 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
637
impl 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
646
impl 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
)]
669
pub struct TransformTreeChanged;
670
671