Skip to content

Commit

Permalink
Render text in 2D scenes (#1122)
Browse files Browse the repository at this point in the history
Render text in 2D scenes
  • Loading branch information
CleanCut authored Dec 27, 2020
1 parent c32c78f commit f574c2c
Show file tree
Hide file tree
Showing 15 changed files with 288 additions and 90 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ path = "examples/2d/texture_atlas.rs"
name = "contributors"
path = "examples/2d/contributors.rs"

[[example]]
name = "text2d"
path = "examples/2d/text2d.rs"

[[example]]
name = "load_gltf"
path = "examples/3d/load_gltf.rs"
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/render_graph/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use bevy_reflect::{Reflect, ReflectComponent};
use bevy_window::WindowId;

/// A component that indicates that an entity should be drawn in the "main pass"
#[derive(Default, Reflect)]
#[derive(Clone, Debug, Default, Reflect)]
#[reflect(Component)]
pub struct MainPass;

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/render_graph/nodes/pass_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub struct PassNode<Q: WorldQuery> {

impl<Q: WorldQuery> fmt::Debug for PassNode<Q> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("PassNose")
f.debug_struct("PassNode")
.field("descriptor", &self.descriptor)
.field("inputs", &self.inputs)
.field("cameras", &self.cameras)
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_text/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ bevy_math = { path = "../bevy_math", version = "0.4.0" }
bevy_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy"] }
bevy_render = { path = "../bevy_render", version = "0.4.0" }
bevy_sprite = { path = "../bevy_sprite", version = "0.4.0" }
bevy_transform = { path = "../bevy_transform", version = "0.4.0" }
bevy_utils = { path = "../bevy_utils", version = "0.4.0" }

# other
Expand Down
15 changes: 12 additions & 3 deletions crates/bevy_text/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ mod font_atlas_set;
mod font_loader;
mod glyph_brush;
mod pipeline;
mod text;
mod text2d;

pub use draw::*;
pub use error::*;
Expand All @@ -15,15 +17,17 @@ pub use font_atlas_set::*;
pub use font_loader::*;
pub use glyph_brush::*;
pub use pipeline::*;
pub use text::*;
pub use text2d::*;

pub mod prelude {
pub use crate::{Font, TextAlignment, TextError, TextStyle};
pub use crate::{Font, Text, Text2dBundle, TextAlignment, TextError, TextStyle};
pub use glyph_brush_layout::{HorizontalAlign, VerticalAlign};
}

use bevy_app::prelude::*;
use bevy_asset::AddAsset;
use bevy_ecs::Entity;
use bevy_ecs::{Entity, IntoSystem};

pub type DefaultTextPipeline = TextPipeline<Entity>;

Expand All @@ -35,6 +39,11 @@ impl Plugin for TextPlugin {
app.add_asset::<Font>()
.add_asset::<FontAtlasSet>()
.init_asset_loader::<FontLoader>()
.add_resource(DefaultTextPipeline::default());
.add_resource(DefaultTextPipeline::default())
.add_system_to_stage(bevy_app::stage::POST_UPDATE, text2d_system.system())
.add_system_to_stage(
bevy_render::stage::DRAW,
text2d::draw_text2d_system.system(),
);
}
}
16 changes: 16 additions & 0 deletions crates/bevy_text/src/text.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use bevy_asset::Handle;
use bevy_math::Size;

use crate::{Font, TextStyle};

#[derive(Debug, Default, Clone)]
pub struct Text {
pub value: String,
pub font: Handle<Font>,
pub style: TextStyle,
}

#[derive(Default, Copy, Clone, Debug)]
pub struct CalculatedSize {
pub size: Size,
}
172 changes: 172 additions & 0 deletions crates/bevy_text/src/text2d.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
use bevy_asset::Assets;
use bevy_ecs::{Bundle, Changed, Entity, Local, Query, QuerySet, Res, ResMut, With};
use bevy_math::{Size, Vec3};
use bevy_render::{
draw::{DrawContext, Drawable},
mesh::Mesh,
prelude::{Draw, Msaa, Texture, Visible},
render_graph::base::MainPass,
renderer::RenderResourceBindings,
};
use bevy_sprite::{TextureAtlas, QUAD_HANDLE};
use bevy_transform::prelude::{GlobalTransform, Transform};
use glyph_brush_layout::{HorizontalAlign, VerticalAlign};

use crate::{
CalculatedSize, DefaultTextPipeline, DrawableText, Font, FontAtlasSet, Text, TextError,
};

/// The bundle of components needed to draw text in a 2D scene via the Camera2dBundle.
#[derive(Bundle, Clone, Debug)]
pub struct Text2dBundle {
pub draw: Draw,
pub visible: Visible,
pub text: Text,
pub transform: Transform,
pub global_transform: GlobalTransform,
pub main_pass: MainPass,
pub calculated_size: CalculatedSize,
}

impl Default for Text2dBundle {
fn default() -> Self {
Self {
draw: Draw {
..Default::default()
},
visible: Visible {
is_transparent: true,
..Default::default()
},
text: Default::default(),
transform: Default::default(),
global_transform: Default::default(),
main_pass: MainPass {},
calculated_size: CalculatedSize {
size: Size::default(),
},
}
}
}

/// System for drawing text in a 2D scene via the Camera2dBundle. Included in the default
/// `TextPlugin`. Position is determined by the `Transform`'s translation, though scale and rotation
/// are ignored.
pub fn draw_text2d_system(
mut context: DrawContext,
msaa: Res<Msaa>,
meshes: Res<Assets<Mesh>>,
mut render_resource_bindings: ResMut<RenderResourceBindings>,
text_pipeline: Res<DefaultTextPipeline>,
mut query: Query<
(
Entity,
&mut Draw,
&Visible,
&Text,
&GlobalTransform,
&CalculatedSize,
),
With<MainPass>,
>,
) {
let font_quad = meshes.get(&QUAD_HANDLE).unwrap();
let vertex_buffer_descriptor = font_quad.get_vertex_buffer_descriptor();

for (entity, mut draw, visible, text, global_transform, calculated_size) in query.iter_mut() {
if !visible.is_visible {
continue;
}

let (width, height) = (calculated_size.size.width, calculated_size.size.height);

if let Some(text_glyphs) = text_pipeline.get_glyphs(&entity) {
let position = global_transform.translation
+ match text.style.alignment.vertical {
VerticalAlign::Top => Vec3::zero(),
VerticalAlign::Center => Vec3::new(0.0, -height * 0.5, 0.0),
VerticalAlign::Bottom => Vec3::new(0.0, -height, 0.0),
}
+ match text.style.alignment.horizontal {
HorizontalAlign::Left => Vec3::new(-width, 0.0, 0.0),
HorizontalAlign::Center => Vec3::new(-width * 0.5, 0.0, 0.0),
HorizontalAlign::Right => Vec3::zero(),
};

let mut drawable_text = DrawableText {
render_resource_bindings: &mut render_resource_bindings,
position,
msaa: &msaa,
text_glyphs: &text_glyphs.glyphs,
font_quad_vertex_descriptor: &vertex_buffer_descriptor,
style: &text.style,
};

drawable_text.draw(&mut draw, &mut context).unwrap();
}
}
}

#[derive(Debug, Default)]
pub struct QueuedText2d {
entities: Vec<Entity>,
}

/// Updates the TextGlyphs with the new computed glyphs from the layout
pub fn text2d_system(
mut queued_text: Local<QueuedText2d>,
mut textures: ResMut<Assets<Texture>>,
fonts: Res<Assets<Font>>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
mut text_pipeline: ResMut<DefaultTextPipeline>,
mut text_queries: QuerySet<(
Query<Entity, Changed<Text>>,
Query<(&Text, &mut CalculatedSize)>,
)>,
) {
// Adds all entities where the text or the style has changed to the local queue
for entity in text_queries.q0_mut().iter_mut() {
queued_text.entities.push(entity);
}

if queued_text.entities.is_empty() {
return;
}

// Computes all text in the local queue
let mut new_queue = Vec::new();
let query = text_queries.q1_mut();
for entity in queued_text.entities.drain(..) {
if let Ok((text, mut calculated_size)) = query.get_mut(entity) {
match text_pipeline.queue_text(
entity,
text.font.clone(),
&fonts,
&text.value,
text.style.font_size,
text.style.alignment,
Size::new(f32::MAX, f32::MAX),
&mut *font_atlas_set_storage,
&mut *texture_atlases,
&mut *textures,
) {
Err(TextError::NoSuchFont) => {
// There was an error processing the text layout, let's add this entity to the queue for further processing
new_queue.push(entity);
}
Err(e @ TextError::FailedToAddGlyph(_)) => {
panic!("Fatal error when processing text: {}.", e);
}
Ok(()) => {
let text_layout_info = text_pipeline.get_glyphs(&entity).expect(
"Failed to get glyphs from the pipeline that have just been computed",
);
calculated_size.size = text_layout_info.size;
}
}
}
}

queued_text.entities = new_queue;
}
5 changes: 3 additions & 2 deletions crates/bevy_ui/src/entity.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::Node;
use crate::{
render::UI_PIPELINE_HANDLE,
widget::{Button, Image, Text},
CalculatedSize, FocusPolicy, Interaction, Style,
widget::{Button, Image},
FocusPolicy, Interaction, Style,
};
use bevy_asset::Handle;
use bevy_ecs::Bundle;
Expand All @@ -15,6 +15,7 @@ use bevy_render::{
prelude::Visible,
};
use bevy_sprite::{ColorMaterial, QUAD_HANDLE};
use bevy_text::{CalculatedSize, Text};
use bevy_transform::prelude::{GlobalTransform, Transform};

#[derive(Bundle, Clone, Debug)]
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_ui/src/flex/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
mod convert;

use crate::{CalculatedSize, Node, Style};
use crate::{Node, Style};
use bevy_ecs::{Changed, Entity, Query, Res, ResMut, With, Without};
use bevy_math::Vec2;
use bevy_text::CalculatedSize;
use bevy_transform::prelude::{Children, Parent, Transform};
use bevy_utils::HashMap;
use bevy_window::{Window, WindowId, Windows};
Expand Down
7 changes: 1 addition & 6 deletions crates/bevy_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ pub use node::*;
pub use render::*;

pub mod prelude {
pub use crate::{
entity::*,
node::*,
widget::{Button, Text},
Anchors, Interaction, Margins,
};
pub use crate::{entity::*, node::*, widget::Button, Anchors, Interaction, Margins};
}

use bevy_app::prelude::*;
Expand Down
5 changes: 0 additions & 5 deletions crates/bevy_ui/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ impl AddAssign<f32> for Val {
}
}

#[derive(Default, Copy, Clone, Debug)]
pub struct CalculatedSize {
pub size: Size,
}

#[derive(Clone, PartialEq, Debug, Reflect)]
pub struct Style {
pub display: Display,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ui/src/widget/image.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::CalculatedSize;
use bevy_asset::{Assets, Handle};
use bevy_ecs::{Query, Res, With};
use bevy_math::Size;
use bevy_render::texture::Texture;
use bevy_sprite::ColorMaterial;
use bevy_text::CalculatedSize;

#[derive(Debug, Clone)]
pub enum Image {
Expand Down
Loading

0 comments on commit f574c2c

Please sign in to comment.