mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Implement text renderering with glyph_brush (lifetime error)
This commit is contained in:
parent
29526977d8
commit
a7458c099c
32
Cargo.lock
generated
32
Cargo.lock
generated
@ -1856,6 +1856,31 @@ dependencies = [
|
||||
"gl_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glyph_brush"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fca6f9d679bff1322c76c9a1ad4b8553b30a94f3f75bea6936e19032c2f2ec3"
|
||||
dependencies = [
|
||||
"glyph_brush_layout",
|
||||
"log",
|
||||
"ordered-float",
|
||||
"rustc-hash",
|
||||
"rusttype 0.8.3",
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glyph_brush_layout"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b70adc570f1dc71b6b32e241cbcc2b42175f5aea71951fbf41e68b04aec24c7"
|
||||
dependencies = [
|
||||
"approx",
|
||||
"rusttype 0.8.3",
|
||||
"xi-unicode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "guillotiere"
|
||||
version = "0.5.2"
|
||||
@ -5083,6 +5108,7 @@ dependencies = [
|
||||
"git2",
|
||||
"glsl-include",
|
||||
"glutin",
|
||||
"glyph_brush",
|
||||
"guillotiere",
|
||||
"hashbrown 0.7.2",
|
||||
"iced_native",
|
||||
@ -5667,6 +5693,12 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
|
||||
|
||||
[[package]]
|
||||
name = "xi-unicode"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e71b85d8b1b8bfaf4b5c834187554d201a8cd621c2bbfa33efd41a3ecabd48b2"
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.3"
|
||||
|
@ -37,6 +37,7 @@ conrod_winit = {git = "https://gitlab.com/veloren/conrod.git", branch="copypasta
|
||||
euc = {git = "https://github.com/zesterer/euc.git"}
|
||||
iced = {package = "iced_native", git = "https://github.com/hecrj/iced"}
|
||||
window_clipboard = "0.1.1"
|
||||
glyph_brush = "0.6.3"
|
||||
|
||||
# ECS
|
||||
specs = {git = "https://github.com/amethyst/specs.git", rev = "7a2e348ab2223818bad487695c66c43db88050a5"}
|
||||
|
@ -378,7 +378,18 @@ impl<'a> MainMenuUi {
|
||||
let fonts = ConrodVoxygenFonts::load(&voxygen_i18n.fonts, &mut ui)
|
||||
.expect("Impossible to load fonts!");
|
||||
|
||||
let mut ice_ui = IcedUi::new(window).unwrap();
|
||||
// TODO: newtype Font
|
||||
let ice_font = {
|
||||
use std::io::Read;
|
||||
let mut buf = Vec::new();
|
||||
common::assets::load_file("voxygen.font.OpenSans-Regular", &["ttf"])
|
||||
.unwrap()
|
||||
.read_to_end(&mut buf)
|
||||
.unwrap();
|
||||
glyph_brush::rusttype::Font::from_bytes(buf).unwrap()
|
||||
};
|
||||
|
||||
let mut ice_ui = IcedUi::new(window, ice_font).unwrap();
|
||||
let ice_state = IcedState {
|
||||
imgs: IcedImgs::load(&mut ice_ui).expect("Failed to load images"),
|
||||
};
|
||||
|
@ -57,6 +57,21 @@ impl<P: Pipeline> Mesh<P> {
|
||||
self.verts.push(quad.a);
|
||||
}
|
||||
|
||||
/// Overwrite a quad
|
||||
pub fn replace_quad(&mut self, index: usize, quad: Quad<P>) {
|
||||
debug_assert!(index % 3 == 0);
|
||||
assert!(index + 5 < self.verts.len());
|
||||
// Tri 1
|
||||
self.verts[index] = quad.a.clone();
|
||||
self.verts[index + 1] = quad.b;
|
||||
self.verts[index + 2] = quad.c.clone();
|
||||
|
||||
// Tri 2
|
||||
self.verts[index + 3] = quad.c;
|
||||
self.verts[index + 4] = quad.d;
|
||||
self.verts[index + 5] = quad.a;
|
||||
}
|
||||
|
||||
/// Push the vertices of another mesh onto the end of this mesh.
|
||||
pub fn push_mesh(&mut self, other: &Mesh<P>) { self.verts.extend_from_slice(other.vertices()); }
|
||||
|
||||
|
153
voxygen/src/ui/ice/cache.rs
Normal file
153
voxygen/src/ui/ice/cache.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use super::{
|
||||
graphic::{Graphic, GraphicCache, Id as GraphicId},
|
||||
renderer::{IcedRenderer, Primitive},
|
||||
};
|
||||
use crate::{
|
||||
render::{Renderer, Texture},
|
||||
Error,
|
||||
};
|
||||
use glyph_brush::{
|
||||
GlyphBrushBuilder, GlyphCalculator, GlyphCalculatorBuilder, GlyphCalculatorGuard,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use vek::*;
|
||||
|
||||
// Multiplied by current window size
|
||||
const GLYPH_CACHE_SIZE: u16 = 1;
|
||||
// Glyph cache tolerances
|
||||
const SCALE_TOLERANCE: f32 = 0.1;
|
||||
const POSITION_TOLERANCE: f32 = 0.1;
|
||||
|
||||
type GlyphBrush = glyph_brush::GlyphBrush<'static, (Aabr<f32>, Aabr<f32>)>;
|
||||
|
||||
pub type Font = glyph_brush::rusttype::Font<'static>;
|
||||
|
||||
pub struct Cache {
|
||||
glyph_cache: GlyphBrush,
|
||||
glyph_cache_tex: Texture,
|
||||
graphic_cache: GraphicCache,
|
||||
}
|
||||
|
||||
// TODO: Should functions be returning UiError instead of Error?
|
||||
impl Cache {
|
||||
pub fn new(renderer: &mut Renderer, default_font: Font) -> Result<Self, Error> {
|
||||
let (w, h) = renderer.get_resolution().into_tuple();
|
||||
|
||||
let max_texture_size = renderer.max_texture_size();
|
||||
|
||||
let glyph_cache_dims =
|
||||
Vec2::new(w, h).map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size as u16).max(512));
|
||||
|
||||
Ok(Self {
|
||||
glyph_cache: GlyphBrushBuilder::using_font(default_font)
|
||||
.initial_cache_size((glyph_cache_dims.x as u32, glyph_cache_dims.y as u32))
|
||||
.gpu_cache_scale_tolerance(SCALE_TOLERANCE)
|
||||
.gpu_cache_position_tolerance(POSITION_TOLERANCE)
|
||||
.build(),
|
||||
glyph_cache_tex: renderer.create_dynamic_texture(glyph_cache_dims.map(|e| e as u16))?,
|
||||
graphic_cache: GraphicCache::new(renderer),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn glyph_cache_tex(&self) -> &Texture { &self.glyph_cache_tex }
|
||||
|
||||
pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphBrush, &Texture) {
|
||||
(&mut self.glyph_cache, &self.glyph_cache_tex)
|
||||
}
|
||||
|
||||
pub fn glyph_cache_mut(&mut self) -> &mut GlyphBrush { &mut self.glyph_cache }
|
||||
|
||||
// TODO: add font fn
|
||||
|
||||
pub fn graphic_cache(&self) -> &GraphicCache { &self.graphic_cache }
|
||||
|
||||
pub fn graphic_cache_mut(&mut self) -> &mut GraphicCache { &mut self.graphic_cache }
|
||||
|
||||
pub fn add_graphic(&mut self, graphic: Graphic) -> GraphicId {
|
||||
self.graphic_cache.add_graphic(graphic)
|
||||
}
|
||||
|
||||
pub fn replace_graphic(&mut self, id: GraphicId, graphic: Graphic) {
|
||||
self.graphic_cache.replace_graphic(id, graphic)
|
||||
}
|
||||
|
||||
// Resizes and clears the GraphicCache
|
||||
pub fn resize_graphic_cache(&mut self, renderer: &mut Renderer) {
|
||||
self.graphic_cache.clear_cache(renderer);
|
||||
}
|
||||
|
||||
// Resizes and clears the GlyphCache
|
||||
pub fn resize_glyph_cache(&mut self, renderer: &mut Renderer) -> Result<(), Error> {
|
||||
let max_texture_size = renderer.max_texture_size();
|
||||
let cache_dims = renderer
|
||||
.get_resolution()
|
||||
.map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size as u16).max(512));
|
||||
self.glyph_cache = self
|
||||
.glyph_cache
|
||||
.to_builder()
|
||||
.initial_cache_size((cache_dims.x as u32, cache_dims.y as u32))
|
||||
.build();
|
||||
self.glyph_cache_tex = renderer.create_dynamic_texture(cache_dims.map(|e| e as u16))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlyphCalcCache {
|
||||
// Hold one of these for adding new fonts
|
||||
builder: GlyphCalculatorBuilder<'static>,
|
||||
calculator: GlyphCalculator<'static>,
|
||||
}
|
||||
|
||||
impl GlyphCalcCache {
|
||||
pub fn new(default_font: Font) -> Self {
|
||||
// Multiple copies of the font in memory :/ (using Arc<[u8]> might help)
|
||||
let builder = GlyphCalculatorBuilder::using_font(default_font);
|
||||
let calculator = builder.clone().build();
|
||||
|
||||
Self {
|
||||
builder,
|
||||
calculator,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn frame_guard<'a>(&'a self) -> GlyphCalculatorGuard<'a, 'static> {
|
||||
self.calculator.cache_scope()
|
||||
}
|
||||
|
||||
// add new font fn
|
||||
}
|
||||
|
||||
pub struct FrameRenderer<'a> {
|
||||
pub renderer: &'a mut IcedRenderer,
|
||||
pub glyph_calc: RefCell<GlyphCalculatorGuard<'a, 'static>>,
|
||||
}
|
||||
|
||||
impl<'a> FrameRenderer<'a> {
|
||||
pub fn new(renderer: &'a mut IcedRenderer, glyph_calc_cache: &'a mut GlyphCalcCache) -> Self {
|
||||
Self {
|
||||
renderer,
|
||||
glyph_calc: RefCell::new(glyph_calc_cache.frame_guard()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl iced::Renderer for FrameRenderer<'_> {
|
||||
// Default styling
|
||||
type Defaults = ();
|
||||
// TODO: use graph of primitives to enable diffing???
|
||||
type Output = (Primitive, iced::mouse::Interaction);
|
||||
|
||||
fn layout<'a, M>(
|
||||
&mut self,
|
||||
element: &iced::Element<'a, M, Self>,
|
||||
limits: &iced::layout::Limits,
|
||||
) -> iced::layout::Node {
|
||||
let node = element.layout(self, limits);
|
||||
|
||||
// Trim text measurements cache?
|
||||
|
||||
node
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: impl Debugger
|
@ -1,9 +1,11 @@
|
||||
// tooltip_manager: TooltipManager,
|
||||
mod cache;
|
||||
mod clipboard;
|
||||
mod renderer;
|
||||
mod widget;
|
||||
mod winit_conversion;
|
||||
|
||||
pub use cache::Font;
|
||||
pub use graphic::{Id, Rotation};
|
||||
pub use iced::Event;
|
||||
pub use renderer::IcedRenderer;
|
||||
@ -19,14 +21,16 @@ use super::{
|
||||
scale::{Scale, ScaleMode},
|
||||
};
|
||||
use crate::{render::Renderer, window::Window, Error};
|
||||
use cache::{FrameRenderer, GlyphCalcCache};
|
||||
use clipboard::Clipboard;
|
||||
use iced::{Cache, MouseCursor, Size, UserInterface};
|
||||
use iced::{mouse, Cache, Size, UserInterface};
|
||||
use vek::*;
|
||||
|
||||
pub type Element<'a, M> = iced::Element<'a, M, IcedRenderer>;
|
||||
pub type Element<'a, 'b, M> = iced::Element<'a, M, FrameRenderer<'b>>;
|
||||
|
||||
pub struct IcedUi {
|
||||
renderer: IcedRenderer,
|
||||
glyph_calc_cache: GlyphCalcCache,
|
||||
cache: Option<Cache>,
|
||||
events: Vec<Event>,
|
||||
clipboard: Clipboard,
|
||||
@ -35,14 +39,16 @@ pub struct IcedUi {
|
||||
window_resized: Option<Vec2<u32>>,
|
||||
}
|
||||
impl IcedUi {
|
||||
pub fn new(window: &mut Window) -> Result<Self, Error> {
|
||||
pub fn new(window: &mut Window, default_font: Font) -> Result<Self, Error> {
|
||||
let scale = Scale::new(window, ScaleMode::Absolute(1.0));
|
||||
let renderer = window.renderer_mut();
|
||||
|
||||
let scaled_dims = scale.scaled_window_size().map(|e| e as f32);
|
||||
|
||||
// TODO: examine how much mem fonts take up and reduce clones if significant
|
||||
Ok(Self {
|
||||
renderer: IcedRenderer::new(renderer, scaled_dims)?,
|
||||
renderer: IcedRenderer::new(renderer, scaled_dims, default_font.clone())?,
|
||||
glyph_calc_cache: GlyphCalcCache::new(default_font),
|
||||
cache: Some(Cache::new()),
|
||||
events: Vec::new(),
|
||||
// TODO: handle None
|
||||
@ -58,7 +64,7 @@ impl IcedUi {
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, event: Event) {
|
||||
use iced::{input::mouse, window};
|
||||
use iced::window;
|
||||
match event {
|
||||
// Intercept resizing events
|
||||
Event::Window(window::Event::Resized { width, height }) => {
|
||||
@ -93,11 +99,12 @@ impl IcedUi {
|
||||
}
|
||||
|
||||
// TODO: produce root internally???
|
||||
pub fn maintain<'a, M, E: Into<Element<'a, M>>>(
|
||||
// TODO: see if this lifetime soup can be simplified
|
||||
pub fn maintain<'a, 'b, M, E: Into<iced::Element<'a, M, FrameRenderer<'b>>>>(
|
||||
&mut self,
|
||||
root: E,
|
||||
renderer: &mut Renderer,
|
||||
) -> (Vec<M>, MouseCursor) {
|
||||
) -> (Vec<M>, mouse::Interaction) {
|
||||
// Handle window resizing
|
||||
if let Some(new_dims) = self.window_resized.take() {
|
||||
let old_scaled_dims = self.scale.scaled_window_size();
|
||||
@ -126,23 +133,28 @@ impl IcedUi {
|
||||
// TODO: convert to f32 at source
|
||||
let window_size = self.scale.scaled_window_size().map(|e| e as f32);
|
||||
|
||||
let mut frame_renderer = FrameRenderer::new(&mut self.renderer, &mut self.glyph_calc_cache);
|
||||
|
||||
let mut user_interface = UserInterface::build(
|
||||
root,
|
||||
Size::new(window_size.x, window_size.y),
|
||||
self.cache.take().unwrap(),
|
||||
&mut self.renderer,
|
||||
&mut frame_renderer,
|
||||
);
|
||||
|
||||
let messages =
|
||||
user_interface.update(self.events.drain(..), Some(&self.clipboard), &self.renderer);
|
||||
let messages = user_interface.update(
|
||||
self.events.drain(..),
|
||||
Some(&self.clipboard),
|
||||
&frame_renderer,
|
||||
);
|
||||
|
||||
let (primitive, mouse_cursor) = user_interface.draw(&mut self.renderer);
|
||||
|
||||
self.renderer.draw(primitive, renderer);
|
||||
let (primitive, mouse_interaction) = user_interface.draw(&mut frame_renderer);
|
||||
|
||||
self.cache = Some(user_interface.into_cache());
|
||||
|
||||
(messages, mouse_cursor)
|
||||
self.renderer.draw(primitive, renderer);
|
||||
|
||||
(messages, mouse_interaction)
|
||||
}
|
||||
|
||||
pub fn render(&self, renderer: &mut Renderer) { self.renderer.render(renderer, None); }
|
||||
|
@ -7,11 +7,9 @@ mod row;
|
||||
mod space;
|
||||
|
||||
use super::{
|
||||
super::{
|
||||
cache::Cache,
|
||||
graphic::{self, Graphic, TexId},
|
||||
},
|
||||
widget, Rotation,
|
||||
super::graphic::{self, Graphic, TexId},
|
||||
cache::Cache,
|
||||
widget, Font, Rotation,
|
||||
};
|
||||
use crate::{
|
||||
render::{
|
||||
@ -70,10 +68,23 @@ pub enum Primitive {
|
||||
bounds: iced::Rectangle,
|
||||
color: Rgba<u8>,
|
||||
},
|
||||
Text {
|
||||
glyphs: Vec<(
|
||||
glyph_brush::rusttype::PositionedGlyph<'static>,
|
||||
glyph_brush::Color,
|
||||
glyph_brush::FontId,
|
||||
)>,
|
||||
//size: f32,
|
||||
bounds: iced::Rectangle,
|
||||
linear_color: Rgba<f32>,
|
||||
/*font: iced::Font,
|
||||
*horizontal_alignment: iced::HorizontalAlignment,
|
||||
*vertical_alignment: iced::VerticalAlignment, */
|
||||
},
|
||||
Nothing,
|
||||
}
|
||||
|
||||
// Optimization idea inspired by what I think iced wgpu renderer may be doing
|
||||
// Optimization idea inspired by what I think iced wgpu renderer may be doing:
|
||||
// Could have layers of things which don't intersect and thus can be reordered
|
||||
// arbitrarily
|
||||
|
||||
@ -90,35 +101,51 @@ pub struct IcedRenderer {
|
||||
|
||||
// Used to delay cache resizing until after current frame is drawn
|
||||
//need_cache_resize: bool,
|
||||
// Half of physical resolution
|
||||
half_res: Vec2<f32>,
|
||||
// Pixel perfection alignment
|
||||
align: Vec2<f32>,
|
||||
// Scale factor between physical and win dims
|
||||
p_scale: f32,
|
||||
// Pretend dims :) (i.e. scaled)
|
||||
win_dims: Vec2<f32>,
|
||||
|
||||
// Per-frame/update
|
||||
current_state: State,
|
||||
mesh: Mesh<UiPipeline>,
|
||||
glyphs: Vec<(usize, usize, Rgba<f32>)>,
|
||||
// Output from glyph_brush in the previous frame
|
||||
// It can sometimes ask you to redraw with these instead (idk if that is done with
|
||||
// pre-positioned glyphs)
|
||||
last_glyph_verts: Vec<(Aabr<f32>, Aabr<f32>)>,
|
||||
start: usize,
|
||||
// Draw commands for the next render
|
||||
draw_commands: Vec<DrawCommand>,
|
||||
//current_scissor: Aabr<u16>,
|
||||
}
|
||||
impl IcedRenderer {
|
||||
pub fn new(renderer: &mut Renderer, scaled_dims: Vec2<f32>) -> Result<Self, Error> {
|
||||
let (half_res, align) = Self::calculate_resolution_dependents(renderer.get_resolution());
|
||||
pub fn new(
|
||||
renderer: &mut Renderer,
|
||||
scaled_dims: Vec2<f32>,
|
||||
default_font: Font,
|
||||
) -> Result<Self, Error> {
|
||||
let (half_res, align, p_scale) =
|
||||
Self::calculate_resolution_dependents(renderer.get_resolution(), scaled_dims);
|
||||
|
||||
Ok(Self {
|
||||
cache: Cache::new(renderer)?,
|
||||
cache: Cache::new(renderer, default_font)?,
|
||||
draw_commands: Vec::new(),
|
||||
model: renderer.create_dynamic_model(100)?,
|
||||
interface_locals: renderer.create_consts(&[UiLocals::default()])?,
|
||||
default_globals: renderer.create_consts(&[Globals::default()])?,
|
||||
ingame_locals: Vec::new(),
|
||||
mesh: Mesh::new(),
|
||||
glyphs: Vec::new(),
|
||||
last_glyph_verts: Vec::new(),
|
||||
current_state: State::Plain,
|
||||
half_res,
|
||||
align,
|
||||
p_scale,
|
||||
win_dims: scaled_dims,
|
||||
start: 0,
|
||||
//current_scissor: default_scissor(renderer),
|
||||
@ -144,6 +171,7 @@ impl IcedRenderer {
|
||||
// Re-use memory
|
||||
self.draw_commands.clear();
|
||||
self.mesh.clear();
|
||||
self.glyphs.clear();
|
||||
|
||||
self.current_state = State::Plain;
|
||||
self.start = 0;
|
||||
@ -177,6 +205,80 @@ impl IcedRenderer {
|
||||
self.draw_commands
|
||||
.push(DrawCommand::plain(start..mesh.vertices().len()));*/
|
||||
|
||||
// Fill in placeholder glyph quads
|
||||
let (glyph_cache, cache_tex) = self.cache.glyph_cache_mut_and_tex();
|
||||
let half_res = self.half_res;
|
||||
|
||||
let brush_result = glyph_cache.process_queued(
|
||||
|rect, tex_data| {
|
||||
let offset = [rect.min.x as u16, rect.min.y as u16];
|
||||
let size = [rect.width() as u16, rect.height() as u16];
|
||||
|
||||
let new_data = tex_data
|
||||
.iter()
|
||||
.map(|x| [255, 255, 255, *x])
|
||||
.collect::<Vec<[u8; 4]>>();
|
||||
|
||||
if let Err(err) = renderer.update_texture(cache_tex, offset, size, &new_data) {
|
||||
log::warn!("Failed to update glyph cache texture: {:?}", err);
|
||||
}
|
||||
},
|
||||
// Urgh more allocation we don't need
|
||||
|vertex_data| {
|
||||
let uv_rect = vertex_data.tex_coords;
|
||||
let uv = Aabr {
|
||||
min: Vec2::new(uv_rect.min.x, uv_rect.max.y),
|
||||
max: Vec2::new(uv_rect.max.x, uv_rect.min.y),
|
||||
};
|
||||
let pixel_coords = vertex_data.pixel_coords;
|
||||
let rect = Aabr {
|
||||
min: Vec2::new(
|
||||
pixel_coords.min.x as f32 / half_res.x - 1.0,
|
||||
pixel_coords.min.y as f32 / half_res.y - 1.0,
|
||||
),
|
||||
max: Vec2::new(
|
||||
pixel_coords.max.x as f32 / half_res.x - 1.0,
|
||||
pixel_coords.max.y as f32 / half_res.y - 1.0,
|
||||
),
|
||||
};
|
||||
(uv, rect)
|
||||
},
|
||||
);
|
||||
|
||||
match brush_result {
|
||||
Ok(brush_action) => {
|
||||
match brush_action {
|
||||
glyph_brush::BrushAction::Draw(verts) => self.last_glyph_verts = verts,
|
||||
glyph_brush::BrushAction::ReDraw => {},
|
||||
}
|
||||
|
||||
let glyphs = &self.glyphs;
|
||||
let mesh = &mut self.mesh;
|
||||
|
||||
glyphs
|
||||
.iter()
|
||||
.flat_map(|(mesh_index, glyph_count, linear_color)| {
|
||||
let mesh_index = *mesh_index;
|
||||
let linear_color = *linear_color;
|
||||
(0..*glyph_count).map(move |i| (mesh_index + i * 6, linear_color))
|
||||
})
|
||||
.zip(self.last_glyph_verts.iter())
|
||||
.for_each(|((mesh_index, linear_color), (uv, rect))| {
|
||||
mesh.replace_quad(
|
||||
mesh_index,
|
||||
create_ui_quad(*rect, *uv, linear_color, UiMode::Text),
|
||||
)
|
||||
});
|
||||
},
|
||||
Err(glyph_brush::BrushError::TextureTooSmall { suggested: (x, y) }) => {
|
||||
log::error!(
|
||||
"Texture to small for all glyphs, would need one of the size: ({}, {})",
|
||||
x,
|
||||
y
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
// Create a larger dynamic model if the mesh is larger than the current model
|
||||
// size.
|
||||
if self.model.vbuf.len() < self.mesh.vertices().len() {
|
||||
@ -203,17 +305,23 @@ impl IcedRenderer {
|
||||
}
|
||||
|
||||
// Returns (half_res, align)
|
||||
fn calculate_resolution_dependents(res: Vec2<u16>) -> (Vec2<f32>, Vec2<f32>) {
|
||||
fn calculate_resolution_dependents(
|
||||
res: Vec2<u16>,
|
||||
win_dims: Vec2<f32>,
|
||||
) -> (Vec2<f32>, Vec2<f32>, f32) {
|
||||
let half_res = res.map(|e| e as f32 / 2.0);
|
||||
let align = align(res);
|
||||
// Assume to be the same in x and y for now...
|
||||
let p_scale = res.x as f32 / win_dims.x;
|
||||
|
||||
(half_res, align)
|
||||
(half_res, align, p_scale)
|
||||
}
|
||||
|
||||
fn update_resolution_dependents(&mut self, res: Vec2<u16>) {
|
||||
let (half_res, align) = Self::calculate_resolution_dependents(res);
|
||||
let (half_res, align, p_scale) = Self::calculate_resolution_dependents(res, self.win_dims);
|
||||
self.half_res = half_res;
|
||||
self.align = align;
|
||||
self.p_scale = p_scale;
|
||||
}
|
||||
|
||||
fn gl_aabr(&self, bounds: iced::Rectangle) -> Aabr<f32> {
|
||||
@ -349,6 +457,63 @@ impl IcedRenderer {
|
||||
UiMode::Geometry,
|
||||
));
|
||||
},
|
||||
Primitive::Text {
|
||||
glyphs,
|
||||
bounds, // iced::Rectangle
|
||||
linear_color,
|
||||
/*font,
|
||||
*horizontal_alignment,
|
||||
*vertical_alignment, */
|
||||
} => {
|
||||
self.switch_state(State::Plain);
|
||||
|
||||
// TODO: Scissor?
|
||||
|
||||
// TODO: makes sure we are not doing all this work for hidden text
|
||||
// e.g. in chat
|
||||
let glyph_cache = self.cache.glyph_cache_mut();
|
||||
|
||||
// Count glyphs
|
||||
let glyph_count = glyphs.len();
|
||||
|
||||
// Queue the glyphs to be cached.
|
||||
glyph_cache.queue_pre_positioned(
|
||||
glyphs,
|
||||
// Since we already passed in `bounds` to position the glyphs some of this
|
||||
// seems redundant...
|
||||
glyph_brush::rusttype::Rect {
|
||||
min: glyph_brush::rusttype::Point {
|
||||
x: bounds.x,
|
||||
y: bounds.y,
|
||||
},
|
||||
max: glyph_brush::rusttype::Point {
|
||||
x: bounds.x + bounds.width,
|
||||
y: bounds.y + bounds.height,
|
||||
},
|
||||
},
|
||||
0.0, // z (we don't use this)
|
||||
);
|
||||
|
||||
// Leave ui and verts blank to fill in when processing cached glyphs
|
||||
let zero_aabr = Aabr {
|
||||
min: Vec2::broadcast(0.0),
|
||||
max: Vec2::broadcast(0.0),
|
||||
};
|
||||
self.glyphs
|
||||
.push((self.mesh.vertices().len(), glyph_count, linear_color));
|
||||
for _ in 0..glyph_count {
|
||||
// Push placeholder quad
|
||||
// Note: moving to some sort of layering / z based system would be an
|
||||
// alternative to this (and might help with reducing draw
|
||||
// calls)
|
||||
self.mesh.push_quad(create_ui_quad(
|
||||
zero_aabr,
|
||||
zero_aabr,
|
||||
linear_color,
|
||||
UiMode::Text,
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::Nothing => {},
|
||||
}
|
||||
}
|
||||
@ -414,24 +579,3 @@ fn default_scissor(renderer: &Renderer) -> Aabr<u16> {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl iced::Renderer for IcedRenderer {
|
||||
// Default styling
|
||||
type Defaults = ();
|
||||
// TODO: use graph of primitives to enable diffing???
|
||||
type Output = (Primitive, iced::MouseCursor);
|
||||
|
||||
fn layout<'a, M>(
|
||||
&mut self,
|
||||
element: &iced::Element<'a, M, Self>,
|
||||
limits: &iced::layout::Limits,
|
||||
) -> iced::layout::Node {
|
||||
let node = element.layout(self, limits);
|
||||
|
||||
// Trim text measurements cache?
|
||||
|
||||
node
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: impl Debugger
|
||||
|
@ -1,7 +1,10 @@
|
||||
use super::{super::widget::background_container, IcedRenderer, Primitive};
|
||||
use super::{
|
||||
super::{cache::FrameRenderer, widget::background_container},
|
||||
Primitive,
|
||||
};
|
||||
use iced::{Element, Layout, Point};
|
||||
|
||||
impl background_container::Renderer for IcedRenderer {
|
||||
impl background_container::Renderer for FrameRenderer<'_> {
|
||||
fn draw<M, B>(
|
||||
&mut self,
|
||||
defaults: &Self::Defaults,
|
||||
@ -17,13 +20,13 @@ impl background_container::Renderer for IcedRenderer {
|
||||
let back_primitive = background
|
||||
.draw(self, defaults, background_layout, cursor_position)
|
||||
.0;
|
||||
let (content_primitive, mouse_cursor) =
|
||||
let (content_primitive, mouse_interaction) =
|
||||
content.draw(self, defaults, content_layout, cursor_position);
|
||||
(
|
||||
Primitive::Group {
|
||||
primitives: vec![back_primitive, content_primitive],
|
||||
},
|
||||
mouse_cursor,
|
||||
mouse_interaction,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,34 @@
|
||||
use super::{IcedRenderer, Primitive};
|
||||
use iced::{column, Element, Layout, MouseCursor, Point};
|
||||
use super::{super::cache::FrameRenderer, Primitive};
|
||||
use iced::{column, mouse, Element, Layout, Point};
|
||||
|
||||
impl column::Renderer for IcedRenderer {
|
||||
impl column::Renderer for FrameRenderer<'_> {
|
||||
fn draw<M>(
|
||||
&mut self,
|
||||
defaults: &Self::Defaults,
|
||||
children: &[Element<'_, M, Self>],
|
||||
content: &[Element<'_, M, Self>],
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
) -> Self::Output {
|
||||
let mut mouse_cursor = MouseCursor::OutOfBounds;
|
||||
let mut mouse_interaction = mouse::Interaction::default();
|
||||
|
||||
(
|
||||
Primitive::Group {
|
||||
primitives: children
|
||||
primitives: content
|
||||
.iter()
|
||||
.zip(layout.children())
|
||||
.map(|(child, layout)| {
|
||||
let (primitive, new_mouse_cursor) =
|
||||
let (primitive, new_mouse_interaction) =
|
||||
child.draw(self, defaults, layout, cursor_position);
|
||||
|
||||
if new_mouse_cursor > mouse_cursor {
|
||||
mouse_cursor = new_mouse_cursor;
|
||||
if new_mouse_interaction > mouse_interaction {
|
||||
mouse_interaction = new_mouse_interaction;
|
||||
}
|
||||
|
||||
primitive
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
mouse_cursor,
|
||||
mouse_interaction,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
use super::{
|
||||
super::{widget::compound_graphic, Rotation},
|
||||
IcedRenderer, Primitive,
|
||||
super::{cache::FrameRenderer, widget::compound_graphic, Rotation},
|
||||
Primitive,
|
||||
};
|
||||
use compound_graphic::GraphicKind;
|
||||
use iced::{MouseCursor, Rectangle};
|
||||
use iced::{mouse, Rectangle};
|
||||
|
||||
impl compound_graphic::Renderer for IcedRenderer {
|
||||
impl compound_graphic::Renderer for FrameRenderer<'_> {
|
||||
fn draw<I>(
|
||||
&mut self,
|
||||
graphics: I,
|
||||
@ -28,7 +28,7 @@ impl compound_graphic::Renderer for IcedRenderer {
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
MouseCursor::OutOfBounds,
|
||||
mouse::Interaction::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::IcedRenderer;
|
||||
use super::super::cache::FrameRenderer;
|
||||
use iced::{container, Element, Layout, Point, Rectangle};
|
||||
|
||||
impl container::Renderer for IcedRenderer {
|
||||
impl container::Renderer for FrameRenderer<'_> {
|
||||
type Style = ();
|
||||
|
||||
fn draw<M>(
|
||||
@ -13,10 +13,11 @@ impl container::Renderer for IcedRenderer {
|
||||
content: &Element<'_, M, Self>,
|
||||
content_layout: Layout<'_>,
|
||||
) -> Self::Output {
|
||||
let (content, mouse_cursor) = content.draw(self, defaults, content_layout, cursor_position);
|
||||
let (content, mouse_interaction) =
|
||||
content.draw(self, defaults, content_layout, cursor_position);
|
||||
|
||||
// We may have more stuff here if styles are used
|
||||
|
||||
(content, mouse_cursor)
|
||||
(content, mouse_interaction)
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
use super::{
|
||||
super::{widget::image, Rotation},
|
||||
IcedRenderer, Primitive,
|
||||
super::{cache::FrameRenderer, widget::image, Rotation},
|
||||
Primitive,
|
||||
};
|
||||
use iced::MouseCursor;
|
||||
use iced::mouse;
|
||||
use vek::Rgba;
|
||||
|
||||
impl image::Renderer for IcedRenderer {
|
||||
impl image::Renderer for FrameRenderer<'_> {
|
||||
fn dimensions(&self, handle: image::Handle) -> (u32, u32) {
|
||||
self.cache
|
||||
self.renderer
|
||||
.cache
|
||||
.graphic_cache()
|
||||
.get_graphic_dims((handle, Rotation::None))
|
||||
// TODO: don't unwrap
|
||||
@ -26,7 +27,7 @@ impl image::Renderer for IcedRenderer {
|
||||
bounds: layout.bounds(),
|
||||
color,
|
||||
},
|
||||
MouseCursor::OutOfBounds,
|
||||
mouse::Interaction::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,34 @@
|
||||
use super::{IcedRenderer, Primitive};
|
||||
use iced::{row, Element, Layout, MouseCursor, Point};
|
||||
use super::{super::cache::FrameRenderer, Primitive};
|
||||
use iced::{mouse, row, Element, Layout, Point};
|
||||
|
||||
impl row::Renderer for IcedRenderer {
|
||||
impl row::Renderer for FrameRenderer<'_> {
|
||||
fn draw<M>(
|
||||
&mut self,
|
||||
defaults: &Self::Defaults,
|
||||
children: &[Element<'_, M, Self>],
|
||||
content: &[Element<'_, M, Self>],
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
) -> Self::Output {
|
||||
let mut mouse_cursor = MouseCursor::OutOfBounds;
|
||||
let mut mouse_interaction = mouse::Interaction::default();
|
||||
|
||||
(
|
||||
Primitive::Group {
|
||||
primitives: children
|
||||
primitives: content
|
||||
.iter()
|
||||
.zip(layout.children())
|
||||
.map(|(child, layout)| {
|
||||
let (primitive, new_mouse_cursor) =
|
||||
let (primitive, new_mouse_interaction) =
|
||||
child.draw(self, defaults, layout, cursor_position);
|
||||
|
||||
if new_mouse_cursor > mouse_cursor {
|
||||
mouse_cursor = new_mouse_cursor;
|
||||
if new_mouse_interaction > mouse_interaction {
|
||||
mouse_interaction = new_mouse_interaction;
|
||||
}
|
||||
|
||||
primitive
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
mouse_cursor,
|
||||
mouse_interaction,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use super::{IcedRenderer, Primitive};
|
||||
use iced::{space, MouseCursor, Rectangle};
|
||||
use super::{super::cache::FrameRenderer, Primitive};
|
||||
use iced::{mouse, space, Rectangle};
|
||||
|
||||
impl space::Renderer for IcedRenderer {
|
||||
impl space::Renderer for FrameRenderer<'_> {
|
||||
fn draw(&mut self, _bounds: Rectangle) -> Self::Output {
|
||||
(Primitive::Nothing, MouseCursor::OutOfBounds)
|
||||
(Primitive::Nothing, mouse::Interaction::default())
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,37 @@
|
||||
use super::{super::widget::stack, IcedRenderer, Primitive};
|
||||
use iced::{Element, Layout, MouseCursor, Point};
|
||||
use super::{
|
||||
super::{cache::FrameRenderer, widget::stack},
|
||||
Primitive,
|
||||
};
|
||||
use iced::{mouse, Element, Layout, Point};
|
||||
|
||||
impl stack::Renderer for IcedRenderer {
|
||||
impl stack::Renderer for FrameRenderer<'_> {
|
||||
fn draw<M>(
|
||||
&mut self,
|
||||
defaults: &Self::Defaults,
|
||||
children: &[Element<'_, M, Self>],
|
||||
content: &[Element<'_, M, Self>],
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
) -> Self::Output {
|
||||
let mut mouse_cursor = MouseCursor::OutOfBounds;
|
||||
let mut mouse_interaction = mouse::Interaction::default();
|
||||
|
||||
(
|
||||
Primitive::Group {
|
||||
primitives: children
|
||||
primitives: content
|
||||
.iter()
|
||||
.zip(layout.children())
|
||||
.map(|(child, layout)| {
|
||||
let (primitive, new_mouse_cursor) =
|
||||
let (primitive, new_mouse_interaction) =
|
||||
child.draw(self, defaults, layout, cursor_position);
|
||||
|
||||
if new_mouse_cursor > mouse_cursor {
|
||||
mouse_cursor = new_mouse_cursor;
|
||||
if new_mouse_interaction > mouse_interaction {
|
||||
mouse_interaction = new_mouse_interaction;
|
||||
}
|
||||
|
||||
primitive
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
mouse_cursor,
|
||||
mouse_interaction,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
97
voxygen/src/ui/ice/renderer/text.rs
Normal file
97
voxygen/src/ui/ice/renderer/text.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use iced::{
|
||||
text, Color, Font, HorizontalAlignment, mouse, Rectangle, Size, VerticalAlignment,
|
||||
};
|
||||
use super::{super::cache::FrameRenderer, Primitive}:
|
||||
|
||||
struct FontId(glyph_brush::FontId);
|
||||
|
||||
impl text::Renderer for FrameRenderer<'_> {
|
||||
type Font = FontId;
|
||||
const DEFAULT_SIZE: u16 = 20;
|
||||
|
||||
fn measure(
|
||||
&self,
|
||||
content: &str,
|
||||
size: u16,
|
||||
font: Self::Font,
|
||||
bounds: Size,
|
||||
) -> (f32, f32) {
|
||||
// Using the physical scale might make these cached info usable below?
|
||||
// Although we also have a position of the screen so this could be useless
|
||||
let p_scale = self.p_scale;
|
||||
// TODO: would be nice if the method was mut
|
||||
let section = glyph_brush::Section {
|
||||
text: content,
|
||||
scale: glyph_brush::rusttype::Scale::uniform(size as f32 * p_scale),
|
||||
font_id: font.0,
|
||||
bounds: (size.width, size.height),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let maybe_rect = self.glyph_calc.borrow_mut().glyph_bounds(section);
|
||||
maybe_rect.map_or((0.0, 0.0), |rect| (rect.width() / p_scale, rect.height() / p_scale))
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&mut self,
|
||||
defaults: &Self::Defaults,
|
||||
bounds: Rectangle,
|
||||
content: &str,
|
||||
size: u16,
|
||||
font: Self::Font,
|
||||
color: Option<Color>,
|
||||
horizontal_alignment: HorizontalAlignment,
|
||||
vertical_alignment: VerticalAlignment,
|
||||
) -> Self::Output {
|
||||
use glyph_brush::{HorizontalAlign, VerticalAlign};
|
||||
let h_align = match horizontal_alignment {
|
||||
HorizontalAlignment::Left => HorizontalAlign::Left,
|
||||
HorizontalAlignment::Center => HorizontalAlign::Center,
|
||||
HorizontalAlignment::Right => HorizontalAlign::Right,
|
||||
};
|
||||
|
||||
let v_align = match vertical_alignment {
|
||||
VerticalAlignment::Top => VerticalAlign::Top,
|
||||
VerticalAlignment::Center => VerticalAlign::Center,
|
||||
VerticalAlignment::Bottom => VerticalAlign::Bottom,
|
||||
};
|
||||
|
||||
let p_scale = self.p_scale;
|
||||
|
||||
let section = glyph_brush::Section {
|
||||
text: content,
|
||||
// TODO: do snap to pixel thing here IF it is being done down the line
|
||||
screen_position: (bounds.x * p_scale, bounds.y * p_scale),
|
||||
bounds: (bounds.width * p_scale, bounds.height * p_scale),
|
||||
scale: glyph_brush::rusttype::Scale::uniform(size as f32 * p_scale),
|
||||
layout: glyph_brush::Layout::Wrap {
|
||||
line_breaker: Default::default(),
|
||||
h_align,
|
||||
v_align,
|
||||
},
|
||||
font_id: font.0,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let glyphs = self.glyph_calc.borrow_mut().glyphs(section).map(|positioned_glyph|
|
||||
(
|
||||
positioned_glyph,
|
||||
[0.0, 0.0, 0.0, 1.0], // Color
|
||||
font.0,
|
||||
)
|
||||
).collect();
|
||||
|
||||
(
|
||||
Primitive::Text {
|
||||
glyphs,
|
||||
//size: size as f32,
|
||||
bounds,
|
||||
color: color.unwrap_or(Color::BLACK).into_linear().into(),
|
||||
//font,
|
||||
//horizontal_alignment,
|
||||
//vertical_alignment,
|
||||
},
|
||||
mouse::Interaction::default,
|
||||
)
|
||||
}
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
// Using reference impl: https://github.com/hecrj/iced/blob/e1438774af809c2951c4c7446638500446c81111/winit/src/conversion.rs
|
||||
use iced::{
|
||||
input::{
|
||||
keyboard::{self, KeyCode, ModifiersState},
|
||||
mouse, ButtonState,
|
||||
},
|
||||
window, Event,
|
||||
keyboard::{self, KeyCode, ModifiersState},
|
||||
mouse, window, Event,
|
||||
};
|
||||
|
||||
/// Converts a winit event into an iced event.
|
||||
@ -28,10 +25,14 @@ pub fn window_event(event: winit::WindowEvent) -> Option<Event> {
|
||||
y: position.y as f32,
|
||||
}))
|
||||
},
|
||||
WindowEvent::MouseInput { button, state, .. } => Some(Event::Mouse(mouse::Event::Input {
|
||||
button: mouse_button(button),
|
||||
state: button_state(state),
|
||||
})),
|
||||
WindowEvent::MouseInput { button, state, .. } => {
|
||||
let button = mouse_button(button);
|
||||
|
||||
Some(Event::Mouse(match state {
|
||||
winit::ElementState::Pressed => mouse::Event::ButtonPressed(button),
|
||||
winit::ElementState::Released => mouse::Event::ButtonReleased(button),
|
||||
}))
|
||||
},
|
||||
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||
winit::MouseScrollDelta::LineDelta(delta_x, delta_y) => {
|
||||
Some(Event::Mouse(mouse::Event::WheelScrolled {
|
||||
@ -63,10 +64,20 @@ pub fn window_event(event: winit::WindowEvent) -> Option<Event> {
|
||||
..
|
||||
},
|
||||
..
|
||||
} => Some(Event::Keyboard(keyboard::Event::Input {
|
||||
key_code: key_code(virtual_keycode),
|
||||
state: button_state(state),
|
||||
modifiers: modifiers_state(modifiers),
|
||||
} => Some(Event::Keyboard({
|
||||
let key_code = key_code(virtual_keycode);
|
||||
let modifiers = modifiers_state(modifiers);
|
||||
|
||||
match state {
|
||||
winit::ElementState::Pressed => keyboard::Event::KeyPressed {
|
||||
key_code,
|
||||
modifiers,
|
||||
},
|
||||
winit::ElementState::Released => keyboard::Event::KeyReleased {
|
||||
key_code,
|
||||
modifiers,
|
||||
},
|
||||
}
|
||||
})),
|
||||
// iced also can use file hovering events but we don't need them right now
|
||||
_ => None,
|
||||
@ -85,14 +96,6 @@ fn mouse_button(mouse_button: winit::MouseButton) -> mouse::Button {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts winit `ElementState` to an iced button state
|
||||
fn button_state(element_state: winit::ElementState) -> ButtonState {
|
||||
match element_state {
|
||||
winit::ElementState::Pressed => ButtonState::Pressed,
|
||||
winit::ElementState::Released => ButtonState::Released,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts winit `ModifiersState` to iced `ModifiersState`
|
||||
fn modifiers_state(modifiers: winit::ModifiersState) -> ModifiersState {
|
||||
ModifiersState {
|
||||
|
Loading…
Reference in New Issue
Block a user