Skip to content

Commit

Permalink
Sprite 9 slice
Browse files Browse the repository at this point in the history
  • Loading branch information
ManevilleF committed Nov 14, 2022
1 parent 1967c3d commit a1cd5a1
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 15 deletions.
1 change: 1 addition & 0 deletions crates/bevy_sprite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod prelude {
#[doc(hidden)]
pub use crate::{
bundle::{SpriteBundle, SpriteSheetBundle},
rect::{BorderRect, Rect},
sprite::Sprite,
texture_atlas::{TextureAtlas, TextureAtlasSprite},
ColorMaterial, ColorMesh2dBundle, TextureAtlasBuilder,
Expand Down
60 changes: 45 additions & 15 deletions crates/bevy_sprite/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::cmp::Ordering;

use crate::{
texture_atlas::{TextureAtlas, TextureAtlasSprite},
Sprite, SPRITE_SHADER_HANDLE,
Rect, Sprite, SpriteSlice, SPRITE_SHADER_HANDLE,
};
use bevy_asset::{AssetEvent, Assets, Handle, HandleId};
use bevy_core_pipeline::{core_2d::Transparent2d, tonemapping::Tonemapping};
Expand Down Expand Up @@ -316,13 +316,15 @@ pub fn extract_sprite_events(
pub fn extract_sprites(
mut extracted_sprites: ResMut<ExtractedSprites>,
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
images: Extract<Res<Assets<Image>>>,
sprite_query: Extract<
Query<(
Entity,
&ComputedVisibility,
&Sprite,
&GlobalTransform,
&Handle<Image>,
Option<&SpriteSlice>
)>,
>,
atlas_query: Extract<
Expand All @@ -336,23 +338,51 @@ pub fn extract_sprites(
>,
) {
extracted_sprites.sprites.clear();
for (entity, visibility, sprite, transform, handle) in sprite_query.iter() {
for (entity, visibility, sprite, transform, handle, slice) in sprite_query.iter() {
if !visibility.is_visible() {
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.push(ExtractedSprite {
entity,
color: sprite.color,
transform: *transform,
rect: sprite.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(),
anchor: sprite.anchor.as_vec(),
});
if let Some(slice) = slice {
let image_size = match images.get(handle) {
None => continue,
Some(i) => Vec2::new(
i.texture_descriptor.size.width as f32,
i.texture_descriptor.size.height as f32,
),
};
// TODO: remove
let slice: &SpriteSlice = slice;
//
for rect in slice.slice_rects(image_size) {
extracted_sprites.sprites.alloc().init(ExtractedSprite {
entity,
color: sprite.color,
transform: *transform,
rect: Some(rect),
// Pass the custom size
custom_size: None,
flip_x: sprite.flip_x,
flip_y: sprite.flip_y,
image_handle_id: handle.id,
anchor: sprite.anchor.as_vec(),
});
}
} else {
// 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 {
entity,
color: sprite.color,
transform: *transform,
// Use the full texture
rect: None,
// Pass the custom size
custom_size: sprite.custom_size,
flip_x: sprite.flip_x,
flip_y: sprite.flip_y,
image_handle_id: handle.id,
anchor: sprite.anchor.as_vec(),
});
}
}
for (entity, visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() {
if !visibility.is_visible() {
Expand Down
77 changes: 77 additions & 0 deletions crates/bevy_sprite/src/sprite.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::{BorderRect, Rect};
use bevy_ecs::component::Component;
use bevy_math::{Rect, Vec2};
use bevy_reflect::Reflect;
Expand All @@ -22,6 +23,27 @@ pub struct Sprite {
pub anchor: Anchor,
}

/// Defines how the non corner sections of a [`SpriteSlice`] are scaled.
#[derive(Debug, Copy, Clone, Default, Reflect)]
pub enum SliceScaleMode {
/// The sections will stretch
#[default]
Stretch,
/// The sections will repeat
Tile,
}

/// Component for [9-sliced](https://en.wikipedia.org/wiki/9-slice_scaling) sprites.
///
/// When resizing a 9-sliced sprite the corners will remain unscaled while the other sections will be scaled or tiled
#[derive(Component, Debug, Clone, Default, Reflect)]
pub struct SpriteSlice {
/// The sprite borders, defining the 9 sections of the image
pub border: BorderRect,
/// How do the the non corner sections scale
pub scale_mode: SliceScaleMode,
}

/// How a sprite is positioned relative to its [`Transform`](bevy_transform::components::Transform).
/// It defaults to `Anchor::Center`.
#[derive(Debug, Clone, Default, Reflect)]
Expand All @@ -42,6 +64,61 @@ pub enum Anchor {
Custom(Vec2),
}

impl SpriteSlice {
/// Computes the 9 [`Rect`] and size values for a [`Sprite`] given its `image_size`
pub fn slice_rects(&self, image_size: Vec2) -> [Rect; 9] {
// corners
let bl_corner = Rect {
min: Vec2::ZERO,
max: Vec2::new(self.border.left, self.border.bottom),
};
let br_corner = Rect {
min: Vec2::new(image_size.x - self.border.right, 0.0),
max: Vec2::new(image_size.x, self.border.bottom),
};
let tl_corner = Rect {
min: Vec2::new(0.0, image_size.y - self.border.top),
max: Vec2::new(self.border.left, image_size.y),
};
let tr_corner = Rect {
min: Vec2::new(
image_size.x - self.border.right,
image_size.y - self.border.top,
),
max: Vec2::new(image_size.x, image_size.y),
};
// Sides
let left_side = Rect {
min: Vec2::new(0.0, self.border.bottom),
max: Vec2::new(self.border.left, image_size.y - self.border.top),
};
let right_side = Rect {
min: Vec2::new(image_size.x - self.border.right, self.border.bottom),
max: Vec2::new(image_size.x, image_size.y - self.border.top),
};
let bot_side = Rect {
min: Vec2::new(self.border.left, 0.0),
max: Vec2::new(image_size.x - self.border.right, self.border.bottom),
};
let top_side = Rect {
min: Vec2::new(self.border.left, image_size.y - self.border.top),
max: Vec2::new(image_size.x - self.border.right, image_size.y),
};
// Center
let center = Rect {
min: Vec2::new(self.border.left, self.border.bottom),
max: Vec2::new(
image_size.x - self.border.right,
image_size.y - self.border.top,
),
};
[
bl_corner, br_corner, tl_corner, tr_corner, left_side, right_side, bot_side, top_side,
center,
]
}
}

impl Anchor {
pub fn as_vec(&self) -> Vec2 {
match self {
Expand Down

0 comments on commit a1cd5a1

Please sign in to comment.