diff options
| author | LLLL Colonq <llll@colonq> | 2025-01-21 15:27:55 -0500 |
|---|---|---|
| committer | LLLL Colonq <llll@colonq> | 2025-01-21 15:27:55 -0500 |
| commit | 47fe18171569582d1af9401013c57045b59f3774 (patch) | |
| tree | b3dbfa96c90e6a36ac74b1f0e4ef1b8705a46470 /src/font.rs | |
| parent | fdafae3cdcb03a8b7fa736039556bcc465a34959 (diff) | |
Fix font rendering
Diffstat (limited to 'src/font.rs')
| -rw-r--r-- | src/font.rs | 232 |
1 files changed, 166 insertions, 66 deletions
diff --git a/src/font.rs b/src/font.rs index 047e1f7..3074dff 100644 --- a/src/font.rs +++ b/src/font.rs @@ -3,73 +3,87 @@ use std::collections::HashMap; use crate::{context, mesh, shader, texture}; use glow::HasContext; -pub const CHAR_WIDTH: i32 = 7; -pub const CHAR_HEIGHT: i32 = 9; -pub const FONT_WIDTH: i32 = 112; -pub const FONT_HEIGHT: i32 = 54; - pub struct Bitmap { + pub char_width: i32, + pub char_height: i32, + pub font_width: i32, + pub font_height: i32, pub shader: shader::Shader, pub font: texture::Texture, pub vao: glow::VertexArray, pub vertex_buf: glow::Buffer, pub texcoords_buf: glow::Buffer, + pub colors_buf: glow::Buffer, pub index_buf: glow::Buffer, } impl Bitmap { - pub fn new(ctx: &context::Context) -> Self { + pub fn from_image( + ctx: &context::Context, + char_width: i32, char_height: i32, + font_width: i32, font_height: i32, + data: &[u8], + ) -> Self { let shader = shader::Shader::new_nolib( &ctx, include_str!("assets/shaders/bitmap/vert.glsl"), include_str!("assets/shaders/bitmap/frag.glsl"), ); - let font = texture::Texture::new(ctx, include_bytes!("assets/fonts/simple.png")); + let font = texture::Texture::new(ctx, data); unsafe { let vao = ctx.gl.create_vertex_array().expect("failed to initialize vao"); ctx.gl.bind_vertex_array(Some(vao)); - let vertex_buf = ctx.gl.create_buffer().expect("failed to create buffer object"); ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buf)); ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_VERTEX, 2, glow::FLOAT, false, 0, 0); ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_VERTEX); - let texcoords_buf = ctx.gl.create_buffer().expect("failed to create buffer object"); ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(texcoords_buf)); ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_TEXCOORD, 2, glow::FLOAT, false, 0, 0); ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_TEXCOORD); - + let colors_buf = ctx.gl.create_buffer().expect("failed to create buffer object"); + ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(colors_buf)); + ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_COLOR, 3, glow::FLOAT, false, 0, 0); + ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_COLOR); let index_buf = ctx.gl.create_buffer().expect("failed to create buffer object"); ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buf)); Self { + char_width, char_height, + font_width, font_height, shader, font, vao, vertex_buf, texcoords_buf, + colors_buf, index_buf, } } } - pub fn render_text_helper(&self, ctx: &context::Context, pos: &glam::Vec2, text: &str, color: &glam::Vec3) { + pub fn new(ctx: &context::Context) -> Self { + Self::from_image(ctx, 7, 9, 112, 54, include_bytes!("assets/fonts/simple.png")) + } + + pub fn render_text_helper(&self, ctx: &context::Context, pos: &glam::Vec2, text: &str, color: &[glam::Vec3]) { let mut cur = glam::Vec2::new(0.0, 0.0); let mut vertices = Vec::new(); let mut texcoords = Vec::new(); + let mut colors = Vec::new(); let mut indices = Vec::new(); - let cwidth = CHAR_WIDTH as f32 / FONT_WIDTH as f32; - let cheight = CHAR_HEIGHT as f32 / FONT_HEIGHT as f32; - let row_len = FONT_WIDTH as u32 / CHAR_WIDTH as u32; - for c in text.chars() { + let cwidth = self.char_width as f32 / self.font_width as f32; + let cheight = self.char_height as f32 / self.font_height as f32; + let row_len = self.font_width as u32 / self.char_width as u32; + for (i, c) in text.chars().enumerate() { if c == '\n' { cur.x = 0.0; - cur.y += CHAR_HEIGHT as f32; + cur.y -= self.char_height as f32; } else { let idx = vertices.len() as u32; vertices.push(cur); - vertices.push(cur + glam::Vec2::new(CHAR_WIDTH as f32, 0.0)); - vertices.push(cur + glam::Vec2::new(CHAR_WIDTH as f32, CHAR_HEIGHT as f32)); - vertices.push(cur + glam::Vec2::new(0.0, CHAR_HEIGHT as f32)); + vertices.push(cur + glam::Vec2::new(self.char_width as f32, 0.0)); + vertices.push(cur + glam::Vec2::new(self.char_width as f32, self.char_height as f32)); + vertices.push(cur + glam::Vec2::new(0.0, self.char_height as f32)); let cidx = c as u32 - ' ' as u32; let col = cidx % row_len; let row = cidx / row_len; @@ -78,9 +92,15 @@ impl Bitmap { texcoords.push(tcbase + glam::Vec2::new(cwidth, cheight)); texcoords.push(tcbase + glam::Vec2::new(cwidth, 0.0)); texcoords.push(tcbase); + let c = if let Some(c) = color.get(i) { + *c + } else { + glam::Vec3::new(1.0, 1.0, 1.0) + }; + colors.push(c); colors.push(c); colors.push(c); colors.push(c); indices.push(idx + 0); indices.push(idx + 1); indices.push(idx + 2); indices.push(idx + 0); indices.push(idx + 3); indices.push(idx + 2); - cur.x += CHAR_WIDTH as f32; + cur.x += self.char_width as f32; } } let index_bytes: Vec<u8> = indices.iter().flat_map(|x| x.to_ne_bytes()).collect(); @@ -89,10 +109,9 @@ impl Bitmap { let scale = glam::Vec2::new(2.0 / ctx.render_width, 2.0 / ctx.render_height); let offset = glam::Vec2::new( -ctx.render_width / 2.0, - ctx.render_height / 2.0 - CHAR_HEIGHT as f32, + ctx.render_height / 2.0 - self.char_height as f32, ); let npos = (glam::Vec2::new(pos.x, -pos.y) + offset) * scale; - self.shader.set_vec3(ctx, "text_color", color as _); self.shader.set_mat4( ctx, "transform", &glam::Mat4::from_scale_rotation_translation( @@ -121,6 +140,15 @@ impl Bitmap { ), glow::STATIC_DRAW, ); + ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.colors_buf)); + ctx.gl.buffer_data_u8_slice( + glow::ARRAY_BUFFER, + std::slice::from_raw_parts( + colors.as_ptr() as _, + colors.len() * std::mem::size_of::<f32>() * 3, + ), + glow::STATIC_DRAW, + ); ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buf)); ctx.gl.buffer_data_u8_slice( glow::ELEMENT_ARRAY_BUFFER, @@ -132,7 +160,7 @@ impl Bitmap { } pub fn render_text(&self, ctx: &context::Context, pos: &glam::Vec2, text: &str) { - self.render_text_helper(ctx, pos, text, &glam::Vec3::new(1.0, 1.0, 1.0)); + self.render_text_helper(ctx, pos, text, &[]); } } @@ -148,20 +176,22 @@ pub struct TrueType { pub cellwidth: usize, pub cellheight: usize, pub info: HashMap<char, AtlasInfo>, + pub vao: glow::VertexArray, + pub vertex_buf: glow::Buffer, + pub texcoords_buf: glow::Buffer, + pub colors_buf: glow::Buffer, + pub index_buf: glow::Buffer, } impl TrueType { - pub fn new(ctx: &context::Context) -> Self { + pub fn new(ctx: &context::Context, size: f32, data: &[u8]) -> Self { let shader = shader::Shader::new_nolib( &ctx, include_str!("assets/shaders/truetype/vert.glsl"), include_str!("assets/shaders/truetype/frag.glsl"), ); - let size = 20.0; - let font = fontdue::Font::from_bytes( - include_bytes!("assets/fonts/ComicNeue-Regular.ttf") as &[u8], - fontdue::FontSettings::default(), - ).expect("failed to load font"); + let font = fontdue::Font::from_bytes(data, fontdue::FontSettings::default()) + .expect("failed to load font"); let mut chardata = HashMap::new(); for ci in 0..128 { if let Some(c) = char::from_u32(ci) { @@ -213,51 +243,121 @@ impl TrueType { Some(&atlas_bmp), ); ctx.gl.generate_mipmap(glow::TEXTURE_2D); + let vao = ctx.gl.create_vertex_array().expect("failed to initialize vao"); + ctx.gl.bind_vertex_array(Some(vao)); + let vertex_buf = ctx.gl.create_buffer().expect("failed to create buffer object"); + ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buf)); + ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_VERTEX, 2, glow::FLOAT, false, 0, 0); + ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_VERTEX); + let texcoords_buf = ctx.gl.create_buffer().expect("failed to create buffer object"); + ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(texcoords_buf)); + ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_TEXCOORD, 2, glow::FLOAT, false, 0, 0); + ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_TEXCOORD); + let colors_buf = ctx.gl.create_buffer().expect("failed to create buffer object"); + ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(colors_buf)); + ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_COLOR, 3, glow::FLOAT, false, 0, 0); + ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_COLOR); + let index_buf = ctx.gl.create_buffer().expect("failed to create buffer object"); + ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buf)); + Self { + shader, font, atlas, atlaswidth, cellwidth, cellheight, info, + vao, vertex_buf, texcoords_buf, colors_buf, index_buf, + } } - Self { shader, font, atlas, atlaswidth, cellwidth, cellheight, info, } } - pub fn render_text(&self, ctx: &context::Context, pos: &glam::Vec2, text: &str) { - self.shader.bind(ctx); - unsafe { - ctx.gl.active_texture(glow::TEXTURE0); - ctx.gl.bind_texture(glow::TEXTURE_2D, Some(self.atlas.tex)); + pub fn render_text_helper(&self, ctx: &context::Context, pos: &glam::Vec2, spacing: &glam::Vec2, text: &str, color: &[glam::Vec3]) { + let mut cur = glam::Vec2::new(0.0, 0.0); + let mut vertices = Vec::new(); + let mut texcoords = Vec::new(); + let mut colors = Vec::new(); + let mut indices = Vec::new(); + let cellwidth = self.cellwidth as f32; + let cellheight = self.cellheight as f32; + let cwidth = cellwidth / self.atlaswidth as f32; + let cheight = 1.0; + for (i, c) in text.chars().enumerate() { + if c == '\n' { + cur.x = 0.0; + cur.y -= spacing.y; + } else { + let idx = vertices.len() as u32; + if let Some(off) = self.info.get(&c) { + vertices.push(cur); + vertices.push(cur + glam::Vec2::new(cellwidth, 0.0)); + vertices.push(cur + glam::Vec2::new(cellwidth, cellheight)); + vertices.push(cur + glam::Vec2::new(0.0, cellheight)); + let tcbase = glam::Vec2::new(off.pos as f32 / self.atlaswidth as f32, 0.0); + texcoords.push(tcbase + glam::Vec2::new(0.0, cheight)); + texcoords.push(tcbase + glam::Vec2::new(cwidth, cheight)); + texcoords.push(tcbase + glam::Vec2::new(cwidth, 0.0)); + texcoords.push(tcbase); + let c = if let Some(c) = color.get(i) { + *c + } else { + glam::Vec3::new(1.0, 1.0, 1.0) + }; + colors.push(c); colors.push(c); colors.push(c); colors.push(c); + indices.push(idx + 0); indices.push(idx + 1); indices.push(idx + 2); + indices.push(idx + 0); indices.push(idx + 3); indices.push(idx + 2); + } + cur.x += spacing.x; + } } - self.shader.set_mat4( - ctx, "view", - &glam::Mat4::from_scale( - glam::Vec3::new( - 2.0 / ctx.render_width, - 2.0 / ctx.render_height, - 1.0, - ), - ), + let index_bytes: Vec<u8> = indices.iter().flat_map(|x| x.to_ne_bytes()).collect(); + let scale = glam::Vec2::new(2.0 / ctx.render_width, 2.0 / ctx.render_height); + let offset = glam::Vec2::new( + -ctx.render_width / 2.0, + ctx.render_height / 2.0 - cellheight as f32, ); - let width = text.len() * self.cellwidth; + let npos = (glam::Vec2::new(pos.x, -pos.y) + offset) * scale; + self.shader.bind(ctx); self.shader.set_mat4( - ctx, "position", + ctx, "transform", &glam::Mat4::from_scale_rotation_translation( - glam::Vec3::new(width as f32 / 2.0, self.cellheight as f32 / 2.0, 1.0), + glam::Vec3::new(scale.x, scale.y, 1.0), glam::Quat::IDENTITY, - glam::Vec3::new( - -ctx.render_width / 2.0 + pos.x + width as f32 / 2.0, - ctx.render_height / 2.0 - pos.y - self.cellheight as f32 / 2.0, - 0.0, - ), - ) + glam::Vec3::new(npos.x, npos.y, 0.0), + ), ); - let len = text.len().min(256); - let textvals: Vec<i32> = text.chars().take(len).map(|c| { - if let Some(i) = self.info.get(&c) { - i.pos as i32 - } else { - 0 - } - }).collect(); - self.shader.set_i32_array(ctx, "text[0]", &textvals); - self.shader.set_i32(ctx, "atlas_width", self.atlaswidth as i32); - self.shader.set_i32(ctx, "cell_width", self.cellwidth as i32); - self.shader.set_i32(ctx, "text_width", width as i32); - ctx.render_no_geometry(); + unsafe { + ctx.gl.active_texture(glow::TEXTURE0); + ctx.gl.bind_texture(glow::TEXTURE_2D, Some(self.atlas.tex)); + ctx.gl.bind_vertex_array(Some(self.vao)); + ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buf)); + ctx.gl.buffer_data_u8_slice( + glow::ARRAY_BUFFER, + std::slice::from_raw_parts( + vertices.as_ptr() as _, + vertices.len() * std::mem::size_of::<f32>() * 2, + ), + glow::STATIC_DRAW, + ); + ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.texcoords_buf)); + ctx.gl.buffer_data_u8_slice( + glow::ARRAY_BUFFER, + std::slice::from_raw_parts( + texcoords.as_ptr() as _, + texcoords.len() * std::mem::size_of::<f32>() * 2, + ), + glow::STATIC_DRAW, + ); + ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.colors_buf)); + ctx.gl.buffer_data_u8_slice( + glow::ARRAY_BUFFER, + std::slice::from_raw_parts( + colors.as_ptr() as _, + colors.len() * std::mem::size_of::<f32>() * 3, + ), + glow::STATIC_DRAW, + ); + ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buf)); + ctx.gl.buffer_data_u8_slice( + glow::ELEMENT_ARRAY_BUFFER, + &index_bytes, + glow::STATIC_DRAW, + ); + ctx.gl.draw_elements(glow::TRIANGLES, indices.len() as _, glow::UNSIGNED_INT, 0); + } } } |
