From ecea30cadb41aedd5e4d53d4110607d2088502be Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 20 Jun 2020 12:39:12 -0700 Subject: [PATCH] text: new atlased rendering finally works! removed old render-to-texture rendering --- crates/bevy_asset/src/handle.rs | 4 + .../bevy_sprite/src/render/sprite_sheet.vert | 7 ++ crates/bevy_text/src/draw.rs | 89 ++++++++++++++----- crates/bevy_ui/src/entity.rs | 13 +-- crates/bevy_ui/src/widget/label.rs | 56 +++++------- examples/ui/text.rs | 3 +- 6 files changed, 101 insertions(+), 71 deletions(-) diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index a29e84f73d..14d2c77ab2 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -37,6 +37,10 @@ impl Handle { } } + /// Gets a handle for the given type that has this handle's id. This is useful when an + /// asset is derived from another asset. In this case, a common handle can be used to + /// correlate them. + /// NOTE: This pattern might eventually be replaced by a more formal asset dependency system. pub fn as_handle(&self) -> Handle { Handle::from_id(self.id) } diff --git a/crates/bevy_sprite/src/render/sprite_sheet.vert b/crates/bevy_sprite/src/render/sprite_sheet.vert index 09f4789ba4..0449f9abf4 100644 --- a/crates/bevy_sprite/src/render/sprite_sheet.vert +++ b/crates/bevy_sprite/src/render/sprite_sheet.vert @@ -6,9 +6,16 @@ layout(location = 2) in vec2 Vertex_Uv; layout(location = 0) out vec2 v_Uv; +// TODO: remove UI shader def and replace with generic "Camera" when its easier to manually bind global RenderResourceBindings +#ifdef UI_CAMERA +layout(set = 0, binding = 0) uniform UiCamera { + mat4 ViewProj; +}; +# else layout(set = 0, binding = 0) uniform Camera2d { mat4 ViewProj; }; +#endif // TODO: merge dimensions into "sprites" buffer when that is supported in the Uniforms derive abstraction layout(set = 1, binding = 0) uniform TextureAtlas_size { diff --git a/crates/bevy_text/src/draw.rs b/crates/bevy_text/src/draw.rs index 2016ed1746..cdcff02317 100644 --- a/crates/bevy_text/src/draw.rs +++ b/crates/bevy_text/src/draw.rs @@ -1,9 +1,10 @@ use crate::{Font, FontAtlasSet}; +use ab_glyph::{Glyph, PxScale, ScaleFont}; use bevy_asset::Assets; use bevy_render::{ draw::{Draw, DrawContext, DrawError, Drawable}, mesh, - pipeline::PipelineSpecialization, + pipeline::{PipelineSpecialization, ShaderSpecialization}, render_resource::{ AssetRenderResourceBindings, BindGroup, BufferUsage, RenderResourceBindings, RenderResourceId, @@ -12,13 +13,13 @@ use bevy_render::{ }; use bevy_sprite::{TextureAtlas, TextureAtlasSprite}; use glam::Vec3; +use std::collections::HashSet; pub struct TextStyle { pub font_size: f32, pub color: Color, } -#[allow(dead_code)] pub struct DrawableText<'a> { font: &'a Font, font_atlas_set: &'a FontAtlasSet, @@ -56,10 +57,16 @@ impl<'a> DrawableText<'a> { impl<'a> Drawable for DrawableText<'a> { fn draw(&mut self, draw: &mut Draw, context: &mut DrawContext) -> Result<(), DrawError> { + let mut shader_defs = HashSet::new(); + shader_defs.insert("UI_CAMERA".to_string()); context.set_pipeline( draw, bevy_sprite::SPRITE_SHEET_PIPELINE_HANDLE, - PipelineSpecialization::empty(), + // TODO: remove this shader def specialization when its easier to manually bind global render resources to specific bind groups + &PipelineSpecialization { + shader_specialization: ShaderSpecialization { shader_defs }, + ..Default::default() + }, )?; let render_resource_context = &**context.render_resource_context; @@ -80,37 +87,73 @@ impl<'a> Drawable for DrawableText<'a> { } } - let current_position = Vec3::new(0.0, 0.0, 0.0); // set global bindings context.set_bind_groups_from_bindings(draw, &mut [self.render_resource_bindings])?; + // NOTE: this uses ab_glyph apis directly. it _might_ be a good idea to add our own layer on top + let font = &self.font.font; + let scale = PxScale::from(self.style.font_size); + let scaled_font = ab_glyph::Font::as_scaled(&font, scale); + let mut caret = self.position; + let mut last_glyph: Option = None; + // set local per-character bindings for character in self.text.chars() { + if character.is_control() { + continue; + } + + let glyph = scaled_font.scaled_glyph(character); + if let Some(last_glyph) = last_glyph.take() { + caret.set_x(caret.x() + scaled_font.kern(last_glyph.id, glyph.id)); + } if let Some(glyph_atlas_info) = self .font_atlas_set .get_glyph_atlas_info(self.style.font_size, character) { - let atlas_render_resource_bindings = self - .asset_render_resource_bindings - .get_mut(glyph_atlas_info.texture_atlas) - .unwrap(); - context - .set_bind_groups_from_bindings(draw, &mut [atlas_render_resource_bindings])?; - let sprite_buffer = TextureAtlasSprite { - index: glyph_atlas_info.char_index, - position: current_position, - scale: 1.0, - }; + if let Some(outlined) = scaled_font.outline_glyph(glyph.clone()) { + let texture_atlas = self + .texture_atlases + .get(&glyph_atlas_info.texture_atlas) + .unwrap(); + let glyph_rect = texture_atlas.textures[glyph_atlas_info.char_index as usize]; + let glyph_width = glyph_rect.width(); + let glyph_height = glyph_rect.height(); + let atlas_render_resource_bindings = self + .asset_render_resource_bindings + .get_mut(glyph_atlas_info.texture_atlas) + .unwrap(); + context.set_bind_groups_from_bindings( + draw, + &mut [atlas_render_resource_bindings], + )?; - let sprite_buffer = context - .shared_buffers - .get_buffer(&sprite_buffer, BufferUsage::UNIFORM) - .unwrap(); - let sprite_bind_group = BindGroup::build().add_binding(0, sprite_buffer).finish(); - context.create_bind_group_resource(2, &sprite_bind_group)?; - draw.set_bind_group(2, &sprite_bind_group); - draw.draw_indexed(indices.clone(), 0, 0..1); + let bounds = outlined.px_bounds(); + let offset = scaled_font.descent() + glyph_height; + let sprite_buffer = TextureAtlasSprite { + index: glyph_atlas_info.char_index, + position: caret + + Vec3::new( + 0.0 + glyph_width / 2.0 + bounds.min.x, + glyph_height / 2.0 - bounds.min.y - offset, + 0.0, + ), + scale: 1.0, + }; + + let sprite_buffer = context + .shared_buffers + .get_buffer(&sprite_buffer, BufferUsage::UNIFORM) + .unwrap(); + let sprite_bind_group = + BindGroup::build().add_binding(0, sprite_buffer).finish(); + context.create_bind_group_resource(2, &sprite_bind_group)?; + draw.set_bind_group(2, &sprite_bind_group); + draw.draw_indexed(indices.clone(), 0, 0..1); + } } + caret.set_x(caret.x() + scaled_font.h_advance(glyph.id)); + last_glyph = Some(glyph); } Ok(()) } diff --git a/crates/bevy_ui/src/entity.rs b/crates/bevy_ui/src/entity.rs index ffca338b52..4c5daed8a6 100644 --- a/crates/bevy_ui/src/entity.rs +++ b/crates/bevy_ui/src/entity.rs @@ -18,12 +18,12 @@ pub struct UiEntity { impl Default for UiEntity { fn default() -> Self { UiEntity { + mesh: QUAD_HANDLE, + render_pipelines: RenderPipelines::from_handles(&[UI_PIPELINE_HANDLE]), node: Default::default(), quad: Default::default(), - mesh: QUAD_HANDLE, material: Default::default(), draw: Default::default(), - render_pipelines: RenderPipelines::from_handles(&[UI_PIPELINE_HANDLE]), } } } @@ -32,24 +32,17 @@ impl Default for UiEntity { pub struct LabelEntity { pub node: Node, pub quad: Quad, - pub mesh: Handle, // TODO: maybe abstract this out - pub material: Handle, pub draw: Draw, - pub render_pipelines: RenderPipelines, pub label: Label, } impl Default for LabelEntity { fn default() -> Self { LabelEntity { + label: Label::default(), node: Default::default(), quad: Default::default(), - mesh: QUAD_HANDLE, - // NOTE: labels each get their own material. - material: Handle::new(), // TODO: maybe abstract this out draw: Default::default(), - render_pipelines: RenderPipelines::from_handles(&[UI_PIPELINE_HANDLE]), - label: Label::default(), } } } diff --git a/crates/bevy_ui/src/widget/label.rs b/crates/bevy_ui/src/widget/label.rs index b66eae22ff..59e2d29440 100644 --- a/crates/bevy_ui/src/widget/label.rs +++ b/crates/bevy_ui/src/widget/label.rs @@ -5,10 +5,10 @@ use bevy_render::{ texture::Texture, Color, }; -use bevy_sprite::{ColorMaterial, ComMut, Quad, TextureAtlas}; +use bevy_sprite::{ComMut, Quad, TextureAtlas}; use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle}; -use legion::prelude::{Com, Res, ResMut}; use glam::Vec3; +use legion::prelude::{Com, Res, ResMut}; pub struct Label { pub text: String, @@ -32,46 +32,29 @@ impl Default for Label { impl Label { // PERF: this is horrendously inefficient. (1) new texture per label per frame (2) no atlas pub fn label_system( - mut color_materials: ResMut>, mut textures: ResMut>, fonts: Res>, mut font_atlas_sets: ResMut>, mut texture_atlases: ResMut>, label: Com