1use super::font::FontRenderer;
2use crate::graphics::Shader;
3use rusttype::PositionedGlyph;
4
5pub struct TextRenderer {
6 font_renderer: FontRenderer,
7 shader: Shader,
8}
9
10impl TextRenderer {
11 pub fn new(font_data: Vec<u8>) -> Self {
12 Self {
13 font_renderer: FontRenderer::new(font_data),
14 shader: Shader::new(
15 include_str!("shaders/text.vert"),
16 include_str!("shaders/text.frag")
17 ),
18 }
19 }
20
21 pub fn render_text(
22 &mut self,
23 text: &str,
24 x: f32,
25 y: f32,
26 scale: f32,
27 screen_width: f32,
28 screen_height: f32,
29 color: [f32; 4]
30 ) {
31 self.shader.use_program();
32 self.shader.set_vec4("textColor", &color);
33
34 let glyphs = self.font_renderer.render_text(text, scale);
35 for glyph in glyphs.iter() {
36 if let Some(bb) = glyph.pixel_bounding_box() {
37 let adjusted_bb = rusttype::Rect {
39 min: rusttype::Point {
40 x: bb.min.x + (x as i32),
41 y: bb.min.y + (y as i32),
42 },
43 max: rusttype::Point {
44 x: bb.max.x + (x as i32),
45 y: bb.max.y + (y as i32),
46 },
47 };
48 self.draw_glyph_with_bb(glyph, &adjusted_bb, screen_width, screen_height);
49 }
50 }
51 }
52
53 fn draw_glyph_with_bb(
59 &self,
60 glyph: &PositionedGlyph<'_>,
61 bb: &rusttype::Rect<i32>,
62 screen_width: f32,
63 screen_height: f32
64 ) {
65 let width = bb.width() as usize;
66 let height = bb.height() as usize;
67
68 let mut pixel_data = vec![0u8; width * height];
70
71 glyph.draw(|x, y, v| {
73 let idx = (y as usize) * width + (x as usize);
74 pixel_data[idx] = (v * 255.0) as u8;
75 });
76
77 let mut tex_id: u32 = 0;
79 unsafe {
80 gl::GenTextures(1, &mut tex_id);
81
82 gl::BindTexture(gl::TEXTURE_2D, tex_id);
83 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
84 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
85 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
86 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
87 gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);
89 gl::TexImage2D(
90 gl::TEXTURE_2D,
91 0,
92 gl::RED as i32,
93 width as i32,
94 height as i32,
95 0,
96 gl::RED,
97 gl::UNSIGNED_BYTE,
98 pixel_data.as_ptr() as *const _
99 );
100 }
101
102 let x0 = bb.min.x as f32;
103 let y0 = bb.min.y as f32;
104 let x1 = bb.max.x as f32;
105 let y1 = bb.max.y as f32;
106
107 let ndc_x0 = (x0 / screen_width) * 2.0 - 1.0;
109 let ndc_y0 = 1.0 - (y0 / screen_height) * 2.0;
110 let ndc_x1 = (x1 / screen_width) * 2.0 - 1.0;
111 let ndc_y1 = 1.0 - (y1 / screen_height) * 2.0;
112
113 let vertices: [f32; 20] = [
115 ndc_x0,
117 ndc_y1,
118 0.0,
119 0.0,
120 1.0, ndc_x1,
122 ndc_y1,
123 0.0,
124 1.0,
125 1.0, ndc_x1,
127 ndc_y0,
128 0.0,
129 1.0,
130 0.0, ndc_x0,
132 ndc_y0,
133 0.0,
134 0.0,
135 0.0, ];
137 let indices: [u32; 6] = [0, 1, 2, 2, 3, 0];
139
140 let (mut vao, mut vbo, mut ebo) = (0, 0, 0);
142 unsafe {
143 gl::GenVertexArrays(1, &mut vao);
144 gl::GenBuffers(1, &mut vbo);
145 gl::GenBuffers(1, &mut ebo);
146
147 gl::BindVertexArray(vao);
148
149 gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
151 gl::BufferData(
152 gl::ARRAY_BUFFER,
153 (vertices.len() * std::mem::size_of::<f32>()) as isize,
154 vertices.as_ptr() as *const _,
155 gl::STATIC_DRAW
156 );
157
158 gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
160 gl::BufferData(
161 gl::ELEMENT_ARRAY_BUFFER,
162 (indices.len() * std::mem::size_of::<u32>()) as isize,
163 indices.as_ptr() as *const _,
164 gl::STATIC_DRAW
165 );
166
167 gl::VertexAttribPointer(
169 0,
170 3,
171 gl::FLOAT,
172 gl::FALSE,
173 (5 * std::mem::size_of::<f32>()) as i32,
174 std::ptr::null()
175 );
176 gl::EnableVertexAttribArray(0);
177 gl::VertexAttribPointer(
178 1,
179 2,
180 gl::FLOAT,
181 gl::FALSE,
182 (5 * std::mem::size_of::<f32>()) as i32,
183 (3 * std::mem::size_of::<f32>()) as *const _
184 );
185 gl::EnableVertexAttribArray(1);
186
187 gl::BindTexture(gl::TEXTURE_2D, tex_id);
189 gl::BindVertexArray(vao);
190 gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, std::ptr::null());
191
192 gl::DeleteBuffers(1, &vbo);
193 gl::DeleteBuffers(1, &ebo);
194 gl::DeleteVertexArrays(1, &vao);
195 gl::DeleteTextures(1, &tex_id);
196 }
197 }
198
199 pub fn font_renderer(&self) -> &FontRenderer {
200 &self.font_renderer
201 }
202}