mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-25 16:38:42 -06:00
kinda works now...
This commit is contained in:
parent
ae26a4d933
commit
cdba2fedd8
|
@ -6,7 +6,7 @@ use winit::{
|
||||||
event_loop::{EventLoopBuilder, ControlFlow}
|
event_loop::{EventLoopBuilder, ControlFlow}
|
||||||
};
|
};
|
||||||
use hui::{
|
use hui::{
|
||||||
UiInstance, elements,
|
UiInstance,
|
||||||
layout::{Alignment, UiDirection, UiSize},
|
layout::{Alignment, UiDirection, UiSize},
|
||||||
rectangle::{Corners, Sides},
|
rectangle::{Corners, Sides},
|
||||||
element::{
|
element::{
|
||||||
|
@ -17,6 +17,12 @@ use hui::{
|
||||||
};
|
};
|
||||||
use hui_glium::GliumUiRenderer;
|
use hui_glium::GliumUiRenderer;
|
||||||
|
|
||||||
|
fn elements(mut f: impl FnMut(&mut Vec<Box<dyn hui::element::UiElement>>)) -> Vec<Box<dyn hui::element::UiElement>> {
|
||||||
|
let mut e = vec![];
|
||||||
|
f(&mut e);
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
kubi_logging::init();
|
kubi_logging::init();
|
||||||
|
|
||||||
|
@ -27,7 +33,7 @@ fn main() {
|
||||||
let mut hui = UiInstance::new();
|
let mut hui = UiInstance::new();
|
||||||
let mut backend = GliumUiRenderer::new(&display);
|
let mut backend = GliumUiRenderer::new(&display);
|
||||||
|
|
||||||
let font_handle = hui.add_font_from_bytes(include_bytes!("../assets/roboto/Roboto-Regular.ttf"));
|
let font_handle = hui.add_font(include_bytes!("../assets/roboto/Roboto-Regular.ttf"));
|
||||||
|
|
||||||
let instant = Instant::now();
|
let instant = Instant::now();
|
||||||
|
|
||||||
|
@ -59,18 +65,18 @@ fn main() {
|
||||||
corner_radius: Corners::all(8.),
|
corner_radius: Corners::all(8.),
|
||||||
elements: elements(|el| {
|
elements: elements(|el| {
|
||||||
if instant.elapsed().as_secs_f32() < 5. {
|
if instant.elapsed().as_secs_f32() < 5. {
|
||||||
el.add(Text {
|
el.push(Box::new(Text {
|
||||||
text: "Downloading your mom...".into(),
|
text: "Downloading your mom...".into(),
|
||||||
font: font_handle,
|
font: font_handle,
|
||||||
text_size: 24,
|
text_size: 24,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
el.add(ProgressBar {
|
el.push(Box::new(ProgressBar {
|
||||||
value: mom_ratio,
|
value: mom_ratio,
|
||||||
corner_radius: Corners::all(0.125 * ProgressBar::DEFAULT_HEIGHT),
|
corner_radius: Corners::all(0.125 * ProgressBar::DEFAULT_HEIGHT),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
el.add(Container {
|
el.push(Box::new(Container {
|
||||||
direction: UiDirection::Horizontal,
|
direction: UiDirection::Horizontal,
|
||||||
align: (Alignment::End, Alignment::Center).into(),
|
align: (Alignment::End, Alignment::Center).into(),
|
||||||
size: (UiSize::Fraction(1.), UiSize::Auto),
|
size: (UiSize::Fraction(1.), UiSize::Auto),
|
||||||
|
@ -81,21 +87,21 @@ fn main() {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})],
|
})],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
} else if instant.elapsed().as_secs() < 10 {
|
} else if instant.elapsed().as_secs() < 10 {
|
||||||
el.add(Text {
|
el.push(Box::new(Text {
|
||||||
text: "Error 413: Request Entity Too Large".into(),
|
text: "Error 413: Request Entity Too Large".into(),
|
||||||
font: font_handle,
|
font: font_handle,
|
||||||
color: vec4(1., 0.125, 0.125, 1.),
|
color: vec4(1., 0.125, 0.125, 1.),
|
||||||
text_size: 20,
|
text_size: 20,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
el.add(Text {
|
el.push(Box::new(Text {
|
||||||
text: format!("Exiting in {}...", 10 - instant.elapsed().as_secs()).into(),
|
text: format!("Exiting in {}...", 10 - instant.elapsed().as_secs()).into(),
|
||||||
font: font_handle,
|
font: font_handle,
|
||||||
text_size: 16,
|
text_size: 16,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
} else {
|
} else {
|
||||||
window_target.exit();
|
window_target.exit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use winit::{
|
||||||
event_loop::{EventLoopBuilder, ControlFlow}
|
event_loop::{EventLoopBuilder, ControlFlow}
|
||||||
};
|
};
|
||||||
use hui::{
|
use hui::{
|
||||||
UiInstance, elements,
|
UiInstance,
|
||||||
layout::UiSize,
|
layout::UiSize,
|
||||||
element::{
|
element::{
|
||||||
container::Container,
|
container::Container,
|
||||||
|
@ -15,6 +15,12 @@ use hui::{
|
||||||
};
|
};
|
||||||
use hui_glium::GliumUiRenderer;
|
use hui_glium::GliumUiRenderer;
|
||||||
|
|
||||||
|
fn elements(mut f: impl FnMut(&mut Vec<Box<dyn hui::element::UiElement>>)) -> Vec<Box<dyn hui::element::UiElement>> {
|
||||||
|
let mut e = vec![];
|
||||||
|
f(&mut e);
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
kubi_logging::init();
|
kubi_logging::init();
|
||||||
|
|
||||||
|
@ -25,7 +31,7 @@ fn main() {
|
||||||
let mut hui = UiInstance::new();
|
let mut hui = UiInstance::new();
|
||||||
let mut backend = GliumUiRenderer::new(&display);
|
let mut backend = GliumUiRenderer::new(&display);
|
||||||
|
|
||||||
let font_handle = hui.add_font_from_bytes(include_bytes!("../assets/roboto/Roboto-Regular.ttf"));
|
let font_handle = hui.add_font(include_bytes!("../assets/roboto/Roboto-Regular.ttf"));
|
||||||
let instant = Instant::now();
|
let instant = Instant::now();
|
||||||
|
|
||||||
event_loop.run(|event, window_target| {
|
event_loop.run(|event, window_target| {
|
||||||
|
@ -46,56 +52,56 @@ fn main() {
|
||||||
size: (UiSize::Fraction(1.), UiSize::Fraction(1.)),
|
size: (UiSize::Fraction(1.), UiSize::Fraction(1.)),
|
||||||
background: vec4(0.1, 0.1, 0.1, 1.).into(),
|
background: vec4(0.1, 0.1, 0.1, 1.).into(),
|
||||||
elements: elements(|elem| {
|
elements: elements(|elem| {
|
||||||
elem.add(Text {
|
elem.push(Box::new(Text {
|
||||||
text: "THIS LINE SHOULD BE SHARP!".into(),
|
text: "THIS LINE SHOULD BE SHARP!".into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
elem.add(Text {
|
elem.push(Box::new(Text {
|
||||||
text: "THIS LINE SHOULD BE SHARP!".into(),
|
text: "THIS LINE SHOULD BE SHARP!".into(),
|
||||||
text_size: 32,
|
text_size: 32,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
elem.add(Text {
|
elem.push(Box::new(Text {
|
||||||
text: "All lines except 3 and 6 below will be blurry:".into(),
|
text: "All lines except 3 and 6 below will be blurry:".into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
for size in [9, 12, 16, 18, 24, 32] {
|
for size in [9, 12, 16, 18, 24, 32] {
|
||||||
elem.add(Text {
|
elem.push(Box::new(Text {
|
||||||
text: "Testing default font, Proggy Tiny".into(),
|
text: "Testing default font, Proggy Tiny".into(),
|
||||||
text_size: size,
|
text_size: size,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
elem.add(Rect {
|
elem.push(Box::new(Rect {
|
||||||
size: (UiSize::Fraction(1.), UiSize::Static(10.)),
|
size: (UiSize::Fraction(1.), UiSize::Static(10.)),
|
||||||
color: vec4(0., 0., 1., 1.).into(),
|
color: vec4(0., 0., 1., 1.).into(),
|
||||||
});
|
}));
|
||||||
elem.add(Rect {
|
elem.push(Box::new(Rect {
|
||||||
size: (UiSize::Fraction(1.), UiSize::Static(10.)),
|
size: (UiSize::Fraction(1.), UiSize::Static(10.)),
|
||||||
color: vec4(1., 1., 0., 1.).into(),
|
color: vec4(1., 1., 0., 1.).into(),
|
||||||
});
|
}));
|
||||||
elem.add(Text {
|
elem.push(Box::new(Text {
|
||||||
text: "Hello, world!\nżółty liść. życie nie ma sensu i wszyscy zginemy;\nтест кирилиці їїїїїїїїїїї\njapanese text: テスト".into(),
|
text: "Hello, world!\nżółty liść. życie nie ma sensu i wszyscy zginemy;\nтест кирилиці їїїїїїїїїїї\njapanese text: テスト".into(),
|
||||||
font: font_handle,
|
font: font_handle,
|
||||||
text_size: 32,
|
text_size: 32,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
if instant.elapsed().as_secs() & 1 != 0 {
|
if instant.elapsed().as_secs() & 1 != 0 {
|
||||||
elem.add(Rect {
|
elem.push(Box::new(Rect {
|
||||||
size: (UiSize::Fraction(1.), UiSize::Static(10.)),
|
size: (UiSize::Fraction(1.), UiSize::Static(10.)),
|
||||||
color: vec4(1., 0., 0., 1.).into(),
|
color: vec4(1., 0., 0., 1.).into(),
|
||||||
});
|
}));
|
||||||
elem.add(Rect {
|
elem.push(Box::new(Rect {
|
||||||
size: (UiSize::Fraction(1.), UiSize::Static(10.)),
|
size: (UiSize::Fraction(1.), UiSize::Static(10.)),
|
||||||
color: vec4(0., 0., 0., 1.).into(),
|
color: vec4(0., 0., 0., 1.).into(),
|
||||||
});
|
}));
|
||||||
elem.add(Spacer(100.));
|
elem.push(Box::new(Spacer(100.)));
|
||||||
elem.add(Text {
|
elem.push(Box::new(Text {
|
||||||
text: "FLAG SHOULD NOT OVERLAP WITH TEXT".into(),
|
text: "FLAG SHOULD NOT OVERLAP WITH TEXT".into(),
|
||||||
text_size: 64,
|
text_size: 64,
|
||||||
color: vec4(1., 0., 1., 1.),
|
color: vec4(1., 0., 1., 1.),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -6,7 +6,8 @@ precision highp sampler2D;
|
||||||
out vec4 out_color;
|
out vec4 out_color;
|
||||||
in vec4 vtx_color;
|
in vec4 vtx_color;
|
||||||
in vec2 vtx_uv;
|
in vec2 vtx_uv;
|
||||||
|
uniform sampler2D tex;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
out_color = vtx_color;
|
out_color = texture(tex, vtx_uv) * vtx_color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
out vec4 out_color;
|
|
||||||
in vec4 vtx_color;
|
|
||||||
in vec2 vtx_uv;
|
|
||||||
uniform sampler2D tex;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
out_color = texture(tex, vtx_uv) * vtx_color;
|
|
||||||
}
|
|
|
@ -10,14 +10,11 @@ use glium::{
|
||||||
uniform, uniforms::{Sampler, SamplerBehavior, SamplerWrapFunction},
|
uniform, uniforms::{Sampler, SamplerBehavior, SamplerWrapFunction},
|
||||||
};
|
};
|
||||||
use hui::{
|
use hui::{
|
||||||
UiInstance,
|
draw::{TextureAtlasMeta, UiDrawCall, UiVertex}, UiInstance
|
||||||
draw::{UiDrawCall, UiVertex, BindTexture},
|
|
||||||
text::FontTextureInfo, IfModified,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const VERTEX_SHADER: &str = include_str!("../shaders/vertex.vert");
|
const VERTEX_SHADER: &str = include_str!("../shaders/vertex.vert");
|
||||||
const FRAGMENT_SHADER: &str = include_str!("../shaders/fragment.frag");
|
const FRAGMENT_SHADER: &str = include_str!("../shaders/fragment.frag");
|
||||||
const FRAGMENT_SHADER_TEX: &str = include_str!("../shaders/fragment_tex.frag");
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -119,7 +116,6 @@ impl BufferPair {
|
||||||
pub struct GliumUiRenderer {
|
pub struct GliumUiRenderer {
|
||||||
context: Rc<Context>,
|
context: Rc<Context>,
|
||||||
program: glium::Program,
|
program: glium::Program,
|
||||||
program_tex: glium::Program,
|
|
||||||
ui_texture: Option<Rc<SrgbTexture2d>>,
|
ui_texture: Option<Rc<SrgbTexture2d>>,
|
||||||
buffer_pair: Option<BufferPair>,
|
buffer_pair: Option<BufferPair>,
|
||||||
}
|
}
|
||||||
|
@ -129,7 +125,6 @@ impl GliumUiRenderer {
|
||||||
log::info!("initializing hui glium backend");
|
log::info!("initializing hui glium backend");
|
||||||
Self {
|
Self {
|
||||||
program: Program::from_source(facade, VERTEX_SHADER, FRAGMENT_SHADER, None).unwrap(),
|
program: Program::from_source(facade, VERTEX_SHADER, FRAGMENT_SHADER, None).unwrap(),
|
||||||
program_tex: Program::from_source(facade, VERTEX_SHADER, FRAGMENT_SHADER_TEX, None).unwrap(),
|
|
||||||
context: Rc::clone(facade.get_context()),
|
context: Rc::clone(facade.get_context()),
|
||||||
ui_texture: None,
|
ui_texture: None,
|
||||||
buffer_pair: None,
|
buffer_pair: None,
|
||||||
|
@ -144,34 +139,25 @@ impl GliumUiRenderer {
|
||||||
} else if !call.indices.is_empty() {
|
} else if !call.indices.is_empty() {
|
||||||
self.buffer_pair = Some(BufferPair::new_with_data(&self.context, data_vtx, data_idx));
|
self.buffer_pair = Some(BufferPair::new_with_data(&self.context, data_vtx, data_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
// self.plan[0].bind_texture = match call.bind_texture {
|
|
||||||
// Some(BindTexture::FontTexture) => {
|
|
||||||
// const NO_FNT_TEX: &str = "Font texture exists in draw plan but not yet inited. Make sure to call update_font_texture() *before* update_draw_plan()";
|
|
||||||
// Some(Rc::clone(self.font_texture.as_ref().expect(NO_FNT_TEX)))
|
|
||||||
// },
|
|
||||||
// Some(BindTexture::UserDefined(_)) => todo!("user defined textures are not implemented yet"),
|
|
||||||
// None => None,
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_ui_texture(&mut self, font_texture: &FontTextureInfo) {
|
pub fn update_texture_atlas(&mut self, atlas: &TextureAtlasMeta) {
|
||||||
log::debug!("updating font texture");
|
log::trace!("updating ui atlas texture");
|
||||||
self.ui_texture = Some(Rc::new(SrgbTexture2d::new(
|
self.ui_texture = Some(Rc::new(SrgbTexture2d::new(
|
||||||
&self.context,
|
&self.context,
|
||||||
RawImage2d::from_raw_rgba(
|
RawImage2d::from_raw_rgba(
|
||||||
font_texture.data.to_owned(),
|
atlas.data.to_owned(),
|
||||||
(font_texture.size.x, font_texture.size.y)
|
(atlas.size.x, atlas.size.y)
|
||||||
)
|
)
|
||||||
).unwrap()));
|
).unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, hui: &UiInstance) {
|
pub fn update(&mut self, hui: &UiInstance) {
|
||||||
if let Some(texture) = hui.font_texture().if_modified() {
|
if hui.atlas().modified {
|
||||||
self.update_ui_texture(texture);
|
self.update_texture_atlas(&hui.atlas());
|
||||||
}
|
}
|
||||||
if let Some(plan) = hui.draw_call().if_modified() {
|
if hui.draw_call().0 {
|
||||||
self.update_draw_plan(plan);
|
self.update_draw_plan(hui.draw_call().1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +178,7 @@ impl GliumUiRenderer {
|
||||||
frame.draw(
|
frame.draw(
|
||||||
vtx_buffer,
|
vtx_buffer,
|
||||||
idx_buffer,
|
idx_buffer,
|
||||||
&self.program_tex,
|
&self.program,
|
||||||
&uniform! {
|
&uniform! {
|
||||||
resolution: resolution.to_array(),
|
resolution: resolution.to_array(),
|
||||||
tex: Sampler(self.ui_texture.as_ref().unwrap().as_ref(), SamplerBehavior {
|
tex: Sampler(self.ui_texture.as_ref().unwrap().as_ref(), SamplerBehavior {
|
||||||
|
@ -202,20 +188,6 @@ impl GliumUiRenderer {
|
||||||
},
|
},
|
||||||
¶ms,
|
¶ms,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
// if let Some(bind_texture) = call.bind_texture.as_ref() {
|
|
||||||
|
|
||||||
// } else {
|
|
||||||
// frame.draw(
|
|
||||||
// vtx_buffer,
|
|
||||||
// idx_buffer,
|
|
||||||
// &self.program,
|
|
||||||
// &uniform! {
|
|
||||||
// resolution: resolution.to_array(),
|
|
||||||
// },
|
|
||||||
// ¶ms,
|
|
||||||
// ).unwrap();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,12 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
rectangle::Corners,
|
rectangle::Corners,
|
||||||
text::{FontHandle, TextRenderer},
|
text::{FontHandle, TextRenderer}
|
||||||
IfModified
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) mod atlas;
|
pub(crate) mod atlas;
|
||||||
//pub(crate) use atlas::{TextureAllocation, TextureAtlasManager};
|
use atlas::TextureAtlasManager;
|
||||||
//pub use atlas::TextureHandle;
|
pub use atlas::{TextureHandle, TextureAtlasMeta};
|
||||||
|
|
||||||
mod corner_radius;
|
mod corner_radius;
|
||||||
pub use corner_radius::RoundedCorners;
|
pub use corner_radius::RoundedCorners;
|
||||||
|
@ -48,7 +47,7 @@ pub enum UiDrawCommand {
|
||||||
///Position in pixels
|
///Position in pixels
|
||||||
position: Vec2,
|
position: Vec2,
|
||||||
///Font size
|
///Font size
|
||||||
size: u8,
|
size: u16,
|
||||||
///Color (RGBA)
|
///Color (RGBA)
|
||||||
color: Vec4,
|
color: Vec4,
|
||||||
///Text to draw
|
///Text to draw
|
||||||
|
@ -78,19 +77,6 @@ impl UiDrawCommandList {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
/// Texture to bind for a draw call
|
|
||||||
///
|
|
||||||
/// - FontTexture: The internally managed font texture
|
|
||||||
/// - UserDefined: User-defined texture, value is user-defined and usually depends on the render backend
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum BindTexture {
|
|
||||||
/// The internally managed font texture
|
|
||||||
FontTexture,
|
|
||||||
/// User-defined texture, value is user-defined and usually depends on the render backend
|
|
||||||
UserDefined(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A vertex for UI rendering
|
/// A vertex for UI rendering
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
||||||
pub struct UiVertex {
|
pub struct UiVertex {
|
||||||
|
@ -108,7 +94,7 @@ pub struct UiDrawCall {
|
||||||
|
|
||||||
impl UiDrawCall {
|
impl UiDrawCall {
|
||||||
/// Tesselate the UI and build a complete draw plan from a list of draw commands
|
/// Tesselate the UI and build a complete draw plan from a list of draw commands
|
||||||
pub fn build(draw_commands: &UiDrawCommandList, tr: &mut TextRenderer) -> Self {
|
pub(crate) fn build(draw_commands: &UiDrawCommandList, atlas: &mut TextureAtlasManager, text_renderer: &mut TextRenderer) -> Self {
|
||||||
let mut draw_call = UiDrawCall::default();
|
let mut draw_call = UiDrawCall::default();
|
||||||
for command in &draw_commands.commands {
|
for command in &draw_commands.commands {
|
||||||
match command {
|
match command {
|
||||||
|
@ -209,17 +195,17 @@ impl UiDrawCall {
|
||||||
UiVertex {
|
UiVertex {
|
||||||
position: *position + vec2(size.x, 0.0),
|
position: *position + vec2(size.x, 0.0),
|
||||||
color: color.top_right,
|
color: color.top_right,
|
||||||
uv: vec2(1.0, 0.0),
|
uv: vec2(0.0, 0.0), // vec2(1.0, 0.0),
|
||||||
},
|
},
|
||||||
UiVertex {
|
UiVertex {
|
||||||
position: *position + *size,
|
position: *position + *size,
|
||||||
color: color.bottom_right,
|
color: color.bottom_right,
|
||||||
uv: vec2(1.0, 1.0),
|
uv: vec2(0.0, 0.0), // vec2(1.0, 1.0),
|
||||||
},
|
},
|
||||||
UiVertex {
|
UiVertex {
|
||||||
position: *position + vec2(0.0, size.y),
|
position: *position + vec2(0.0, size.y),
|
||||||
color: color.bottom_left,
|
color: color.bottom_left,
|
||||||
uv: vec2(0.0, 1.0),
|
uv: vec2(0.0, 0.0), // vec2(0.0, 1.0),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -227,7 +213,7 @@ impl UiDrawCall {
|
||||||
UiDrawCommand::Circle { .. } => {
|
UiDrawCommand::Circle { .. } => {
|
||||||
todo!("circle draw command not implemented yet")
|
todo!("circle draw command not implemented yet")
|
||||||
},
|
},
|
||||||
UiDrawCommand::Text { position, size, color, text, font } => {
|
UiDrawCommand::Text { position, size, color, text, font: font_handle } => {
|
||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -235,7 +221,7 @@ impl UiDrawCall {
|
||||||
//XXX: should we be doing this every time?
|
//XXX: should we be doing this every time?
|
||||||
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
||||||
layout.append(
|
layout.append(
|
||||||
&[tr.internal_font(*font)],
|
&[text_renderer.internal_font(*font_handle)],
|
||||||
&TextStyle::new(text, *size as f32, 0)
|
&TextStyle::new(text, *size as f32, 0)
|
||||||
);
|
);
|
||||||
let glyphs = layout.glyphs();
|
let glyphs = layout.glyphs();
|
||||||
|
@ -245,38 +231,32 @@ impl UiDrawCall {
|
||||||
if !layout_glyph.char_data.rasterize() {
|
if !layout_glyph.char_data.rasterize() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let font_texture_size = (
|
let atlas_size = atlas.meta().size.as_vec2();
|
||||||
tr.font_texture().size.x as f32,
|
|
||||||
tr.font_texture().size.y as f32
|
|
||||||
);
|
|
||||||
let vidx = draw_call.vertices.len() as u32;
|
let vidx = draw_call.vertices.len() as u32;
|
||||||
let glyph = tr.glyph(*font, layout_glyph.parent, layout_glyph.key.px as u8);
|
let glyph = text_renderer.glyph(atlas, *font_handle, layout_glyph.parent, layout_glyph.key.px as u8);
|
||||||
|
let uv = atlas.get_uv(glyph.texture);
|
||||||
//rpos_x += glyph.metrics.advance_width;//glyph.metrics.advance_width;
|
//rpos_x += glyph.metrics.advance_width;//glyph.metrics.advance_width;
|
||||||
draw_call.indices.extend([vidx, vidx + 1, vidx + 2, vidx, vidx + 2, vidx + 3]);
|
draw_call.indices.extend([vidx, vidx + 1, vidx + 2, vidx, vidx + 2, vidx + 3]);
|
||||||
let p0x = glyph.position.x as f32 / font_texture_size.0;
|
|
||||||
let p1x = (glyph.position.x + glyph.size.x as i32) as f32 / font_texture_size.0;
|
|
||||||
let p0y = glyph.position.y as f32 / font_texture_size.1;
|
|
||||||
let p1y = (glyph.position.y + glyph.size.y as i32) as f32 / font_texture_size.1;
|
|
||||||
draw_call.vertices.extend([
|
draw_call.vertices.extend([
|
||||||
UiVertex {
|
UiVertex {
|
||||||
position: *position + vec2(layout_glyph.x, layout_glyph.y),
|
position: *position + vec2(layout_glyph.x, layout_glyph.y),
|
||||||
color: *color,
|
color: *color,
|
||||||
uv: vec2(p0x, p0y),
|
uv: uv.top_left,
|
||||||
},
|
},
|
||||||
UiVertex {
|
UiVertex {
|
||||||
position: *position + vec2(layout_glyph.x + glyph.metrics.width as f32, layout_glyph.y),
|
position: *position + vec2(layout_glyph.x + glyph.metrics.width as f32, layout_glyph.y),
|
||||||
color: *color,
|
color: *color,
|
||||||
uv: vec2(p1x, p0y),
|
uv: uv.top_right,
|
||||||
},
|
},
|
||||||
UiVertex {
|
UiVertex {
|
||||||
position: *position + vec2(layout_glyph.x + glyph.metrics.width as f32, layout_glyph.y + glyph.metrics.height as f32),
|
position: *position + vec2(layout_glyph.x + glyph.metrics.width as f32, layout_glyph.y + glyph.metrics.height as f32),
|
||||||
color: *color,
|
color: *color,
|
||||||
uv: vec2(p1x, p1y),
|
uv: uv.bottom_right,
|
||||||
},
|
},
|
||||||
UiVertex {
|
UiVertex {
|
||||||
position: *position + vec2(layout_glyph.x, layout_glyph.y + glyph.metrics.height as f32),
|
position: *position + vec2(layout_glyph.x, layout_glyph.y + glyph.metrics.height as f32),
|
||||||
color: *color,
|
color: *color,
|
||||||
uv: vec2(p0x, p1y),
|
uv: uv.bottom_left,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
|
@ -298,13 +278,3 @@ impl UiDrawCall {
|
||||||
draw_call
|
draw_call
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl IfModified<UiDrawCall> for (bool, &UiDrawCall) {
|
|
||||||
fn if_modified(&self) -> Option<&UiDrawCall> {
|
|
||||||
match self.0 {
|
|
||||||
true => Some(self.1),
|
|
||||||
false => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
use glam::{uvec2, UVec2};
|
use glam::{uvec2, vec2, UVec2, Vec2};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use nohash_hasher::BuildNoHashHasher;
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
use rect_packer::DensePacker;
|
use rect_packer::DensePacker;
|
||||||
|
use crate::rectangle::Corners;
|
||||||
use crate::IfModified;
|
|
||||||
|
|
||||||
const CHANNEL_COUNT: u32 = 4;
|
const CHANNEL_COUNT: u32 = 4;
|
||||||
//TODO: make this work
|
//TODO: make this work
|
||||||
const ALLOW_ROTATION: bool = false;
|
const ALLOW_ROTATION: bool = false;
|
||||||
|
|
||||||
|
pub struct TextureAtlasMeta<'a> {
|
||||||
|
pub data: &'a [u8],
|
||||||
|
pub size: UVec2,
|
||||||
|
pub modified: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct TextureHandle {
|
pub struct TextureHandle {
|
||||||
//TODO automatic cleanup when handle is dropped
|
//TODO automatic cleanup when handle is dropped
|
||||||
|
@ -22,13 +27,13 @@ pub(crate) struct TextureAllocation {
|
||||||
pub index: u32,
|
pub index: u32,
|
||||||
|
|
||||||
/// Position in the texture atlas
|
/// Position in the texture atlas
|
||||||
pub position: UVec2,
|
pub(crate) position: UVec2,
|
||||||
|
|
||||||
/// Requested texture size
|
/// Requested texture size
|
||||||
pub size: UVec2,
|
pub size: UVec2,
|
||||||
|
|
||||||
/// True if the texture was rotated by 90 degrees
|
/// True if the texture was rotated by 90 degrees
|
||||||
pub rotated: bool,
|
pub(crate) rotated: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manages a texture atlas and the allocation of space within it\
|
/// Manages a texture atlas and the allocation of space within it\
|
||||||
|
@ -39,25 +44,35 @@ pub(crate) struct TextureAtlasManager {
|
||||||
size: UVec2,
|
size: UVec2,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
allocations: HashMap<u32, TextureAllocation, BuildNoHashHasher<u32>>,
|
allocations: HashMap<u32, TextureAllocation, BuildNoHashHasher<u32>>,
|
||||||
|
/// True if the atlas has been modified in a way which requires a texture reupload
|
||||||
|
/// since the beginning of the current frame
|
||||||
modified: bool,
|
modified: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureAtlasManager {
|
impl TextureAtlasManager {
|
||||||
/// Create a new texture atlas with the specified size\
|
/// Create a new texture atlas with the specified size\
|
||||||
/// 512x512 is a good default size for most applications, and the texture atlas can grow dynamically as needed
|
/// 512x512 is a good default size for most applications, and the texture atlas can grow dynamically as needed
|
||||||
|
/// By default, the texture atlas gets initialized with a single white pixel texture
|
||||||
pub fn new(size: UVec2) -> Self {
|
pub fn new(size: UVec2) -> Self {
|
||||||
Self {
|
let mut tmp = Self {
|
||||||
packer: DensePacker::new(size.x as i32, size.y as i32),
|
packer: DensePacker::new(size.x as i32, size.y as i32),
|
||||||
count: 0,
|
count: 0,
|
||||||
size: UVec2::new(0, 0),
|
size,
|
||||||
data: vec![0; (size.x * size.y * CHANNEL_COUNT) as usize],
|
data: vec![0; (size.x * size.y * CHANNEL_COUNT) as usize],
|
||||||
allocations: HashMap::default(),
|
allocations: HashMap::default(),
|
||||||
modified: true,
|
modified: true,
|
||||||
}
|
};
|
||||||
|
tmp.add(1, &[255,255,255,255]);
|
||||||
|
tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resize the texture atlas to the new size in-place, preserving the existing data
|
/// Resize the texture atlas to the new size in-place, preserving the existing data
|
||||||
pub fn resize(&mut self, new_size: UVec2) {
|
pub fn resize(&mut self, new_size: UVec2) {
|
||||||
|
log::trace!("resizing texture atlas to {:?}", new_size);
|
||||||
|
if self.size == new_size {
|
||||||
|
log::warn!("Texture atlas is already the requested size");
|
||||||
|
return
|
||||||
|
}
|
||||||
if new_size.x > self.size.x && new_size.y > self.size.y{
|
if new_size.x > self.size.x && new_size.y > self.size.y{
|
||||||
self.packer.resize(new_size.x as i32, new_size.y as i32);
|
self.packer.resize(new_size.x as i32, new_size.y as i32);
|
||||||
//Resize the data array in-place
|
//Resize the data array in-place
|
||||||
|
@ -84,6 +99,7 @@ impl TextureAtlasManager {
|
||||||
/// Use `allocate` to allocate a texture and resize the atlas if necessary\
|
/// Use `allocate` to allocate a texture and resize the atlas if necessary\
|
||||||
/// Does not modify the texture data
|
/// Does not modify the texture data
|
||||||
fn try_allocate(&mut self, size: UVec2) -> Option<TextureHandle> {
|
fn try_allocate(&mut self, size: UVec2) -> Option<TextureHandle> {
|
||||||
|
log::trace!("Allocating texture of size {:?}", size);
|
||||||
let result = self.packer.pack(size.x as i32, size.y as i32, ALLOW_ROTATION)?;
|
let result = self.packer.pack(size.x as i32, size.y as i32, ALLOW_ROTATION)?;
|
||||||
let index = self.count;
|
let index = self.count;
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
|
@ -107,7 +123,9 @@ impl TextureAtlasManager {
|
||||||
new_size *= 2;
|
new_size *= 2;
|
||||||
self.packer.resize(new_size.x as i32, new_size.y as i32);
|
self.packer.resize(new_size.x as i32, new_size.y as i32);
|
||||||
}
|
}
|
||||||
self.resize(new_size);
|
if new_size != self.size {
|
||||||
|
self.resize(new_size);
|
||||||
|
}
|
||||||
self.try_allocate(size).unwrap()
|
self.try_allocate(size).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,8 +133,8 @@ impl TextureAtlasManager {
|
||||||
/// This function may resize the atlas as needed, and should never fail under normal circumstances.
|
/// This function may resize the atlas as needed, and should never fail under normal circumstances.
|
||||||
pub fn add(&mut self, width: usize, data: &[u8]) -> TextureHandle {
|
pub fn add(&mut self, width: usize, data: &[u8]) -> TextureHandle {
|
||||||
let size = uvec2(width as u32, (data.len() / (width * CHANNEL_COUNT as usize)) as u32);
|
let size = uvec2(width as u32, (data.len() / (width * CHANNEL_COUNT as usize)) as u32);
|
||||||
let handle = self.allocate(size);
|
let handle: TextureHandle = self.allocate(size);
|
||||||
let allocation = self.allocations.get_mut(&handle.index).unwrap();
|
let allocation = self.allocations.get(&handle.index).unwrap();
|
||||||
assert!(!allocation.rotated, "Rotated textures are not implemented yet");
|
assert!(!allocation.rotated, "Rotated textures are not implemented yet");
|
||||||
for y in 0..size.y {
|
for y in 0..size.y {
|
||||||
for x in 0..size.x {
|
for x in 0..size.x {
|
||||||
|
@ -127,6 +145,26 @@ impl TextureAtlasManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.modified = true;
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Works the same way as [`TextureAtlasManager::add`], but the input data is assumed to be grayscale (1 channel per pixel)\
|
||||||
|
/// The data is copied into the alpha channel of the texture, while all the other channels are set to 255\
|
||||||
|
/// May resize the atlas as needed, and should never fail under normal circumstances.
|
||||||
|
pub fn add_grayscale(&mut self, width: usize, data: &[u8]) -> TextureHandle {
|
||||||
|
let size = uvec2(width as u32, (data.len() / width) as u32);
|
||||||
|
let handle = self.allocate(size);
|
||||||
|
let allocation = self.allocations.get(&handle.index).unwrap();
|
||||||
|
assert!(!allocation.rotated, "Rotated textures are not implemented yet");
|
||||||
|
for y in 0..size.y {
|
||||||
|
for x in 0..size.x {
|
||||||
|
let src_idx = (y * size.x + x) as usize;
|
||||||
|
let dst_idx = (((allocation.position.y + y) * self.size.x + allocation.position.x + x) * CHANNEL_COUNT) as usize;
|
||||||
|
self.data[dst_idx..(dst_idx + CHANNEL_COUNT as usize)].copy_from_slice(&[255, 255, 255, data[src_idx]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.modified = true;
|
||||||
handle
|
handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,25 +176,43 @@ impl TextureAtlasManager {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn atlas_size(&self) -> UVec2 {
|
|
||||||
self.size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, handle: TextureHandle) -> Option<&TextureAllocation> {
|
pub fn get(&self, handle: TextureHandle) -> Option<&TextureAllocation> {
|
||||||
self.allocations.get(&handle.index)
|
self.allocations.get(&handle.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_uv(&self, handle: TextureHandle) -> Corners<Vec2> {
|
||||||
|
let info = self.get(handle).unwrap();
|
||||||
|
let atlas_size = self.meta().size.as_vec2();
|
||||||
|
let p0x = info.position.x as f32 / atlas_size.x;
|
||||||
|
let p1x = (info.position.x as f32 + info.size.x as f32) / atlas_size.x;
|
||||||
|
let p0y = info.position.y as f32 / atlas_size.y;
|
||||||
|
let p1y = (info.position.y as f32 + info.size.y as f32) / atlas_size.y;
|
||||||
|
Corners {
|
||||||
|
top_left: vec2(p0x, p0y),
|
||||||
|
top_right: vec2(p1x, p0y),
|
||||||
|
bottom_left: vec2(p0x, p1y),
|
||||||
|
bottom_right: vec2(p1x, p1y),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Reset the `is_modified` flag
|
/// Reset the `is_modified` flag
|
||||||
pub fn reset_modified(&mut self) {
|
pub(crate) fn reset_modified(&mut self) {
|
||||||
self.modified = false;
|
self.modified = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the atlas has been modified since the last call to `reset_modified`\
|
/// Returns true if the atlas has been modified since the beginning of the current frame\
|
||||||
/// If this function returns true, the texture atlas should be re-uploaded to the GPU\
|
/// If this function returns true, the texture atlas should be re-uploaded to the GPU before rendering\
|
||||||
/// This function is mostly useful for developers of graphics backends
|
|
||||||
pub fn is_modified(&self) -> bool {
|
pub fn is_modified(&self) -> bool {
|
||||||
self.modified
|
self.modified
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn meta(&self) -> TextureAtlasMeta {
|
||||||
|
TextureAtlasMeta {
|
||||||
|
data: &self.data,
|
||||||
|
size: self.size,
|
||||||
|
modified: self.modified,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextureAtlasManager {
|
impl Default for TextureAtlasManager {
|
||||||
|
@ -165,13 +221,3 @@ impl Default for TextureAtlasManager {
|
||||||
Self::new(UVec2::new(512, 512))
|
Self::new(UVec2::new(512, 512))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl<'a> IfModified<TextureAtlasManager> for TextureAtlasManager {
|
|
||||||
fn if_modified(&self) -> Option<&Self> {
|
|
||||||
match self.modified {
|
|
||||||
true => Some(self),
|
|
||||||
false => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub struct Text {
|
||||||
pub size: (UiSize, UiSize),
|
pub size: (UiSize, UiSize),
|
||||||
pub color: Vec4,
|
pub color: Vec4,
|
||||||
pub font: FontHandle,
|
pub font: FontHandle,
|
||||||
pub text_size: u8,
|
pub text_size: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Text {
|
impl Default for Text {
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use crate:: {
|
use crate::{
|
||||||
layout::{UiDirection, LayoutInfo},
|
draw::{
|
||||||
|
atlas::{TextureAtlasManager, TextureAtlasMeta},
|
||||||
|
UiDrawCall, UiDrawCommandList,
|
||||||
|
},
|
||||||
element::{MeasureContext, ProcessContext, UiElement},
|
element::{MeasureContext, ProcessContext, UiElement},
|
||||||
event::UiEvent,
|
event::UiEvent,
|
||||||
|
layout::{LayoutInfo, UiDirection},
|
||||||
state::StateRepo,
|
state::StateRepo,
|
||||||
draw::{UiDrawCommandList, UiDrawCall},
|
text::{FontHandle, TextRenderer}
|
||||||
text::{TextRenderer, FontTextureInfo, FontHandle},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The main instance of the UI system.\
|
/// The main instance of the UI system.\
|
||||||
|
@ -20,6 +23,7 @@ pub struct UiInstance {
|
||||||
draw_call: UiDrawCall,
|
draw_call: UiDrawCall,
|
||||||
draw_call_modified: bool,
|
draw_call_modified: bool,
|
||||||
text_renderer: TextRenderer,
|
text_renderer: TextRenderer,
|
||||||
|
atlas: TextureAtlasManager,
|
||||||
events: VecDeque<UiEvent>,
|
events: VecDeque<UiEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,13 +43,19 @@ impl UiInstance {
|
||||||
draw_call_modified: false,
|
draw_call_modified: false,
|
||||||
// ftm: FontTextureManager::default(),
|
// ftm: FontTextureManager::default(),
|
||||||
text_renderer: TextRenderer::new(),
|
text_renderer: TextRenderer::new(),
|
||||||
|
atlas: {
|
||||||
|
let mut atlas = TextureAtlasManager::default();
|
||||||
|
//HACK: Ensure that vec(0, 0) uv is white square
|
||||||
|
atlas.add_grayscale(1, &[255]);
|
||||||
|
atlas
|
||||||
|
},
|
||||||
events: VecDeque::new(),
|
events: VecDeque::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse and add a font from a raw byte slice to the UI\
|
/// Parse and add a font from a raw byte slice to the UI\
|
||||||
/// Returns a font handle.
|
/// Returns a font handle.
|
||||||
pub fn add_font_from_bytes(&mut self, font: &[u8]) -> FontHandle {
|
pub fn add_font(&mut self, font: &[u8]) -> FontHandle {
|
||||||
self.text_renderer.add_font_from_bytes(font)
|
self.text_renderer.add_font_from_bytes(font)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +90,7 @@ impl UiInstance {
|
||||||
std::mem::swap(&mut self.prev_draw_commands, &mut self.draw_commands);
|
std::mem::swap(&mut self.prev_draw_commands, &mut self.draw_commands);
|
||||||
self.draw_call_modified = false;
|
self.draw_call_modified = false;
|
||||||
self.draw_commands.commands.clear();
|
self.draw_commands.commands.clear();
|
||||||
self.text_renderer.reset_frame();
|
self.atlas.reset_modified();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// End the frame and prepare the UI for rendering
|
/// End the frame and prepare the UI for rendering
|
||||||
|
@ -90,29 +100,29 @@ impl UiInstance {
|
||||||
if self.draw_commands.commands == self.prev_draw_commands.commands {
|
if self.draw_commands.commands == self.prev_draw_commands.commands {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.draw_call = UiDrawCall::build(&self.draw_commands, &mut self.text_renderer);
|
self.draw_call = UiDrawCall::build(&self.draw_commands, &mut self.atlas, &mut self.text_renderer);
|
||||||
self.draw_call_modified = true;
|
self.draw_call_modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the draw call for the current frame
|
/// Get the draw call information for the current frame
|
||||||
///
|
///
|
||||||
/// This function should only be used by the render backend.\
|
/// This function should only be used by the render backend.\
|
||||||
/// You should not call this directly unless you're implementing a custom render backend
|
/// You should not call this directly unless you're implementing a custom render backend
|
||||||
///
|
///
|
||||||
/// Returns a tuple with a boolean indicating if the draw plan was modified since the last frame
|
/// Returns a tuple with a boolean indicating if the buffers have been modified since the last frame
|
||||||
pub fn draw_call(&self) -> (bool, &UiDrawCall) {
|
pub fn draw_call(&self) -> (bool, &UiDrawCall) {
|
||||||
(self.draw_call_modified, &self.draw_call)
|
(self.draw_call_modified, &self.draw_call)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the font texture for the current frame
|
/// Get the texture atlas size and data for the current frame
|
||||||
///
|
///
|
||||||
/// This function should only be used by the render backend.\
|
/// This function should only be used by the render backend.\
|
||||||
/// You should not call this directly unless you're implementing a custom render backend
|
/// You should not call this directly unless you're implementing a custom render backend
|
||||||
///
|
///
|
||||||
/// Make sure to check `FontTextureInfo::modified` to see if the texture was modified
|
/// Make sure to check [`TextureAtlasMeta::modified`] to see if the texture has been modified
|
||||||
/// since the last frame before uploading it to the GPU
|
/// since the beginning of the current frame before uploading it to the GPU
|
||||||
pub fn font_texture(&self) -> FontTextureInfo {
|
pub fn atlas(&self) -> TextureAtlasMeta {
|
||||||
self.text_renderer.font_texture()
|
self.atlas.meta()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a platform event to the UI event queue
|
/// Push a platform event to the UI event queue
|
||||||
|
|
|
@ -22,9 +22,3 @@ pub mod state;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
pub use instance::UiInstance;
|
pub use instance::UiInstance;
|
||||||
|
|
||||||
#[deprecated(since = "0.1.0-alpha.4", note = "will be removed in the next release")]
|
|
||||||
pub trait IfModified<T> {
|
|
||||||
#[deprecated(since = "0.1.0-alpha.4", note = "will be removed in the next release")]
|
|
||||||
fn if_modified(&self) -> Option<&T>;
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub use font::BUILTIN_FONT;
|
||||||
pub(crate) use font::DEFAULT_FONT;
|
pub(crate) use font::DEFAULT_FONT;
|
||||||
use fontdue::{Font, FontSettings};
|
use fontdue::{Font, FontSettings};
|
||||||
use ftm::FontTextureManager;
|
use ftm::FontTextureManager;
|
||||||
pub use ftm::{FontTextureInfo, GlyphCacheEntry};
|
pub use ftm::GlyphCacheEntry;
|
||||||
|
|
||||||
use crate::draw::atlas::TextureAtlasManager;
|
use crate::draw::atlas::TextureAtlasManager;
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ impl TextRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn glyph(&mut self, atlas: &mut TextureAtlasManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
pub fn glyph(&mut self, atlas: &mut TextureAtlasManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
||||||
self.ftm.glyph(&self.fm, atlas, font_handle, character, size)
|
self.ftm.glyph(atlas, &self.fm, font_handle, character, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn internal_font(&self, handle: FontHandle) -> &Font {
|
pub(crate) fn internal_font(&self, handle: FontHandle) -> &Font {
|
||||||
|
@ -55,7 +55,7 @@ pub struct TextMeasureResponse {
|
||||||
pub struct TextMeasure<'a>(&'a TextRenderer);
|
pub struct TextMeasure<'a>(&'a TextRenderer);
|
||||||
|
|
||||||
impl<'a> TextMeasure<'a> {
|
impl<'a> TextMeasure<'a> {
|
||||||
pub fn measure(&self, font: FontHandle, size: u8, text: &str) -> TextMeasureResponse {
|
pub fn measure(&self, font: FontHandle, size: u16, text: &str) -> TextMeasureResponse {
|
||||||
use fontdue::layout::{Layout, CoordinateSystem, TextStyle};
|
use fontdue::layout::{Layout, CoordinateSystem, TextStyle};
|
||||||
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
||||||
layout.append(
|
layout.append(
|
||||||
|
@ -79,7 +79,7 @@ impl TextRenderer {
|
||||||
TextMeasure(self)
|
TextMeasure(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn measure(&self, font: FontHandle, size: u8, text: &str) -> TextMeasureResponse {
|
pub fn measure(&self, font: FontHandle, size: u16, text: &str) -> TextMeasureResponse {
|
||||||
TextMeasure(self).measure(font, size, text)
|
TextMeasure(self).measure(font, size, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use fontdue::Metrics;
|
use fontdue::Metrics;
|
||||||
use glam::UVec2;
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use crate::{
|
use crate::draw::atlas::{TextureAtlasManager, TextureHandle};
|
||||||
draw::atlas::{TextureAtlasManager, TextureHandle},
|
|
||||||
IfModified
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::font::{FontHandle, FontManager};
|
use super::font::{FontHandle, FontManager};
|
||||||
|
|
||||||
|
@ -51,8 +47,8 @@ impl FontTextureManager {
|
||||||
}
|
}
|
||||||
let font = font_manager.get(font_handle).unwrap();
|
let font = font_manager.get(font_handle).unwrap();
|
||||||
let (metrics, bitmap) = font.rasterize(character, size as f32);
|
let (metrics, bitmap) = font.rasterize(character, size as f32);
|
||||||
log::debug!("rasterized glyph: {}, {:?}, {:?}", character, metrics, bitmap);
|
log::trace!("rasterized glyph: {}, {:?}, {:?}", character, metrics, bitmap);
|
||||||
let texture = atlas.add(metrics.width, &bitmap);
|
let texture = atlas.add_grayscale(metrics.width, &bitmap);
|
||||||
let entry = Arc::new(GlyphCacheEntry {
|
let entry = Arc::new(GlyphCacheEntry {
|
||||||
metrics,
|
metrics,
|
||||||
texture
|
texture
|
||||||
|
|
Loading…
Reference in a new issue