Add a reparented_to method to GlobalTransform (#7020)
# Objective It is often necessary to update an entity's parent while keeping its GlobalTransform static. Currently it is cumbersome and error-prone (two questions in the discord `#help` channel in the past week) - Part 1 of #5475 - Part 2: #7024. ## Solution - Add a `reparented_to` method to `GlobalTransform` --- ## Changelog - Add a `reparented_to` method to `GlobalTransform`
This commit is contained in:
parent
48b4a45d82
commit
b8a9933d46
@ -106,6 +106,50 @@ impl GlobalTransform {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`Transform`] `self` would have if it was a child of an entity
|
||||
/// with the `parent` [`GlobalTransform`].
|
||||
///
|
||||
/// This is useful if you want to "reparent" an `Entity`. Say you have an entity
|
||||
/// `e1` that you want to turn into a child of `e2`, but you want `e1` to keep the
|
||||
/// same global transform, even after re-partenting. You would use:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bevy_transform::prelude::{GlobalTransform, Transform};
|
||||
/// # use bevy_ecs::prelude::{Entity, Query, Component, Commands};
|
||||
/// # use bevy_hierarchy::{prelude::Parent, BuildChildren};
|
||||
/// #[derive(Component)]
|
||||
/// struct ToReparent {
|
||||
/// new_parent: Entity,
|
||||
/// }
|
||||
/// fn reparent_system(
|
||||
/// mut commands: Commands,
|
||||
/// mut targets: Query<(&mut Transform, Entity, &GlobalTransform, &ToReparent)>,
|
||||
/// transforms: Query<&GlobalTransform>,
|
||||
/// ) {
|
||||
/// for (mut transform, entity, initial, to_reparent) in targets.iter_mut() {
|
||||
/// if let Ok(parent_transform) = transforms.get(to_reparent.new_parent) {
|
||||
/// *transform = initial.reparented_to(parent_transform);
|
||||
/// commands.entity(entity)
|
||||
/// .remove::<ToReparent>()
|
||||
/// .set_parent(to_reparent.new_parent);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The transform is expected to be non-degenerate and without shearing, or the output
|
||||
/// will be invalid.
|
||||
#[inline]
|
||||
pub fn reparented_to(&self, parent: &GlobalTransform) -> Transform {
|
||||
let relative_affine = parent.affine().inverse() * self.affine();
|
||||
let (scale, rotation, translation) = relative_affine.to_scale_rotation_translation();
|
||||
Transform {
|
||||
translation,
|
||||
rotation,
|
||||
scale,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts `scale`, `rotation` and `translation` from `self`.
|
||||
///
|
||||
/// The transform is expected to be non-degenerate and without shearing, or the output
|
||||
@ -209,3 +253,60 @@ impl Mul<Vec3> for GlobalTransform {
|
||||
self.transform_point(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use bevy_math::EulerRot::XYZ;
|
||||
|
||||
fn transform_equal(left: GlobalTransform, right: Transform) -> bool {
|
||||
left.0.abs_diff_eq(right.compute_affine(), 0.01)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reparented_to_transform_identity() {
|
||||
fn reparent_to_same(t1: GlobalTransform, t2: GlobalTransform) -> Transform {
|
||||
t2.mul_transform(t1.into()).reparented_to(&t2)
|
||||
}
|
||||
let t1 = GlobalTransform::from(Transform {
|
||||
translation: Vec3::new(1034.0, 34.0, -1324.34),
|
||||
rotation: Quat::from_euler(XYZ, 1.0, 0.9, 2.1),
|
||||
scale: Vec3::new(1.0, 1.0, 1.0),
|
||||
});
|
||||
let t2 = GlobalTransform::from(Transform {
|
||||
translation: Vec3::new(0.0, -54.493, 324.34),
|
||||
rotation: Quat::from_euler(XYZ, 1.9, 0.3, 3.0),
|
||||
scale: Vec3::new(1.345, 1.345, 1.345),
|
||||
});
|
||||
let retransformed = reparent_to_same(t1, t2);
|
||||
assert!(
|
||||
transform_equal(t1, retransformed),
|
||||
"t1:{:#?} retransformed:{:#?}",
|
||||
t1.compute_transform(),
|
||||
retransformed,
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn reparented_usecase() {
|
||||
let t1 = GlobalTransform::from(Transform {
|
||||
translation: Vec3::new(1034.0, 34.0, -1324.34),
|
||||
rotation: Quat::from_euler(XYZ, 0.8, 1.9, 2.1),
|
||||
scale: Vec3::new(10.9, 10.9, 10.9),
|
||||
});
|
||||
let t2 = GlobalTransform::from(Transform {
|
||||
translation: Vec3::new(28.0, -54.493, 324.34),
|
||||
rotation: Quat::from_euler(XYZ, 0.0, 3.1, 0.1),
|
||||
scale: Vec3::new(0.9, 0.9, 0.9),
|
||||
});
|
||||
// goal: find `X` such as `t2 * X = t1`
|
||||
let reparented = t1.reparented_to(&t2);
|
||||
let t1_prime = t2 * reparented;
|
||||
assert!(
|
||||
transform_equal(t1, t1_prime.into()),
|
||||
"t1:{:#?} t1_prime:{:#?}",
|
||||
t1.compute_transform(),
|
||||
t1_prime.compute_transform(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user