diff --git a/crates/bevy_sprite/src/bundle.rs b/crates/bevy_sprite/src/bundle.rs index 82fdcd9db6292..0aaa5d2da3432 100644 --- a/crates/bevy_sprite/src/bundle.rs +++ b/crates/bevy_sprite/src/bundle.rs @@ -1,47 +1,15 @@ -use crate::{ - texture_atlas::{TextureAtlas, TextureAtlasSprite}, - Sprite, -}; -use bevy_asset::Handle; +use crate::{Sprite, SpriteImage}; use bevy_ecs::bundle::Bundle; -use bevy_render::{ - texture::{Image, DEFAULT_IMAGE_HANDLE}, - view::Visibility, -}; +use bevy_render::view::Visibility; use bevy_transform::components::{GlobalTransform, Transform}; -#[derive(Bundle, Clone)] +#[derive(Bundle, Clone, Default)] pub struct SpriteBundle { pub sprite: Sprite, pub transform: Transform, pub global_transform: GlobalTransform, - pub texture: Handle, - /// User indication of whether an entity is visible - pub visibility: Visibility, -} - -impl Default for SpriteBundle { - fn default() -> Self { - Self { - sprite: Default::default(), - transform: Default::default(), - global_transform: Default::default(), - texture: DEFAULT_IMAGE_HANDLE.typed(), - visibility: Default::default(), - } - } -} -/// A Bundle of components for drawing a single sprite from a sprite sheet (also referred -/// to as a `TextureAtlas`) -#[derive(Bundle, Clone, Default)] -pub struct SpriteSheetBundle { - /// The specific sprite from the texture atlas to be drawn - pub sprite: TextureAtlasSprite, - /// A handle to the texture atlas that holds the sprite images - pub texture_atlas: Handle, - /// Data pertaining to how the sprite is drawn on the screen - pub transform: Transform, - pub global_transform: GlobalTransform, + /// The sprite texture + pub texture: SpriteImage, /// User indication of whether an entity is visible pub visibility: Visibility, } diff --git a/crates/bevy_sprite/src/image.rs b/crates/bevy_sprite/src/image.rs new file mode 100644 index 0000000000000..62c9d7817cd9e --- /dev/null +++ b/crates/bevy_sprite/src/image.rs @@ -0,0 +1,31 @@ +use crate::TextureAtlas; +use bevy_asset::Handle; +use bevy_ecs::component::Component; +use bevy_reflect::Reflect; +use bevy_render::texture::{Image, DEFAULT_IMAGE_HANDLE}; + +/// The sprite texture +#[derive(Component, Clone, Debug, Reflect)] +pub enum SpriteImage { + /// Single texture + Image(Handle), + /// Texture atlas. + TextureAtlas { + /// Texture atlas handle + handle: Handle, + /// Texture atlas index + index: usize, + }, +} + +impl Default for SpriteImage { + fn default() -> Self { + Self::Image(DEFAULT_IMAGE_HANDLE.typed()) + } +} + +impl From> for SpriteImage { + fn from(handle: Handle) -> Self { + Self::Image(handle) + } +} diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index a7be1a4ab983b..bd10fa346b057 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -1,5 +1,6 @@ mod bundle; mod dynamic_texture_atlas_builder; +mod image; mod mesh2d; mod rect; mod render; @@ -12,15 +13,14 @@ pub mod collide_aabb; pub mod prelude { #[doc(hidden)] pub use crate::{ - bundle::{SpriteBundle, SpriteSheetBundle}, - sprite::Sprite, - texture_atlas::{TextureAtlas, TextureAtlasSprite}, + bundle::SpriteBundle, image::SpriteImage, sprite::Sprite, texture_atlas::TextureAtlas, ColorMaterial, ColorMesh2dBundle, TextureAtlasBuilder, }; } pub use bundle::*; pub use dynamic_texture_atlas_builder::*; +pub use image::*; pub use mesh2d::*; pub use rect::*; pub use render::*; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index c0066482014f7..4e59167df39a7 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -1,9 +1,6 @@ use std::cmp::Ordering; -use crate::{ - texture_atlas::{TextureAtlas, TextureAtlasSprite}, - Rect, Sprite, SPRITE_SHADER_HANDLE, -}; +use crate::{texture_atlas::TextureAtlas, Rect, Sprite, SpriteImage, SPRITE_SHADER_HANDLE}; use bevy_asset::{AssetEvent, Assets, Handle, HandleId}; use bevy_core_pipeline::core_2d::Transparent2d; use bevy_ecs::{ @@ -224,54 +221,43 @@ pub fn extract_sprite_events( pub fn extract_sprites( mut render_world: ResMut, texture_atlases: Res>, - sprite_query: Query<(&Visibility, &Sprite, &GlobalTransform, &Handle)>, - atlas_query: Query<( - &Visibility, - &TextureAtlasSprite, - &GlobalTransform, - &Handle, - )>, + sprite_query: Query<(&Visibility, &Sprite, &GlobalTransform, &SpriteImage)>, ) { let mut extracted_sprites = render_world.resource_mut::(); extracted_sprites.sprites.clear(); - for (visibility, sprite, transform, handle) in sprite_query.iter() { + for (visibility, sprite, transform, image) in sprite_query.iter() { if !visibility.is_visible { continue; } + + let (rect, image_handle_id) = match image { + SpriteImage::Image(handle) => (None, handle.id), + SpriteImage::TextureAtlas { handle, index } => { + if let Some(atlas) = texture_atlases.get(handle) { + let rect = atlas.textures.get(*index).copied().unwrap_or_else(|| { + panic!("TextureAtlas {:?} as no texture at index {}", atlas, index) + }); + (Some(rect), atlas.texture.id) + } else { + // Skip loading images + continue; + } + } + }; // PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive extracted_sprites.sprites.alloc().init(ExtractedSprite { color: sprite.color, transform: *transform, // Use the full texture - rect: None, + rect, // Pass the custom size custom_size: sprite.custom_size, flip_x: sprite.flip_x, flip_y: sprite.flip_y, - image_handle_id: handle.id, + image_handle_id, anchor: sprite.anchor.as_vec(), }); } - for (visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() { - if !visibility.is_visible { - continue; - } - if let Some(texture_atlas) = texture_atlases.get(texture_atlas_handle) { - let rect = Some(texture_atlas.textures[atlas_sprite.index as usize]); - extracted_sprites.sprites.alloc().init(ExtractedSprite { - color: atlas_sprite.color, - transform: *transform, - // Select the area in the texture atlas - rect, - // Pass the custom size - custom_size: atlas_sprite.custom_size, - flip_x: atlas_sprite.flip_x, - flip_y: atlas_sprite.flip_y, - image_handle_id: texture_atlas.texture.id, - anchor: atlas_sprite.anchor.as_vec(), - }); - } - } } #[repr(C)] diff --git a/crates/bevy_sprite/src/texture_atlas.rs b/crates/bevy_sprite/src/texture_atlas.rs index bc3cea9400f87..cfdef711684f7 100644 --- a/crates/bevy_sprite/src/texture_atlas.rs +++ b/crates/bevy_sprite/src/texture_atlas.rs @@ -1,9 +1,8 @@ -use crate::{Anchor, Rect}; +use crate::Rect; use bevy_asset::Handle; -use bevy_ecs::component::Component; use bevy_math::Vec2; -use bevy_reflect::{Reflect, TypeUuid}; -use bevy_render::{color::Color, texture::Image}; +use bevy_reflect::TypeUuid; +use bevy_render::texture::Image; use bevy_utils::HashMap; /// An atlas containing multiple textures (like a spritesheet or a tilemap). @@ -21,40 +20,6 @@ pub struct TextureAtlas { pub texture_handles: Option, usize>>, } -#[derive(Component, Debug, Clone, Reflect)] -pub struct TextureAtlasSprite { - pub color: Color, - pub index: usize, - pub flip_x: bool, - pub flip_y: bool, - /// An optional custom size for the sprite that will be used when rendering, instead of the size - /// of the sprite's image in the atlas - pub custom_size: Option, - pub anchor: Anchor, -} - -impl Default for TextureAtlasSprite { - fn default() -> Self { - Self { - index: 0, - color: Color::WHITE, - flip_x: false, - flip_y: false, - custom_size: None, - anchor: Anchor::default(), - } - } -} - -impl TextureAtlasSprite { - pub fn new(index: usize) -> TextureAtlasSprite { - Self { - index, - ..Default::default() - } - } -} - impl TextureAtlas { /// Create a new `TextureAtlas` that has a texture, but does not have /// any individual sprites specified diff --git a/examples/2d/move_sprite.rs b/examples/2d/move_sprite.rs index 295d340f853e1..a57737cf3d033 100644 --- a/examples/2d/move_sprite.rs +++ b/examples/2d/move_sprite.rs @@ -20,7 +20,7 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn_bundle(Camera2dBundle::default()); commands .spawn_bundle(SpriteBundle { - texture: asset_server.load("branding/icon.png"), + texture: asset_server.load("branding/icon.png").into(), transform: Transform::from_xyz(100., 0., 0.), ..default() }) diff --git a/examples/2d/rotation.rs b/examples/2d/rotation.rs index d38b911d23f4d..1b7309f2566be 100644 --- a/examples/2d/rotation.rs +++ b/examples/2d/rotation.rs @@ -67,7 +67,7 @@ fn setup(mut commands: Commands, asset_server: Res) { // player controlled ship commands .spawn_bundle(SpriteBundle { - texture: ship_handle, + texture: ship_handle.into(), ..default() }) .insert(Player { @@ -78,14 +78,14 @@ fn setup(mut commands: Commands, asset_server: Res) { // enemy that snaps to face the player spawns on the bottom and left commands .spawn_bundle(SpriteBundle { - texture: enemy_a_handle.clone(), + texture: enemy_a_handle.clone().into(), transform: Transform::from_xyz(0.0 - horizontal_margin, 0.0, 0.0), ..default() }) .insert(SnapToPlayer); commands .spawn_bundle(SpriteBundle { - texture: enemy_a_handle, + texture: enemy_a_handle.into(), transform: Transform::from_xyz(0.0, 0.0 - vertical_margin, 0.0), ..default() }) @@ -94,7 +94,7 @@ fn setup(mut commands: Commands, asset_server: Res) { // enemy that rotates to face the player enemy spawns on the top and right commands .spawn_bundle(SpriteBundle { - texture: enemy_b_handle.clone(), + texture: enemy_b_handle.clone().into(), transform: Transform::from_xyz(0.0 + horizontal_margin, 0.0, 0.0), ..default() }) @@ -103,7 +103,7 @@ fn setup(mut commands: Commands, asset_server: Res) { }); commands .spawn_bundle(SpriteBundle { - texture: enemy_b_handle, + texture: enemy_b_handle.into(), transform: Transform::from_xyz(0.0, 0.0 + vertical_margin, 0.0), ..default() }) diff --git a/examples/2d/sprite.rs b/examples/2d/sprite.rs index eac15a94c35a9..06f7488491e3f 100644 --- a/examples/2d/sprite.rs +++ b/examples/2d/sprite.rs @@ -12,7 +12,7 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn_bundle(Camera2dBundle::default()); commands.spawn_bundle(SpriteBundle { - texture: asset_server.load("branding/icon.png"), + texture: asset_server.load("branding/icon.png").into(), ..default() }); } diff --git a/examples/2d/sprite_flipping.rs b/examples/2d/sprite_flipping.rs index b77e18c2b5da1..7f2f4be830e79 100644 --- a/examples/2d/sprite_flipping.rs +++ b/examples/2d/sprite_flipping.rs @@ -12,7 +12,7 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn_bundle(Camera2dBundle::default()); commands.spawn_bundle(SpriteBundle { - texture: asset_server.load("branding/icon.png"), + texture: asset_server.load("branding/icon.png").into(), sprite: Sprite { // Flip the logo to the left flip_x: true, diff --git a/examples/2d/sprite_sheet.rs b/examples/2d/sprite_sheet.rs index 4286b88a24737..4470bba14e729 100644 --- a/examples/2d/sprite_sheet.rs +++ b/examples/2d/sprite_sheet.rs @@ -2,6 +2,7 @@ //! into a texture atlas, and changing the displayed image periodically. use bevy::{prelude::*, render::texture::ImageSettings}; +use std::ops::DerefMut; fn main() { App::new() @@ -18,17 +19,15 @@ struct AnimationTimer(Timer); fn animate_sprite( time: Res