-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Sprite slicing and tiling #10588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sprite slicing and tiling #10588
Changes from all commits
cc9ae89
5e27898
c520369
d857b32
1f55f8c
3589a08
272d43b
6d124d7
42b29cc
3c98c03
3b89390
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ use std::ops::Range; | |
|
||
use crate::{ | ||
texture_atlas::{TextureAtlas, TextureAtlasSprite}, | ||
Sprite, SPRITE_SHADER_HANDLE, | ||
ComputedTextureSlices, Sprite, SPRITE_SHADER_HANDLE, | ||
}; | ||
use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; | ||
use bevy_core_pipeline::{ | ||
|
@@ -333,6 +333,7 @@ pub fn extract_sprite_events( | |
} | ||
|
||
pub fn extract_sprites( | ||
mut commands: Commands, | ||
mut extracted_sprites: ResMut<ExtractedSprites>, | ||
texture_atlases: Extract<Res<Assets<TextureAtlas>>>, | ||
sprite_query: Extract< | ||
|
@@ -342,6 +343,7 @@ pub fn extract_sprites( | |
&Sprite, | ||
&GlobalTransform, | ||
&Handle<Image>, | ||
Option<&ComputedTextureSlices>, | ||
)>, | ||
>, | ||
atlas_query: Extract< | ||
|
@@ -356,26 +358,34 @@ pub fn extract_sprites( | |
) { | ||
extracted_sprites.sprites.clear(); | ||
|
||
for (entity, view_visibility, sprite, transform, handle) in sprite_query.iter() { | ||
for (entity, view_visibility, sprite, transform, handle, slices) in sprite_query.iter() { | ||
if !view_visibility.get() { | ||
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.insert( | ||
entity, | ||
ExtractedSprite { | ||
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(), | ||
original_entity: None, | ||
}, | ||
); | ||
if let Some(slices) = slices { | ||
extracted_sprites.sprites.extend( | ||
slices | ||
.extract_sprites(transform, entity, sprite, handle) | ||
.map(|e| (commands.spawn_empty().id(), e)), | ||
Comment on lines
+368
to
+369
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could use a custom shader here that draws all the slices in one pass, then only need one entity id is needed. With a shader based solution, the whole texture_slice module wouldn't be needed even. All it would need is just the ImageScaleMode component, extraction function, some pipeline specialisation stuff, and a fragment shader. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also it isn't very difficult either to modify batching to group sprites together with the same entity id when there isn't any need to sort the group internally. I thought I even wrote a PR somewhere that implemented it for use with Text2d. |
||
); | ||
} 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.insert( | ||
entity, | ||
ExtractedSprite { | ||
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(), | ||
original_entity: None, | ||
}, | ||
); | ||
} | ||
} | ||
for (entity, view_visibility, atlas_sprite, transform, texture_atlas_handle) in | ||
atlas_query.iter() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,8 @@ use bevy_math::{Rect, Vec2}; | |
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; | ||
use bevy_render::color::Color; | ||
|
||
use crate::TextureSlicer; | ||
|
||
/// Specifies the rendering properties of a sprite. | ||
/// | ||
/// This is commonly used as a component within [`SpriteBundle`](crate::bundle::SpriteBundle). | ||
|
@@ -26,6 +28,27 @@ pub struct Sprite { | |
pub anchor: Anchor, | ||
} | ||
|
||
/// Controls how the image is altered when scaled. | ||
#[derive(Component, Debug, Default, Clone, Reflect)] | ||
#[reflect(Component, Default)] | ||
pub enum ImageScaleMode { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure about the name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ImageScalingMode or ImageScalingBehavior is a marginal improvement I think. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe instead of scale, because the other variants aren't really scalings, something like |
||
/// The entire texture stretches when its dimensions change. This is the default option. | ||
#[default] | ||
Stretched, | ||
/// The texture will be cut in 9 slices, keeping the texture in proportions on resize | ||
Sliced(TextureSlicer), | ||
/// The texture will be repeated if stretched beyond `stretched_value` | ||
Tiled { | ||
/// Should the image repeat horizontally | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. change to: /// The image will repeat horizontally. |
||
tile_x: bool, | ||
/// Should the image repeat vertically | ||
tile_y: bool, | ||
/// The texture will repeat when the ratio between the *drawing dimensions* of texture and the | ||
/// *original texture size* are above this value. | ||
stretch_value: f32, | ||
}, | ||
} | ||
|
||
/// How a sprite is positioned relative to its [`Transform`](bevy_transform::components::Transform). | ||
/// It defaults to `Anchor::Center`. | ||
#[derive(Component, Debug, Clone, Copy, PartialEq, Default, Reflect)] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
use bevy_reflect::Reflect; | ||
|
||
/// Struct defining a [`Sprite`](crate::Sprite) border with padding values | ||
#[derive(Default, Copy, Clone, PartialEq, Debug, Reflect)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Couldn't this type reflect Default and PartialEq? #[reflect(Default, PartialEq)] |
||
pub struct BorderRect { | ||
/// Pixel padding to the left | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These comments probably aren't necessary (like the UiRect fields). |
||
pub left: f32, | ||
/// Pixel padding to the right | ||
pub right: f32, | ||
/// Pixel padding to the top | ||
pub top: f32, | ||
/// Pixel padding to the bottom | ||
pub bottom: f32, | ||
} | ||
|
||
impl BorderRect { | ||
/// Creates a new border as a square, with identical pixel padding values on every direction | ||
#[must_use] | ||
#[inline] | ||
pub const fn square(value: f32) -> Self { | ||
Self { | ||
left: value, | ||
right: value, | ||
top: value, | ||
bottom: value, | ||
} | ||
} | ||
|
||
/// Creates a new border as a rectangle, with: | ||
/// - `horizontal` for left and right pixel padding | ||
/// - `vertical` for top and bottom pixel padding | ||
#[must_use] | ||
#[inline] | ||
pub const fn rectangle(horizontal: f32, vertical: f32) -> Self { | ||
Self { | ||
left: horizontal, | ||
right: horizontal, | ||
top: vertical, | ||
bottom: vertical, | ||
} | ||
} | ||
} | ||
|
||
impl From<f32> for BorderRect { | ||
fn from(v: f32) -> Self { | ||
Self::square(v) | ||
} | ||
} | ||
|
||
impl From<[f32; 4]> for BorderRect { | ||
fn from([left, right, top, bottom]: [f32; 4]) -> Self { | ||
Self { | ||
left, | ||
right, | ||
top, | ||
bottom, | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.