Path: blob/main/crates/bevy_sprite_render/src/texture_slice/computed_slices.rs
6600 views
use crate::{ExtractedSlice, TextureAtlasLayout};1use bevy_asset::{AssetEvent, Assets};2use bevy_ecs::prelude::*;3use bevy_image::Image;4use bevy_math::{Rect, Vec2};5use bevy_platform::collections::HashSet;6use bevy_sprite::{Sprite, SpriteImageMode, TextureSlice};78/// Component storing texture slices for tiled or sliced sprite entities9///10/// This component is automatically inserted and updated11#[derive(Debug, Clone, Component)]12pub struct ComputedTextureSlices(Vec<TextureSlice>);1314impl ComputedTextureSlices {15/// Computes [`ExtractedSlice`] iterator from the sprite slices16///17/// # Arguments18///19/// * `sprite` - The sprite component20#[must_use]21pub(crate) fn extract_slices<'a>(22&'a self,23sprite: &'a Sprite,24anchor: Vec2,25) -> impl ExactSizeIterator<Item = ExtractedSlice> + 'a {26let mut flip = Vec2::ONE;27if sprite.flip_x {28flip.x *= -1.0;29}30if sprite.flip_y {31flip.y *= -1.0;32}33let anchor = anchor34* sprite35.custom_size36.unwrap_or(sprite.rect.unwrap_or_default().size());37self.0.iter().map(move |slice| ExtractedSlice {38offset: slice.offset * flip - anchor,39rect: slice.texture_rect,40size: slice.draw_size,41})42}43}4445/// Generates sprite slices for a [`Sprite`] with [`SpriteImageMode::Sliced`] or [`SpriteImageMode::Sliced`]. The slices46/// will be computed according to the `image_handle` dimensions or the sprite rect.47///48/// Returns `None` if the image asset is not loaded49///50/// # Arguments51///52/// * `sprite` - The sprite component with the image handle and image mode53/// * `images` - The image assets, use to retrieve the image dimensions54/// * `atlas_layouts` - The atlas layout assets, used to retrieve the texture atlas section rect55#[must_use]56fn compute_sprite_slices(57sprite: &Sprite,58images: &Assets<Image>,59atlas_layouts: &Assets<TextureAtlasLayout>,60) -> Option<ComputedTextureSlices> {61let (image_size, texture_rect) = match &sprite.texture_atlas {62Some(a) => {63let layout = atlas_layouts.get(&a.layout)?;64(65layout.size.as_vec2(),66layout.textures.get(a.index)?.as_rect(),67)68}69None => {70let image = images.get(&sprite.image)?;71let size = Vec2::new(72image.texture_descriptor.size.width as f32,73image.texture_descriptor.size.height as f32,74);75let rect = sprite.rect.unwrap_or(Rect {76min: Vec2::ZERO,77max: size,78});79(size, rect)80}81};82let slices = match &sprite.image_mode {83SpriteImageMode::Sliced(slicer) => slicer.compute_slices(texture_rect, sprite.custom_size),84SpriteImageMode::Tiled {85tile_x,86tile_y,87stretch_value,88} => {89let slice = TextureSlice {90texture_rect,91draw_size: sprite.custom_size.unwrap_or(image_size),92offset: Vec2::ZERO,93};94slice.tiled(*stretch_value, (*tile_x, *tile_y))95}96SpriteImageMode::Auto => {97unreachable!("Slices should not be computed for SpriteImageMode::Stretch")98}99SpriteImageMode::Scale(_) => {100unreachable!("Slices should not be computed for SpriteImageMode::Scale")101}102};103Some(ComputedTextureSlices(slices))104}105106/// System reacting to added or modified [`Image`] handles, and recompute sprite slices107/// on sprite entities with a matching [`SpriteImageMode`]108pub(crate) fn compute_slices_on_asset_event(109mut commands: Commands,110mut events: EventReader<AssetEvent<Image>>,111images: Res<Assets<Image>>,112atlas_layouts: Res<Assets<TextureAtlasLayout>>,113sprites: Query<(Entity, &Sprite)>,114) {115// We store the asset ids of added/modified image assets116let added_handles: HashSet<_> = events117.read()118.filter_map(|e| match e {119AssetEvent::Added { id } | AssetEvent::Modified { id } => Some(*id),120_ => None,121})122.collect();123if added_handles.is_empty() {124return;125}126// We recompute the sprite slices for sprite entities with a matching asset handle id127for (entity, sprite) in &sprites {128if !sprite.image_mode.uses_slices() {129continue;130}131if !added_handles.contains(&sprite.image.id()) {132continue;133}134if let Some(slices) = compute_sprite_slices(sprite, &images, &atlas_layouts) {135commands.entity(entity).insert(slices);136}137}138}139140/// System reacting to changes on the [`Sprite`] component to compute the sprite slices141pub(crate) fn compute_slices_on_sprite_change(142mut commands: Commands,143images: Res<Assets<Image>>,144atlas_layouts: Res<Assets<TextureAtlasLayout>>,145changed_sprites: Query<(Entity, &Sprite), Changed<Sprite>>,146) {147for (entity, sprite) in &changed_sprites {148if !sprite.image_mode.uses_slices() {149continue;150}151if let Some(slices) = compute_sprite_slices(sprite, &images, &atlas_layouts) {152commands.entity(entity).insert(slices);153}154}155}156157158