use crate::{gl, gl::types::*};
use crate::{types::*, utils::*};
use rusttype::{gpu_cache::Cache, PositionedGlyph};
extern crate nalgebra_glm as glm;
pub struct TextRenderer {
program: ShaderProgram,
buf: ArrayBuffer,
tex_id: u32,
loc_vertex: i32,
loc_tex_coord: i32,
loc_col: i32,
loc_mvp: i32,
pub window_width: f32,
pub window_height: f32,
cache: Cache<'static>,
used_glyphs: Vec<PositionedGlyph<'static>>,
}
impl TextRenderer {
pub fn new(window_width: f32, window_height: f32) -> Self {
let cache: Cache<'static> = Cache::builder().dimensions(0, 0).build();
let mut tex_id: GLuint = 0;
unsafe {
gl::GenTextures(1, &mut tex_id);
}
let program = ShaderProgram::new(vec![
ShaderSource::VertexShader(
"
#version 300 es
precision mediump float;
uniform mat4 mvp;
in vec2 vertex;
in vec2 tex_coord;
out vec2 fragment_tex_coord;
void main() {
gl_Position=mvp*vec4(vertex,0.0,1.0);
fragment_tex_coord=tex_coord;
}
"
.to_string(),
),
ShaderSource::FragmentShader(
"
#version 300 es
precision mediump float;
uniform vec4 col;
uniform sampler2D texture0;
in vec2 fragment_tex_coord;
out vec4 fragment_color;
void main() {
vec4 tex_data=texture(texture0,fragment_tex_coord);
fragment_color=vec4(col.r,col.g,col.b,tex_data.r);
}
"
.to_string(),
),
]);
let mut slf = Self {
loc_vertex: program.get_attrib_location("vertex"),
loc_tex_coord: program.get_attrib_location("tex_coord"),
loc_col: program.get_uniform_location("col"),
loc_mvp: program.get_uniform_location("mvp"),
buf: ArrayBuffer::new(4),
program,
tex_id,
window_width,
window_height,
cache,
used_glyphs: vec![],
};
slf.set_cache_size(1);
slf
}
fn set_cache_size(&mut self, size: u32) {
self.cache
.to_builder()
.dimensions(size, size)
.rebuild(&mut self.cache);
unsafe {
gl::BindTexture(gl::TEXTURE_2D, self.tex_id);
gl::ActiveTexture(gl::TEXTURE0);
gl::TexImage2D(
gl::TEXTURE_2D, 0, gl::R8 as i32,
size as i32, size as i32, 0, gl::RED as u32,
gl::UNSIGNED_BYTE,
std::ptr::null(),
);
}
}
fn render_cache(&mut self) {
unsafe {
gl::BindTexture(gl::TEXTURE_2D, self.tex_id);
}
let mut cache_misses = 0;
let mut do_build = true;
while do_build {
let res = self.cache.cache_queued(|rect, data| {
unsafe {
gl::ActiveTexture(gl::TEXTURE0);
gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);
gl::TexSubImage2D(
gl::TEXTURE_2D,
0,
rect.min.x as i32,
rect.min.y as i32,
(rect.width()) as i32,
rect.height() as i32,
gl::RED,
gl::UNSIGNED_BYTE,
data.as_ptr() as *const _,
);
}
cache_misses += 1;
});
match res {
Err(_) => {
cache_misses = 0;
self.set_cache_size(self.cache.dimensions().0 * 2);
}
Ok(_) => {
do_build = false;
}
};
}
if cache_misses > 0 {
}
}
fn vertices_for(&self, glyph: &PositionedGlyph, pr: f32) -> Vec<f32> {
let rect = self.cache.rect_for(0, glyph).unwrap();
if rect.is_none() {
return vec![];
}
let (uv, screen) = rect.unwrap();
vec![
screen.min.x as f32 / pr,
screen.min.y as f32 / pr,
uv.min.x,
uv.min.y,
screen.max.x as f32 / pr,
screen.min.y as f32 / pr,
uv.max.x,
uv.min.y,
screen.max.x as f32 / pr,
screen.max.y as f32 / pr,
uv.max.x,
uv.max.y,
screen.min.x as f32 / pr,
screen.min.y as f32 / pr,
uv.min.x,
uv.min.y,
screen.max.x as f32 / pr,
screen.max.y as f32 / pr,
uv.max.x,
uv.max.y,
screen.min.x as f32 / pr,
screen.max.y as f32 / pr,
uv.min.x,
uv.max.y,
]
}
pub fn draw(
&mut self,
text: &str,
x: f32,
mut y: f32,
font: &Font,
size: f32,
col: u32,
pr: f32,
) {
let m = glm::ortho(0.0, self.window_width, self.window_height, 0.0, -1.0, 1.0);
let c = glm::vec4(
((col & 0xff0000) >> 16) as f32 / 255.0,
((col & 0x00ff00) >> 8) as f32 / 255.0,
(col & 0x0000ff) as f32 / 255.0,
1.0,
);
y += font.baseline(size);
let glyphs = font.create_glyphs(text, x * pr, y * pr, size * pr);
for glyph in glyphs.clone() {
self.used_glyphs.push(glyph.clone());
self.cache.queue_glyph(0, glyph);
}
self.render_cache();
let mut data: Vec<f32> = vec![];
for glyph in glyphs {
data.append(&mut self.vertices_for(&glyph, pr));
}
self.buf.set_data(data);
self.program.use_program();
self.buf.bind(self.loc_vertex, 0, 2);
self.buf.bind(self.loc_tex_coord, 2, 2);
unsafe {
gl::BindTexture(gl::TEXTURE_2D, self.tex_id);
gl::ActiveTexture(gl::TEXTURE0);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
gl::Uniform4fv(self.loc_col, 1, c.as_ptr());
gl::UniformMatrix4fv(self.loc_mvp, 1, gl::FALSE, m.as_ptr());
gl::Enable(gl::BLEND);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
gl::DrawArrays(gl::TRIANGLES, 0, self.buf.len() as i32);
}
}
pub fn begin_frame(&mut self) {
self.used_glyphs = vec![];
}
pub fn end_frame(&mut self) {
for glyph in self.used_glyphs.clone() {
self.cache.queue_glyph(0, glyph);
}
self.render_cache();
self.used_glyphs = vec![];
}
}