diff --git a/Cargo.toml b/Cargo.toml index 719723e12d5006..83bc30f148407b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,6 +98,10 @@ path = "examples/2d/contributors.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 1f4c2005ed8f1b..f0a73d804e8839 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 fe1619d14b7fc7..6de9a136d89068 100644 --- a/crates/bevy_sprite/src/render/sprite.vert +++ b/crates/bevy_sprite/src/render/sprite.vert @@ -13,12 +13,34 @@ 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 by flipping the UVs + + // Bit that means flip x: the first byte in the 32 bit uint will be set to 1 + uint x_flip_bit = 1; + // Bit that means flip y: the second byte in the 32 bit uint will be set to 1 + uint y_flip_bit = 256; + + // Note: Here we subtract f32::EPSILON from the flipped UV coord. This is due to reasons unknown + // to me (@zicklag ) that causes the uv's to be slightly offset and causes over/under running of + // the sprite UV sampling which is visible when resizing the screen. + float epsilon = 0.00000011920929; + if ((flip & x_flip_bit) == x_flip_bit) { + uv = vec2(1.0 - uv.x - epsilon, uv.y); + } + if ((flip & y_flip_bit) == y_flip_bit) { + uv = vec2(uv.x, 1.0 - uv.y - epsilon); + } + + 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 e16012abef6fd2..d4ddabad7df5e1 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 f35f712f252257..471f36c227ef35 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -1,19 +1,29 @@ use crate::ColorMaterial; use bevy_asset::{Assets, Handle}; +use bevy_core::Byteable; 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, RenderResources}, + texture::Texture, +}; use serde::{Deserialize, Serialize}; -#[derive(Debug, Default, Clone, RenderResources, TypeUuid, Reflect)] +#[derive(Debug, Default, Clone, TypeUuid, Reflect, RenderResource, RenderResources)] +#[render_resources(from_self)] #[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"] pub struct Sprite { pub size: Vec2, - #[render_resources(ignore)] + pub flip_x: bool, + pub flip_y: bool, + #[render_resources(skip)] pub resize_mode: SpriteResizeMode, } +unsafe impl Byteable for Sprite {} + + /// Determines how `Sprite` resize should be handled #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] @@ -34,6 +44,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 aa525bd71fe622..be868a0f72259b 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 3ca7f82d1072c9..9e26a456b52072 100644 --- a/crates/bevy_text/src/draw.rs +++ b/crates/bevy_text/src/draw.rs @@ -72,6 +72,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 821196b9c1c8b8..041feff06ef10f 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 00000000000000..3f382f36bba6f6 --- /dev/null +++ b/examples/2d/sprite_flipping.rs @@ -0,0 +1,29 @@ +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, + // And don't flip it upside-down ( the default ) + flip_y: false, + ..Default::default() + }, + ..Default::default() + }); +} diff --git a/examples/README.md b/examples/README.md index 6d0545cde77ff2..423e48849e77e9 100644 --- a/examples/README.md +++ b/examples/README.md @@ -68,6 +68,7 @@ Example | Main | Description `sprite` | [`2d/sprite.rs`](./2d/sprite.rs) | Renders a sprite `sprite_sheet` | [`2d/sprite_sheet.rs`](./2d/sprite_sheet.rs) | Renders an animated sprite `text2d` | [`2d/text2d.rs`](./2d/text2d.rs) | Generates text in 2d +`sprite_flipping` | [`2d/sprite_flipping.rs`](./2d/sprite_flipping.rs) | Renders a sprite flipped along an axis `texture_atlas` | [`2d/texture_atlas.rs`](./2d/texture_atlas.rs) | Generates a texture atlas (sprite sheet) from individual sprites ## 3D Rendering