1use super::shader::Shader;
2use crate::text::TextRenderer;
3use nalgebra_glm as glm;
4
5pub struct Renderer {
6 shader: Shader,
7 texture_shader: Shader,
8 text_renderer: TextRenderer,
9 screen_width: f32,
10 screen_height: f32,
11 background_color: [f32; 4],
12 vao: u32,
13 vbo: u32,
14 circle_vertices: Vec<f32>, }
16
17const CIRCLE_SEGMENTS: usize = 32;
18
19impl Renderer {
20 pub fn new(font_data: Vec<u8>) -> Self {
21 let vertex_source = include_str!("shaders/basic.vert");
22 let fragment_source = include_str!("shaders/basic.frag");
23 let shader = Shader::new(vertex_source, fragment_source);
24 let texture_shader = Shader::new(
25 include_str!("shaders/texture.vert"),
26 include_str!("shaders/texture.frag")
27 );
28 let text_renderer = TextRenderer::new(font_data);
29
30 let mut vao = 0;
31 let mut vbo = 0;
32
33 unsafe {
34 gl::GenVertexArrays(1, &mut vao);
35 gl::GenBuffers(1, &mut vbo);
36
37 gl::BindVertexArray(vao);
38 gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
39
40 gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, 0, std::ptr::null());
41 gl::EnableVertexAttribArray(0);
42 }
43
44 let mut circle_vertices = Vec::with_capacity(CIRCLE_SEGMENTS * 3 * 3);
46 for i in 0..CIRCLE_SEGMENTS {
47 let angle = (2.0 * std::f32::consts::PI * (i as f32)) / (CIRCLE_SEGMENTS as f32);
48 let x = angle.cos();
49 let y = angle.sin();
50 circle_vertices.extend_from_slice(&[x, y, 0.0]);
51 }
52
53 Self {
54 shader,
55 texture_shader,
56 text_renderer,
57 screen_width: 0.0,
58 screen_height: 0.0,
59 background_color: [0.1, 0.35, 0.33, 1.0],
60 vao,
61 vbo,
62 circle_vertices,
63 }
64 }
65
66 pub fn set_background_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
67 self.background_color = [r, g, b, a];
68 }
69
70 pub fn render(&mut self, screen_width: f32, screen_height: f32) {
73 self.screen_width = screen_width;
75 self.screen_height = screen_height;
76
77 unsafe {
78 gl::ClearColor(
80 self.background_color[0],
81 self.background_color[1],
82 self.background_color[2],
83 self.background_color[3]
84 );
85 gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
86 }
87
88 self.shader.use_program();
90
91 let projection = glm::ortho(0.0, screen_width, screen_height, 0.0, -1.0, 1.0);
94 self.shader.set_mat4("projection", &projection);
95 }
96
97 pub fn text_renderer(&self) -> &TextRenderer {
99 &self.text_renderer
100 }
101
102 pub fn draw_rect(&mut self, x: f32, y: f32, width: f32, height: f32, color: [f32; 4]) {
105 let current_shader = unsafe {
107 let mut current_program = 0;
108 gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut current_program);
109 current_program
110 };
111
112 self.shader.use_program();
114 self.shader.set_vec4("uColor", &color);
115
116 let x_ndc = (x / self.screen_width) * 2.0 - 1.0;
118 let y_ndc = 1.0 - (y / self.screen_height) * 2.0;
119 let x2_ndc = ((x + width) / self.screen_width) * 2.0 - 1.0;
120 let y2_ndc = 1.0 - ((y + height) / self.screen_height) * 2.0;
121
122 let vertices: [f32; 12] = [
123 x_ndc,
124 y_ndc,
125 0.0, x2_ndc,
127 y_ndc,
128 0.0, x2_ndc,
130 y2_ndc,
131 0.0, x_ndc,
133 y2_ndc,
134 0.0, ];
136 let indices: [u32; 6] = [0, 1, 2, 2, 3, 0];
137
138 unsafe {
139 let mut vao = 0;
140 let mut vbo = 0;
141 let mut ebo = 0;
142
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);
150 gl::BufferData(
151 gl::ARRAY_BUFFER,
152 (vertices.len() * std::mem::size_of::<f32>()) as isize,
153 vertices.as_ptr() as *const _,
154 gl::STATIC_DRAW
155 );
156
157 gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
158 gl::BufferData(
159 gl::ELEMENT_ARRAY_BUFFER,
160 (indices.len() * std::mem::size_of::<u32>()) as isize,
161 indices.as_ptr() as *const _,
162 gl::STATIC_DRAW
163 );
164
165 gl::VertexAttribPointer(
166 0,
167 3,
168 gl::FLOAT,
169 gl::FALSE,
170 (3 * std::mem::size_of::<f32>()) as i32,
171 std::ptr::null()
172 );
173 gl::EnableVertexAttribArray(0);
174
175 gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, std::ptr::null());
176
177 gl::DeleteVertexArrays(1, &vao);
179 gl::DeleteBuffers(1, &vbo);
180 gl::DeleteBuffers(1, &ebo);
181
182 if current_shader != 0 {
184 gl::UseProgram(current_shader as u32);
185 }
186 }
187 }
188
189 pub fn text_renderer_mut(&mut self) -> &mut TextRenderer {
191 &mut self.text_renderer
192 }
193
194 pub fn draw_textured_rect(
195 &self,
196 x: f32,
197 y: f32,
198 width: f32,
199 height: f32,
200 texture_id: u32,
201 opacity: f32
202 ) {
203 self.texture_shader.use_program();
204
205 let projection = glm::ortho(0.0, self.screen_width, self.screen_height, 0.0, -1.0, 1.0);
207 self.texture_shader.set_mat4("projection", &projection);
208 self.texture_shader.set_float("uOpacity", opacity);
209
210 unsafe {
211 gl::ActiveTexture(gl::TEXTURE0);
212 gl::BindTexture(gl::TEXTURE_2D, texture_id);
213
214 let vertices: [f32; 20] = [
215 x,
216 y,
217 0.0,
218 0.0,
219 1.0,
220 x + width,
221 y,
222 0.0,
223 1.0,
224 1.0,
225 x + width,
226 y + height,
227 0.0,
228 1.0,
229 0.0,
230 x,
231 y + height,
232 0.0,
233 0.0,
234 0.0,
235 ];
236
237 let indices: [u32; 6] = [0, 1, 2, 2, 3, 0];
238
239 let mut vao = 0;
240 let mut vbo = 0;
241 let mut ebo = 0;
242
243 gl::GenVertexArrays(1, &mut vao);
244 gl::GenBuffers(1, &mut vbo);
245 gl::GenBuffers(1, &mut ebo);
246
247 gl::BindVertexArray(vao);
248
249 gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
250 gl::BufferData(
251 gl::ARRAY_BUFFER,
252 (vertices.len() * std::mem::size_of::<f32>()) as isize,
253 vertices.as_ptr() as *const _,
254 gl::STATIC_DRAW
255 );
256
257 gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
258 gl::BufferData(
259 gl::ELEMENT_ARRAY_BUFFER,
260 (indices.len() * std::mem::size_of::<u32>()) as isize,
261 indices.as_ptr() as *const _,
262 gl::STATIC_DRAW
263 );
264
265 gl::VertexAttribPointer(
267 0,
268 3,
269 gl::FLOAT,
270 gl::FALSE,
271 (5 * std::mem::size_of::<f32>()) as i32,
272 std::ptr::null()
273 );
274 gl::EnableVertexAttribArray(0);
275
276 gl::VertexAttribPointer(
278 1,
279 2,
280 gl::FLOAT,
281 gl::FALSE,
282 (5 * std::mem::size_of::<f32>()) as i32,
283 (3 * std::mem::size_of::<f32>()) as *const _
284 );
285 gl::EnableVertexAttribArray(1);
286
287 gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, std::ptr::null());
288
289 gl::DeleteVertexArrays(1, &vao);
290 gl::DeleteBuffers(1, &vbo);
291 gl::DeleteBuffers(1, &ebo);
292 }
293 }
294
295 pub fn draw_triangle(&mut self, vertices: [(f32, f32); 3], color: [f32; 4]) {
296 let current_shader = unsafe {
297 let mut current_program = 0;
298 gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut current_program);
299 current_program
300 };
301
302 self.shader.use_program();
303 self.shader.set_vec4("uColor", &color);
304
305 let vertices_ndc: Vec<f32> = vertices
307 .iter()
308 .flat_map(|(x, y)| {
309 let x_ndc = (x / self.screen_width) * 2.0 - 1.0;
310 let y_ndc = 1.0 - (y / self.screen_height) * 2.0;
311 vec![x_ndc, y_ndc, 0.0]
312 })
313 .collect();
314
315 unsafe {
316 gl::BindVertexArray(self.vao);
317 gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
318 gl::BufferData(
319 gl::ARRAY_BUFFER,
320 (vertices_ndc.len() * std::mem::size_of::<f32>()) as isize,
321 vertices_ndc.as_ptr() as *const _,
322 gl::STATIC_DRAW
323 );
324 gl::DrawArrays(gl::TRIANGLES, 0, 3);
325 }
326
327 unsafe {
329 gl::UseProgram(current_shader as u32);
330 }
331 }
332
333 pub fn draw_circle(&mut self, center_x: f32, center_y: f32, radius: f32, color: [f32; 4]) {
334 let current_shader = unsafe {
335 let mut current_program = 0;
336 gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut current_program);
337 current_program
338 };
339
340 self.shader.use_program();
341 self.shader.set_vec4("uColor", &color);
342
343 let vertices_transformed: Vec<f32> = self.circle_vertices
345 .chunks(3)
346 .flat_map(|v| {
347 let x = center_x + v[0] * radius;
348 let y = center_y + v[1] * radius;
349 vec![(x / self.screen_width) * 2.0 - 1.0, 1.0 - (y / self.screen_height) * 2.0, 0.0]
350 })
351 .collect();
352
353 unsafe {
354 gl::BindVertexArray(self.vao);
355 gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
356 gl::BufferData(
357 gl::ARRAY_BUFFER,
358 (vertices_transformed.len() * std::mem::size_of::<f32>()) as isize,
359 vertices_transformed.as_ptr() as *const _,
360 gl::STATIC_DRAW
361 );
362 gl::DrawArrays(gl::TRIANGLE_FAN, 0, CIRCLE_SEGMENTS as i32);
363 }
364
365 unsafe {
366 gl::UseProgram(current_shader as u32);
367 }
368 }
369}