HIDPI Text (#1132)
HIDPI Text * add more operator overloads to `bevy::math::Size` * render UI text at physical resolution
This commit is contained in:
parent
2754a9dde8
commit
6531fcdfd2
@ -1,6 +1,6 @@
|
|||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||||
|
|
||||||
/// A two dimensional "size" as defined by a width and height
|
/// A two dimensional "size" as defined by a width and height
|
||||||
#[derive(Copy, Clone, PartialEq, Debug, Reflect)]
|
#[derive(Copy, Clone, PartialEq, Debug, Reflect)]
|
||||||
@ -81,3 +81,102 @@ where
|
|||||||
self.height += rhs.y;
|
self.height += rhs.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Reflect> Sub<Vec2> for Size<T>
|
||||||
|
where
|
||||||
|
T: Sub<f32, Output = T>,
|
||||||
|
{
|
||||||
|
type Output = Size<T>;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Vec2) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
width: self.width - rhs.x,
|
||||||
|
height: self.height - rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Reflect> SubAssign<Vec2> for Size<T>
|
||||||
|
where
|
||||||
|
T: SubAssign<f32>,
|
||||||
|
{
|
||||||
|
fn sub_assign(&mut self, rhs: Vec2) {
|
||||||
|
self.width -= rhs.x;
|
||||||
|
self.height -= rhs.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Reflect> Mul<f32> for Size<T>
|
||||||
|
where
|
||||||
|
T: Mul<f32, Output = T>,
|
||||||
|
{
|
||||||
|
type Output = Size<T>;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
|
Self::Output {
|
||||||
|
width: self.width * rhs,
|
||||||
|
height: self.height * rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Reflect> MulAssign<f32> for Size<T>
|
||||||
|
where
|
||||||
|
T: MulAssign<f32>,
|
||||||
|
{
|
||||||
|
fn mul_assign(&mut self, rhs: f32) {
|
||||||
|
self.width *= rhs;
|
||||||
|
self.height *= rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Reflect> Div<f32> for Size<T>
|
||||||
|
where
|
||||||
|
T: Div<f32, Output = T>,
|
||||||
|
{
|
||||||
|
type Output = Size<T>;
|
||||||
|
|
||||||
|
fn div(self, rhs: f32) -> Self::Output {
|
||||||
|
Self::Output {
|
||||||
|
width: self.width / rhs,
|
||||||
|
height: self.height / rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Reflect> DivAssign<f32> for Size<T>
|
||||||
|
where
|
||||||
|
T: DivAssign<f32>,
|
||||||
|
{
|
||||||
|
fn div_assign(&mut self, rhs: f32) {
|
||||||
|
self.width /= rhs;
|
||||||
|
self.height /= rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn size_ops() {
|
||||||
|
type SizeF = Size<f32>;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
SizeF::new(10., 10.) + Vec2::new(10., 10.),
|
||||||
|
SizeF::new(20., 20.)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
SizeF::new(20., 20.) - Vec2::new(10., 10.),
|
||||||
|
SizeF::new(10., 10.)
|
||||||
|
);
|
||||||
|
assert_eq!(SizeF::new(10., 10.) * 2., SizeF::new(20., 20.));
|
||||||
|
assert_eq!(SizeF::new(20., 20.) / 2., SizeF::new(10., 10.));
|
||||||
|
|
||||||
|
let mut size = SizeF::new(10., 10.);
|
||||||
|
|
||||||
|
size += Vec2::new(10., 10.);
|
||||||
|
|
||||||
|
assert_eq!(size, SizeF::new(20., 20.));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -48,6 +48,7 @@ impl Default for TextStyle {
|
|||||||
pub struct DrawableText<'a> {
|
pub struct DrawableText<'a> {
|
||||||
pub render_resource_bindings: &'a mut RenderResourceBindings,
|
pub render_resource_bindings: &'a mut RenderResourceBindings,
|
||||||
pub position: Vec3,
|
pub position: Vec3,
|
||||||
|
pub scale_factor: f32,
|
||||||
pub style: &'a TextStyle,
|
pub style: &'a TextStyle,
|
||||||
pub text_glyphs: &'a Vec<PositionedGlyph>,
|
pub text_glyphs: &'a Vec<PositionedGlyph>,
|
||||||
pub msaa: &'a Msaa,
|
pub msaa: &'a Msaa,
|
||||||
@ -105,7 +106,20 @@ impl<'a> Drawable for DrawableText<'a> {
|
|||||||
color: self.style.color,
|
color: self.style.color,
|
||||||
};
|
};
|
||||||
|
|
||||||
let transform = Mat4::from_translation(self.position + tv.position.extend(0.));
|
// To get the rendering right for non-one scaling factors, we need
|
||||||
|
// the sprite to be drawn in "physical" coordinates. This is because
|
||||||
|
// the shader uses the size of the sprite to control the size on
|
||||||
|
// screen. To accomplish this we make the sprite transform
|
||||||
|
// convert from physical coordinates to logical coordinates in
|
||||||
|
// addition to altering the origin. Since individual glyphs will
|
||||||
|
// already be in physical coordinates, we just need to convert the
|
||||||
|
// overall position to physical coordinates to get the sprites
|
||||||
|
// physical position.
|
||||||
|
|
||||||
|
let transform = Mat4::from_scale(Vec3::splat(1. / self.scale_factor))
|
||||||
|
* Mat4::from_translation(
|
||||||
|
self.position * self.scale_factor + tv.position.extend(0.),
|
||||||
|
);
|
||||||
|
|
||||||
let transform_buffer = context.get_uniform_buffer(&transform).unwrap();
|
let transform_buffer = context.get_uniform_buffer(&transform).unwrap();
|
||||||
let sprite_buffer = context.get_uniform_buffer(&sprite).unwrap();
|
let sprite_buffer = context.get_uniform_buffer(&sprite).unwrap();
|
||||||
|
|||||||
@ -99,6 +99,7 @@ pub fn draw_text2d_system(
|
|||||||
msaa: &msaa,
|
msaa: &msaa,
|
||||||
text_glyphs: &text_glyphs.glyphs,
|
text_glyphs: &text_glyphs.glyphs,
|
||||||
font_quad_vertex_descriptor: &vertex_buffer_descriptor,
|
font_quad_vertex_descriptor: &vertex_buffer_descriptor,
|
||||||
|
scale_factor: 1.,
|
||||||
style: &text.style,
|
style: &text.style,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -121,8 +122,8 @@ pub fn text2d_system(
|
|||||||
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
|
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
|
||||||
mut text_pipeline: ResMut<DefaultTextPipeline>,
|
mut text_pipeline: ResMut<DefaultTextPipeline>,
|
||||||
mut text_queries: QuerySet<(
|
mut text_queries: QuerySet<(
|
||||||
Query<Entity, Changed<Text>>,
|
Query<Entity, (With<MainPass>, Changed<Text>)>,
|
||||||
Query<(&Text, &mut CalculatedSize)>,
|
Query<(&Text, &mut CalculatedSize), With<MainPass>>,
|
||||||
)>,
|
)>,
|
||||||
) {
|
) {
|
||||||
// Adds all entities where the text or the style has changed to the local queue
|
// Adds all entities where the text or the style has changed to the local queue
|
||||||
|
|||||||
@ -14,31 +14,38 @@ use bevy_text::{
|
|||||||
CalculatedSize, DefaultTextPipeline, DrawableText, Font, FontAtlasSet, Text, TextError,
|
CalculatedSize, DefaultTextPipeline, DrawableText, Font, FontAtlasSet, Text, TextError,
|
||||||
};
|
};
|
||||||
use bevy_transform::prelude::GlobalTransform;
|
use bevy_transform::prelude::GlobalTransform;
|
||||||
|
use bevy_window::Windows;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct QueuedText {
|
pub struct QueuedText {
|
||||||
entities: Vec<Entity>,
|
entities: Vec<Entity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scale_value(value: f32, factor: f64) -> f32 {
|
||||||
|
(value as f64 * factor) as f32
|
||||||
|
}
|
||||||
|
|
||||||
/// Defines how min_size, size, and max_size affects the bounds of a text
|
/// Defines how min_size, size, and max_size affects the bounds of a text
|
||||||
/// block.
|
/// block.
|
||||||
pub fn text_constraint(min_size: Val, size: Val, max_size: Val) -> f32 {
|
pub fn text_constraint(min_size: Val, size: Val, max_size: Val, scale_factor: f64) -> f32 {
|
||||||
// Needs support for percentages
|
// Needs support for percentages
|
||||||
match (min_size, size, max_size) {
|
match (min_size, size, max_size) {
|
||||||
(_, _, Val::Px(max)) => max,
|
(_, _, Val::Px(max)) => scale_value(max, scale_factor),
|
||||||
(Val::Px(min), _, _) => min,
|
(Val::Px(min), _, _) => scale_value(min, scale_factor),
|
||||||
(Val::Undefined, Val::Px(size), Val::Undefined) => size,
|
(Val::Undefined, Val::Px(size), Val::Undefined) => scale_value(size, scale_factor),
|
||||||
(Val::Auto, Val::Px(size), Val::Auto) => size,
|
(Val::Auto, Val::Px(size), Val::Auto) => scale_value(size, scale_factor),
|
||||||
_ => f32::MAX,
|
_ => f32::MAX,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the size of a text block and updates the TextGlyphs with the
|
/// Computes the size of a text block and updates the TextGlyphs with the
|
||||||
/// new computed glyphs from the layout
|
/// new computed glyphs from the layout
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn text_system(
|
pub fn text_system(
|
||||||
mut queued_text: Local<QueuedText>,
|
mut queued_text: Local<QueuedText>,
|
||||||
mut textures: ResMut<Assets<Texture>>,
|
mut textures: ResMut<Assets<Texture>>,
|
||||||
fonts: Res<Assets<Font>>,
|
fonts: Res<Assets<Font>>,
|
||||||
|
windows: Res<Windows>,
|
||||||
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
|
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
|
||||||
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
|
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
|
||||||
mut text_pipeline: ResMut<DefaultTextPipeline>,
|
mut text_pipeline: ResMut<DefaultTextPipeline>,
|
||||||
@ -47,6 +54,14 @@ pub fn text_system(
|
|||||||
Query<(&Text, &Style, &mut CalculatedSize)>,
|
Query<(&Text, &Style, &mut CalculatedSize)>,
|
||||||
)>,
|
)>,
|
||||||
) {
|
) {
|
||||||
|
let scale_factor = if let Some(window) = windows.get_primary() {
|
||||||
|
window.scale_factor()
|
||||||
|
} else {
|
||||||
|
1.
|
||||||
|
};
|
||||||
|
|
||||||
|
let inv_scale_factor = 1. / scale_factor;
|
||||||
|
|
||||||
// Adds all entities where the text or the style has changed to the local queue
|
// Adds all entities where the text or the style has changed to the local queue
|
||||||
for entity in text_queries.q0_mut().iter_mut() {
|
for entity in text_queries.q0_mut().iter_mut() {
|
||||||
queued_text.entities.push(entity);
|
queued_text.entities.push(entity);
|
||||||
@ -62,11 +77,17 @@ pub fn text_system(
|
|||||||
for entity in queued_text.entities.drain(..) {
|
for entity in queued_text.entities.drain(..) {
|
||||||
if let Ok((text, style, mut calculated_size)) = query.get_mut(entity) {
|
if let Ok((text, style, mut calculated_size)) = query.get_mut(entity) {
|
||||||
let node_size = Size::new(
|
let node_size = Size::new(
|
||||||
text_constraint(style.min_size.width, style.size.width, style.max_size.width),
|
text_constraint(
|
||||||
|
style.min_size.width,
|
||||||
|
style.size.width,
|
||||||
|
style.max_size.width,
|
||||||
|
scale_factor,
|
||||||
|
),
|
||||||
text_constraint(
|
text_constraint(
|
||||||
style.min_size.height,
|
style.min_size.height,
|
||||||
style.size.height,
|
style.size.height,
|
||||||
style.max_size.height,
|
style.max_size.height,
|
||||||
|
scale_factor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -75,7 +96,7 @@ pub fn text_system(
|
|||||||
text.font.clone(),
|
text.font.clone(),
|
||||||
&fonts,
|
&fonts,
|
||||||
&text.value,
|
&text.value,
|
||||||
text.style.font_size,
|
scale_value(text.style.font_size, scale_factor),
|
||||||
text.style.alignment,
|
text.style.alignment,
|
||||||
node_size,
|
node_size,
|
||||||
&mut *font_atlas_set_storage,
|
&mut *font_atlas_set_storage,
|
||||||
@ -93,7 +114,10 @@ pub fn text_system(
|
|||||||
let text_layout_info = text_pipeline.get_glyphs(&entity).expect(
|
let text_layout_info = text_pipeline.get_glyphs(&entity).expect(
|
||||||
"Failed to get glyphs from the pipeline that have just been computed",
|
"Failed to get glyphs from the pipeline that have just been computed",
|
||||||
);
|
);
|
||||||
calculated_size.size = text_layout_info.size;
|
calculated_size.size = Size {
|
||||||
|
width: scale_value(text_layout_info.size.width, inv_scale_factor),
|
||||||
|
height: scale_value(text_layout_info.size.height, inv_scale_factor),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,11 +130,18 @@ pub fn text_system(
|
|||||||
pub fn draw_text_system(
|
pub fn draw_text_system(
|
||||||
mut context: DrawContext,
|
mut context: DrawContext,
|
||||||
msaa: Res<Msaa>,
|
msaa: Res<Msaa>,
|
||||||
|
windows: Res<Windows>,
|
||||||
meshes: Res<Assets<Mesh>>,
|
meshes: Res<Assets<Mesh>>,
|
||||||
mut render_resource_bindings: ResMut<RenderResourceBindings>,
|
mut render_resource_bindings: ResMut<RenderResourceBindings>,
|
||||||
text_pipeline: Res<DefaultTextPipeline>,
|
text_pipeline: Res<DefaultTextPipeline>,
|
||||||
mut query: Query<(Entity, &mut Draw, &Visible, &Text, &Node, &GlobalTransform)>,
|
mut query: Query<(Entity, &mut Draw, &Visible, &Text, &Node, &GlobalTransform)>,
|
||||||
) {
|
) {
|
||||||
|
let scale_factor = if let Some(window) = windows.get_primary() {
|
||||||
|
window.scale_factor()
|
||||||
|
} else {
|
||||||
|
1.
|
||||||
|
};
|
||||||
|
|
||||||
let font_quad = meshes.get(&QUAD_HANDLE).unwrap();
|
let font_quad = meshes.get(&QUAD_HANDLE).unwrap();
|
||||||
let vertex_buffer_descriptor = font_quad.get_vertex_buffer_descriptor();
|
let vertex_buffer_descriptor = font_quad.get_vertex_buffer_descriptor();
|
||||||
|
|
||||||
@ -125,6 +156,7 @@ pub fn draw_text_system(
|
|||||||
let mut drawable_text = DrawableText {
|
let mut drawable_text = DrawableText {
|
||||||
render_resource_bindings: &mut render_resource_bindings,
|
render_resource_bindings: &mut render_resource_bindings,
|
||||||
position,
|
position,
|
||||||
|
scale_factor: scale_factor as f32,
|
||||||
msaa: &msaa,
|
msaa: &msaa,
|
||||||
text_glyphs: &text_glyphs.glyphs,
|
text_glyphs: &text_glyphs.glyphs,
|
||||||
font_quad_vertex_descriptor: &vertex_buffer_descriptor,
|
font_quad_vertex_descriptor: &vertex_buffer_descriptor,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user