From 46a8bc4b1328815deb1059d3dff6d84c04f18be9 Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Sun, 6 Dec 2020 00:48:04 +0100 Subject: [PATCH] Implemented Text Manager --- Cargo.lock | 141 +++- glc/src/error.rs | 5 +- glc/src/matrix.rs | 48 ++ glc/src/texture.rs | 4 + glc/src/vector.rs | 16 + glc/src/vertex_array.rs | 21 +- space-crush/Cargo.toml | 2 + space-crush/resources/fonts/DroidSansMono.ttf | Bin 0 -> 119380 bytes space-crush/resources/shader/noise.frag | 3 +- space-crush/resources/shader/quad.vert | 5 +- space-crush/resources/shader/text.frag | 20 + space-crush/resources/shader/text.vert | 52 ++ space-crush/src/app/misc/camera.rs | 94 +-- space-crush/src/app/misc/frame_counter.rs | 12 +- space-crush/src/app/misc/mod.rs | 1 + space-crush/src/app/misc/text.rs | 643 ++++++++++++++++++ space-crush/src/app/misc/vfs.rs | 1 + space-crush/src/app/misc/window.rs | 4 + space-crush/src/app/mod.rs | 9 +- space-crush/src/app/render/debug.rs | 52 ++ space-crush/src/app/render/init.rs | 12 +- space-crush/src/app/render/mod.rs | 2 + space-crush/src/error.rs | 17 +- 23 files changed, 1055 insertions(+), 109 deletions(-) create mode 100644 space-crush/resources/fonts/DroidSansMono.ttf create mode 100644 space-crush/resources/shader/text.frag create mode 100644 space-crush/resources/shader/text.vert create mode 100644 space-crush/src/app/misc/text.rs create mode 100644 space-crush/src/app/render/debug.rs diff --git a/Cargo.lock b/Cargo.lock index 93302de..a75679b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "ab_glyph" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a685fe66654266f321a8b572660953f4df36a2135706503a4c89981d76e1a2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser 0.8.0", +] + [[package]] name = "ab_glyph_rasterizer" version = "0.1.4" @@ -46,6 +56,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" +[[package]] +name = "approx" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" +dependencies = [ + "num-traits", +] + [[package]] name = "arrayvec" version = "0.5.2" @@ -709,6 +728,45 @@ dependencies = [ "gl_generator", ] +[[package]] +name = "glyph_brush" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afd3e2cfd503a5218dd56172a8bf7c8655a4a7cf745737c606a6edfeea1b343f" +dependencies = [ + "glyph_brush_draw_cache", + "glyph_brush_layout", + "log", + "ordered-float 1.1.0", + "rustc-hash", + "twox-hash", +] + +[[package]] +name = "glyph_brush_draw_cache" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cef969a091be5565c2c10b31fd2f115cbeed9f783a27c96ae240ff8ceee067c" +dependencies = [ + "ab_glyph", + "crossbeam-channel", + "crossbeam-deque", + "linked-hash-map", + "rayon", + "rustc-hash", +] + +[[package]] +name = "glyph_brush_layout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10bc06d530bf20c1902f1b02799ab7372ff43f6119770c49b0bc3f21bd148820" +dependencies = [ + "ab_glyph", + "approx", + "xi-unicode", +] + [[package]] name = "hashbrown" version = "0.7.2" @@ -827,6 +885,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "linked-hash-map" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" + [[package]] name = "lock_api" version = "0.4.2" @@ -1041,6 +1105,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -1088,6 +1161,24 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" +[[package]] +name = "ordered-float" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3741934be594d77de1c8461ebcbbe866f585ea616a9753aa78f2bdc69f0e4579" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-float" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fe9037165d7023b1228bc4ae9a2fa1a2b0095eca6c2998c624723dfd01314a5" +dependencies = [ + "num-traits", +] + [[package]] name = "osmesa-sys" version = "0.1.2" @@ -1103,7 +1194,16 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3" dependencies = [ - "ttf-parser", + "ttf-parser 0.6.2", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb477c7fd2a3a6e04e1dc6ca2e4e9b04f2df702021dc5a5d1cf078c587dc59f7" +dependencies = [ + "ttf-parser 0.8.3", ] [[package]] @@ -1320,6 +1420,12 @@ version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rusttype" version = "0.9.2" @@ -1327,7 +1433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59" dependencies = [ "ab_glyph_rasterizer", - "owned_ttf_parser", + "owned_ttf_parser 0.6.0", ] [[package]] @@ -1428,8 +1534,10 @@ dependencies = [ "gl", "glc", "glutin", + "glyph_brush", "log", "num_cpus", + "ordered-float 2.0.0", "rand", "shrev", "specs", @@ -1455,6 +1563,12 @@ dependencies = [ "tuple_utils", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.9.3" @@ -1535,12 +1649,29 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" +[[package]] +name = "ttf-parser" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7622061403fd00f0820df288e5a580e87d3ce15a1c4313c59fd1ffb77129903f" + [[package]] name = "tuple_utils" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44834418e2c5b16f47bedf35c28e148db099187dd5feee6367fb2525863af4f1" +[[package]] +name = "twox-hash" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f8ab788026715fa63b31960869617cba39117e520eb415b0139543e325ab59" +dependencies = [ + "cfg-if 0.1.10", + "rand", + "static_assertions", +] + [[package]] name = "tynm" version = "0.1.6" @@ -1793,6 +1924,12 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" +[[package]] +name = "xi-unicode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" + [[package]] name = "xml-rs" version = "0.8.3" diff --git a/glc/src/error.rs b/glc/src/error.rs index 9530c68..3ef101b 100644 --- a/glc/src/error.rs +++ b/glc/src/error.rs @@ -24,9 +24,12 @@ pub enum Error { #[error("Error while linking shader program: {0}")] ShaderLink(String), - #[error("Vertex Array Index is already in use: {0}!")] + #[error("Vertex Array: Index is already in use: {0}!")] VertexArrayIndexAlreadyInUse(gl::GLuint), + #[error("Vertex Array: Expected pointer!")] + VertexArrayExpectedPointer, + #[error("Invalid Parameter!")] InvalidParameter, diff --git a/glc/src/matrix.rs b/glc/src/matrix.rs index 000d9c0..fa8ab8a 100644 --- a/glc/src/matrix.rs +++ b/glc/src/matrix.rs @@ -419,6 +419,54 @@ where } } +impl Matrix4 +where + T: Element, +{ + #[inline] + pub fn ortho(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self { + let one = T::one(); + let two = one + one; + let zero = T::zero(); + + Matrix4::new( + Vector4::new(two / (right - left), zero, zero, zero), + Vector4::new(zero, two / (top - bottom), zero, zero), + Vector4::new(zero, zero, -zero / (far - near), zero), + Vector4::new( + -(right + left) / (right - left), + -(top + bottom) / (top - bottom), + -(far + near) / (far - near), + one, + ), + ) + } + + #[inline] + pub fn perspective(fov: Angle, ratio: T, near: T, far: T) -> Self { + let one = T::one(); + let two = one + one; + let zero = T::zero(); + + let top = near * fov.into_rad().into_inner().tan(); + let bottom = -top; + let right = ratio * top; + let left = -right; + + Matrix4::new( + Vector4::new(two * near / (right - left), zero, zero, zero), + Vector4::new(zero, two * near / (top - bottom), zero, zero), + Vector4::new( + (right + left) / (right - left), + (top + bottom) / (top - bottom), + -(far + near) / (far - near), + -one, + ), + Vector4::new(zero, zero, -two * far * near / (far - near), zero), + ) + } +} + impl Mul> for Matrix4 where T: Element, diff --git a/glc/src/texture.rs b/glc/src/texture.rs index f8e1050..9d2e63b 100644 --- a/glc/src/texture.rs +++ b/glc/src/texture.rs @@ -38,6 +38,10 @@ impl Texture { }) } + pub fn id(&self) -> gl::GLuint { + self.id + } + pub fn upload(&mut self, data: &Data, generate_mipmap: bool) -> Result<(), Error> { let info = data.format.info(); diff --git a/glc/src/vector.rs b/glc/src/vector.rs index eae4f40..05adab0 100644 --- a/glc/src/vector.rs +++ b/glc/src/vector.rs @@ -494,6 +494,7 @@ pub trait Element: fn sin(self) -> Self; fn cos(self) -> Self; fn sqrt(self) -> Self; + fn tan(self) -> Self::AsFloat; fn acos(self) -> Self::AsFloat; fn atan(self) -> Self::AsFloat; fn atan2(a: Self, b: Self) -> Self::AsFloat; @@ -530,6 +531,11 @@ impl Element for gl::GLfloat { f32::sqrt(self) } + #[inline] + fn tan(self) -> Self::AsFloat { + f32::tan(self) + } + #[inline] fn acos(self) -> Self::AsFloat { f32::acos(self) @@ -589,6 +595,11 @@ impl Element for gl::GLdouble { f64::sqrt(self) } + #[inline] + fn tan(self) -> Self::AsFloat { + f64::tan(self) + } + #[inline] fn acos(self) -> Self::AsFloat { f64::acos(self) @@ -648,6 +659,11 @@ impl Element for gl::GLint { f64::sqrt(self as f64) as i32 } + #[inline] + fn tan(self) -> Self::AsFloat { + f32::tan(self as f32) + } + #[inline] fn acos(self) -> Self::AsFloat { f32::acos(self as f32) diff --git a/glc/src/vertex_array.rs b/glc/src/vertex_array.rs index 55b09e9..5f27108 100644 --- a/glc/src/vertex_array.rs +++ b/glc/src/vertex_array.rs @@ -77,8 +77,9 @@ impl Builder { let guard = BindGuard::new(&binding.buffer); for pointer in binding.pointers { + Error::checked(|| gl::enable_vertex_attrib_array(pointer.index))?; + Error::checked(|| { - gl::enable_vertex_attrib_array(pointer.index); gl::vertex_attrib_pointer( pointer.index, pointer.size, @@ -92,6 +93,10 @@ impl Builder { pointer.offset as *const _, ) })?; + + if let Some(divisor) = pointer.divisor { + Error::checked(|| gl::vertex_attrib_divisor(pointer.index, divisor))?; + } } drop(guard); @@ -142,6 +147,7 @@ impl BindingBuilder { normalize, stride, offset, + divisor: None, }; self.builder @@ -154,6 +160,18 @@ impl BindingBuilder { Ok(self) } + pub fn vertex_attrib_divisor(mut self, divisor: gl::GLuint) -> Result { + let binding = self.builder.bindings.last_mut().unwrap(); + let pointer = binding + .pointers + .last_mut() + .ok_or(Error::VertexArrayExpectedPointer)?; + + pointer.divisor = Some(divisor); + + Ok(self) + } + pub fn build(self) -> Result { self.builder.build() } @@ -214,4 +232,5 @@ struct Pointer { normalize: bool, stride: gl::GLsizei, offset: gl::GLsizei, + divisor: Option, } diff --git a/space-crush/Cargo.toml b/space-crush/Cargo.toml index f0b9f06..b0004b5 100644 --- a/space-crush/Cargo.toml +++ b/space-crush/Cargo.toml @@ -10,8 +10,10 @@ futures = "0.3" gl = { version = "0.1", features = [ "use_log_crate" ] } glc = "0.1" glutin = "0.25" +glyph_brush = "0.7" log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } num_cpus = "1.13" +ordered-float = "2.0" rand = "0.7" shrev = "1.1" specs = "0.16" diff --git a/space-crush/resources/fonts/DroidSansMono.ttf b/space-crush/resources/fonts/DroidSansMono.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a007071944f7c90020e798eea53b10065e2b45a5 GIT binary patch literal 119380 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⪚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 Result<(), Error> { + pub fn resize(&mut self, w: f32, h: f32) -> Result<(), Error> { let mut data = self.buffer.map_mut::(true)?; - data[0].projection = Matrix4f::new( - Vector4f::new(2.0 / (right - left), 0.0, 0.0, 0.0), - Vector4f::new(0.0, 2.0 / (top - bottom), 0.0, 0.0), - Vector4f::new(0.0, 0.0, -2.0 / (far - near), 0.0), - Vector4f::new( - -(right + left) / (right - left), - -(top + bottom) / (top - bottom), - -(far + near) / (far - near), - 1.0, - ), - ); - Ok(()) - } - - pub fn perspective( - &mut self, - fov: Angle, - ratio: f32, - near: f32, - far: f32, - ) -> Result<(), Error> { - let top = near * fov.into_rad().into_inner().tan(); - let bottom = -top; - let right = ratio * top; - let left = -right; - - let mut data = self.buffer.map_mut::(true)?; - data[0].projection = Matrix4f::new( - Vector4f::new(2.0 * near / (right - left), 0.0, 0.0, 0.0), - Vector4f::new(0.0, 2.0 * near / (top - bottom), 0.0, 0.0), - Vector4f::new( - (right + left) / (right - left), - (top + bottom) / (top - bottom), - -(far + near) / (far - near), - -1.0, - ), - Vector4f::new(0.0, 0.0, -2.0 * far * near / (far - near), 0.0), - ); - - Ok(()) - } - - pub fn set_projection(&mut self, m: Matrix4f) -> Result<(), Error> { - let mut data = self.buffer.map_mut::(false)?; - data[0].projection = m; + data[0].proj_world = Matrix4f::ortho(-w / 2.0, w / 2.0, -h / 2.0, h / 2.0, -100.0, 100.0); + data[0].proj_ui = Matrix4f::ortho(0.0, w, h, 0.0, -100.0, 100.0); Ok(()) } - pub fn set_view(&mut self, m: Matrix4f) -> Result<(), Error> { - let mut data = self.buffer.map_mut::(false)?; - data[0].view = m; - - Ok(()) - } - - pub fn update_view(&mut self, m: Matrix4f) -> Result<(), Error> { - let mut data = self.buffer.map_mut::(false)?; - data[0].view = data[0].view * m; - - Ok(()) - } - - pub fn projection(&self) -> Result { - let data = self.buffer.map::()?; - let ret = data[0].view; - - Ok(ret) - } - - pub fn view(&self) -> Result { - let data = self.buffer.map::()?; - let ret = data[0].view; - - Ok(ret) - } - pub fn bind(&self, index: gl::GLuint) -> Result<(), Error> { Error::checked(|| self.buffer.bind_buffer_base(index)) } @@ -120,6 +41,7 @@ impl Camera { #[repr(C, packed)] struct Data { - projection: Matrix4f, + proj_world: Matrix4f, + proj_ui: Matrix4f, view: Matrix4f, } diff --git a/space-crush/src/app/misc/frame_counter.rs b/space-crush/src/app/misc/frame_counter.rs index b5e8ead..61cd229 100644 --- a/space-crush/src/app/misc/frame_counter.rs +++ b/space-crush/src/app/misc/frame_counter.rs @@ -9,9 +9,14 @@ pub struct FrameCounter { count: usize, fps: usize, delta: f32, + updated: bool, } impl FrameCounter { + pub fn updated(&self) -> bool { + self.updated + } + #[inline] pub fn fps(&self) -> usize { self.fps @@ -31,7 +36,7 @@ impl FrameCounter { self.time += self.delta; self.count += 1; - if self.time >= 2.0 { + self.updated = if self.time >= 2.0 { self.time = 0.0; self.fps = 0; self.count = 0; @@ -44,7 +49,9 @@ impl FrameCounter { true } else { false - } + }; + + self.updated } } @@ -56,6 +63,7 @@ impl Default for FrameCounter { count: 0, fps: 0, delta: 0.0, + updated: false, } } } diff --git a/space-crush/src/app/misc/mod.rs b/space-crush/src/app/misc/mod.rs index 273e78d..c821bac 100644 --- a/space-crush/src/app/misc/mod.rs +++ b/space-crush/src/app/misc/mod.rs @@ -2,6 +2,7 @@ pub mod camera; pub mod events; pub mod frame_counter; pub mod geometry; +pub mod text; pub mod vfs; pub mod window; diff --git a/space-crush/src/app/misc/text.rs b/space-crush/src/app/misc/text.rs new file mode 100644 index 0000000..399fb75 --- /dev/null +++ b/space-crush/src/app/misc/text.rs @@ -0,0 +1,643 @@ +#![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::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 crate::Error; + +use super::{load_program, vfs::Vfs}; + +/* TextManager */ + +#[derive(Clone)] +pub struct TextManager(Rc>); + +struct TextManagerInner { + vfs: Vfs, + program: Rc, + fonts: HashMap, +} + +impl TextManager { + pub fn new(vfs: &Vfs) -> Result { + let vfs = vfs.clone(); + let program = load_program( + &vfs, + 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_by_name(&self, name: &str) -> Result { + if let Some(font) = self.0.borrow().fonts.get(name) { + return Ok(font.clone()); + } + + let path = format!("resources/fonts/{}", name); + self.font_by_path(&path) + } + + pub fn font_by_path(&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, +} + +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, + })))) + } + + pub fn new_text(&self) -> TextBuilder { + TextBuilder::new(self) + } + + fn font_by_name(&self, name: &str) -> Result { + let mut inner = self.0.borrow_mut(); + + if let Some(id) = inner.fonts.get(name) { + return Ok(*id); + } + + let font = inner.manager.font_by_name(name)?; + + inner.add_font(font) + } + + fn font_by_path(&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_by_path(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; + + /* 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)?; + } + } + + 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) + } +} + +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), + FontName(String), + FontPath(String), + Color(Vector4f), + 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::FontName(name) => font_id = Some(self.cache.font_by_name(&name)?), + BuilderItem::FontPath(path) => font_id = Some(self.cache.font_by_path(&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_name(mut self, name: S) -> Self + where + S: Display, + { + self.items.push(BuilderItem::FontName(name.to_string())); + + self + } + + pub fn font_path(mut self, path: S) -> Self + where + S: Display, + { + self.items.push(BuilderItem::FontPath(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) + } +} diff --git a/space-crush/src/app/misc/vfs.rs b/space-crush/src/app/misc/vfs.rs index c3f25e7..9bec47a 100644 --- a/space-crush/src/app/misc/vfs.rs +++ b/space-crush/src/app/misc/vfs.rs @@ -13,6 +13,7 @@ use vfs_zip::ZipReadOnly as ZipFS; use crate::Error; +#[derive(Clone)] pub struct Vfs(pub VfsPath); impl Vfs { diff --git a/space-crush/src/app/misc/window.rs b/space-crush/src/app/misc/window.rs index 140108c..350c60a 100644 --- a/space-crush/src/app/misc/window.rs +++ b/space-crush/src/app/misc/window.rs @@ -54,6 +54,10 @@ impl Window { let context = unsafe { context.make_current().unwrap() }; gl::load_with(|s| context.get_proc_address(s)); + gl::disable(gl::CULL_FACE); + gl::disable(gl::DEPTH_TEST); + gl::pixel_store_i(gl::UNPACK_ALIGNMENT, 1); + Ok(Self { context }) } diff --git a/space-crush/src/app/mod.rs b/space-crush/src/app/mod.rs index cd687dd..9399a13 100644 --- a/space-crush/src/app/mod.rs +++ b/space-crush/src/app/mod.rs @@ -6,8 +6,8 @@ use specs::{Dispatcher, DispatcherBuilder, World}; use crate::Error; -use misc::{events::Events, geometry::Geometry, vfs::Vfs, window::Window}; -use render::{Init, Test}; +use misc::{events::Events, geometry::Geometry, text::TextManager, vfs::Vfs, window::Window}; +use render::{Debug, Init, Test}; use systems::{State, StateUpdate}; pub struct App<'a, 'b> { @@ -19,16 +19,19 @@ pub struct App<'a, 'b> { impl<'a, 'b> App<'a, 'b> { pub fn new(world: &mut World) -> Result { + let vfs = Vfs::new()?; let events = Events::new(world); let window = Window::new(events.handle())?; + let text_manager = TextManager::new(&vfs)?; - world.insert(Vfs::new()?); + world.insert(vfs); world.insert(Geometry::new()?); let mut dispatcher = DispatcherBuilder::new() .with(StateUpdate::new(world), "state_update", &[]) .with_thread_local(Init::new(world)?) .with_thread_local(Test::new(world)?) + .with_thread_local(Debug::new(text_manager)?) .build(); dispatcher.setup(world); diff --git a/space-crush/src/app/render/debug.rs b/space-crush/src/app/render/debug.rs new file mode 100644 index 0000000..320075c --- /dev/null +++ b/space-crush/src/app/render/debug.rs @@ -0,0 +1,52 @@ +use log::warn; +use specs::{ReadExpect, System}; + +use crate::Error; + +use super::{ + super::misc::text::{Text, TextManager}, + Global, +}; + +/* Debug */ + +pub struct Debug { + text: Text, +} + +impl Debug { + pub fn new(manager: TextManager) -> Result { + let text = manager + .create_cache()? + .new_text() + .scale(12.0) + .font_name("DroidSansMono.ttf") + .color(0.7, 0.7, 0.7, 1.0) + .position(5.0, 5.0) + .text(format!("Space Crush v{}\n", env!("CARGO_PKG_VERSION"))) + .text("\nFPS: ") + .text("-") + .text("\nResolution: ") + .text("1280 x 720") + .build()?; + + Ok(Self { text }) + } +} + +impl<'a> System<'a> for Debug { + type SystemData = ReadExpect<'a, Global>; + + fn run(&mut self, global: Self::SystemData) { + if global.frame_counter.updated() { + let ret = self + .text + .update(2, format!("{}", global.frame_counter.fps())); + if let Err(err) = ret { + warn!("Unable to update debug text: {}", err); + } + } + + self.text.render(true); + } +} diff --git a/space-crush/src/app/render/init.rs b/space-crush/src/app/render/init.rs index 24224e5..367ddc5 100644 --- a/space-crush/src/app/render/init.rs +++ b/space-crush/src/app/render/init.rs @@ -2,7 +2,7 @@ use glc::{ misc::Bindable, shader::{Program, Type}, }; -use log::{error, info}; +use log::error; use shrev::{EventChannel, ReaderId}; use specs::{ReadExpect, System, World, WriteExpect}; @@ -75,20 +75,14 @@ impl<'a> System<'a> for Init { let w = *w as f32; let h = *h as f32; - if let Err(err) = - global - .camera - .ortho(-w / 2.0, w / 2.0, -h / 2.0, h / 2.0, -100.0, 100.0) - { + if let Err(err) = global.camera.resize(w, h) { error!("Error while updating camera: {}", err); panic!("Error while updating camera: {}", err); } } } - if global.frame_counter.next() { - info!("FPS: {}", global.frame_counter.fps()); - } + global.frame_counter.next(); self.program.bind(); geometry.render_quad(); diff --git a/space-crush/src/app/render/mod.rs b/space-crush/src/app/render/mod.rs index bdbacf3..85687da 100644 --- a/space-crush/src/app/render/mod.rs +++ b/space-crush/src/app/render/mod.rs @@ -1,5 +1,7 @@ +mod debug; mod init; mod test; +pub use debug::Debug; pub use init::{Global, Init}; pub use test::Test; diff --git a/space-crush/src/error.rs b/space-crush/src/error.rs index 18bce4d..c092076 100644 --- a/space-crush/src/error.rs +++ b/space-crush/src/error.rs @@ -2,6 +2,7 @@ use std::io::Error as IoError; use glc::error::Error as GlcError; use glutin::{ContextError as GlutinContextError, CreationError as GlutinCreationError}; +use glyph_brush::ab_glyph::InvalidFont; use thiserror::Error; use vfs::VfsError; use vfs_zip::Error as VfsZipError; @@ -26,11 +27,17 @@ pub enum Error { #[error("glutin Creation Error: {0}")] GlutinCreationError(GlutinCreationError), - #[error("Unable to create OpenGL context")] + #[error("Invalid Font: {0}")] + InvalidFont(InvalidFont), + + #[error("Unable to create OpenGL context!")] CreateContext, - #[error("Unable to initialize VFS")] + #[error("Unable to initialize VFS!")] InitVFS, + + #[error("Font is not set!")] + FontNotSet, } impl From for Error { @@ -68,3 +75,9 @@ impl From for Error { Self::GlutinCreationError(err) } } + +impl From for Error { + fn from(err: InvalidFont) -> Self { + Self::InvalidFont(err) + } +}