#![allow(dead_code)] use std::cell::RefCell; use std::cmp::PartialEq; use std::collections::HashMap; use std::fmt::Display; use std::hash::{Hash, Hasher}; use std::mem::size_of; use std::ops::Deref; use std::ptr::null; use std::rc::{Rc, Weak}; use glc::{ array_buffer::{ArrayBuffer, Target, Usage}, error::Error as GlcError, misc::BindGuard, shader::{Program, Type}, texture::{FilterMag, FilterMin, Target as TextureTarget, Texture, Wrap}, vector::{Vector2f, Vector4f}, vertex_array::{DataType, VertexArray}, }; use glyph_brush::{ ab_glyph::{FontArc, FontVec}, BrushAction, BrushError, FontId, GlyphBrush, GlyphBrushBuilder, GlyphVertex, OwnedSection, OwnedText, Rectangle, }; use log::warn; use ordered_float::OrderedFloat; use specs::World; use crate::{misc::Vfs, Error}; use super::super::misc::WorldHelper; /* TextManager */ #[derive(Clone)] pub struct TextManager(Rc>); struct TextManagerInner { vfs: Vfs, program: Rc, fonts: HashMap, } impl TextManager { pub fn new(world: &World) -> Result { let vfs = world.resource::()?.deref().clone(); let program = world.load_program(vec![ (Type::Vertex, "resources/shader/text.vert"), (Type::Fragment, "resources/shader/text.frag"), ])?; let program = Rc::new(program); Ok(Self(Rc::new(RefCell::new(TextManagerInner { vfs, program, fonts: HashMap::new(), })))) } pub fn create_cache(&self) -> Result { TextCache::new(self) } pub fn font(&self, path: &str) -> Result { if let Some(font) = self.0.borrow().fonts.get(path) { return Ok(font.clone()); } let path = self.0.borrow().vfs.join(path)?; let mut data = Vec::new(); path.open_file()?.read_to_end(&mut data)?; let font = FontVec::try_from_vec(data)?; let font = FontArc::new(font); { let mut this = self.0.borrow_mut(); this.fonts.insert(path.filename(), font.clone()); this.fonts.insert(path.as_str().to_owned(), font.clone()); } Ok(font) } } /* TextCache */ #[derive(Clone)] pub struct TextCache(Rc>); pub struct TextCacheInner { manager: TextManager, fonts: HashMap, glyphs: Option>, texture: Rc>, texts: HashMap>>, text_ids: Vec, next_text_id: usize, update_counter: usize, update_requested: bool, } pub struct UpdateGuard(TextCache); impl TextCache { fn new(manager: &TextManager) -> Result { let mut texture = Texture::new(TextureTarget::Texture2D)?; texture.set_filter(FilterMin::Linear, FilterMag::Linear)?; texture.set_wrap(Wrap::ClampToEdge, Wrap::ClampToEdge, Wrap::ClampToEdge)?; Ok(Self(Rc::new(RefCell::new(TextCacheInner { manager: manager.clone(), fonts: HashMap::new(), glyphs: None, texture: Rc::new(RefCell::new(texture)), texts: HashMap::new(), text_ids: Vec::new(), next_text_id: 0, update_counter: 0, update_requested: false, })))) } pub fn new_text(&self) -> TextBuilder { TextBuilder::new(self) } pub fn begin_update(&self) -> UpdateGuard { self.0.borrow_mut().update_counter += 1; UpdateGuard(self.clone()) } fn end_update(&self) { let mut inner = self.0.borrow_mut(); if inner.update_counter > 0 { inner.update_counter -= 1; } if inner.update_counter > 0 { return; } let update_requested = inner.update_requested; drop(inner); if update_requested { if let Err(err) = self.update() { warn!("Unable to update text cache: {}", err); } } } fn font(&self, path: &str) -> Result { let mut inner = self.0.borrow_mut(); if let Some(id) = inner.fonts.get(path) { return Ok(*id); } let font = inner.manager.font(path)?; inner.add_font(font) } fn next_text_id(&self) -> usize { let mut inner = self.0.borrow_mut(); if let Some(id) = inner.text_ids.pop() { return id; } let ret = inner.next_text_id; inner.next_text_id += 1; ret } fn add_text(&self, text: Weak>) -> Result<(), Error> { let text_id = self.next_text_id(); for section in &mut text.upgrade().unwrap().borrow_mut().sections { for t in &mut section.text { t.extra.text_id = text_id; } } self.0.borrow_mut().texts.insert(text_id, text); self.update() } fn update(&self) -> Result<(), Error> { let mut inner = self.0.borrow_mut(); let inner = &mut *inner; if inner.update_counter > 0 { inner.update_requested = true; return Ok(()); } /* remove droped texts */ let texts = &mut inner.texts; let text_ids = &mut inner.text_ids; texts.retain(|id, t| { if t.strong_count() > 0 { true } else { text_ids.push(*id); false } }); /* add sections to glyph queue */ let glyphs = inner.glyphs.as_mut().unwrap(); for text in texts.values_mut() { for section in text.upgrade().unwrap().borrow_mut().sections.iter_mut() { glyphs.queue(section.to_borrowed()); } } /* process glyph queue */ let action = loop { let glyphs = inner.glyphs.as_mut().unwrap(); let texture = inner.texture.borrow(); let ret = glyphs.process_queued( move |rect, data| update_texture(&*texture, rect, data), create_vertex, ); match ret { Ok(action) => break action, Err(BrushError::TextureTooSmall { suggested, .. }) => { let new_size = glyphs.texture_dimensions(); let new_size = resize_texture(suggested, Some(new_size))?; glyphs.resize_texture(new_size.0, new_size.1); } } }; // upate vertex data if let BrushAction::Draw(vertices) = action { let mut map = Vec::>::new(); map.resize_with(inner.next_text_id, Default::default); for vertex in vertices { map[vertex.text_id].push(vertex.data); } for (text_id, data) in map.into_iter().enumerate() { if data.is_empty() { continue; } inner .texts .get(&text_id) .unwrap() .upgrade() .unwrap() .borrow_mut() .update(data)?; } } inner.update_requested = false; Ok(()) } } impl TextCacheInner { fn add_font(&mut self, font: FontArc) -> Result { let (id, size) = match self.glyphs.as_mut() { Some(glyphs) => (glyphs.add_font(font), None), None => { let glyphs = GlyphBrushBuilder::using_font(font) .draw_cache_position_tolerance(2.0) .build(); let size = glyphs.texture_dimensions(); self.glyphs = Some(glyphs); (FontId(0), Some(size)) } }; if let Some(size) = size { let texture = self.texture.borrow(); let _guard = BindGuard::new(&*texture); let new_size = resize_texture(size, None)?; self.glyphs .as_mut() .unwrap() .resize_texture(new_size.0, new_size.1); } Ok(id) } } impl Drop for UpdateGuard { fn drop(&mut self) { self.0.end_update() } } fn resize_texture(new_size: (u32, u32), cur_size: Option<(u32, u32)>) -> Result<(u32, u32), Error> { let mut max_size = 0; gl::get_integer_v(gl::MAX_TEXTURE_SIZE, &mut max_size); let max_size = max_size as u32; let (w, h) = if let Some(cur_size) = cur_size { if (new_size.0 > max_size || new_size.1 > max_size) && (cur_size.0 < max_size || cur_size.1 < max_size) { (max_size, max_size) } else { new_size } } else { new_size }; GlcError::checked(|| { gl::tex_image_2d( gl::TEXTURE_2D, 0, gl::RED as _, w as _, h as _, 0, gl::RED, gl::UNSIGNED_BYTE, null(), ) })?; Ok(new_size) } fn update_texture(texture: &Texture, rect: Rectangle, data: &[u8]) { let ret = GlcError::checked(|| { gl::texture_sub_image_2d( texture.id(), 0, rect.min[0] as _, rect.min[1] as _, rect.width() as _, rect.height() as _, gl::RED, gl::UNSIGNED_BYTE, data.as_ptr() as _, ) }); if let Err(err) = ret { warn!("Unable to update text texture: {}", err); } } fn create_vertex(data: GlyphVertex) -> Vertex { let pos_min = Vector2f::new(data.pixel_coords.min.x, data.pixel_coords.min.y); let pos_max = Vector2f::new(data.pixel_coords.max.x, data.pixel_coords.max.y); let tex_min = Vector2f::new(data.tex_coords.min.x, data.tex_coords.min.y); let tex_max = Vector2f::new(data.tex_coords.max.x, data.tex_coords.max.y); Vertex { text_id: data.extra.text_id, data: VertexData { pos_min, pos_max, tex_min, tex_max, color: data.extra.color, }, } } /* TextBuilder */ pub struct TextBuilder { cache: TextCache, items: Vec, } enum BuilderItem { Position(f32, f32), Color(Vector4f), Font(String), Text(String), Scale(f32), } impl TextBuilder { fn new(cache: &TextCache) -> Self { Self { cache: cache.clone(), items: Vec::new(), } } pub fn build(self) -> Result { let mut sections = Vec::>::new(); let mut scale = 20.0; let mut font_id = None; let mut color = Vector4f::new(0.0, 0.0, 0.0, 1.0); let mut position = None; for item in self.items { match item { BuilderItem::Text(text) => { let font_id = font_id.ok_or(Error::FontNotSet)?; let text = OwnedText::new(text) .with_extra(Extra { text_id: 0, color }) .with_scale(scale) .with_font_id(font_id); match (sections.pop(), position.take()) { (Some(section), Some(pos)) => { sections.push(section); sections.push( OwnedSection::default() .with_screen_position(pos) .add_text(text), ); } (Some(section), None) => { sections.push(section.add_text(text)); } (None, Some(pos)) => sections.push( OwnedSection::::default() .with_screen_position(pos) .add_text(text), ), (None, None) => { sections.push(OwnedSection::::default().add_text(text)) } }; } BuilderItem::Position(x, y) => position = Some((x, y)), BuilderItem::Color(c) => color = c, BuilderItem::Scale(s) => scale = s, BuilderItem::Font(path) => font_id = Some(self.cache.font(&path)?), } } let text = Text::new(&self.cache, sections)?; let weak = Rc::downgrade(&text.inner); self.cache.add_text(weak)?; Ok(text) } pub fn position(mut self, x: f32, y: f32) -> Self { self.items.push(BuilderItem::Position(x, y)); self } pub fn font(mut self, path: S) -> Self where S: Display, { self.items.push(BuilderItem::Font(path.to_string())); self } pub fn color(self, r: f32, g: f32, b: f32, a: f32) -> Self { self.color_vec(Vector4f::new(r, g, b, a)) } pub fn color_vec(mut self, color: Vector4f) -> Self { self.items.push(BuilderItem::Color(color)); self } pub fn text(mut self, text: S) -> Self where S: Display, { self.items.push(BuilderItem::Text(text.to_string())); self } pub fn scale(mut self, scale: f32) -> Self { self.items.push(BuilderItem::Scale(scale)); self } } /* Text */ pub struct Text { cache: TextCache, inner: Rc>, } struct TextInner { array: VertexArray, program: Rc, texture: Rc>, sections: Vec>, vertex_count: usize, } #[derive(Default, Debug, Clone)] struct Extra { text_id: usize, color: Vector4f, } #[derive(Default, Clone)] struct Vertex { text_id: usize, data: VertexData, } #[repr(C, packed)] #[derive(Default, Debug, Clone)] struct VertexData { pos_min: Vector2f, pos_max: Vector2f, tex_min: Vector2f, tex_max: Vector2f, color: Vector4f, } impl Text { fn new(cache: &TextCache, sections: Vec>) -> Result { const STRIDE: gl::GLsizei = size_of::() as _; const SIZE_VEC2: gl::GLsizei = size_of::() as _; const OFFSET_POS_MIN: gl::GLsizei = 0; const OFFSET_POS_MAX: gl::GLsizei = OFFSET_POS_MIN + SIZE_VEC2; const OFFSET_TEX_MIN: gl::GLsizei = OFFSET_POS_MAX + SIZE_VEC2; const OFFSET_TEX_MAX: gl::GLsizei = OFFSET_TEX_MIN + SIZE_VEC2; const OFFSET_COLOR: gl::GLsizei = OFFSET_TEX_MAX + SIZE_VEC2; let buffer = ArrayBuffer::new(Target::ArrayBuffer)?; let array = VertexArray::builder() .bind_buffer(buffer) .vertex_attrib_pointer(0, 2, DataType::Float, false, STRIDE, OFFSET_POS_MIN)? .vertex_attrib_divisor(1)? .vertex_attrib_pointer(1, 2, DataType::Float, false, STRIDE, OFFSET_POS_MAX)? .vertex_attrib_divisor(1)? .vertex_attrib_pointer(2, 2, DataType::Float, false, STRIDE, OFFSET_TEX_MIN)? .vertex_attrib_divisor(1)? .vertex_attrib_pointer(3, 2, DataType::Float, false, STRIDE, OFFSET_TEX_MAX)? .vertex_attrib_divisor(1)? .vertex_attrib_pointer(4, 4, DataType::Float, false, STRIDE, OFFSET_COLOR)? .vertex_attrib_divisor(1)? .build()?; let cache = cache.clone(); let program = cache.0.borrow().manager.0.borrow().program.clone(); let texture = cache.0.borrow().texture.clone(); let inner = TextInner { array, program, texture, sections, vertex_count: 0, }; let text = Text { cache, inner: Rc::new(RefCell::new(inner)), }; Ok(text) } pub fn render(&self, enable_blending: bool) { let inner = self.inner.borrow(); let texture = inner.texture.borrow(); if enable_blending { gl::enable(gl::BLEND); gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); } let _guard = BindGuard::new(&*texture); let _guard = BindGuard::new(&inner.array); let _guard = BindGuard::new(&*inner.program); gl::draw_arrays_instanced(gl::TRIANGLE_STRIP, 0, 4, inner.vertex_count as _); if enable_blending { gl::disable(gl::BLEND); } } pub fn update(&mut self, mut index: usize, text: S) -> Result<(), Error> where S: Display, { let mut inner = self.inner.borrow_mut(); for section in &mut inner.sections { if index < section.text.len() { section.text[index].text = text.to_string(); break; } else { index -= section.text.len(); } } drop(inner); self.cache.update() } } impl TextInner { fn update(&mut self, data: Vec) -> Result<(), Error> { let buffers = self.array.buffers_mut(); buffers[0].buffer_data(Usage::StaticDraw, &data)?; self.vertex_count = data.len(); Ok(()) } } impl Hash for Extra { fn hash(&self, state: &mut H) where H: Hasher, { self.text_id.hash(state); OrderedFloat::from(self.color[0]).hash(state); OrderedFloat::from(self.color[1]).hash(state); OrderedFloat::from(self.color[2]).hash(state); OrderedFloat::from(self.color[3]).hash(state); } } impl PartialEq for Extra { fn eq(&self, other: &Self) -> bool { self.text_id.eq(&other.text_id) && self.color.eq(&other.color) } }