use super::font::FontRenderer;
use crate::graphics::Shader;
use rusttype::PositionedGlyph;
pub struct TextRenderer {
font_renderer: FontRenderer,
shader: Shader,
}
impl TextRenderer {
pub fn new(font_data: Vec<u8>) -> Self {
Self {
font_renderer: FontRenderer::new(font_data),
shader: Shader::new(
include_str!("shaders/text.vert"),
include_str!("shaders/text.frag")
),
}
}
pub fn render_text(
&mut self,
text: &str,
x: f32,
y: f32,
scale: f32,
screen_width: f32,
screen_height: f32,
color: [f32; 4]
) {
self.shader.use_program();
self.shader.set_vec4("textColor", &color);
let glyphs = self.font_renderer.render_text(text, scale);
for glyph in glyphs.iter() {
if let Some(bb) = glyph.pixel_bounding_box() {
let adjusted_bb = rusttype::Rect {
min: rusttype::Point {
x: bb.min.x + (x as i32),
y: bb.min.y + (y as i32),
},
max: rusttype::Point {
x: bb.max.x + (x as i32),
y: bb.max.y + (y as i32),
},
};
self.draw_glyph_with_bb(glyph, &adjusted_bb, screen_width, screen_height);
}
}
}
fn draw_glyph_with_bb(
&self,
glyph: &PositionedGlyph<'_>,
bb: &rusttype::Rect<i32>,
screen_width: f32,
screen_height: f32
) {
let width = bb.width() as usize;
let height = bb.height() as usize;
let mut pixel_data = vec![0u8; width * height];
glyph.draw(|x, y, v| {
let idx = (y as usize) * width + (x as usize);
pixel_data[idx] = (v * 255.0) as u8;
});
let mut tex_id: u32 = 0;
unsafe {
gl::GenTextures(1, &mut tex_id);
gl::BindTexture(gl::TEXTURE_2D, tex_id);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);
gl::TexImage2D(
gl::TEXTURE_2D,
0,
gl::RED as i32,
width as i32,
height as i32,
0,
gl::RED,
gl::UNSIGNED_BYTE,
pixel_data.as_ptr() as *const _
);
}
let x0 = bb.min.x as f32;
let y0 = bb.min.y as f32;
let x1 = bb.max.x as f32;
let y1 = bb.max.y as f32;
let ndc_x0 = (x0 / screen_width) * 2.0 - 1.0;
let ndc_y0 = 1.0 - (y0 / screen_height) * 2.0;
let ndc_x1 = (x1 / screen_width) * 2.0 - 1.0;
let ndc_y1 = 1.0 - (y1 / screen_height) * 2.0;
let vertices: [f32; 20] = [
ndc_x0,
ndc_y1,
0.0,
0.0,
1.0, ndc_x1,
ndc_y1,
0.0,
1.0,
1.0, ndc_x1,
ndc_y0,
0.0,
1.0,
0.0, ndc_x0,
ndc_y0,
0.0,
0.0,
0.0, ];
let indices: [u32; 6] = [0, 1, 2, 2, 3, 0];
let (mut vao, mut vbo, mut ebo) = (0, 0, 0);
unsafe {
gl::GenVertexArrays(1, &mut vao);
gl::GenBuffers(1, &mut vbo);
gl::GenBuffers(1, &mut ebo);
gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
(vertices.len() * std::mem::size_of::<f32>()) as isize,
vertices.as_ptr() as *const _,
gl::STATIC_DRAW
);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER,
(indices.len() * std::mem::size_of::<u32>()) as isize,
indices.as_ptr() as *const _,
gl::STATIC_DRAW
);
gl::VertexAttribPointer(
0,
3,
gl::FLOAT,
gl::FALSE,
(5 * std::mem::size_of::<f32>()) as i32,
std::ptr::null()
);
gl::EnableVertexAttribArray(0);
gl::VertexAttribPointer(
1,
2,
gl::FLOAT,
gl::FALSE,
(5 * std::mem::size_of::<f32>()) as i32,
(3 * std::mem::size_of::<f32>()) as *const _
);
gl::EnableVertexAttribArray(1);
gl::BindTexture(gl::TEXTURE_2D, tex_id);
gl::BindVertexArray(vao);
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, std::ptr::null());
gl::DeleteBuffers(1, &vbo);
gl::DeleteBuffers(1, &ebo);
gl::DeleteVertexArrays(1, &vao);
gl::DeleteTextures(1, &tex_id);
}
}
pub fn font_renderer(&self) -> &FontRenderer {
&self.font_renderer
}
}