Skip to content

Commit 9f8bdee

Browse files
HackerFoocart
andcommitted
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 <[email protected]>
1 parent 8810a73 commit 9f8bdee

File tree

16 files changed

+160
-252
lines changed

16 files changed

+160
-252
lines changed

crates/bevy_pbr/src/light.rs

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::HashSet;
22

33
use bevy_ecs::prelude::*;
4-
use bevy_math::{Mat4, Quat, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles};
4+
use bevy_math::{Mat4, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles};
55
use bevy_reflect::prelude::*;
66
use bevy_render::{
77
camera::{Camera, CameraProjection, OrthographicProjection},
@@ -12,7 +12,7 @@ use bevy_render::{
1212
renderer::RenderDevice,
1313
view::{ComputedVisibility, RenderLayers, VisibleEntities},
1414
};
15-
use bevy_transform::components::GlobalTransform;
15+
use bevy_transform::{components::GlobalTransform, prelude::Transform};
1616
use bevy_utils::tracing::warn;
1717

1818
use crate::{
@@ -755,13 +755,21 @@ pub(crate) fn point_light_order(
755755
// data required for assigning lights to clusters
756756
pub(crate) struct PointLightAssignmentData {
757757
entity: Entity,
758-
translation: Vec3,
759-
rotation: Quat,
758+
transform: GlobalTransform,
760759
range: f32,
761760
shadows_enabled: bool,
762761
spot_light_angle: Option<f32>,
763762
}
764763

764+
impl PointLightAssignmentData {
765+
pub fn sphere(&self) -> Sphere {
766+
Sphere {
767+
center: self.transform.translation_vec3a(),
768+
radius: self.range,
769+
}
770+
}
771+
}
772+
765773
#[derive(Default)]
766774
pub struct GlobalVisiblePointLights {
767775
entities: HashSet<Entity>,
@@ -815,8 +823,7 @@ pub(crate) fn assign_lights_to_clusters(
815823
.map(
816824
|(entity, transform, point_light, _visibility)| PointLightAssignmentData {
817825
entity,
818-
translation: transform.translation,
819-
rotation: Quat::default(),
826+
transform: GlobalTransform::from_translation(transform.translation()),
820827
shadows_enabled: point_light.shadows_enabled,
821828
range: point_light.range,
822829
spot_light_angle: None,
@@ -830,8 +837,7 @@ pub(crate) fn assign_lights_to_clusters(
830837
.map(
831838
|(entity, transform, spot_light, _visibility)| PointLightAssignmentData {
832839
entity,
833-
translation: transform.translation,
834-
rotation: transform.rotation,
840+
transform: *transform,
835841
shadows_enabled: spot_light.shadows_enabled,
836842
range: spot_light.range,
837843
spot_light_angle: Some(spot_light.outer_angle),
@@ -872,11 +878,7 @@ pub(crate) fn assign_lights_to_clusters(
872878
if lights_in_view_count == MAX_UNIFORM_BUFFER_POINT_LIGHTS + 1 {
873879
false
874880
} else {
875-
let light_sphere = Sphere {
876-
center: Vec3A::from(light.translation),
877-
radius: light.range,
878-
};
879-
881+
let light_sphere = light.sphere();
880882
let light_in_view = frusta
881883
.iter()
882884
.any(|frustum| frustum.intersects_sphere(&light_sphere, true));
@@ -932,7 +934,8 @@ pub(crate) fn assign_lights_to_clusters(
932934
lights
933935
.iter()
934936
.map(|light| {
935-
-inverse_view_row_2.dot(light.translation.extend(1.0)) + light.range
937+
-inverse_view_row_2.dot(light.transform.translation().extend(1.0))
938+
+ light.range
936939
})
937940
.reduce(f32::max)
938941
.unwrap_or(0.0)
@@ -966,10 +969,7 @@ pub(crate) fn assign_lights_to_clusters(
966969
if config.dynamic_resizing() {
967970
let mut cluster_index_estimate = 0.0;
968971
for light in lights.iter() {
969-
let light_sphere = Sphere {
970-
center: Vec3A::from(light.translation),
971-
radius: light.range,
972-
};
972+
let light_sphere = light.sphere();
973973

974974
// Check if the light is within the view frustum
975975
if !frustum.intersects_sphere(&light_sphere, true) {
@@ -1124,10 +1124,7 @@ pub(crate) fn assign_lights_to_clusters(
11241124

11251125
let mut update_from_light_intersections = |visible_lights: &mut Vec<Entity>| {
11261126
for light in lights.iter() {
1127-
let light_sphere = Sphere {
1128-
center: Vec3A::from(light.translation),
1129-
radius: light.range,
1130-
};
1127+
let light_sphere = light.sphere();
11311128

11321129
// Check if the light is within the view frustum
11331130
if !frustum.intersects_sphere(&light_sphere, true) {
@@ -1177,8 +1174,7 @@ pub(crate) fn assign_lights_to_clusters(
11771174
let spot_light_dir_sin_cos = light.spot_light_angle.map(|angle| {
11781175
let (angle_sin, angle_cos) = angle.sin_cos();
11791176
(
1180-
(inverse_view_transform * (light.rotation * Vec3::Z).extend(0.0))
1181-
.truncate(),
1177+
(inverse_view_transform * light.transform.back().extend(0.0)).truncate(),
11821178
angle_sin,
11831179
angle_cos,
11841180
)
@@ -1432,7 +1428,7 @@ pub fn update_directional_light_frusta(
14321428
* transform.compute_matrix().inverse();
14331429
*frustum = Frustum::from_view_projection(
14341430
&view_projection,
1435-
&transform.translation,
1431+
&transform.translation(),
14361432
&transform.back(),
14371433
directional_light.shadow_projection.far(),
14381434
);
@@ -1451,7 +1447,7 @@ pub fn update_point_light_frusta(
14511447
Mat4::perspective_infinite_reverse_rh(std::f32::consts::FRAC_PI_2, 1.0, POINT_LIGHT_NEAR_Z);
14521448
let view_rotations = CUBE_MAP_FACES
14531449
.iter()
1454-
.map(|CubeMapFace { target, up }| GlobalTransform::identity().looking_at(*target, *up))
1450+
.map(|CubeMapFace { target, up }| Transform::identity().looking_at(*target, *up))
14551451
.collect::<Vec<_>>();
14561452

14571453
for (entity, transform, point_light, mut cubemap_frusta) in &mut views {
@@ -1467,7 +1463,7 @@ pub fn update_point_light_frusta(
14671463
// ignore scale because we don't want to effectively scale light radius and range
14681464
// by applying those as a view transform to shadow map rendering of objects
14691465
// and ignore rotation because we want the shadow map projections to align with the axes
1470-
let view_translation = GlobalTransform::from_translation(transform.translation);
1466+
let view_translation = Transform::from_translation(transform.translation());
14711467
let view_backward = transform.back();
14721468

14731469
for (view_rotation, frustum) in view_rotations.iter().zip(cubemap_frusta.iter_mut()) {
@@ -1476,7 +1472,7 @@ pub fn update_point_light_frusta(
14761472

14771473
*frustum = Frustum::from_view_projection(
14781474
&view_projection,
1479-
&transform.translation,
1475+
&transform.translation(),
14801476
&view_backward,
14811477
point_light.range,
14821478
);
@@ -1503,7 +1499,6 @@ pub fn update_spot_light_frusta(
15031499

15041500
// ignore scale because we don't want to effectively scale light radius and range
15051501
// by applying those as a view transform to shadow map rendering of objects
1506-
let view_translation = GlobalTransform::from_translation(transform.translation);
15071502
let view_backward = transform.back();
15081503

15091504
let spot_view = spot_light_view_matrix(transform);
@@ -1512,7 +1507,7 @@ pub fn update_spot_light_frusta(
15121507

15131508
*frustum = Frustum::from_view_projection(
15141509
&view_projection,
1515-
&view_translation.translation,
1510+
&transform.translation(),
15161511
&view_backward,
15171512
spot_light.range,
15181513
);
@@ -1623,7 +1618,7 @@ pub fn check_light_mesh_visibility(
16231618

16241619
let view_mask = maybe_view_mask.copied().unwrap_or_default();
16251620
let light_sphere = Sphere {
1626-
center: Vec3A::from(transform.translation),
1621+
center: Vec3A::from(transform.translation()),
16271622
radius: point_light.range,
16281623
};
16291624

@@ -1686,7 +1681,7 @@ pub fn check_light_mesh_visibility(
16861681

16871682
let view_mask = maybe_view_mask.copied().unwrap_or_default();
16881683
let light_sphere = Sphere {
1689-
center: Vec3A::from(transform.translation),
1684+
center: Vec3A::from(transform.translation()),
16901685
radius: point_light.range,
16911686
};
16921687

crates/bevy_pbr/src/render/light.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use bevy_render::{
3131
},
3232
Extract,
3333
};
34-
use bevy_transform::components::GlobalTransform;
34+
use bevy_transform::{components::GlobalTransform, prelude::Transform};
3535
use bevy_utils::FloatOrd;
3636
use bevy_utils::{
3737
tracing::{error, warn},
@@ -728,7 +728,7 @@ pub fn calculate_cluster_factors(
728728
// could move this onto transform but it's pretty niche
729729
pub(crate) fn spot_light_view_matrix(transform: &GlobalTransform) -> Mat4 {
730730
// the matrix z_local (opposite of transform.forward())
731-
let fwd_dir = transform.local_z().extend(0.0);
731+
let fwd_dir = transform.back().extend(0.0);
732732

733733
let sign = 1f32.copysign(fwd_dir.z);
734734
let a = -1.0 / (fwd_dir.z + sign);
@@ -745,7 +745,7 @@ pub(crate) fn spot_light_view_matrix(transform: &GlobalTransform) -> Mat4 {
745745
right_dir,
746746
up_dir,
747747
fwd_dir,
748-
transform.translation.extend(1.0),
748+
transform.translation().extend(1.0),
749749
)
750750
}
751751

@@ -779,7 +779,7 @@ pub fn prepare_lights(
779779
Mat4::perspective_infinite_reverse_rh(std::f32::consts::FRAC_PI_2, 1.0, POINT_LIGHT_NEAR_Z);
780780
let cube_face_rotations = CUBE_MAP_FACES
781781
.iter()
782-
.map(|CubeMapFace { target, up }| GlobalTransform::identity().looking_at(*target, *up))
782+
.map(|CubeMapFace { target, up }| Transform::identity().looking_at(*target, *up))
783783
.collect::<Vec<_>>();
784784

785785
global_light_meta.entity_to_index.clear();
@@ -893,7 +893,7 @@ pub fn prepare_lights(
893893
* light.intensity)
894894
.xyz()
895895
.extend(1.0 / (light.range * light.range)),
896-
position_radius: light.transform.translation.extend(light.radius),
896+
position_radius: light.transform.translation().extend(light.radius),
897897
flags: flags.bits,
898898
shadow_depth_bias: light.shadow_depth_bias,
899899
shadow_normal_bias: light.shadow_normal_bias,
@@ -989,7 +989,7 @@ pub fn prepare_lights(
989989
// ignore scale because we don't want to effectively scale light radius and range
990990
// by applying those as a view transform to shadow map rendering of objects
991991
// and ignore rotation because we want the shadow map projections to align with the axes
992-
let view_translation = GlobalTransform::from_translation(light.transform.translation);
992+
let view_translation = GlobalTransform::from_translation(light.transform.translation());
993993

994994
for (face_index, view_rotation) in cube_face_rotations.iter().enumerate() {
995995
let depth_texture_view =
@@ -1042,7 +1042,7 @@ pub fn prepare_lights(
10421042
.enumerate()
10431043
{
10441044
let spot_view_matrix = spot_light_view_matrix(&light.transform);
1045-
let spot_view_transform = GlobalTransform::from_matrix(spot_view_matrix);
1045+
let spot_view_transform = spot_view_matrix.into();
10461046

10471047
let angle = light.spot_light_angles.expect("lights should be sorted so that \
10481048
[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(
11521152
ExtractedView {
11531153
width: directional_light_shadow_map.size as u32,
11541154
height: directional_light_shadow_map.size as u32,
1155-
transform: GlobalTransform::from_matrix(view.inverse()),
1155+
transform: GlobalTransform::from(view.inverse()),
11561156
projection,
11571157
},
11581158
RenderPhase::<Shadow>::default(),

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ impl SkinnedMeshJoints {
189189
let start = buffer.len();
190190
for (inverse_bindpose, joint) in bindposes.zip(skin_joints).take(MAX_JOINTS) {
191191
if let Ok(joint) = joints.get(*joint) {
192-
buffer.push(joint.compute_affine() * *inverse_bindpose);
192+
buffer.push(joint.affine() * *inverse_bindpose);
193193
} else {
194194
buffer.truncate(start);
195195
return None;

crates/bevy_render/src/view/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ fn prepare_view_uniforms(
162162
inverse_view,
163163
projection,
164164
inverse_projection,
165-
world_position: camera.transform.translation,
165+
world_position: camera.transform.translation(),
166166
width: camera.width as f32,
167167
height: camera.height as f32,
168168
}),

crates/bevy_render/src/view/visibility/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
mod render_layers;
22

3-
use bevy_math::Vec3A;
43
pub use render_layers::*;
54

65
use bevy_app::{CoreStage, Plugin};
@@ -205,7 +204,7 @@ pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(
205204
projection.get_projection_matrix() * transform.compute_matrix().inverse();
206205
*frustum = Frustum::from_view_projection(
207206
&view_projection,
208-
&transform.translation,
207+
&transform.translation(),
209208
&transform.back(),
210209
projection.far(),
211210
);
@@ -324,7 +323,7 @@ pub fn check_visibility(
324323
let model = transform.compute_matrix();
325324
let model_sphere = Sphere {
326325
center: model.transform_point3a(model_aabb.center),
327-
radius: (Vec3A::from(transform.scale) * model_aabb.half_extents).length(),
326+
radius: transform.radius_vec3a(model_aabb.half_extents),
328327
};
329328
// Do quick sphere-based frustum culling
330329
if !frustum.intersects_sphere(&model_sphere, false) {

crates/bevy_sprite/src/render/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,9 +410,9 @@ pub fn queue_sprites(
410410
extracted_sprites.sort_unstable_by(|a, b| {
411411
match a
412412
.transform
413-
.translation
413+
.translation()
414414
.z
415-
.partial_cmp(&b.transform.translation.z)
415+
.partial_cmp(&b.transform.translation().z)
416416
{
417417
Some(Ordering::Equal) | None => a.image_handle_id.cmp(&b.image_handle_id),
418418
Some(other) => other,
@@ -517,7 +517,7 @@ pub fn queue_sprites(
517517
});
518518

519519
// These items will be sorted by depth with other phase items
520-
let sort_key = FloatOrd(extracted_sprite.transform.translation.z);
520+
let sort_key = FloatOrd(extracted_sprite.transform.translation().z);
521521

522522
// Store the vertex data and add the item to the render phase
523523
if current_batch.colored {

crates/bevy_text/src/text2d.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ pub fn extract_text2d_sprite(
8282
) {
8383
let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
8484

85-
for (entity, computed_visibility, text, transform, calculated_size) in text2d_query.iter() {
85+
for (entity, computed_visibility, text, text_transform, calculated_size) in text2d_query.iter()
86+
{
8687
if !computed_visibility.is_visible() {
8788
continue;
8889
}
@@ -100,9 +101,6 @@ pub fn extract_text2d_sprite(
100101
HorizontalAlign::Right => Vec3::new(-width, 0.0, 0.0),
101102
};
102103

103-
let mut text_transform = *transform;
104-
text_transform.scale /= scale_factor;
105-
106104
for text_glyph in text_glyphs {
107105
let color = text.sections[text_glyph.section_index]
108106
.style
@@ -118,8 +116,10 @@ pub fn extract_text2d_sprite(
118116
let glyph_transform = Transform::from_translation(
119117
alignment_offset * scale_factor + text_glyph.position.extend(0.),
120118
);
121-
122-
let transform = text_transform.mul_transform(glyph_transform);
119+
// NOTE: Should match `bevy_ui::render::extract_text_uinodes`
120+
let transform = *text_transform
121+
* GlobalTransform::from_scale(Vec3::splat(scale_factor.recip()))
122+
* glyph_transform;
123123

124124
extracted_sprites.sprites.push(ExtractedSprite {
125125
entity,

0 commit comments

Comments
 (0)