diff --git a/crates/bevy_sprite/src/texture_atlas.rs b/crates/bevy_sprite/src/texture_atlas.rs index 18ab621a20e02..a4d82b2ca1874 100644 --- a/crates/bevy_sprite/src/texture_atlas.rs +++ b/crates/bevy_sprite/src/texture_atlas.rs @@ -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")] @@ -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 @@ -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, diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 041ed36072987..0b936a38f9fa7 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -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 bevy_text::{ComputedTextBlock, PositionedGlyph, TextColor, TextLayoutInfo}; use bevy_transform::components::GlobalTransform; @@ -316,14 +316,13 @@ pub fn extract_uinode_images( Option<&CalculatedClip>, Option<&TargetCamera>, &UiImage, - Option<&TextureAtlas>, ), Without, >, >, mapping: Extract>, ) { - 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; @@ -336,12 +335,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()); @@ -375,7 +376,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, diff --git a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs index a08bedc417f36..a46dfbc6f81f6 100644 --- a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs @@ -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; @@ -258,22 +257,12 @@ pub fn extract_ui_texture_slices( Option<&TargetCamera>, &UiImage, &ImageScaleMode, - Option<&TextureAtlas>, )>, >, mapping: Extract>, ) { - 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 { @@ -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()); @@ -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, diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index c0d6e6b6017c0..88b950b7b479b 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -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}; @@ -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, + pub image: Handle, + /// The (optional) texture atlas used to render the image + pub texture_atlas: Option, /// 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, } @@ -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, @@ -2092,7 +2095,7 @@ impl UiImage { /// Create a new [`UiImage`] with the given texture. pub fn new(texture: Handle) -> Self { Self { - texture, + image: texture, color: Color::WHITE, ..Default::default() } @@ -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, 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 { diff --git a/crates/bevy_ui/src/widget/image.rs b/crates/bevy_ui/src/widget/image.rs index 91a024c08075e..60a0ac5292738 100644 --- a/crates/bevy_ui/src/widget/image.rs +++ b/crates/bevy_ui/src/widget/image.rs @@ -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}; @@ -97,15 +97,7 @@ pub fn update_image_content_size_system( textures: Res>, atlases: Res>, - 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() @@ -113,10 +105,10 @@ pub fn update_image_content_size_system( .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 diff --git a/examples/3d/auto_exposure.rs b/examples/3d/auto_exposure.rs index ecd09bbf31ed1..ee623982db189 100644 --- a/examples/3d/auto_exposure.rs +++ b/examples/3d/auto_exposure.rs @@ -116,7 +116,7 @@ fn setup( commands.spawn(( UiImage { - texture: metering_mask, + image: metering_mask, ..default() }, Node { diff --git a/examples/stress_tests/many_animated_sprites.rs b/examples/stress_tests/many_animated_sprites.rs index a89e86382ded3..c2282a293f566 100644 --- a/examples/stress_tests/many_animated_sprites.rs +++ b/examples/stress_tests/many_animated_sprites.rs @@ -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() }, @@ -92,7 +93,6 @@ fn setup( rotation, scale, }, - TextureAtlas::from(texture_atlas_handle.clone()), AnimationTimer(timer), )); } @@ -112,13 +112,16 @@ struct AnimationTimer(Timer); fn animate_sprite( time: Res