Skip to content

Commit

Permalink
Add Sprite Flipping
Browse files Browse the repository at this point in the history
  • Loading branch information
zicklag committed Mar 3, 2021
1 parent bc4fe9b commit f75a4d3
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 20 deletions.
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
26 changes: 24 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,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);
}
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);
}
17 changes: 14 additions & 3 deletions crates/bevy_sprite/src/sprite.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
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)]
Expand All @@ -34,6 +43,8 @@ impl Sprite {
Self {
size,
resize_mode: SpriteResizeMode::Manual,
flip_x: false,
flip_y: false,
}
}
}
Expand Down
69 changes: 63 additions & 6 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, RenderResourceIterator, RenderResourceType, RenderResources},
texture::Texture,
};
use bevy_utils::HashMap;
Expand All @@ -25,24 +25,81 @@ pub struct TextureAtlas {
pub texture_handles: Option<HashMap<Handle<Texture>, 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<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

0 comments on commit f75a4d3

Please sign in to comment.