Skip to content

Commit

Permalink
Move TextureAtlas into UiImage and remove impl Component for TextureA…
Browse files Browse the repository at this point in the history
…tlas (#16072)

# Objective

Fixes #16064

## Solution

- Add TextureAtlas to `UiImage::texture_atlas`
- Add `TextureAtlas::from_atlas_image` for parity with `Sprite`
- Rename `UiImage::texture` to `UiImage::image` for parity with `Sprite`
- Port relevant implementations and uses
- Remove `derive(Component)` for `TextureAtlas`

---

## Migration Guide

Before:
```rust
commands.spawn((
  UiImage::new(image),
  TextureAtlas { index, layout },
));
```

After:
```rust
commands.spawn(UiImage::from_atlas_image(image, TextureAtlas { index, layout }));
```

Before:
```rust
commands.spawn(UiImage {
    texture: some_image,
    ..default()
})
```

After:
```rust
commands.spawn(UiImage {
    image: some_image,
    ..default()
})
```
  • Loading branch information
cart authored Oct 23, 2024
1 parent 2cdad48 commit 9274bfe
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 70 deletions.
7 changes: 3 additions & 4 deletions crates/bevy_sprite/src/texture_atlas.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use bevy_asset::{Asset, AssetId, Assets, Handle};
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::{URect, UVec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
#[cfg(feature = "serialize")]
Expand Down Expand Up @@ -152,7 +151,7 @@ impl TextureAtlasLayout {
}
}

/// Component used to draw a specific section of a texture.
/// An index into a [`TextureAtlasLayout`], which corresponds to a specific section of a texture.
///
/// It stores a handle to [`TextureAtlasLayout`] and the index of the current section of the atlas.
/// The texture atlas contains various *sections* of a given texture, allowing users to have a single
Expand All @@ -164,8 +163,8 @@ impl TextureAtlasLayout {
/// - [`animated sprite sheet example`](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_sheet.rs)
/// - [`sprite animation event example`](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_animation.rs)
/// - [`texture atlas example`](https://github.com/bevyengine/bevy/blob/latest/examples/2d/texture_atlas.rs)
#[derive(Component, Default, Debug, Clone, Reflect)]
#[reflect(Component, Default, Debug)]
#[derive(Default, Debug, Clone, Reflect)]
#[reflect(Default, Debug)]
pub struct TextureAtlas {
/// Texture atlas layout handle
pub layout: Handle<TextureAtlasLayout>,
Expand Down
13 changes: 7 additions & 6 deletions crates/bevy_ui/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use bevy_render::{
ExtractSchedule, Render,
};
use bevy_sprite::TextureAtlasLayout;
use bevy_sprite::{BorderRect, ImageScaleMode, SpriteAssetEvents, TextureAtlas};
use bevy_sprite::{BorderRect, ImageScaleMode, SpriteAssetEvents};

use crate::{Display, Node};
use bevy_text::{ComputedTextBlock, PositionedGlyph, TextColor, TextLayoutInfo};
Expand Down Expand Up @@ -317,14 +317,13 @@ pub fn extract_uinode_images(
Option<&CalculatedClip>,
Option<&TargetCamera>,
&UiImage,
Option<&TextureAtlas>,
),
Without<ImageScaleMode>,
>,
>,
mapping: Extract<Query<RenderEntity>>,
) {
for (entity, uinode, transform, view_visibility, clip, camera, image, atlas) in &uinode_query {
for (entity, uinode, transform, view_visibility, clip, camera, image) in &uinode_query {
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
else {
continue;
Expand All @@ -337,12 +336,14 @@ pub fn extract_uinode_images(
// Skip invisible images
if !view_visibility.get()
|| image.color.is_fully_transparent()
|| image.texture.id() == TRANSPARENT_IMAGE_HANDLE.id()
|| image.image.id() == TRANSPARENT_IMAGE_HANDLE.id()
{
continue;
}

let atlas_rect = atlas
let atlas_rect = image
.texture_atlas
.as_ref()
.and_then(|s| s.texture_rect(&texture_atlases))
.map(|r| r.as_rect());

Expand Down Expand Up @@ -376,7 +377,7 @@ pub fn extract_uinode_images(
color: image.color.into(),
rect,
clip: clip.map(|clip| clip.clip),
image: image.texture.id(),
image: image.image.id(),
camera_entity: render_camera_entity,
item: ExtractedUiItem::Node {
atlas_scaling,
Expand Down
25 changes: 8 additions & 17 deletions crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ use bevy_render::{
Extract, ExtractSchedule, Render, RenderSet,
};
use bevy_sprite::{
ImageScaleMode, SliceScaleMode, SpriteAssetEvents, TextureAtlas, TextureAtlasLayout,
TextureSlicer,
ImageScaleMode, SliceScaleMode, SpriteAssetEvents, TextureAtlasLayout, TextureSlicer,
};
use bevy_transform::prelude::GlobalTransform;
use bevy_utils::HashMap;
Expand Down Expand Up @@ -258,22 +257,12 @@ pub fn extract_ui_texture_slices(
Option<&TargetCamera>,
&UiImage,
&ImageScaleMode,
Option<&TextureAtlas>,
)>,
>,
mapping: Extract<Query<RenderEntity>>,
) {
for (
entity,
uinode,
transform,
view_visibility,
clip,
camera,
image,
image_scale_mode,
atlas,
) in &slicers_query
for (entity, uinode, transform, view_visibility, clip, camera, image, image_scale_mode) in
&slicers_query
{
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
else {
Expand All @@ -287,12 +276,14 @@ pub fn extract_ui_texture_slices(
// Skip invisible images
if !view_visibility.get()
|| image.color.is_fully_transparent()
|| image.texture.id() == TRANSPARENT_IMAGE_HANDLE.id()
|| image.image.id() == TRANSPARENT_IMAGE_HANDLE.id()
{
continue;
}

let atlas_rect = atlas
let atlas_rect = image
.texture_atlas
.as_ref()
.and_then(|s| s.texture_rect(&texture_atlases))
.map(|r| r.as_rect());

Expand All @@ -318,7 +309,7 @@ pub fn extract_ui_texture_slices(
max: uinode.calculated_size,
},
clip: clip.map(|clip| clip.clip),
image: image.texture.id(),
image: image.image.id(),
camera_entity,
image_scale_mode: image_scale_mode.clone(),
atlas_rect,
Expand Down
27 changes: 20 additions & 7 deletions crates/bevy_ui/src/ui_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use bevy_render::{
texture::{Image, TRANSPARENT_IMAGE_HANDLE},
view::Visibility,
};
use bevy_sprite::BorderRect;
use bevy_sprite::{BorderRect, TextureAtlas};
use bevy_transform::components::Transform;
use bevy_utils::warn_once;
use bevy_window::{PrimaryWindow, WindowRef};
Expand Down Expand Up @@ -2053,15 +2053,17 @@ pub struct UiImage {
/// Handle to the texture.
///
/// This defaults to a [`TRANSPARENT_IMAGE_HANDLE`], which points to a fully transparent 1x1 texture.
pub texture: Handle<Image>,
pub image: Handle<Image>,
/// The (optional) texture atlas used to render the image
pub texture_atlas: Option<TextureAtlas>,
/// Whether the image should be flipped along its x-axis
pub flip_x: bool,
/// Whether the image should be flipped along its y-axis
pub flip_y: bool,
/// An optional rectangle representing the region of the image to render, instead of rendering
/// the full image. This is an easy one-off alternative to using a [`TextureAtlas`](bevy_sprite::TextureAtlas).
/// the full image. This is an easy one-off alternative to using a [`TextureAtlas`].
///
/// When used with a [`TextureAtlas`](bevy_sprite::TextureAtlas), the rect
/// When used with a [`TextureAtlas`], the rect
/// is offset by the atlas's minimal (top-left) corner position.
pub rect: Option<Rect>,
}
Expand All @@ -2079,8 +2081,9 @@ impl Default for UiImage {
// This should be white because the tint is multiplied with the image,
// so if you set an actual image with default tint you'd want its original colors
color: Color::WHITE,
texture_atlas: None,
// This texture needs to be transparent by default, to avoid covering the background color
texture: TRANSPARENT_IMAGE_HANDLE,
image: TRANSPARENT_IMAGE_HANDLE,
flip_x: false,
flip_y: false,
rect: None,
Expand All @@ -2092,7 +2095,7 @@ impl UiImage {
/// Create a new [`UiImage`] with the given texture.
pub fn new(texture: Handle<Image>) -> Self {
Self {
texture,
image: texture,
color: Color::WHITE,
..Default::default()
}
Expand All @@ -2103,14 +2106,24 @@ impl UiImage {
/// This is primarily useful for debugging / mocking the extents of your image.
pub fn solid_color(color: Color) -> Self {
Self {
texture: Handle::default(),
image: Handle::default(),
color,
flip_x: false,
flip_y: false,
texture_atlas: None,
rect: None,
}
}

/// Create a [`UiImage`] from an image, with an associated texture atlas
pub fn from_atlas_image(image: Handle<Image>, atlas: TextureAtlas) -> Self {
Self {
image,
texture_atlas: Some(atlas),
..Default::default()
}
}

/// Set the color tint
#[must_use]
pub const fn with_color(mut self, color: Color) -> Self {
Expand Down
18 changes: 5 additions & 13 deletions crates/bevy_ui/src/widget/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bevy_ecs::prelude::*;
use bevy_math::{UVec2, Vec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::texture::Image;
use bevy_sprite::{TextureAtlas, TextureAtlasLayout};
use bevy_sprite::TextureAtlasLayout;
use bevy_window::{PrimaryWindow, Window};
use taffy::{MaybeMath, MaybeResolve};

Expand Down Expand Up @@ -97,26 +97,18 @@ pub fn update_image_content_size_system(
textures: Res<Assets<Image>>,

atlases: Res<Assets<TextureAtlasLayout>>,
mut query: Query<
(
&mut ContentSize,
&UiImage,
&mut UiImageSize,
Option<&TextureAtlas>,
),
UpdateImageFilter,
>,
mut query: Query<(&mut ContentSize, &UiImage, &mut UiImageSize), UpdateImageFilter>,
) {
let combined_scale_factor = windows
.get_single()
.map(|window| window.resolution.scale_factor())
.unwrap_or(1.)
* ui_scale.0;

for (mut content_size, image, mut image_size, atlas_image) in &mut query {
if let Some(size) = match atlas_image {
for (mut content_size, image, mut image_size) in &mut query {
if let Some(size) = match &image.texture_atlas {
Some(atlas) => atlas.texture_rect(&atlases).map(|t| t.size()),
None => textures.get(&image.texture).map(Image::size),
None => textures.get(&image.image).map(Image::size),
} {
// Update only if size or scale factor has changed to avoid needless layout calculations
if size != image_size.size
Expand Down
2 changes: 1 addition & 1 deletion examples/3d/auto_exposure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ fn setup(

commands.spawn((
UiImage {
texture: metering_mask,
image: metering_mask,
..default()
},
Node {
Expand Down
13 changes: 8 additions & 5 deletions examples/stress_tests/many_animated_sprites.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ fn setup(
commands.spawn((
Sprite {
image: texture_handle.clone(),
texture_atlas: Some(TextureAtlas::from(texture_atlas_handle.clone())),
custom_size: Some(tile_size),
..default()
},
Expand All @@ -92,7 +93,6 @@ fn setup(
rotation,
scale,
},
TextureAtlas::from(texture_atlas_handle.clone()),
AnimationTimer(timer),
));
}
Expand All @@ -112,13 +112,16 @@ struct AnimationTimer(Timer);
fn animate_sprite(
time: Res<Time>,
texture_atlases: Res<Assets<TextureAtlasLayout>>,
mut query: Query<(&mut AnimationTimer, &mut TextureAtlas)>,
mut query: Query<(&mut AnimationTimer, &mut Sprite)>,
) {
for (mut timer, mut sheet) in query.iter_mut() {
for (mut timer, mut sprite) in query.iter_mut() {
timer.tick(time.delta());
if timer.just_finished() {
let texture_atlas = texture_atlases.get(&sheet.layout).unwrap();
sheet.index = (sheet.index + 1) % texture_atlas.textures.len();
let Some(atlas) = &mut sprite.texture_atlas else {
continue;
};
let texture_atlas = texture_atlases.get(&atlas.layout).unwrap();
atlas.index = (atlas.index + 1) % texture_atlas.textures.len();
}
}
}
Expand Down
14 changes: 6 additions & 8 deletions examples/ui/ui_texture_atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,13 @@ fn setup(
})
.with_children(|parent| {
parent.spawn((
UiImage::new(texture_handle),
UiImage::from_atlas_image(texture_handle, TextureAtlas::from(texture_atlas_handle)),
Node {
width: Val::Px(256.),
height: Val::Px(256.),
..default()
},
BackgroundColor(ANTIQUE_WHITE.into()),
TextureAtlas::from(texture_atlas_handle),
Outline::new(Val::Px(8.0), Val::ZERO, CRIMSON.into()),
));
parent
Expand All @@ -65,13 +64,12 @@ fn setup(
});
}

fn increment_atlas_index(
mut atlas_images: Query<&mut TextureAtlas>,
keyboard: Res<ButtonInput<KeyCode>>,
) {
fn increment_atlas_index(mut ui_images: Query<&mut UiImage>, keyboard: Res<ButtonInput<KeyCode>>) {
if keyboard.just_pressed(KeyCode::Space) {
for mut atlas_image in &mut atlas_images {
atlas_image.index = (atlas_image.index + 1) % 6;
for mut ui_image in &mut ui_images {
if let Some(atlas) = &mut ui_image.texture_atlas {
atlas.index = (atlas.index + 1) % 6;
}
}
}
}
20 changes: 12 additions & 8 deletions examples/ui/ui_texture_atlas_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ fn main() {

fn button_system(
mut interaction_query: Query<
(&Interaction, &mut TextureAtlas, &Children, &mut UiImage),
(&Interaction, &Children, &mut UiImage),
(Changed<Interaction>, With<Button>),
>,
mut text_query: Query<&mut Text>,
) {
for (interaction, mut atlas, children, mut image) in &mut interaction_query {
for (interaction, children, mut image) in &mut interaction_query {
let mut text = text_query.get_mut(children[0]).unwrap();
match *interaction {
Interaction::Pressed => {
**text = "Press".to_string();
atlas.index = (atlas.index + 1) % 30;
if let Some(atlas) = &mut image.texture_atlas {
atlas.index = (atlas.index + 1) % 30;
}
image.color = GOLD.into();
}
Interaction::Hovered => {
Expand Down Expand Up @@ -79,7 +81,13 @@ fn setup(
parent
.spawn((
Button,
UiImage::new(texture_handle.clone()),
UiImage::from_atlas_image(
texture_handle.clone(),
TextureAtlas {
index: idx,
layout: atlas_layout_handle.clone(),
},
),
Node {
width: Val::Px(w),
height: Val::Px(h),
Expand All @@ -91,10 +99,6 @@ fn setup(
..default()
},
ImageScaleMode::Sliced(slicer.clone()),
TextureAtlas {
index: idx,
layout: atlas_layout_handle.clone(),
},
))
.with_children(|parent| {
parent.spawn((
Expand Down
2 changes: 1 addition & 1 deletion examples/ui/ui_texture_slice_flip_and_tile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
] {
parent.spawn((
UiImage {
texture: image.clone(),
image: image.clone(),
flip_x,
flip_y,
..default()
Expand Down

0 comments on commit 9274bfe

Please sign in to comment.