summaryrefslogtreecommitdiff
path: root/src/font.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/font.rs')
-rw-r--r--src/font.rs232
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);
+ }
}
}