
> Replaces #5213 # Objective Implement sprite tiling and [9 slice scaling](https://en.wikipedia.org/wiki/9-slice_scaling) for `bevy_sprite`. Allowing slice scaling and texture tiling. Basic scaling vs 9 slice scaling:  Slicing example: <img width="481" alt="Screenshot 2022-07-05 at 15 05 49" src="https://user-images.githubusercontent.com/26703856/177336112-9e961af0-c0af-4197-aec9-430c1170a79d.png"> Tiling example: <img width="1329" alt="Screenshot 2023-11-16 at 13 53 32" src="https://github.com/bevyengine/bevy/assets/26703856/14db39b7-d9e0-4bc3-ba0e-b1f2db39ae8f"> # Solution - `SpriteBundlue` now has a `scale_mode` component storing a `SpriteScaleMode` enum with three variants: - `Stretched` (default) - `Tiled` to have sprites tile horizontally and/or vertically - `Sliced` allowing 9 slicing the texture and optionally tile some sections with a `Textureslicer`. - `bevy_sprite` has two extra systems to compute a `ComputedTextureSlices` if necessary,: - One system react to changes on `Sprite`, `Handle<Image>` or `SpriteScaleMode` - The other listens to `AssetEvent<Image>` to compute slices on sprites when the texture is ready or changed - I updated the `bevy_sprite` extraction stage to extract potentially multiple textures instead of one, depending on the presence of `ComputedTextureSlices` - I added two examples showcasing the slicing and tiling feature. The addition of `ComputedTextureSlices` as a cache is to avoid querying the image data, to retrieve its dimensions, every frame in a extract or prepare stage. Also it reacts to changes so we can have stuff like this (tiling example): https://github.com/bevyengine/bevy/assets/26703856/a349a9f3-33c3-471f-8ef4-a0e5dfce3b01 # Related - [ ] Once #5103 or #10099 is merged I can enable tiling and slicing for texture sheets as ui # To discuss There is an other option, to consider slice/tiling as part of the asset, using the new asset preprocessing but I have no clue on how to do it. Also, instead of retrieving the Image dimensions, we could use the same system as the sprite sheet and have the user give the image dimensions directly (grid). But I think it's less user friendly --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: ickshonpe <david.curthoys@googlemail.com> Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
87 lines
2.8 KiB
Rust
87 lines
2.8 KiB
Rust
mod border_rect;
|
|
mod computed_slices;
|
|
mod slicer;
|
|
|
|
use bevy_math::{Rect, Vec2};
|
|
pub use border_rect::BorderRect;
|
|
pub use slicer::{SliceScaleMode, TextureSlicer};
|
|
|
|
pub(crate) use computed_slices::{
|
|
compute_slices_on_asset_event, compute_slices_on_sprite_change, ComputedTextureSlices,
|
|
};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub(crate) struct TextureSlice {
|
|
/// texture area to draw
|
|
pub texture_rect: Rect,
|
|
/// slice draw size
|
|
pub draw_size: Vec2,
|
|
/// offset of the slice
|
|
pub offset: Vec2,
|
|
}
|
|
|
|
impl TextureSlice {
|
|
/// Transforms the given slice in an collection of tiled subdivisions.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `stretch_value` - The slice will repeat when the ratio between the *drawing dimensions* of texture and the
|
|
/// *original texture size* (rect) are above `stretch_value`.
|
|
/// - `tile_x` - should the slice be tiled horizontally
|
|
/// - `tile_y` - should the slice be tiled vertically
|
|
#[must_use]
|
|
pub fn tiled(self, stretch_value: f32, (tile_x, tile_y): (bool, bool)) -> Vec<Self> {
|
|
if !tile_x && !tile_y {
|
|
return vec![self];
|
|
}
|
|
let stretch_value = stretch_value.max(0.001);
|
|
let rect_size = self.texture_rect.size();
|
|
// Each tile expected size
|
|
let expected_size = Vec2::new(
|
|
if tile_x {
|
|
rect_size.x * stretch_value
|
|
} else {
|
|
self.draw_size.x
|
|
},
|
|
if tile_y {
|
|
rect_size.y * stretch_value
|
|
} else {
|
|
self.draw_size.y
|
|
},
|
|
);
|
|
let mut slices = Vec::new();
|
|
let base_offset = Vec2::new(
|
|
-self.draw_size.x / 2.0,
|
|
self.draw_size.y / 2.0, // Start from top
|
|
);
|
|
let mut offset = base_offset;
|
|
|
|
let mut remaining_columns = self.draw_size.y;
|
|
while remaining_columns > 0.0 {
|
|
let size_y = expected_size.y.min(remaining_columns);
|
|
offset.x = base_offset.x;
|
|
offset.y -= size_y / 2.0;
|
|
let mut remaining_rows = self.draw_size.x;
|
|
while remaining_rows > 0.0 {
|
|
let size_x = expected_size.x.min(remaining_rows);
|
|
offset.x += size_x / 2.0;
|
|
let draw_size = Vec2::new(size_x, size_y);
|
|
let delta = draw_size / expected_size;
|
|
slices.push(Self {
|
|
texture_rect: Rect {
|
|
min: self.texture_rect.min,
|
|
max: self.texture_rect.min + self.texture_rect.size() * delta,
|
|
},
|
|
draw_size,
|
|
offset: self.offset + offset,
|
|
});
|
|
offset.x += size_x / 2.0;
|
|
remaining_rows -= size_x;
|
|
}
|
|
offset.y -= size_y / 2.0;
|
|
remaining_columns -= size_y;
|
|
}
|
|
slices
|
|
}
|
|
}
|