Path: blob/main/crates/bevy_transform/src/components/global_transform.rs
6598 views
use core::ops::Mul;12use super::Transform;3use bevy_math::{ops, Affine3A, Dir3, Isometry3d, Mat4, Quat, Vec3, Vec3A};4use derive_more::derive::From;56#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]7use bevy_reflect::{ReflectDeserialize, ReflectSerialize};89#[cfg(feature = "bevy-support")]10use bevy_ecs::{component::Component, hierarchy::validate_parent_has_component};1112#[cfg(feature = "bevy_reflect")]13use {14bevy_ecs::reflect::ReflectComponent,15bevy_reflect::{std_traits::ReflectDefault, Reflect},16};1718/// [`GlobalTransform`] is an affine transformation from entity-local coordinates to worldspace coordinates.19///20/// You cannot directly mutate [`GlobalTransform`]; instead, you change an entity's transform by manipulating21/// its [`Transform`], which indirectly causes Bevy to update its [`GlobalTransform`].22///23/// * To get the global transform of an entity, you should get its [`GlobalTransform`].24/// * For transform hierarchies to work correctly, you must have both a [`Transform`] and a [`GlobalTransform`].25/// [`GlobalTransform`] is automatically inserted whenever [`Transform`] is inserted.26///27/// ## [`Transform`] and [`GlobalTransform`]28///29/// [`Transform`] transforms an entity relative to its parent's reference frame, or relative to world space coordinates,30/// if it doesn't have a [`ChildOf`](bevy_ecs::hierarchy::ChildOf) component.31///32/// [`GlobalTransform`] is managed by Bevy; it is computed by successively applying the [`Transform`] of each ancestor33/// entity which has a Transform. This is done automatically by Bevy-internal systems in the [`TransformSystems::Propagate`]34/// system set.35///36/// This system runs during [`PostUpdate`](bevy_app::PostUpdate). If you37/// update the [`Transform`] of an entity in this schedule or after, you will notice a 1 frame lag38/// before the [`GlobalTransform`] is updated.39///40/// [`TransformSystems::Propagate`]: crate::TransformSystems::Propagate41///42/// # Examples43///44/// - [`transform`][transform_example]45///46/// [transform_example]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/transform.rs47#[derive(Debug, PartialEq, Clone, Copy, From)]48#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]49#[cfg_attr(50feature = "bevy-support",51derive(Component),52component(on_insert = validate_parent_has_component::<GlobalTransform>)53)]54#[cfg_attr(55feature = "bevy_reflect",56derive(Reflect),57reflect(Component, Default, PartialEq, Debug, Clone)58)]59#[cfg_attr(60all(feature = "bevy_reflect", feature = "serialize"),61reflect(Serialize, Deserialize)62)]63pub struct GlobalTransform(Affine3A);6465macro_rules! impl_local_axis {66($pos_name: ident, $neg_name: ident, $axis: ident) => {67#[doc=core::concat!("Return the local ", core::stringify!($pos_name), " vector (", core::stringify!($axis) ,").")]68#[inline]69pub fn $pos_name(&self) -> Dir3 {70Dir3::new_unchecked((self.0.matrix3 * Vec3::$axis).normalize())71}7273#[doc=core::concat!("Return the local ", core::stringify!($neg_name), " vector (-", core::stringify!($axis) ,").")]74#[inline]75pub fn $neg_name(&self) -> Dir3 {76-self.$pos_name()77}78};79}8081impl GlobalTransform {82/// An identity [`GlobalTransform`] that maps all points in space to themselves.83pub const IDENTITY: Self = Self(Affine3A::IDENTITY);8485#[doc(hidden)]86#[inline]87pub fn from_xyz(x: f32, y: f32, z: f32) -> Self {88Self::from_translation(Vec3::new(x, y, z))89}9091#[doc(hidden)]92#[inline]93pub fn from_translation(translation: Vec3) -> Self {94GlobalTransform(Affine3A::from_translation(translation))95}9697#[doc(hidden)]98#[inline]99pub fn from_rotation(rotation: Quat) -> Self {100GlobalTransform(Affine3A::from_rotation_translation(rotation, Vec3::ZERO))101}102103#[doc(hidden)]104#[inline]105pub fn from_scale(scale: Vec3) -> Self {106GlobalTransform(Affine3A::from_scale(scale))107}108109#[doc(hidden)]110#[inline]111pub fn from_isometry(iso: Isometry3d) -> Self {112Self(iso.into())113}114115/// Returns the 3d affine transformation matrix as a [`Mat4`].116#[inline]117pub fn to_matrix(&self) -> Mat4 {118Mat4::from(self.0)119}120121/// Returns the 3d affine transformation matrix as an [`Affine3A`].122#[inline]123pub fn affine(&self) -> Affine3A {124self.0125}126127/// Returns the transformation as a [`Transform`].128///129/// The transform is expected to be non-degenerate and without shearing, or the output130/// will be invalid.131#[inline]132pub fn compute_transform(&self) -> Transform {133let (scale, rotation, translation) = self.0.to_scale_rotation_translation();134Transform {135translation,136rotation,137scale,138}139}140141/// Computes a Scale-Rotation-Translation decomposition of the transformation and returns142/// the isometric part as an [isometry]. Any scaling done by the transformation will be ignored.143/// Note: this is a somewhat costly and lossy conversion.144///145/// The transform is expected to be non-degenerate and without shearing, or the output146/// will be invalid.147///148/// [isometry]: Isometry3d149#[inline]150pub fn to_isometry(&self) -> Isometry3d {151let (_, rotation, translation) = self.0.to_scale_rotation_translation();152Isometry3d::new(translation, rotation)153}154155/// Returns the [`Transform`] `self` would have if it was a child of an entity156/// with the `parent` [`GlobalTransform`].157///158/// This is useful if you want to "reparent" an [`Entity`](bevy_ecs::entity::Entity).159/// Say you have an entity `e1` that you want to turn into a child of `e2`,160/// but you want `e1` to keep the same global transform, even after re-parenting. You would use:161///162/// ```163/// # use bevy_transform::prelude::{GlobalTransform, Transform};164/// # use bevy_ecs::prelude::{Entity, Query, Component, Commands, ChildOf};165/// #[derive(Component)]166/// struct ToReparent {167/// new_parent: Entity,168/// }169/// fn reparent_system(170/// mut commands: Commands,171/// mut targets: Query<(&mut Transform, Entity, &GlobalTransform, &ToReparent)>,172/// transforms: Query<&GlobalTransform>,173/// ) {174/// for (mut transform, entity, initial, to_reparent) in targets.iter_mut() {175/// if let Ok(parent_transform) = transforms.get(to_reparent.new_parent) {176/// *transform = initial.reparented_to(parent_transform);177/// commands.entity(entity)178/// .remove::<ToReparent>()179/// .insert(ChildOf(to_reparent.new_parent));180/// }181/// }182/// }183/// ```184///185/// The transform is expected to be non-degenerate and without shearing, or the output186/// will be invalid.187#[inline]188pub fn reparented_to(&self, parent: &GlobalTransform) -> Transform {189let relative_affine = parent.affine().inverse() * self.affine();190let (scale, rotation, translation) = relative_affine.to_scale_rotation_translation();191Transform {192translation,193rotation,194scale,195}196}197198/// Extracts `scale`, `rotation` and `translation` from `self`.199///200/// The transform is expected to be non-degenerate and without shearing, or the output201/// will be invalid.202#[inline]203pub fn to_scale_rotation_translation(&self) -> (Vec3, Quat, Vec3) {204self.0.to_scale_rotation_translation()205}206207impl_local_axis!(right, left, X);208impl_local_axis!(up, down, Y);209impl_local_axis!(back, forward, Z);210211/// Get the translation as a [`Vec3`].212#[inline]213pub fn translation(&self) -> Vec3 {214self.0.translation.into()215}216217/// Get the translation as a [`Vec3A`].218#[inline]219pub fn translation_vec3a(&self) -> Vec3A {220self.0.translation221}222223/// Get the rotation as a [`Quat`].224///225/// The transform is expected to be non-degenerate and without shearing, or the output will be invalid.226///227/// # Warning228///229/// This is calculated using `to_scale_rotation_translation`, meaning that you230/// should probably use it directly if you also need translation or scale.231#[inline]232pub fn rotation(&self) -> Quat {233self.to_scale_rotation_translation().1234}235236/// Get the scale as a [`Vec3`].237///238/// The transform is expected to be non-degenerate and without shearing, or the output will be invalid.239///240/// Some of the computations overlap with `to_scale_rotation_translation`, which means you should use241/// it instead if you also need rotation.242#[inline]243pub fn scale(&self) -> Vec3 {244//Formula based on glam's implementation https://github.com/bitshifter/glam-rs/blob/2e4443e70c709710dfb25958d866d29b11ed3e2b/src/f32/affine3a.rs#L290245let det = self.0.matrix3.determinant();246Vec3::new(247self.0.matrix3.x_axis.length() * ops::copysign(1., det),248self.0.matrix3.y_axis.length(),249self.0.matrix3.z_axis.length(),250)251}252253/// Get an upper bound of the radius from the given `extents`.254#[inline]255pub fn radius_vec3a(&self, extents: Vec3A) -> f32 {256(self.0.matrix3 * extents).length()257}258259/// Transforms the given point from local space to global space, applying shear, scale, rotation and translation.260///261/// It can be used like this:262///263/// ```264/// # use bevy_transform::prelude::{GlobalTransform};265/// # use bevy_math::prelude::Vec3;266/// let global_transform = GlobalTransform::from_xyz(1., 2., 3.);267/// let local_point = Vec3::new(1., 2., 3.);268/// let global_point = global_transform.transform_point(local_point);269/// assert_eq!(global_point, Vec3::new(2., 4., 6.));270/// ```271///272/// ```273/// # use bevy_transform::prelude::{GlobalTransform};274/// # use bevy_math::Vec3;275/// let global_point = Vec3::new(2., 4., 6.);276/// let global_transform = GlobalTransform::from_xyz(1., 2., 3.);277/// let local_point = global_transform.affine().inverse().transform_point3(global_point);278/// assert_eq!(local_point, Vec3::new(1., 2., 3.))279/// ```280///281/// To apply shear, scale, and rotation *without* applying translation, different functions are available:282/// ```283/// # use bevy_transform::prelude::{GlobalTransform};284/// # use bevy_math::prelude::Vec3;285/// let global_transform = GlobalTransform::from_xyz(1., 2., 3.);286/// let local_direction = Vec3::new(1., 2., 3.);287/// let global_direction = global_transform.affine().transform_vector3(local_direction);288/// assert_eq!(global_direction, Vec3::new(1., 2., 3.));289/// let roundtripped_local_direction = global_transform.affine().inverse().transform_vector3(global_direction);290/// assert_eq!(roundtripped_local_direction, local_direction);291/// ```292#[inline]293pub fn transform_point(&self, point: Vec3) -> Vec3 {294self.0.transform_point3(point)295}296297/// Multiplies `self` with `transform` component by component, returning the298/// resulting [`GlobalTransform`]299#[inline]300pub fn mul_transform(&self, transform: Transform) -> Self {301Self(self.0 * transform.compute_affine())302}303}304305impl Default for GlobalTransform {306fn default() -> Self {307Self::IDENTITY308}309}310311impl From<Transform> for GlobalTransform {312fn from(transform: Transform) -> Self {313Self(transform.compute_affine())314}315}316317impl From<Mat4> for GlobalTransform {318fn from(world_from_local: Mat4) -> Self {319Self(Affine3A::from_mat4(world_from_local))320}321}322323impl Mul<GlobalTransform> for GlobalTransform {324type Output = GlobalTransform;325326#[inline]327fn mul(self, global_transform: GlobalTransform) -> Self::Output {328GlobalTransform(self.0 * global_transform.0)329}330}331332impl Mul<Transform> for GlobalTransform {333type Output = GlobalTransform;334335#[inline]336fn mul(self, transform: Transform) -> Self::Output {337self.mul_transform(transform)338}339}340341impl Mul<Vec3> for GlobalTransform {342type Output = Vec3;343344#[inline]345fn mul(self, value: Vec3) -> Self::Output {346self.transform_point(value)347}348}349350#[cfg(test)]351mod test {352use super::*;353354use bevy_math::EulerRot::XYZ;355356fn transform_equal(left: GlobalTransform, right: Transform) -> bool {357left.0.abs_diff_eq(right.compute_affine(), 0.01)358}359360#[test]361fn reparented_to_transform_identity() {362fn reparent_to_same(t1: GlobalTransform, t2: GlobalTransform) -> Transform {363t2.mul_transform(t1.into()).reparented_to(&t2)364}365let t1 = GlobalTransform::from(Transform {366translation: Vec3::new(1034.0, 34.0, -1324.34),367rotation: Quat::from_euler(XYZ, 1.0, 0.9, 2.1),368scale: Vec3::new(1.0, 1.0, 1.0),369});370let t2 = GlobalTransform::from(Transform {371translation: Vec3::new(0.0, -54.493, 324.34),372rotation: Quat::from_euler(XYZ, 1.9, 0.3, 3.0),373scale: Vec3::new(1.345, 1.345, 1.345),374});375let retransformed = reparent_to_same(t1, t2);376assert!(377transform_equal(t1, retransformed),378"t1:{:#?} retransformed:{:#?}",379t1.compute_transform(),380retransformed,381);382}383#[test]384fn reparented_usecase() {385let t1 = GlobalTransform::from(Transform {386translation: Vec3::new(1034.0, 34.0, -1324.34),387rotation: Quat::from_euler(XYZ, 0.8, 1.9, 2.1),388scale: Vec3::new(10.9, 10.9, 10.9),389});390let t2 = GlobalTransform::from(Transform {391translation: Vec3::new(28.0, -54.493, 324.34),392rotation: Quat::from_euler(XYZ, 0.0, 3.1, 0.1),393scale: Vec3::new(0.9, 0.9, 0.9),394});395// goal: find `X` such as `t2 * X = t1`396let reparented = t1.reparented_to(&t2);397let t1_prime = t2 * reparented;398assert!(399transform_equal(t1, t1_prime.into()),400"t1:{:#?} t1_prime:{:#?}",401t1.compute_transform(),402t1_prime.compute_transform(),403);404}405406#[test]407fn scale() {408let test_values = [-42.42, 0., 42.42];409for x in test_values {410for y in test_values {411for z in test_values {412let scale = Vec3::new(x, y, z);413let gt = GlobalTransform::from_scale(scale);414assert_eq!(gt.scale(), gt.to_scale_rotation_translation().0);415}416}417}418}419}420421422