Text2d bounding box fix (#20148)

# Objective

`calculate_bounds_text2d` generates `Aabb`s with the wrong size and
position for `Text2d` entities with `TextBounds`, which breaks culling.

## Solution

Calculate the correct bounds.

Fixes #20145

## Testing

```
cargo run --example testbed_2d
```

#### main
<img width="661" height="469" alt="text2daabb"
src="https://github.com/user-attachments/assets/c10b64ed-6e0d-4c4e-a81d-6ae2248d752a"
/>

#### This PR

<img width="441" height="308" alt="fixed-text2d-aabbs"
src="https://github.com/user-attachments/assets/bc715bf0-b77f-4149-9c6d-a5a8c1982780"
/>
This commit is contained in:
ickshonpe 2025-07-15 18:14:49 +01:00 committed by GitHub
parent 57086d4416
commit f774d6b7ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 13 additions and 14 deletions

View File

@ -17,7 +17,7 @@ use bevy_ecs::{
system::{Commands, Local, Query, Res, ResMut}, system::{Commands, Local, Query, Res, ResMut},
}; };
use bevy_image::prelude::*; use bevy_image::prelude::*;
use bevy_math::Vec2; use bevy_math::{Vec2, Vec3};
use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_reflect::{prelude::ReflectDefault, Reflect};
use bevy_render::sync_world::TemporaryRenderEntity; use bevy_render::sync_world::TemporaryRenderEntity;
use bevy_render::view::{self, Visibility, VisibilityClass}; use bevy_render::view::{self, Visibility, VisibilityClass};
@ -186,6 +186,7 @@ pub fn extract_text2d_sprite(
let top_left = (Anchor::TOP_LEFT.0 - anchor.as_vec()) * size; let top_left = (Anchor::TOP_LEFT.0 - anchor.as_vec()) * size;
let transform = let transform =
*global_transform * GlobalTransform::from_translation(top_left.extend(0.)) * scaling; *global_transform * GlobalTransform::from_translation(top_left.extend(0.)) * scaling;
let mut color = LinearRgba::WHITE; let mut color = LinearRgba::WHITE;
let mut current_span = usize::MAX; let mut current_span = usize::MAX;
@ -366,22 +367,17 @@ pub fn calculate_bounds_text2d(
text_bounds.width.unwrap_or(layout_info.size.x), text_bounds.width.unwrap_or(layout_info.size.x),
text_bounds.height.unwrap_or(layout_info.size.y), text_bounds.height.unwrap_or(layout_info.size.y),
); );
let center = (-anchor.as_vec() * size + (size.y - layout_info.size.y) * Vec2::Y)
.extend(0.)
.into();
let half_extents = (0.5 * layout_info.size).extend(0.0).into(); let x1 = (Anchor::TOP_LEFT.0.x - anchor.as_vec().x) * size.x;
let x2 = (Anchor::TOP_LEFT.0.x - anchor.as_vec().x + 1.) * size.x;
let y1 = (Anchor::TOP_LEFT.0.y - anchor.as_vec().y - 1.) * size.y;
let y2 = (Anchor::TOP_LEFT.0.y - anchor.as_vec().y) * size.y;
let new_aabb = Aabb::from_min_max(Vec3::new(x1, y1, 0.), Vec3::new(x2, y2, 0.));
if let Some(mut aabb) = aabb { if let Some(mut aabb) = aabb {
*aabb = Aabb { *aabb = new_aabb;
center,
half_extents,
};
} else { } else {
commands.entity(entity).try_insert(Aabb { commands.entity(entity).try_insert(new_aabb);
center,
half_extents,
});
} }
} }
} }

View File

@ -165,7 +165,7 @@ mod text {
&mut commands, &mut commands,
300. * Vec3::X + y * Vec3::Y, 300. * Vec3::X + y * Vec3::Y,
justify, justify,
Some(TextBounds::new(150., 55.)), Some(TextBounds::new(150., 60.)),
); );
} }
@ -221,6 +221,9 @@ mod text {
Transform::from_translation(dest + Vec3::Z), Transform::from_translation(dest + Vec3::Z),
anchor, anchor,
DespawnOnExitState(super::Scene::Text), DespawnOnExitState(super::Scene::Text),
ShowAabbGizmo {
color: Some(palettes::tailwind::AMBER_400.into()),
},
children![ children![
( (
TextSpan::new(format!("{}, {}\n", anchor.x, anchor.y)), TextSpan::new(format!("{}, {}\n", anchor.x, anchor.y)),