Skip to content

Commit dc2683c

Browse files
committed
Add Sprite Flipping
1 parent bc4fe9b commit dc2683c

File tree

10 files changed

+178
-19
lines changed

10 files changed

+178
-19
lines changed

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ path = "examples/2d/contributors.rs"
9898
name = "sprite"
9999
path = "examples/2d/sprite.rs"
100100

101+
[[example]]
102+
name = "sprite_flipping"
103+
path = "examples/2d/sprite_flipping.rs"
104+
101105
[[example]]
102106
name = "sprite_sheet"
103107
path = "examples/2d/sprite_sheet.rs"

crates/bevy_sprite/src/render/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ pub fn build_sprite_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor
109109
topology: PrimitiveTopology::TriangleList,
110110
strip_index_format: None,
111111
front_face: FrontFace::Ccw,
112-
cull_mode: CullMode::None,
112+
cull_mode: CullMode::Back,
113113
polygon_mode: PolygonMode::Fill,
114114
},
115115
..PipelineDescriptor::new(ShaderStages {

crates/bevy_sprite/src/render/sprite.vert

+22-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,32 @@ layout(set = 0, binding = 0) uniform Camera {
1313
layout(set = 2, binding = 0) uniform Transform {
1414
mat4 Model;
1515
};
16-
layout(set = 2, binding = 1) uniform Sprite_size {
16+
layout(set = 2, binding = 1) uniform Sprite {
1717
vec2 size;
18+
uint flip;
1819
};
1920

2021
void main() {
21-
v_Uv = Vertex_Uv;
22+
vec2 uv = Vertex_Uv;
23+
24+
// Flip the sprite if necessary by flipping the UVs
25+
26+
uint x_flip_bit = 1; // The X flip bit
27+
uint y_flip_bit = 2; // The Y flip bit
28+
29+
// Note: Here we subtract f32::EPSILON from the flipped UV coord. This is due to reasons unknown
30+
// to me (@zicklag ) that causes the uv's to be slightly offset and causes over/under running of
31+
// the sprite UV sampling which is visible when resizing the screen.
32+
float epsilon = 0.00000011920929;
33+
if ((flip & x_flip_bit) == x_flip_bit) {
34+
uv = vec2(1.0 - uv.x - epsilon, uv.y);
35+
}
36+
if ((flip & y_flip_bit) == y_flip_bit) {
37+
uv = vec2(uv.x, 1.0 - uv.y - epsilon);
38+
}
39+
40+
v_Uv = uv;
41+
2242
vec3 position = Vertex_Position * vec3(size, 1.0);
2343
gl_Position = ViewProj * Model * vec4(position, 1.0);
2444
}

crates/bevy_sprite/src/render/sprite_sheet.vert

+42-8
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,55 @@ layout(set = 2, binding = 0) uniform Transform {
3131
};
3232

3333
layout(set = 2, binding = 1) uniform TextureAtlasSprite {
34-
vec4 TextureAtlasSprite_color;
35-
uint TextureAtlasSprite_index;
34+
vec4 color;
35+
uint index;
36+
uint flip;
3637
};
3738

3839
void main() {
39-
Rect sprite_rect = Textures[TextureAtlasSprite_index];
40+
Rect sprite_rect = Textures[index];
4041
vec2 sprite_dimensions = sprite_rect.end - sprite_rect.begin;
4142
vec3 vertex_position = vec3(Vertex_Position.xy * sprite_dimensions, 0.0);
43+
44+
// Specify the corners of the sprite
45+
vec2 bottom_left = vec2(sprite_rect.begin.x, sprite_rect.end.y);
46+
vec2 top_left = sprite_rect.begin;
47+
vec2 top_right = vec2(sprite_rect.end.x, sprite_rect.begin.y);
48+
vec2 bottom_right = sprite_rect.end;
49+
50+
// Flip the sprite if necessary
51+
uint x_flip_bit = 1;
52+
uint y_flip_bit = 2;
53+
54+
vec2 tmp;
55+
if ((flip & x_flip_bit) == x_flip_bit) {
56+
// Shuffle the corners to flip around x
57+
tmp = bottom_left;
58+
bottom_left = bottom_right;
59+
bottom_right = tmp;
60+
tmp = top_left;
61+
top_left = top_right;
62+
top_right = tmp;
63+
}
64+
if ((flip & y_flip_bit) == y_flip_bit) {
65+
// Shuffle the corners to flip around y
66+
tmp = bottom_left;
67+
bottom_left = top_left;
68+
top_left = tmp;
69+
tmp = bottom_right;
70+
bottom_right = top_right;
71+
top_right = tmp;
72+
}
73+
4274
vec2 atlas_positions[4] = vec2[](
43-
vec2(sprite_rect.begin.x, sprite_rect.end.y),
44-
sprite_rect.begin,
45-
vec2(sprite_rect.end.x, sprite_rect.begin.y),
46-
sprite_rect.end
75+
bottom_left,
76+
top_left,
77+
top_right,
78+
bottom_right
4779
);
80+
4881
v_Uv = (atlas_positions[gl_VertexIndex]) / AtlasSize;
49-
v_Color = TextureAtlasSprite_color;
82+
83+
v_Color = color;
5084
gl_Position = ViewProj * SpriteTransform * vec4(vertex_position, 1.0);
5185
}

crates/bevy_sprite/src/sprite.rs

+38-3
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,52 @@
11
use crate::ColorMaterial;
22
use bevy_asset::{Assets, Handle};
3+
use bevy_core::Bytes;
34
use bevy_ecs::{Query, Res};
45
use bevy_math::Vec2;
56
use bevy_reflect::{Reflect, ReflectDeserialize, TypeUuid};
6-
use bevy_render::{renderer::RenderResources, texture::Texture};
7+
use bevy_render::{
8+
renderer::{RenderResource, RenderResourceType, RenderResources},
9+
texture::Texture,
10+
};
711
use serde::{Deserialize, Serialize};
812

9-
#[derive(Debug, Default, Clone, RenderResources, TypeUuid, Reflect)]
13+
#[derive(Debug, Default, Clone, TypeUuid, Reflect, RenderResources)]
14+
#[render_resources(from_self)]
1015
#[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"]
16+
#[repr(C)]
1117
pub struct Sprite {
1218
pub size: Vec2,
13-
#[render_resources(ignore)]
19+
pub flip_x: bool,
20+
pub flip_y: bool,
1421
pub resize_mode: SpriteResizeMode,
1522
}
1623

24+
impl RenderResource for Sprite {
25+
fn resource_type(&self) -> Option<RenderResourceType> {
26+
Some(RenderResourceType::Buffer)
27+
}
28+
29+
fn buffer_byte_len(&self) -> Option<usize> {
30+
Some(12)
31+
}
32+
33+
fn write_buffer_bytes(&self, buffer: &mut [u8]) {
34+
// Write the size buffer
35+
let (size_buf, flip_buf) = buffer.split_at_mut(8);
36+
self.size.write_bytes(size_buf);
37+
38+
// First bit means flip x, second bit means flip y
39+
flip_buf[0] = if self.flip_x { 0b01 } else { 0 } | if self.flip_y { 0b10 } else { 0 };
40+
flip_buf[1] = 0;
41+
flip_buf[2] = 0;
42+
flip_buf[3] = 0;
43+
}
44+
45+
fn texture(&self) -> Option<&Handle<Texture>> {
46+
None
47+
}
48+
}
49+
1750
/// Determines how `Sprite` resize should be handled
1851
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
1952
#[reflect_value(PartialEq, Serialize, Deserialize)]
@@ -34,6 +67,8 @@ impl Sprite {
3467
Self {
3568
size,
3669
resize_mode: SpriteResizeMode::Manual,
70+
flip_x: false,
71+
flip_y: false,
3772
}
3873
}
3974
}

crates/bevy_sprite/src/texture_atlas.rs

+38-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::Rect;
22
use bevy_asset::Handle;
3-
use bevy_core::Byteable;
3+
use bevy_core::Bytes;
44
use bevy_math::Vec2;
55
use bevy_reflect::TypeUuid;
66
use bevy_render::{
77
color::Color,
8-
renderer::{RenderResource, RenderResources},
8+
renderer::{RenderResource, RenderResourceType, RenderResources},
99
texture::Texture,
1010
};
1111
use bevy_utils::HashMap;
@@ -25,24 +25,57 @@ pub struct TextureAtlas {
2525
pub texture_handles: Option<HashMap<Handle<Texture>, usize>>,
2626
}
2727

28-
#[derive(Debug, RenderResources, RenderResource, Clone)]
28+
#[derive(Debug, Clone, RenderResources)]
2929
#[render_resources(from_self)]
30+
#[repr(C)]
3031
pub struct TextureAtlasSprite {
3132
pub color: Color,
3233
pub index: u32,
34+
pub flip_x: bool,
35+
pub flip_y: bool,
36+
}
37+
38+
impl RenderResource for TextureAtlasSprite {
39+
fn resource_type(&self) -> Option<RenderResourceType> {
40+
Some(RenderResourceType::Buffer)
41+
}
42+
43+
fn buffer_byte_len(&self) -> Option<usize> {
44+
Some(24)
45+
}
46+
47+
fn write_buffer_bytes(&self, buffer: &mut [u8]) {
48+
// Write the color buffer
49+
let (color_buf, rest) = buffer.split_at_mut(16);
50+
self.color.write_bytes(color_buf);
51+
52+
// Write the index buffer
53+
let (index_buf, flip_buf) = rest.split_at_mut(4);
54+
self.index.write_bytes(index_buf);
55+
56+
// First bit means flip x, second bit means flip y
57+
flip_buf[0] = if self.flip_x { 0b01 } else { 0 } | if self.flip_y { 0b10 } else { 0 };
58+
flip_buf[1] = 0;
59+
flip_buf[2] = 0;
60+
flip_buf[3] = 0;
61+
}
62+
63+
fn texture(&self) -> Option<&Handle<Texture>> {
64+
None
65+
}
3366
}
3467

3568
impl Default for TextureAtlasSprite {
3669
fn default() -> Self {
3770
Self {
3871
index: 0,
3972
color: Color::WHITE,
73+
flip_x: false,
74+
flip_y: false,
4075
}
4176
}
4277
}
4378

44-
unsafe impl Byteable for TextureAtlasSprite {}
45-
4679
impl TextureAtlasSprite {
4780
pub fn new(index: u32) -> TextureAtlasSprite {
4881
Self {

crates/bevy_text/src/draw.rs

+2
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ impl<'a> Drawable for DrawableText<'a> {
7272
let sprite = TextureAtlasSprite {
7373
index: tv.atlas_info.glyph_index,
7474
color: self.sections[tv.section_index].style.color,
75+
flip_x: false,
76+
flip_y: false,
7577
};
7678

7779
// To get the rendering right for non-one scaling factors, we need

examples/2d/contributors.rs

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ fn setup(
8787
sprite: Sprite {
8888
size: Vec2::new(1.0, 1.0) * SPRITE_SIZE,
8989
resize_mode: SpriteResizeMode::Manual,
90+
..Default::default()
9091
},
9192
material: materials.add(ColorMaterial {
9293
color: COL_DESELECTED * col,

examples/2d/sprite_flipping.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use bevy::prelude::*;
2+
3+
fn main() {
4+
App::build()
5+
.add_plugins(DefaultPlugins)
6+
.add_startup_system(setup.system())
7+
.run();
8+
}
9+
10+
fn setup(
11+
commands: &mut Commands,
12+
asset_server: Res<AssetServer>,
13+
mut materials: ResMut<Assets<ColorMaterial>>,
14+
) {
15+
let texture_handle = asset_server.load("branding/icon.png");
16+
commands
17+
.spawn(OrthographicCameraBundle::new_2d())
18+
.spawn(SpriteBundle {
19+
material: materials.add(texture_handle.into()),
20+
sprite: Sprite {
21+
// Flip the logo to the left
22+
flip_x: true,
23+
// And don't flip it upside-down ( the default )
24+
flip_y: false,
25+
..Default::default()
26+
},
27+
..Default::default()
28+
});
29+
}

examples/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Example | Main | Description
6868
`sprite` | [`2d/sprite.rs`](./2d/sprite.rs) | Renders a sprite
6969
`sprite_sheet` | [`2d/sprite_sheet.rs`](./2d/sprite_sheet.rs) | Renders an animated sprite
7070
`text2d` | [`2d/text2d.rs`](./2d/text2d.rs) | Generates text in 2d
71+
`sprite_flipping` | [`2d/sprite_flipping.rs`](./2d/sprite_flipping.rs) | Renders a sprite flipped along an axis
7172
`texture_atlas` | [`2d/texture_atlas.rs`](./2d/texture_atlas.rs) | Generates a texture atlas (sprite sheet) from individual sprites
7273

7374
## 3D Rendering

0 commit comments

Comments
 (0)