Skip to content
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

[Merged by Bors] - Add Sprite Flipping #1407

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_sprite/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub fn build_sprite_pipeline(shaders: &mut Assets<Shader>) -> 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 {
Expand Down
24 changes: 22 additions & 2 deletions crates/bevy_sprite/src/render/sprite.vert
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,32 @@ 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

uint x_flip_bit = 1; // The X flip bit
uint y_flip_bit = 2; // The Y flip bit

// 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);
}
50 changes: 42 additions & 8 deletions crates/bevy_sprite/src/render/sprite_sheet.vert
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
41 changes: 38 additions & 3 deletions crates/bevy_sprite/src/sprite.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
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, RenderResourceType, RenderResources},
texture::Texture,
};
use serde::{Deserialize, Serialize};

#[derive(Debug, Default, Clone, RenderResources, TypeUuid, Reflect)]
#[derive(Debug, Default, Clone, TypeUuid, Reflect, RenderResources)]
#[render_resources(from_self)]
#[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"]
#[repr(C)]
pub struct Sprite {
pub size: Vec2,
#[render_resources(ignore)]
pub flip_x: bool,
pub flip_y: bool,
pub resize_mode: SpriteResizeMode,
}

impl RenderResource for Sprite {
fn resource_type(&self) -> Option<RenderResourceType> {
Some(RenderResourceType::Buffer)
}

fn buffer_byte_len(&self) -> Option<usize> {
Some(12)
}

fn write_buffer_bytes(&self, buffer: &mut [u8]) {
// Write the size buffer
let (size_buf, flip_buf) = buffer.split_at_mut(8);
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<Texture>> {
None
}
}

/// Determines how `Sprite` resize should be handled
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
#[reflect_value(PartialEq, Serialize, Deserialize)]
Expand All @@ -34,6 +67,8 @@ impl Sprite {
Self {
size,
resize_mode: SpriteResizeMode::Manual,
flip_x: false,
flip_y: false,
}
}
}
Expand Down
43 changes: 38 additions & 5 deletions crates/bevy_sprite/src/texture_atlas.rs
Original file line number Diff line number Diff line change
@@ -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, RenderResourceType, RenderResources},
texture::Texture,
};
use bevy_utils::HashMap;
Expand All @@ -25,24 +25,57 @@ pub struct TextureAtlas {
pub texture_handles: Option<HashMap<Handle<Texture>, usize>>,
}

#[derive(Debug, RenderResources, RenderResource, Clone)]
#[derive(Debug, Clone, RenderResources)]
#[render_resources(from_self)]
#[repr(C)]
pub struct TextureAtlasSprite {
pub color: Color,
pub index: u32,
pub flip_x: bool,
pub flip_y: bool,
}

impl RenderResource for TextureAtlasSprite {
fn resource_type(&self) -> Option<RenderResourceType> {
Some(RenderResourceType::Buffer)
}

fn buffer_byte_len(&self) -> Option<usize> {
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<Texture>> {
None
}
}

impl Default for TextureAtlasSprite {
fn default() -> Self {
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 {
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_text/src/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions examples/2d/contributors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
29 changes: 29 additions & 0 deletions examples/2d/sprite_flipping.rs
Original file line number Diff line number Diff line change
@@ -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<AssetServer>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
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()
});
}
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down