|
- #![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<RefCell<TextManagerInner>>);
-
- struct TextManagerInner {
- vfs: Vfs,
- program: Rc<Program>,
- fonts: HashMap<String, FontArc>,
- }
-
- impl TextManager {
- pub fn new(world: &World) -> Result<Self, Error> {
- let vfs = world.resource::<Vfs>()?.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, Error> {
- TextCache::new(self)
- }
-
- pub fn font(&self, path: &str) -> Result<FontArc, Error> {
- 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<RefCell<TextCacheInner>>);
-
- pub struct TextCacheInner {
- manager: TextManager,
- fonts: HashMap<String, FontId>,
- glyphs: Option<GlyphBrush<Vertex, Extra>>,
- texture: Rc<RefCell<Texture>>,
- texts: HashMap<usize, Weak<RefCell<TextInner>>>,
- text_ids: Vec<usize>,
- next_text_id: usize,
- update_counter: usize,
- update_requested: bool,
- }
-
- pub struct UpdateGuard(TextCache);
-
- impl TextCache {
- fn new(manager: &TextManager) -> Result<Self, Error> {
- 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<FontId, Error> {
- 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<RefCell<TextInner>>) -> 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::<Vec<VertexData>>::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<FontId, Error> {
- 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<u32>, 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<Extra>) -> 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<BuilderItem>,
- }
-
- 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<Text, Error> {
- let mut sections = Vec::<OwnedSection<Extra>>::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::<Extra>::default()
- .with_screen_position(pos)
- .add_text(text),
- ),
- (None, None) => {
- sections.push(OwnedSection::<Extra>::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<S>(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<S>(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<RefCell<TextInner>>,
- }
-
- struct TextInner {
- array: VertexArray,
- program: Rc<Program>,
- texture: Rc<RefCell<Texture>>,
- sections: Vec<OwnedSection<Extra>>,
- 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<OwnedSection<Extra>>) -> Result<Self, Error> {
- const STRIDE: gl::GLsizei = size_of::<VertexData>() as _;
-
- const SIZE_VEC2: gl::GLsizei = size_of::<Vector2f>() 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<S>(&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<VertexData>) -> 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<H>(&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)
- }
- }
|