From 5226cd653cdcf91432786c28ea7366237b2618c6 Mon Sep 17 00:00:00 2001 From: Zicklag Date: Fri, 5 Feb 2021 17:02:21 -0600 Subject: [PATCH] Add Sprite Flipping --- Cargo.toml | 4 ++ crates/bevy_sprite/src/render/mod.rs | 2 +- crates/bevy_sprite/src/render/sprite.vert | 18 ++++- .../bevy_sprite/src/render/sprite_sheet.vert | 50 +++++++++++--- crates/bevy_sprite/src/sprite.rs | 67 +++++++++++++++++- crates/bevy_sprite/src/texture_atlas.rs | 69 +++++++++++++++++-- crates/bevy_text/src/draw.rs | 2 + examples/2d/contributors.rs | 1 + examples/2d/sprite_flipping.rs | 27 ++++++++ examples/README.md | 1 + 10 files changed, 221 insertions(+), 20 deletions(-) create mode 100644 examples/2d/sprite_flipping.rs diff --git a/Cargo.toml b/Cargo.toml index fa0cbd53498ac..381b626215379 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,10 @@ path = "examples/hello_world.rs" name = "sprite" path = "examples/2d/sprite.rs" +[[example]] +name = "sprite_flipping" +path = "examples/2d/sprite_flipping.rs" + [[example]] name = "sprite_sheet" path = "examples/2d/sprite_sheet.rs" diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 1f4c2005ed8f1..f0a73d804e883 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -109,7 +109,7 @@ pub fn build_sprite_pipeline(shaders: &mut Assets) -> PipelineDescriptor topology: PrimitiveTopology::TriangleList, strip_index_format: None, front_face: FrontFace::Ccw, - cull_mode: CullMode::None, + cull_mode: CullMode::Back, polygon_mode: PolygonMode::Fill, }, ..PipelineDescriptor::new(ShaderStages { diff --git a/crates/bevy_sprite/src/render/sprite.vert b/crates/bevy_sprite/src/render/sprite.vert index fe1619d14b7fc..45d39158593ff 100644 --- a/crates/bevy_sprite/src/render/sprite.vert +++ b/crates/bevy_sprite/src/render/sprite.vert @@ -13,12 +13,26 @@ layout(set = 0, binding = 0) uniform Camera { layout(set = 2, binding = 0) uniform Transform { mat4 Model; }; -layout(set = 2, binding = 1) uniform Sprite_size { +layout(set = 2, binding = 1) uniform Sprite { vec2 size; + uint flip; }; void main() { - v_Uv = Vertex_Uv; + vec2 uv = Vertex_Uv; + + // Flip the sprite if necessary + uint x_flip_bit = 1; + uint y_flip_bit = 2; + if ((flip & x_flip_bit) == x_flip_bit) { + uv = vec2(1.0 - uv.x , uv.y); + } + if ((flip & y_flip_bit) == y_flip_bit) { + uv = vec2(uv.x, 1.0 - uv.y); + } + + v_Uv = uv; + vec3 position = Vertex_Position * vec3(size, 1.0); gl_Position = ViewProj * Model * vec4(position, 1.0); } \ No newline at end of file diff --git a/crates/bevy_sprite/src/render/sprite_sheet.vert b/crates/bevy_sprite/src/render/sprite_sheet.vert index e16012abef6fd..d4ddabad7df5e 100644 --- a/crates/bevy_sprite/src/render/sprite_sheet.vert +++ b/crates/bevy_sprite/src/render/sprite_sheet.vert @@ -31,21 +31,55 @@ layout(set = 2, binding = 0) uniform Transform { }; layout(set = 2, binding = 1) uniform TextureAtlasSprite { - vec4 TextureAtlasSprite_color; - uint TextureAtlasSprite_index; + vec4 color; + uint index; + uint flip; }; void main() { - Rect sprite_rect = Textures[TextureAtlasSprite_index]; + Rect sprite_rect = Textures[index]; vec2 sprite_dimensions = sprite_rect.end - sprite_rect.begin; vec3 vertex_position = vec3(Vertex_Position.xy * sprite_dimensions, 0.0); + + // Specify the corners of the sprite + vec2 bottom_left = vec2(sprite_rect.begin.x, sprite_rect.end.y); + vec2 top_left = sprite_rect.begin; + vec2 top_right = vec2(sprite_rect.end.x, sprite_rect.begin.y); + vec2 bottom_right = sprite_rect.end; + + // Flip the sprite if necessary + uint x_flip_bit = 1; + uint y_flip_bit = 2; + + vec2 tmp; + if ((flip & x_flip_bit) == x_flip_bit) { + // Shuffle the corners to flip around x + tmp = bottom_left; + bottom_left = bottom_right; + bottom_right = tmp; + tmp = top_left; + top_left = top_right; + top_right = tmp; + } + if ((flip & y_flip_bit) == y_flip_bit) { + // Shuffle the corners to flip around y + tmp = bottom_left; + bottom_left = top_left; + top_left = tmp; + tmp = bottom_right; + bottom_right = top_right; + top_right = tmp; + } + vec2 atlas_positions[4] = vec2[]( - vec2(sprite_rect.begin.x, sprite_rect.end.y), - sprite_rect.begin, - vec2(sprite_rect.end.x, sprite_rect.begin.y), - sprite_rect.end + bottom_left, + top_left, + top_right, + bottom_right ); + v_Uv = (atlas_positions[gl_VertexIndex]) / AtlasSize; - v_Color = TextureAtlasSprite_color; + + v_Color = color; gl_Position = ViewProj * SpriteTransform * vec4(vertex_position, 1.0); } \ No newline at end of file diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index f35f712f25225..063234f25235a 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -1,19 +1,78 @@ use crate::ColorMaterial; use bevy_asset::{Assets, Handle}; +use bevy_core::Bytes; use bevy_ecs::{Query, Res}; use bevy_math::Vec2; use bevy_reflect::{Reflect, ReflectDeserialize, TypeUuid}; -use bevy_render::{renderer::RenderResources, texture::Texture}; +use bevy_render::{ + renderer::{RenderResource, RenderResourceIterator, RenderResourceType, RenderResources}, + texture::Texture, +}; use serde::{Deserialize, Serialize}; -#[derive(Debug, Default, Clone, RenderResources, TypeUuid, Reflect)] +#[derive(Debug, Default, Clone, TypeUuid, Reflect)] #[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"] pub struct Sprite { pub size: Vec2, - #[render_resources(ignore)] + pub flip_x: bool, + pub flip_y: bool, pub resize_mode: SpriteResizeMode, } +impl RenderResources for Sprite { + fn render_resources_len(&self) -> usize { + 1 + } + + fn get_render_resource(&self, index: usize) -> Option<&dyn RenderResource> { + if index == 0 { + Some(self) + } else { + None + } + } + + fn get_render_resource_name(&self, index: usize) -> Option<&str> { + if index == 0 { + Some("Sprite") + } else { + None + } + } + + fn iter(&self) -> bevy_render::renderer::RenderResourceIterator { + RenderResourceIterator::new(self) + } +} + +impl RenderResource for Sprite { + fn resource_type(&self) -> Option { + Some(RenderResourceType::Buffer) + } + + fn buffer_byte_len(&self) -> Option { + Some(12) + } + + fn write_buffer_bytes(&self, buffer: &mut [u8]) { + // Split buffer into size and flip buffers + let (size_buf, flip_buf) = buffer.split_at_mut(8 /* 8 bytes for the vec2 */); + + // Write the size vector + self.size.write_bytes(size_buf); + + // First bit means flip x, second bit means flip y + flip_buf[0] = if self.flip_x { 0b01 } else { 0 } | if self.flip_y { 0b10 } else { 0 }; + flip_buf[1] = 0; + flip_buf[2] = 0; + flip_buf[3] = 0; + } + + fn texture(&self) -> Option<&Handle> { + None + } +} + /// Determines how `Sprite` resize should be handled #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] @@ -34,6 +93,8 @@ impl Sprite { Self { size, resize_mode: SpriteResizeMode::Manual, + flip_x: false, + flip_y: false, } } } diff --git a/crates/bevy_sprite/src/texture_atlas.rs b/crates/bevy_sprite/src/texture_atlas.rs index aa525bd71fe62..be868a0f72259 100644 --- a/crates/bevy_sprite/src/texture_atlas.rs +++ b/crates/bevy_sprite/src/texture_atlas.rs @@ -1,11 +1,11 @@ use crate::Rect; use bevy_asset::Handle; -use bevy_core::Byteable; +use bevy_core::Bytes; use bevy_math::Vec2; use bevy_reflect::TypeUuid; use bevy_render::{ color::Color, - renderer::{RenderResource, RenderResources}, + renderer::{RenderResource, RenderResourceIterator, RenderResourceType, RenderResources}, texture::Texture, }; use bevy_utils::HashMap; @@ -25,11 +25,68 @@ pub struct TextureAtlas { pub texture_handles: Option, usize>>, } -#[derive(Debug, RenderResources, RenderResource, Clone)] -#[render_resources(from_self)] +#[derive(Debug, Clone)] pub struct TextureAtlasSprite { pub color: Color, pub index: u32, + pub flip_x: bool, + pub flip_y: bool, +} + +impl RenderResources for TextureAtlasSprite { + fn render_resources_len(&self) -> usize { + 1 + } + + fn get_render_resource(&self, index: usize) -> Option<&dyn RenderResource> { + if index == 0 { + Some(self) + } else { + None + } + } + + fn get_render_resource_name(&self, index: usize) -> Option<&str> { + if index == 0 { + Some("TextureAtlasSprite") + } else { + None + } + } + + fn iter(&self) -> bevy_render::renderer::RenderResourceIterator { + RenderResourceIterator::new(self) + } +} + +impl RenderResource for TextureAtlasSprite { + fn resource_type(&self) -> Option { + Some(RenderResourceType::Buffer) + } + + fn buffer_byte_len(&self) -> Option { + Some(24) + } + + fn write_buffer_bytes(&self, buffer: &mut [u8]) { + // Write the color buffer + let (color_buf, rest) = buffer.split_at_mut(16); + self.color.write_bytes(color_buf); + + // Write the index buffer + let (index_buf, flip_buf) = rest.split_at_mut(4); + self.index.write_bytes(index_buf); + + // First bit means flip x, second bit means flip y + flip_buf[0] = if self.flip_x { 0b01 } else { 0 } | if self.flip_y { 0b10 } else { 0 }; + flip_buf[1] = 0; + flip_buf[2] = 0; + flip_buf[3] = 0; + } + + fn texture(&self) -> Option<&Handle> { + None + } } impl Default for TextureAtlasSprite { @@ -37,12 +94,12 @@ impl Default for TextureAtlasSprite { Self { index: 0, color: Color::WHITE, + flip_x: false, + flip_y: false, } } } -unsafe impl Byteable for TextureAtlasSprite {} - impl TextureAtlasSprite { pub fn new(index: u32) -> TextureAtlasSprite { Self { diff --git a/crates/bevy_text/src/draw.rs b/crates/bevy_text/src/draw.rs index 3a00d7bda02ad..6a5aab522ba41 100644 --- a/crates/bevy_text/src/draw.rs +++ b/crates/bevy_text/src/draw.rs @@ -71,6 +71,8 @@ impl<'a> Drawable for DrawableText<'a> { let sprite = TextureAtlasSprite { index: tv.atlas_info.glyph_index, color: self.sections[tv.section_index].style.color, + flip_x: false, + flip_y: false, }; // To get the rendering right for non-one scaling factors, we need diff --git a/examples/2d/contributors.rs b/examples/2d/contributors.rs index 821196b9c1c8b..041feff06ef10 100644 --- a/examples/2d/contributors.rs +++ b/examples/2d/contributors.rs @@ -87,6 +87,7 @@ fn setup( sprite: Sprite { size: Vec2::new(1.0, 1.0) * SPRITE_SIZE, resize_mode: SpriteResizeMode::Manual, + ..Default::default() }, material: materials.add(ColorMaterial { color: COL_DESELECTED * col, diff --git a/examples/2d/sprite_flipping.rs b/examples/2d/sprite_flipping.rs new file mode 100644 index 0000000000000..ad4786282fd6d --- /dev/null +++ b/examples/2d/sprite_flipping.rs @@ -0,0 +1,27 @@ +use bevy::prelude::*; + +fn main() { + App::build() + .add_plugins(DefaultPlugins) + .add_startup_system(setup.system()) + .run(); +} + +fn setup( + commands: &mut Commands, + asset_server: Res, + mut materials: ResMut>, +) { + let texture_handle = asset_server.load("branding/icon.png"); + commands + .spawn(OrthographicCameraBundle::new_2d()) + .spawn(SpriteBundle { + material: materials.add(texture_handle.into()), + sprite: Sprite { + // Flip the logo to the left + flip_x: true, + ..Default::default() + }, + ..Default::default() + }); +} diff --git a/examples/README.md b/examples/README.md index 1968eeaa16ec9..c0b13e9c1baca 100644 --- a/examples/README.md +++ b/examples/README.md @@ -61,6 +61,7 @@ Example | Main | Description `contributors` | [`2d/contributors.rs`](./2d/contributors.rs) | Displays each contributor as a bouncy bevy-ball! `sprite` | [`2d/sprite.rs`](./2d/sprite.rs) | Renders a sprite `sprite_sheet` | [`2d/sprite_sheet.rs`](./2d/sprite_sheet.rs) | Renders an animated sprite +`sprite_flipping` | [`2d/sprite_flipping.rs`](./2d/sprite_flipping.rs) | Renders a sprite flipped along an axis `texture2d` | [`2d/text2d.rs`](./2d/text2d.rs) | Generates text in 2d `texture_atlas` | [`2d/texture_atlas.rs`](./2d/texture_atlas.rs) | Generates a texture atlas (sprite sheet) from individual sprites