Use Affine3A for GlobalTransform to allow any affine transformation (#4379)

# Objective

- Add capability to use `Affine3A`s for some `GlobalTransform`s. This allows affine transformations that are not possible using a single `Transform` such as shear and non-uniform scaling along an arbitrary axis.
- Related to #1755 and #2026

## Solution

- `GlobalTransform` becomes an enum wrapping either a `Transform` or an `Affine3A`.
- The API of `GlobalTransform` is minimized to avoid inefficiency, and to make it clear that operations should be performed using the underlying data types.
- using `GlobalTransform::Affine3A` disables transform propagation, because the main use is for cases that `Transform`s cannot support.

---

## Changelog

- `GlobalTransform`s can optionally support any affine transformation using an `Affine3A`.


Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
Dusty DeWeese 2022-07-16 00:51:12 +00:00
parent 8810a73e87
commit 9f8bdeeeb9
16 changed files with 178 additions and 270 deletions

View File

@ -1,7 +1,7 @@
use std::collections::HashSet;
use bevy_ecs::prelude::*;
use bevy_math::{Mat4, Quat, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_math::{Mat4, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_reflect::prelude::*;
use bevy_render::{
camera::{Camera, CameraProjection, OrthographicProjection},
@ -12,7 +12,7 @@ use bevy_render::{
renderer::RenderDevice,
view::{ComputedVisibility, RenderLayers, VisibleEntities},
};
use bevy_transform::components::GlobalTransform;
use bevy_transform::{components::GlobalTransform, prelude::Transform};
use bevy_utils::tracing::warn;
use crate::{
@ -755,13 +755,21 @@ pub(crate) fn point_light_order(
// data required for assigning lights to clusters
pub(crate) struct PointLightAssignmentData {
entity: Entity,
translation: Vec3,
rotation: Quat,
transform: GlobalTransform,
range: f32,
shadows_enabled: bool,
spot_light_angle: Option<f32>,
}
impl PointLightAssignmentData {
pub fn sphere(&self) -> Sphere {
Sphere {
center: self.transform.translation_vec3a(),
radius: self.range,
}
}
}
#[derive(Default)]
pub struct GlobalVisiblePointLights {
entities: HashSet<Entity>,
@ -815,8 +823,7 @@ pub(crate) fn assign_lights_to_clusters(
.map(
|(entity, transform, point_light, _visibility)| PointLightAssignmentData {
entity,
translation: transform.translation,
rotation: Quat::default(),
transform: GlobalTransform::from_translation(transform.translation()),
shadows_enabled: point_light.shadows_enabled,
range: point_light.range,
spot_light_angle: None,
@ -830,8 +837,7 @@ pub(crate) fn assign_lights_to_clusters(
.map(
|(entity, transform, spot_light, _visibility)| PointLightAssignmentData {
entity,
translation: transform.translation,
rotation: transform.rotation,
transform: *transform,
shadows_enabled: spot_light.shadows_enabled,
range: spot_light.range,
spot_light_angle: Some(spot_light.outer_angle),
@ -872,11 +878,7 @@ pub(crate) fn assign_lights_to_clusters(
if lights_in_view_count == MAX_UNIFORM_BUFFER_POINT_LIGHTS + 1 {
false
} else {
let light_sphere = Sphere {
center: Vec3A::from(light.translation),
radius: light.range,
};
let light_sphere = light.sphere();
let light_in_view = frusta
.iter()
.any(|frustum| frustum.intersects_sphere(&light_sphere, true));
@ -932,7 +934,8 @@ pub(crate) fn assign_lights_to_clusters(
lights
.iter()
.map(|light| {
-inverse_view_row_2.dot(light.translation.extend(1.0)) + light.range
-inverse_view_row_2.dot(light.transform.translation().extend(1.0))
+ light.range
})
.reduce(f32::max)
.unwrap_or(0.0)
@ -966,10 +969,7 @@ pub(crate) fn assign_lights_to_clusters(
if config.dynamic_resizing() {
let mut cluster_index_estimate = 0.0;
for light in lights.iter() {
let light_sphere = Sphere {
center: Vec3A::from(light.translation),
radius: light.range,
};
let light_sphere = light.sphere();
// Check if the light is within the view frustum
if !frustum.intersects_sphere(&light_sphere, true) {
@ -1124,10 +1124,7 @@ pub(crate) fn assign_lights_to_clusters(
let mut update_from_light_intersections = |visible_lights: &mut Vec<Entity>| {
for light in lights.iter() {
let light_sphere = Sphere {
center: Vec3A::from(light.translation),
radius: light.range,
};
let light_sphere = light.sphere();
// Check if the light is within the view frustum
if !frustum.intersects_sphere(&light_sphere, true) {
@ -1177,8 +1174,7 @@ pub(crate) fn assign_lights_to_clusters(
let spot_light_dir_sin_cos = light.spot_light_angle.map(|angle| {
let (angle_sin, angle_cos) = angle.sin_cos();
(
(inverse_view_transform * (light.rotation * Vec3::Z).extend(0.0))
.truncate(),
(inverse_view_transform * light.transform.back().extend(0.0)).truncate(),
angle_sin,
angle_cos,
)
@ -1432,7 +1428,7 @@ pub fn update_directional_light_frusta(
* transform.compute_matrix().inverse();
*frustum = Frustum::from_view_projection(
&view_projection,
&transform.translation,
&transform.translation(),
&transform.back(),
directional_light.shadow_projection.far(),
);
@ -1451,7 +1447,7 @@ pub fn update_point_light_frusta(
Mat4::perspective_infinite_reverse_rh(std::f32::consts::FRAC_PI_2, 1.0, POINT_LIGHT_NEAR_Z);
let view_rotations = CUBE_MAP_FACES
.iter()
.map(|CubeMapFace { target, up }| GlobalTransform::identity().looking_at(*target, *up))
.map(|CubeMapFace { target, up }| Transform::identity().looking_at(*target, *up))
.collect::<Vec<_>>();
for (entity, transform, point_light, mut cubemap_frusta) in &mut views {
@ -1467,7 +1463,7 @@ pub fn update_point_light_frusta(
// ignore scale because we don't want to effectively scale light radius and range
// by applying those as a view transform to shadow map rendering of objects
// and ignore rotation because we want the shadow map projections to align with the axes
let view_translation = GlobalTransform::from_translation(transform.translation);
let view_translation = Transform::from_translation(transform.translation());
let view_backward = transform.back();
for (view_rotation, frustum) in view_rotations.iter().zip(cubemap_frusta.iter_mut()) {
@ -1476,7 +1472,7 @@ pub fn update_point_light_frusta(
*frustum = Frustum::from_view_projection(
&view_projection,
&transform.translation,
&transform.translation(),
&view_backward,
point_light.range,
);
@ -1503,7 +1499,6 @@ pub fn update_spot_light_frusta(
// ignore scale because we don't want to effectively scale light radius and range
// by applying those as a view transform to shadow map rendering of objects
let view_translation = GlobalTransform::from_translation(transform.translation);
let view_backward = transform.back();
let spot_view = spot_light_view_matrix(transform);
@ -1512,7 +1507,7 @@ pub fn update_spot_light_frusta(
*frustum = Frustum::from_view_projection(
&view_projection,
&view_translation.translation,
&transform.translation(),
&view_backward,
spot_light.range,
);
@ -1623,7 +1618,7 @@ pub fn check_light_mesh_visibility(
let view_mask = maybe_view_mask.copied().unwrap_or_default();
let light_sphere = Sphere {
center: Vec3A::from(transform.translation),
center: Vec3A::from(transform.translation()),
radius: point_light.range,
};
@ -1686,7 +1681,7 @@ pub fn check_light_mesh_visibility(
let view_mask = maybe_view_mask.copied().unwrap_or_default();
let light_sphere = Sphere {
center: Vec3A::from(transform.translation),
center: Vec3A::from(transform.translation()),
radius: point_light.range,
};

View File

@ -31,7 +31,7 @@ use bevy_render::{
},
Extract,
};
use bevy_transform::components::GlobalTransform;
use bevy_transform::{components::GlobalTransform, prelude::Transform};
use bevy_utils::FloatOrd;
use bevy_utils::{
tracing::{error, warn},
@ -728,7 +728,7 @@ pub fn calculate_cluster_factors(
// could move this onto transform but it's pretty niche
pub(crate) fn spot_light_view_matrix(transform: &GlobalTransform) -> Mat4 {
// the matrix z_local (opposite of transform.forward())
let fwd_dir = transform.local_z().extend(0.0);
let fwd_dir = transform.back().extend(0.0);
let sign = 1f32.copysign(fwd_dir.z);
let a = -1.0 / (fwd_dir.z + sign);
@ -745,7 +745,7 @@ pub(crate) fn spot_light_view_matrix(transform: &GlobalTransform) -> Mat4 {
right_dir,
up_dir,
fwd_dir,
transform.translation.extend(1.0),
transform.translation().extend(1.0),
)
}
@ -779,7 +779,7 @@ pub fn prepare_lights(
Mat4::perspective_infinite_reverse_rh(std::f32::consts::FRAC_PI_2, 1.0, POINT_LIGHT_NEAR_Z);
let cube_face_rotations = CUBE_MAP_FACES
.iter()
.map(|CubeMapFace { target, up }| GlobalTransform::identity().looking_at(*target, *up))
.map(|CubeMapFace { target, up }| Transform::identity().looking_at(*target, *up))
.collect::<Vec<_>>();
global_light_meta.entity_to_index.clear();
@ -893,7 +893,7 @@ pub fn prepare_lights(
* light.intensity)
.xyz()
.extend(1.0 / (light.range * light.range)),
position_radius: light.transform.translation.extend(light.radius),
position_radius: light.transform.translation().extend(light.radius),
flags: flags.bits,
shadow_depth_bias: light.shadow_depth_bias,
shadow_normal_bias: light.shadow_normal_bias,
@ -989,7 +989,7 @@ pub fn prepare_lights(
// ignore scale because we don't want to effectively scale light radius and range
// by applying those as a view transform to shadow map rendering of objects
// and ignore rotation because we want the shadow map projections to align with the axes
let view_translation = GlobalTransform::from_translation(light.transform.translation);
let view_translation = GlobalTransform::from_translation(light.transform.translation());
for (face_index, view_rotation) in cube_face_rotations.iter().enumerate() {
let depth_texture_view =
@ -1042,7 +1042,7 @@ pub fn prepare_lights(
.enumerate()
{
let spot_view_matrix = spot_light_view_matrix(&light.transform);
let spot_view_transform = GlobalTransform::from_matrix(spot_view_matrix);
let spot_view_transform = spot_view_matrix.into();
let angle = light.spot_light_angles.expect("lights should be sorted so that \
[point_light_shadow_maps_count..point_light_shadow_maps_count + spot_light_shadow_maps_count] are spot lights").1;
@ -1152,7 +1152,7 @@ pub fn prepare_lights(
ExtractedView {
width: directional_light_shadow_map.size as u32,
height: directional_light_shadow_map.size as u32,
transform: GlobalTransform::from_matrix(view.inverse()),
transform: GlobalTransform::from(view.inverse()),
projection,
},
RenderPhase::<Shadow>::default(),

View File

@ -189,7 +189,7 @@ impl SkinnedMeshJoints {
let start = buffer.len();
for (inverse_bindpose, joint) in bindposes.zip(skin_joints).take(MAX_JOINTS) {
if let Ok(joint) = joints.get(*joint) {
buffer.push(joint.compute_affine() * *inverse_bindpose);
buffer.push(joint.affine() * *inverse_bindpose);
} else {
buffer.truncate(start);
return None;

View File

@ -162,7 +162,7 @@ fn prepare_view_uniforms(
inverse_view,
projection,
inverse_projection,
world_position: camera.transform.translation,
world_position: camera.transform.translation(),
width: camera.width as f32,
height: camera.height as f32,
}),

View File

@ -1,6 +1,5 @@
mod render_layers;
use bevy_math::Vec3A;
pub use render_layers::*;
use bevy_app::{CoreStage, Plugin};
@ -205,7 +204,7 @@ pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(
projection.get_projection_matrix() * transform.compute_matrix().inverse();
*frustum = Frustum::from_view_projection(
&view_projection,
&transform.translation,
&transform.translation(),
&transform.back(),
projection.far(),
);
@ -324,7 +323,7 @@ pub fn check_visibility(
let model = transform.compute_matrix();
let model_sphere = Sphere {
center: model.transform_point3a(model_aabb.center),
radius: (Vec3A::from(transform.scale) * model_aabb.half_extents).length(),
radius: transform.radius_vec3a(model_aabb.half_extents),
};
// Do quick sphere-based frustum culling
if !frustum.intersects_sphere(&model_sphere, false) {

View File

@ -410,9 +410,9 @@ pub fn queue_sprites(
extracted_sprites.sort_unstable_by(|a, b| {
match a
.transform
.translation
.translation()
.z
.partial_cmp(&b.transform.translation.z)
.partial_cmp(&b.transform.translation().z)
{
Some(Ordering::Equal) | None => a.image_handle_id.cmp(&b.image_handle_id),
Some(other) => other,
@ -517,7 +517,7 @@ pub fn queue_sprites(
});
// These items will be sorted by depth with other phase items
let sort_key = FloatOrd(extracted_sprite.transform.translation.z);
let sort_key = FloatOrd(extracted_sprite.transform.translation().z);
// Store the vertex data and add the item to the render phase
if current_batch.colored {

View File

@ -82,7 +82,8 @@ pub fn extract_text2d_sprite(
) {
let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
for (entity, computed_visibility, text, transform, calculated_size) in text2d_query.iter() {
for (entity, computed_visibility, text, text_transform, calculated_size) in text2d_query.iter()
{
if !computed_visibility.is_visible() {
continue;
}
@ -100,9 +101,6 @@ pub fn extract_text2d_sprite(
HorizontalAlign::Right => Vec3::new(-width, 0.0, 0.0),
};
let mut text_transform = *transform;
text_transform.scale /= scale_factor;
for text_glyph in text_glyphs {
let color = text.sections[text_glyph.section_index]
.style
@ -118,8 +116,10 @@ pub fn extract_text2d_sprite(
let glyph_transform = Transform::from_translation(
alignment_offset * scale_factor + text_glyph.position.extend(0.),
);
let transform = text_transform.mul_transform(glyph_transform);
// NOTE: Should match `bevy_ui::render::extract_text_uinodes`
let transform = *text_transform
* GlobalTransform::from_scale(Vec3::splat(scale_factor.recip()))
* glyph_transform;
extracted_sprites.sprites.push(ExtractedSprite {
entity,

View File

@ -1,8 +1,9 @@
use std::ops::Mul;
use super::Transform;
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::{Affine3A, Mat3, Mat4, Quat, Vec3};
use bevy_reflect::prelude::*;
use std::ops::Mul;
use bevy_math::{Affine3A, Mat4, Quat, Vec3, Vec3A};
use bevy_reflect::Reflect;
/// Describe the position of an entity relative to the reference frame.
///
@ -25,223 +26,129 @@ use std::ops::Mul;
/// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag
/// before the [`GlobalTransform`] is updated.
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
#[reflect(Component, Default, PartialEq)]
pub struct GlobalTransform {
/// The position of the global transform
pub translation: Vec3,
/// The rotation of the global transform
pub rotation: Quat,
/// The scale of the global transform
pub scale: Vec3,
#[reflect(Component, PartialEq)]
pub struct GlobalTransform(Affine3A);
macro_rules! impl_local_axis {
($pos_name: ident, $neg_name: ident, $axis: ident) => {
#[doc=std::concat!("Return the local ", std::stringify!($pos_name), " vector (", std::stringify!($axis) ,").")]
#[inline]
pub fn $pos_name(&self) -> Vec3 {
(self.0.matrix3 * Vec3::$axis).normalize()
}
#[doc=std::concat!("Return the local ", std::stringify!($neg_name), " vector (-", std::stringify!($axis) ,").")]
#[inline]
pub fn $neg_name(&self) -> Vec3 {
-self.$pos_name()
}
};
}
impl GlobalTransform {
#[doc(hidden)]
#[inline]
pub const fn from_xyz(x: f32, y: f32, z: f32) -> Self {
pub fn from_xyz(x: f32, y: f32, z: f32) -> Self {
Self::from_translation(Vec3::new(x, y, z))
}
/// Creates a new identity [`GlobalTransform`], with no translation, rotation, and a scale of 1
/// on all axes.
#[doc(hidden)]
#[inline]
pub const fn identity() -> Self {
GlobalTransform {
translation: Vec3::ZERO,
rotation: Quat::IDENTITY,
scale: Vec3::ONE,
}
pub fn from_translation(translation: Vec3) -> Self {
GlobalTransform(Affine3A::from_translation(translation))
}
#[doc(hidden)]
#[inline]
pub fn from_matrix(matrix: Mat4) -> Self {
let (scale, rotation, translation) = matrix.to_scale_rotation_translation();
GlobalTransform {
translation,
rotation,
scale,
}
pub fn from_rotation(rotation: Quat) -> Self {
GlobalTransform(Affine3A::from_rotation_translation(rotation, Vec3::ZERO))
}
#[doc(hidden)]
#[inline]
pub const fn from_translation(translation: Vec3) -> Self {
GlobalTransform {
translation,
..Self::identity()
}
pub fn from_scale(scale: Vec3) -> Self {
GlobalTransform(Affine3A::from_scale(scale))
}
#[doc(hidden)]
#[inline]
pub const fn from_rotation(rotation: Quat) -> Self {
GlobalTransform {
rotation,
..Self::identity()
}
}
#[doc(hidden)]
#[inline]
pub const fn from_scale(scale: Vec3) -> Self {
GlobalTransform {
scale,
..Self::identity()
}
}
#[doc(hidden)]
#[inline]
#[must_use]
pub fn looking_at(mut self, target: Vec3, up: Vec3) -> Self {
self.look_at(target, up);
self
}
#[doc(hidden)]
#[inline]
#[must_use]
pub const fn with_translation(mut self, translation: Vec3) -> Self {
self.translation = translation;
self
}
#[doc(hidden)]
#[inline]
#[must_use]
pub const fn with_rotation(mut self, rotation: Quat) -> Self {
self.rotation = rotation;
self
}
#[doc(hidden)]
#[inline]
#[must_use]
pub const fn with_scale(mut self, scale: Vec3) -> Self {
self.scale = scale;
self
}
/// Returns the 3d affine transformation matrix from this transforms translation,
/// rotation, and scale.
/// Returns the 3d affine transformation matrix as a [`Mat4`].
#[inline]
pub fn compute_matrix(&self) -> Mat4 {
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
Mat4::from(self.0)
}
/// Returns the 3d affine transformation from this transforms translation,
/// rotation, and scale.
/// Returns the 3d affine transformation matrix as an [`Affine3A`].
#[inline]
pub fn compute_affine(&self) -> Affine3A {
Affine3A::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
pub fn affine(&self) -> Affine3A {
self.0
}
/// Get the unit vector in the local `X` direction
/// Returns the transformation as a [`Transform`].
///
/// The transform is expected to be non-degenerate and without shearing, or the output
/// will be invalid.
#[inline]
pub fn local_x(&self) -> Vec3 {
self.rotation * Vec3::X
}
/// Equivalent to [`-local_x()`][GlobalTransform::local_x]
#[inline]
pub fn left(&self) -> Vec3 {
-self.local_x()
}
/// Equivalent to [`local_x()`][GlobalTransform::local_x]
#[inline]
pub fn right(&self) -> Vec3 {
self.local_x()
}
/// Get the unit vector in the local `Y` direction
#[inline]
pub fn local_y(&self) -> Vec3 {
self.rotation * Vec3::Y
}
/// Equivalent to [`local_y()`][GlobalTransform::local_y]
#[inline]
pub fn up(&self) -> Vec3 {
self.local_y()
}
/// Equivalent to [`-local_y()`][GlobalTransform::local_y]
#[inline]
pub fn down(&self) -> Vec3 {
-self.local_y()
}
/// Get the unit vector in the local `Z` direction
#[inline]
pub fn local_z(&self) -> Vec3 {
self.rotation * Vec3::Z
}
/// Equivalent to [`-local_z()`][GlobalTransform::local_z]
#[inline]
pub fn forward(&self) -> Vec3 {
-self.local_z()
}
/// Equivalent to [`local_z()`][GlobalTransform::local_z]
#[inline]
pub fn back(&self) -> Vec3 {
self.local_z()
}
#[doc(hidden)]
#[inline]
pub fn rotate(&mut self, rotation: Quat) {
self.rotation = rotation * self.rotation;
}
#[doc(hidden)]
#[inline]
pub fn rotate_around(&mut self, point: Vec3, rotation: Quat) {
self.translation = point + rotation * (self.translation - point);
self.rotate(rotation);
}
/// Multiplies `self` with `transform` component by component, returning the
/// resulting [`GlobalTransform`]
#[inline]
#[must_use]
pub fn mul_transform(&self, transform: Transform) -> Self {
let translation = self.mul_vec3(transform.translation);
let rotation = self.rotation * transform.rotation;
let scale = self.scale * transform.scale;
Self {
pub fn compute_transform(&self) -> Transform {
let (scale, rotation, translation) = self.0.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
/// will be invalid.
#[inline]
pub fn to_scale_rotation_translation(&self) -> (Vec3, Quat, Vec3) {
self.0.to_scale_rotation_translation()
}
/// Creates a new identity [`GlobalTransform`], that maps all points in space to themselves.
#[inline]
pub const fn identity() -> Self {
Self(Affine3A::IDENTITY)
}
impl_local_axis!(right, left, X);
impl_local_axis!(up, down, Y);
impl_local_axis!(back, forward, Z);
/// Get the translation as a [`Vec3`].
#[inline]
pub fn translation(&self) -> Vec3 {
self.0.translation.into()
}
/// Mutably access the internal translation.
#[inline]
pub fn translation_mut(&mut self) -> &mut Vec3A {
&mut self.0.translation
}
/// Get the translation as a [`Vec3A`].
#[inline]
pub fn translation_vec3a(&self) -> Vec3A {
self.0.translation
}
/// Get an upper bound of the radius from the given `extents`.
#[inline]
pub fn radius_vec3a(&self, extents: Vec3A) -> f32 {
(self.0.matrix3 * extents).length()
}
/// Returns a [`Vec3`] of this [`Transform`] applied to `value`.
#[inline]
pub fn mul_vec3(&self, mut value: Vec3) -> Vec3 {
value = self.scale * value;
value = self.rotation * value;
value += self.translation;
value
pub fn mul_vec3(&self, v: Vec3) -> Vec3 {
self.0.transform_point3(v)
}
#[doc(hidden)]
#[inline]
pub fn apply_non_uniform_scale(&mut self, scale: Vec3) {
self.scale *= scale;
}
#[doc(hidden)]
#[inline]
pub fn look_at(&mut self, target: Vec3, up: Vec3) {
let forward = Vec3::normalize(self.translation - target);
let right = up.cross(forward).normalize();
let up = forward.cross(right);
self.rotation = Quat::from_mat3(&Mat3::from_cols(right, up, forward));
/// Multiplies `self` with `transform` component by component, returning the
/// resulting [`GlobalTransform`]
pub fn mul_transform(&self, transform: Transform) -> Self {
Self(self.0 * transform.compute_affine())
}
}
@ -253,11 +160,19 @@ impl Default for GlobalTransform {
impl From<Transform> for GlobalTransform {
fn from(transform: Transform) -> Self {
Self {
translation: transform.translation,
rotation: transform.rotation,
scale: transform.scale,
}
Self(transform.compute_affine())
}
}
impl From<Affine3A> for GlobalTransform {
fn from(affine: Affine3A) -> Self {
Self(affine)
}
}
impl From<Mat4> for GlobalTransform {
fn from(matrix: Mat4) -> Self {
Self(Affine3A::from_mat4(matrix))
}
}
@ -266,7 +181,7 @@ impl Mul<GlobalTransform> for GlobalTransform {
#[inline]
fn mul(self, global_transform: GlobalTransform) -> Self::Output {
self.mul_transform(global_transform.into())
GlobalTransform(self.0 * global_transform.0)
}
}

View File

@ -1,6 +1,6 @@
use super::GlobalTransform;
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::{Mat3, Mat4, Quat, Vec3};
use bevy_math::{Affine3A, Mat3, Mat4, Quat, Vec3};
use bevy_reflect::prelude::*;
use bevy_reflect::Reflect;
use std::ops::Mul;
@ -141,6 +141,13 @@ impl Transform {
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
}
/// Returns the 3d affine transformation matrix from this transforms translation,
/// rotation, and scale.
#[inline]
pub fn compute_affine(&self) -> Affine3A {
Affine3A::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
}
/// Get the unit vector in the local `X` direction.
#[inline]
pub fn local_x(&self) -> Vec3 {
@ -332,13 +339,11 @@ impl Default for Transform {
}
}
/// The transform is expected to be non-degenerate and without shearing, or the output
/// will be invalid.
impl From<GlobalTransform> for Transform {
fn from(transform: GlobalTransform) -> Self {
Self {
translation: transform.translation,
rotation: transform.rotation,
scale: transform.scale,
}
transform.compute_transform()
}
}

View File

@ -320,13 +320,7 @@ mod test {
let mut state = app.world.query::<&GlobalTransform>();
for global in state.iter(&app.world) {
assert_eq!(
global,
&GlobalTransform {
translation,
..Default::default()
},
);
assert_eq!(global, &GlobalTransform::from_translation(translation));
}
}

View File

@ -112,7 +112,7 @@ pub fn ui_focus_system(
.iter_mut()
.filter_map(
|(entity, node, global_transform, interaction, focus_policy, clip)| {
let position = global_transform.translation;
let position = global_transform.translation();
let ui_position = position.truncate();
let extents = node.size / 2.0;
let mut min = ui_position - extents;

View File

@ -283,7 +283,7 @@ pub fn extract_text_uinodes(
>,
) {
let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
for (entity, uinode, transform, text, visibility, clip) in uinode_query.iter() {
for (entity, uinode, global_transform, text, visibility, clip) in uinode_query.iter() {
if !visibility.is_visible() {
continue;
}
@ -305,15 +305,15 @@ pub fn extract_text_uinodes(
let rect = atlas.textures[index];
let atlas_size = Some(atlas.size);
let transform =
Mat4::from_rotation_translation(transform.rotation, transform.translation)
* Mat4::from_scale(transform.scale / scale_factor)
* Mat4::from_translation(
alignment_offset * scale_factor + text_glyph.position.extend(0.),
);
// NOTE: Should match `bevy_text::text2d::extract_text2d_sprite`
let extracted_transform = global_transform.compute_matrix()
* Mat4::from_scale(Vec3::splat(scale_factor.recip()))
* Mat4::from_translation(
alignment_offset * scale_factor + text_glyph.position.extend(0.),
);
extracted_uinodes.uinodes.push(ExtractedUiNode {
transform,
transform: extracted_transform,
color,
rect,
image: texture,

View File

@ -108,7 +108,7 @@ fn update_clipping(
let children_clip = match style.overflow {
Overflow::Visible => clip,
Overflow::Hidden => {
let node_center = global_transform.translation.truncate();
let node_center = global_transform.translation().truncate();
let node_rect = Rect {
min: node_center - node.size / 2.,
max: node_center + node.size / 2.,

View File

@ -152,7 +152,7 @@ fn interact_bodies(mut query: Query<(&Mass, &GlobalTransform, &mut Acceleration)
while let Some([(Mass(m1), transform1, mut acc1), (Mass(m2), transform2, mut acc2)]) =
iter.fetch_next()
{
let delta = transform2.translation - transform1.translation;
let delta = transform2.translation() - transform1.translation();
let distance_sq: f32 = delta.length_squared();
let f = GRAVITY_CONSTANT / distance_sq;

View File

@ -226,7 +226,7 @@ fn setup_scene_after_load(
// a Sphere, and then back to an Aabb to find the conservative min and max points.
let sphere = Sphere {
center: Vec3A::from(transform.mul_vec3(Vec3::from(aabb.center))),
radius: (Vec3A::from(transform.scale) * aabb.half_extents).length(),
radius: transform.radius_vec3a(aabb.half_extents),
};
let aabb = Aabb::from(sphere);
min = min.min(aabb.min());

View File

@ -1,7 +1,7 @@
//! Illustrates the difference between direction of a translation in respect to local object or
//! global object Transform.
use bevy::prelude::*;
use bevy::{math::Vec3A, prelude::*};
// Define a marker for entities that should be changed via their global transform.
#[derive(Component)]
@ -129,7 +129,7 @@ fn move_cubes_according_to_global_transform(
timer: Res<Time>,
) {
for mut global_transform in &mut cubes {
global_transform.translation += direction.0 * timer.delta_seconds();
*global_transform.translation_mut() += Vec3A::from(direction.0) * timer.delta_seconds();
}
}