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

Fix for SpriteSheetComponents + TextComponents Crash #555

Closed
wants to merge 15 commits into from
69 changes: 65 additions & 4 deletions crates/bevy_sprite/src/render/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{ColorMaterial, Sprite, TextureAtlas, TextureAtlasSprite};
use crate::{ColorMaterial, Sprite, TextTextureAtlasSprite, TextureAtlas, TextureAtlasSprite};
use bevy_asset::{Assets, Handle};
use bevy_ecs::Resources;
use bevy_render::{
Expand All @@ -18,7 +18,10 @@ pub const SPRITE_PIPELINE_HANDLE: Handle<PipelineDescriptor> =
pub const SPRITE_SHEET_PIPELINE_HANDLE: Handle<PipelineDescriptor> =
Handle::from_u128(90168858051802816124217444474933884151);

pub fn build_sprite_sheet_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
pub const TEXT_SHEET_PIPELINE_HANDLE: Handle<PipelineDescriptor> =
Handle::from_u128(90168858051802816124217444474933884051);

pub fn build_text_sheet_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
PipelineDescriptor {
rasterization_state: Some(RasterizationStateDescriptor {
front_face: FrontFace::Ccw,
Expand Down Expand Up @@ -56,11 +59,11 @@ pub fn build_sprite_sheet_pipeline(shaders: &mut Assets<Shader>) -> PipelineDesc
..PipelineDescriptor::new(ShaderStages {
vertex: shaders.add(Shader::from_glsl(
ShaderStage::Vertex,
include_str!("sprite_sheet.vert"),
include_str!("text_sheet.vert"),
)),
fragment: Some(shaders.add(Shader::from_glsl(
ShaderStage::Fragment,
include_str!("sprite_sheet.frag"),
include_str!("text_sheet.frag"),
))),
})
}
Expand Down Expand Up @@ -114,11 +117,60 @@ pub fn build_sprite_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor
}
}

pub fn build_sprite_sheet_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
PipelineDescriptor {
rasterization_state: Some(RasterizationStateDescriptor {
front_face: FrontFace::Ccw,
cull_mode: CullMode::None,
depth_bias: 0,
depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0,
clamp_depth: false,
}),
depth_stencil_state: Some(DepthStencilStateDescriptor {
format: TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: CompareFunction::Less,
stencil: StencilStateDescriptor {
front: StencilStateFaceDescriptor::IGNORE,
back: StencilStateFaceDescriptor::IGNORE,
read_mask: 0,
write_mask: 0,
},
}),
color_states: vec![ColorStateDescriptor {
format: TextureFormat::Bgra8UnormSrgb,
color_blend: BlendDescriptor {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha_blend: BlendDescriptor {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
write_mask: ColorWrite::ALL,
}],
..PipelineDescriptor::new(ShaderStages {
vertex: shaders.add(Shader::from_glsl(
ShaderStage::Vertex,
include_str!("sprite_sheet.vert"),
)),
fragment: Some(shaders.add(Shader::from_glsl(
ShaderStage::Fragment,
include_str!("sprite_sheet.frag"),
))),
})
}
}

pub mod node {
pub const COLOR_MATERIAL: &str = "color_material";
pub const SPRITE: &str = "sprite";
pub const SPRITE_SHEET: &str = "sprite_sheet";
pub const SPRITE_SHEET_SPRITE: &str = "sprite_sheet_sprite";
pub const TEXT_SPRITE_SHEET_SPRITE: &str = "text_sprite_sheet_sprite";
}

pub trait SpriteRenderGraphBuilder {
Expand Down Expand Up @@ -148,13 +200,22 @@ impl SpriteRenderGraphBuilder for RenderGraph {
RenderResourcesNode::<TextureAtlasSprite>::new(true),
);

self.add_system_node(
node::TEXT_SPRITE_SHEET_SPRITE,
RenderResourcesNode::<TextTextureAtlasSprite>::new(true),
);

let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
pipelines.set(SPRITE_PIPELINE_HANDLE, build_sprite_pipeline(&mut shaders));
pipelines.set(
SPRITE_SHEET_PIPELINE_HANDLE,
build_sprite_sheet_pipeline(&mut shaders),
);
pipelines.set(
TEXT_SHEET_PIPELINE_HANDLE,
build_text_sheet_pipeline(&mut shaders),
);
self
}
}
15 changes: 15 additions & 0 deletions crates/bevy_sprite/src/render/text_sheet.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#version 450

layout(location = 0) in vec2 v_Uv;
layout(location = 1) in vec4 v_Color;

layout(location = 0) out vec4 o_Target;

layout(set = 1, binding = 2) uniform texture2D TextureAtlas_texture;
layout(set = 1, binding = 3) uniform sampler TextureAtlas_texture_sampler;

void main() {
o_Target = v_Color * texture(
sampler2D(TextureAtlas_texture, TextureAtlas_texture_sampler),
v_Uv);
}
51 changes: 51 additions & 0 deletions crates/bevy_sprite/src/render/text_sheet.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#version 450

layout(location = 0) in vec3 Vertex_Position;
layout(location = 1) in vec3 Vertex_Normal;
layout(location = 2) in vec2 Vertex_Uv;

layout(location = 0) out vec2 v_Uv;
layout(location = 1) out vec4 v_Color;

layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
};

// TODO: merge dimensions into "sprites" buffer when that is supported in the Uniforms derive abstraction
layout(set = 1, binding = 0) uniform TextureAtlas_size {
vec2 AtlasSize;
};

struct Rect {
vec2 begin;
vec2 end;
};

layout(set = 1, binding = 1) buffer TextureAtlas_textures {
Rect[] Textures;
};


layout(set = 2, binding = 0) uniform Transform {
mat4 SpriteTransform;
};

layout(set = 2, binding = 1) uniform TextTextureAtlasSprite {
vec4 TextureAtlasSprite_color;
uint TextureAtlasSprite_index;
};

void main() {
Rect sprite_rect = Textures[TextureAtlasSprite_index];
vec2 sprite_dimensions = sprite_rect.end - sprite_rect.begin;
vec3 vertex_position = vec3(Vertex_Position.xy * sprite_dimensions, 0.0);
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
);
v_Uv = (atlas_positions[gl_VertexIndex] + vec2(0.01, 0.01)) / AtlasSize;
v_Color = TextureAtlasSprite_color;
gl_Position = ViewProj * SpriteTransform * vec4(ceil(vertex_position), 1.0);
}
27 changes: 27 additions & 0 deletions crates/bevy_sprite/src/texture_atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,33 @@ impl TextureAtlasSprite {
}
}

// NOTE: cannot do `unsafe impl Byteable` here because Vec3 takes up the space of a Vec4. If/when glam changes this we can swap out
// Bytes for Byteable as a micro-optimization. /~https://github.com/bitshifter/glam-rs/issues/36
#[derive(Bytes, RenderResources, RenderResource)]
#[render_resources(from_self)]
pub struct TextTextureAtlasSprite {
pub color: Color,
pub index: u32,
}

impl Default for TextTextureAtlasSprite {
fn default() -> Self {
Self {
index: 0,
color: Color::WHITE,
}
}
}

impl TextTextureAtlasSprite {
pub fn new(index: u32) -> TextTextureAtlasSprite {
Self {
index,
..Default::default()
}
}
}

impl TextureAtlas {
/// Create a new `TextureAtlas` that has a texture, but does not have
/// any individual sprites specified
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_text/src/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use bevy_render::{
RenderResourceId,
},
};
use bevy_sprite::{TextureAtlas, TextureAtlasSprite};
use bevy_sprite::{TextTextureAtlasSprite, TextureAtlas, TextureAtlasSprite};

#[derive(Clone)]
pub struct TextStyle {
Expand Down Expand Up @@ -47,7 +47,7 @@ impl<'a> Drawable for DrawableText<'a> {
fn draw(&mut self, draw: &mut Draw, context: &mut DrawContext) -> Result<(), DrawError> {
context.set_pipeline(
draw,
bevy_sprite::SPRITE_SHEET_PIPELINE_HANDLE,
bevy_sprite::TEXT_SHEET_PIPELINE_HANDLE,
&PipelineSpecialization {
sample_count: self.msaa.samples,
..Default::default()
Expand Down Expand Up @@ -128,7 +128,7 @@ impl<'a> Drawable for DrawableText<'a> {
0.0,
),
);
let sprite = TextureAtlasSprite {
let sprite = TextTextureAtlasSprite {
index: glyph_atlas_info.char_index,
color: self.style.color,
};
Expand Down
66 changes: 66 additions & 0 deletions examples/2d/sprite_sheet_text.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use bevy::prelude::*;

fn main() {
App::build()
.add_default_plugins()
.add_startup_system(setup.system())
.add_system(animate_sprite_system.system())
.run();
}

fn animate_sprite_system(
texture_atlases: Res<Assets<TextureAtlas>>,
mut query: Query<(&mut Timer, &mut TextureAtlasSprite, &Handle<TextureAtlas>)>,
) {
for (timer, mut sprite, texture_atlas_handle) in &mut query.iter() {
if timer.finished {
let texture_atlas = texture_atlases.get(&texture_atlas_handle).unwrap();
sprite.index = ((sprite.index as usize + 1) % texture_atlas.textures.len()) as u32;
}
}
}

fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut textures: ResMut<Assets<Texture>>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
) {
let texture_handle = asset_server
.load_sync(
&mut textures,
"assets/textures/rpg/chars/gabe/gabe-idle-run.png",
)
.unwrap();
let texture = textures.get(&texture_handle).unwrap();
let texture_atlas = TextureAtlas::from_grid(texture_handle, texture.size, 7, 1);
let texture_atlas_handle = texture_atlases.add(texture_atlas);
commands
.spawn(Camera2dComponents::default())
.spawn(SpriteSheetComponents {
texture_atlas: texture_atlas_handle,
scale: Scale(6.0),
..Default::default()
})
.with(Timer::from_seconds(0.1, true));
let font_handle = asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap();
commands
// 2d camera
.spawn(UiCameraComponents::default())
// texture
.spawn(TextComponents {
style: Style {
align_self: AlignSelf::FlexEnd,
..Default::default()
},
text: Text {
value: "Text And Sprite Example!".to_string(),
font: font_handle,
style: TextStyle {
font_size: 60.0,
color: Color::WHITE,
},
},
..Default::default()
});
}