moon_engine/
renderer.rs

1//! The [`Renderer`] annd [`Quad`] structs. Used for rendering.
2
3use std::collections::BTreeMap;
4use std::fmt;
5use std::rc::Rc;
6use web_sys::WebGlUniformLocation;
7
8use crate::component::Component;
9use crate::{gl, mesh, texture, Color32};
10use crate::{Camera, Shader, Transform, GL};
11
12use gl::Bind;
13use mesh::{Mesh, Vertex};
14use texture::{SubTexture, Texture};
15
16/// Maximum [`Quad`]s in a single batch.
17pub const MAX_BATCH_QUADS: i32 = 1000;
18const MAX_BATCH_VERTICES: i32 = MAX_BATCH_QUADS * 4;
19const MAX_BATCH_INDICES: i32 = MAX_BATCH_QUADS * 6;
20
21/// A [`Quad`] is a simple mesh definition with four [`Vertices`](Vertex).
22#[derive(Debug)]
23pub struct Quad([Vertex; 4]);
24
25impl Default for Quad {
26    fn default() -> Self {
27        Self([
28            Vertex {
29                position: [-0.5, 0.5],
30                uv: [0.0, 0.0],
31                ..Default::default()
32            },
33            Vertex {
34                position: [-0.5, -0.5],
35                uv: [0.0, 1.0],
36                ..Default::default()
37            },
38            Vertex {
39                position: [0.5, -0.5],
40                uv: [1.0, 1.0],
41                ..Default::default()
42            },
43            Vertex {
44                position: [0.5, 0.5],
45                uv: [1.0, 0.0],
46                ..Default::default()
47            },
48        ])
49    }
50}
51
52impl Quad {
53    /// Create a new [`Quad`] from a given position, rotation, size and color.
54    pub fn new_from_position_and_rotation_and_size_and_color(
55        pos_x: f32,
56        pos_y: f32,
57        rotation: f32,
58        size_x: f32,
59        size_y: f32,
60        color: Color32,
61    ) -> Self {
62        let size_x = size_x / 2.0;
63        let size_y = size_y / 2.0;
64        let color = <[f32; 4]>::from(color);
65        let (sin_theta, cos_theta) = rotation.sin_cos();
66        let mut points = [
67            [-size_x, -size_y],
68            [-size_x, size_y],
69            [size_x, size_y],
70            [size_x, -size_y],
71        ];
72        for point in points.iter_mut() {
73            let (x, y) = (point[0], point[1]);
74            point[0] = pos_x + x * cos_theta - y * sin_theta;
75            point[1] = pos_y + x * sin_theta + y * cos_theta;
76        }
77        Self([
78            Vertex {
79                position: points[0],
80                uv: [0.0, 0.0],
81                color,
82            },
83            Vertex {
84                position: points[1],
85                uv: [0.0, 1.0],
86                color,
87            },
88            Vertex {
89                position: points[2],
90                uv: [1.0, 1.0],
91                color,
92            },
93            Vertex {
94                position: points[3],
95                uv: [1.0, 0.0],
96                color,
97            },
98        ])
99    }
100
101    /// Create a new [`Quad`] from a given position, size and color.
102    pub fn new_from_position_and_size_and_color(
103        pos_x: f32,
104        pos_y: f32,
105        size_x: f32,
106        size_y: f32,
107        color: Color32,
108    ) -> Self {
109        let size_x = size_x / 2.0;
110        let size_y = size_y / 2.0;
111        let color = <[f32; 4]>::from(color);
112        Self([
113            Vertex {
114                position: [pos_x - size_x, pos_y + size_y],
115                uv: [0.0, 0.0],
116                color,
117            },
118            Vertex {
119                position: [pos_x - size_x, pos_y - size_y],
120                uv: [0.0, 1.0],
121                color,
122            },
123            Vertex {
124                position: [pos_x + size_x, pos_y - size_y],
125                uv: [1.0, 1.0],
126                color,
127            },
128            Vertex {
129                position: [pos_x + size_x, pos_y + size_y],
130                uv: [1.0, 0.0],
131                color,
132            },
133        ])
134    }
135
136    /// Create a new [`Quad`] from a given position and size.
137    pub fn new_from_position_and_size(pos_x: f32, pos_y: f32, size_x: f32, size_y: f32) -> Self {
138        Self::new_from_position_and_size_and_color(pos_x, pos_y, size_x, size_y, Color32::WHITE)
139    }
140
141    /// Create a new [`Quad`] from a given position, size, and a reference to a [`SubTexture`].
142    pub fn new_from_position_and_size_and_sprite(
143        pos_x: f32,
144        pos_y: f32,
145        size_x: f32,
146        size_y: f32,
147        sprite: &SubTexture,
148    ) -> Self {
149        let uv = sprite.get_uv_coords();
150        let size_x = size_x / 2.0;
151        let size_y = size_y / 2.0;
152        Self([
153            Vertex {
154                position: [pos_x - size_x, pos_y + size_y],
155                uv: uv[0],
156                ..Default::default()
157            },
158            Vertex {
159                position: [pos_x - size_x, pos_y - size_y],
160                uv: uv[1],
161                ..Default::default()
162            },
163            Vertex {
164                position: [pos_x + size_x, pos_y - size_y],
165                uv: uv[2],
166                ..Default::default()
167            },
168            Vertex {
169                position: [pos_x + size_x, pos_y + size_y],
170                uv: uv[3],
171                ..Default::default()
172            },
173        ])
174    }
175
176    /// Create a new [`Quad`] using a given [`Transform`] for its position and scale.
177    pub fn new_from_transform(transform: Transform) -> Self {
178        Self::new_from_position_and_size(
179            transform.position.x,
180            transform.position.x,
181            transform.scale.x,
182            transform.scale.y,
183        )
184    }
185
186    /// Create a new [`Quad`] using a given [`Transform`] for its position and scale, and a reference to [`SubTexture`].
187    pub fn new_from_transform_and_sprite(transform: Transform, sprite: &SubTexture) -> Self {
188        Self::new_from_position_and_size_and_sprite(
189            transform.position.x,
190            transform.position.x,
191            transform.scale.x,
192            transform.scale.y,
193            sprite,
194        )
195    }
196
197    /// Get the [`Vertices`](Vertex) of the [`Quad`] as a [`Vec`].
198    pub fn get_vertices(&self) -> Vec<Vertex> {
199        self.0.to_vec()
200    }
201}
202
203/// The [`Renderer`] is responsible for drawing on the screen. It handles the [`Camera`] and [`Shader`]s.
204pub struct Renderer {
205    /// The [`WebGl2RenderingContext`](web_sys::WebGl2RenderingContext) used by the [`Renderer`].
206    pub gl: GL,
207    /// The [`Shader`] used by the [`Renderer`].
208    pub program: Shader,
209    /// The [`Camera`] used by the [`Renderer`].
210    pub camera: Camera,
211    batches: Vec<Mesh>,
212    /// [`Components`](Component) that can be added to the [`Renderer`].
213    pub components: BTreeMap<&'static str, Box<dyn Component>>,
214    textures: BTreeMap<&'static str, Rc<Texture>>,
215    u_time: Option<WebGlUniformLocation>,
216    u_color: Option<WebGlUniformLocation>,
217    u_model_matrix: Option<WebGlUniformLocation>,
218    u_view_matrix: Option<WebGlUniformLocation>,
219    u_projection_matrix: Option<WebGlUniformLocation>,
220}
221
222impl Default for Renderer {
223    fn default() -> Self {
224        let gl = gl::get_context();
225        let program = Shader::new(&gl);
226        Self {
227            camera: Camera::default(),
228            batches: Vec::new(),
229            components: BTreeMap::new(),
230            u_time: program.get_uniform_location(&gl, "uTime"),
231            u_color: program.get_uniform_location(&gl, "uColor"),
232            u_model_matrix: program.get_uniform_location(&gl, "uModel"),
233            u_view_matrix: program.get_uniform_location(&gl, "uView"),
234            u_projection_matrix: program.get_uniform_location(&gl, "uProj"),
235            program,
236            textures: {
237                let mut textues = BTreeMap::<&str, Rc<Texture>>::new();
238                textues.insert("WHITE", Rc::new(Texture::white(&gl)));
239                textues.insert("MAGENTA", Rc::new(Texture::colored(&gl, Color32::MAGENTA)));
240                textues.insert("CHECKERBOARD", Rc::new(Texture::checkerboard(&gl)));
241                textues
242            },
243            gl,
244        }
245    }
246}
247
248impl fmt::Debug for Renderer {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        f.debug_struct("Renderer")
251            .field("gl", &self.gl)
252            .field("program", &self.program)
253            .field("camera", &self.camera)
254            .field("batches", &self.batches)
255            .field("textures", &self.textures)
256            .field("u_time", &self.u_time)
257            .field("u_color", &self.u_color)
258            .field("u_model_matrix", &self.u_model_matrix)
259            .field("u_view_matrix", &self.u_view_matrix)
260            .field("u_projection_matrix", &self.u_projection_matrix)
261            .finish()
262    }
263}
264
265impl Renderer {
266    /// Create a new [`Renderer`] with a given [`Camera`].
267    pub fn new_with_camera(camera: Camera) -> Self {
268        Self {
269            camera,
270            ..Default::default()
271        }
272    }
273
274    /// Create a new [`Renderer`] with a given [`Camera`] and [`Shader`].
275    pub fn new_with_camera_and_program(camera: Camera, program: Shader) -> Self {
276        let gl = gl::get_context();
277        program.bind(&gl);
278
279        Self {
280            camera,
281            u_time: program.get_uniform_location(&gl, "uTime"),
282            u_color: program.get_uniform_location(&gl, "uColor"),
283            u_model_matrix: program.get_uniform_location(&gl, "uModel"),
284            u_view_matrix: program.get_uniform_location(&gl, "uView"),
285            u_projection_matrix: program.get_uniform_location(&gl, "uProj"),
286            program,
287            gl,
288            ..Default::default()
289        }
290    }
291
292    /// Set the [`Camera`] that the [`Renderer`] will use.
293    pub fn set_camera(&mut self, camera: Camera) {
294        self.camera = camera;
295    }
296
297    /// Set the [`Shader`] that the [`Renderer`] will use.
298    pub fn set_shader(&mut self, program: Shader) {
299        let gl = &self.gl;
300
301        self.u_time = program.get_uniform_location(gl, "uTime");
302        self.u_color = program.get_uniform_location(gl, "uColor");
303        self.u_model_matrix = program.get_uniform_location(gl, "uModel");
304        self.u_view_matrix = program.get_uniform_location(gl, "uView");
305        self.u_projection_matrix = program.get_uniform_location(gl, "uProj");
306        self.program = program;
307    }
308
309    /// Handle screen resizes.
310    pub fn resize(&mut self, width: f32, height: f32) {
311        self.camera.set_width_and_height(width, height);
312        self.gl.viewport(0, 0, width as i32, height as i32);
313        self.gl.uniform_matrix4fv_with_f32_array(
314            self.u_projection_matrix.as_ref(),
315            false,
316            self.camera.projection(),
317        );
318    }
319
320    /// Initialise the uniforms for the current [`Shader`].
321    pub fn init_shader(&mut self) {
322        let gl = &self.gl;
323        self.program.bind(gl);
324
325        gl.uniform1f(self.u_time.as_ref(), 0.0);
326        gl.uniform4f(self.u_color.as_ref(), 1.0, 1.0, 1.0, 1.0);
327        gl.uniform_matrix4fv_with_f32_array(
328            self.u_view_matrix.as_ref(),
329            false,
330            crate::Mat4::identity().as_slice(),
331        );
332        gl.uniform_matrix4fv_with_f32_array(
333            self.u_view_matrix.as_ref(),
334            false,
335            self.camera.transform.matrix_slice(),
336        );
337        gl.uniform_matrix4fv_with_f32_array(
338            self.u_projection_matrix.as_ref(),
339            false,
340            self.camera.projection(),
341        );
342    }
343
344    /// Add a [`Texture`] to the [`Renderer`].
345    ///
346    /// The renderer stores [`Texture`]s that can be retreived later, via a string slice.
347    pub fn add_texture(&mut self, key: &'static str, texture: Texture) {
348        self.textures.insert(key, Rc::new(texture));
349    }
350
351    /// Use the requested [`Texture`].
352    ///
353    /// Sets the currently bound [`Texture`] to the one that matches the key. If no such texture is found, a default MAGENTA one is found.
354    pub fn use_texture(&self, key: &str) {
355        let gl = &self.gl;
356        self.textures
357            .get(key)
358            .unwrap_or_else(|| self.textures.get("MAGENTA").unwrap())
359            .bind(gl);
360    }
361
362    /// Get the requested [`Texture`], or MAGENTA if none is found.
363    pub fn get_texture(&mut self, key: &str) -> Rc<Texture> {
364        Rc::clone(
365            self.textures
366                .get(key)
367                .unwrap_or_else(|| self.textures.get("MAGENTA").unwrap())
368        )
369    }
370
371    /// Clear the batch queue and start a new batch.
372    pub fn begin_draw(&mut self) {
373        let gl = &self.gl;
374
375        self.batches.clear();
376
377        let mesh = Mesh::new(
378            gl,
379            Vec::with_capacity(MAX_BATCH_VERTICES as usize),
380            Vec::with_capacity(MAX_BATCH_INDICES as usize),
381        );
382
383        self.batches.push(mesh);
384    }
385
386    /// Add a [`Quad`] to the batching queue.
387    pub fn add_quad(&mut self, quad: &Quad) {
388        let gl = &self.gl;
389
390        // Get last batch. This should never be empty becase begin_draw should have been called before.
391        let mut batch = self
392            .batches
393            .last_mut()
394            .expect("Batch list empty. Check if begin_draw was called before.");
395        if batch.vertices.len() + 4 > MAX_BATCH_VERTICES as usize {
396            let mesh = Mesh::new(
397                gl,
398                Vec::with_capacity(MAX_BATCH_VERTICES as usize),
399                Vec::with_capacity(MAX_BATCH_INDICES as usize),
400            );
401            self.batches.push(mesh);
402
403            batch = self.batches.last_mut().unwrap();
404        }
405
406        let last = batch.vertices.len() as u32;
407        batch.vertices.append(&mut quad.get_vertices());
408        let mut indices = vec![last, last + 2, last + 1, last, last + 3, last + 2];
409        batch.indices.append(&mut indices);
410    }
411
412    /// Begin a new layer.
413    ///
414    /// A new mesh is added to the batches and subsequent calls are made on this layer.
415    pub fn begin_layer(&mut self) {
416        let gl = &self.gl;
417        let mesh = Mesh::new(
418            gl,
419            Vec::with_capacity(MAX_BATCH_VERTICES as usize),
420            Vec::with_capacity(MAX_BATCH_INDICES as usize),
421        );
422
423        self.batches.push(mesh);
424    }
425
426    /// Remove the last layer.
427    pub fn delete_layer(&mut self) -> Option<Mesh> {
428        self.batches.pop()
429    }
430
431    /// Draw the current layer.
432    pub fn draw_layer(&self) {
433        let gl = &self.gl;
434        if let Some(batch) = self.batches.last() {
435            batch.setup(gl);
436            gl.draw_elements_with_i32(
437                GL::TRIANGLES,
438                batch.indices.len() as i32,
439                GL::UNSIGNED_INT,
440                0,
441            );
442        }
443    }
444
445    /// Draw all batched geometry.
446    pub fn end_draw(&mut self) {
447        let gl = &self.gl;
448        gl.uniform_matrix4fv_with_f32_array(
449            self.u_view_matrix.as_ref(),
450            false,
451            self.camera.transform.matrix_slice(),
452        );
453        self.program.bind(gl);
454        for batch in self.batches.iter() {
455            batch.setup(gl);
456            gl.draw_elements_with_i32(
457                GL::TRIANGLES,
458                batch.indices.len() as i32,
459                GL::UNSIGNED_INT,
460                0,
461            );
462        }
463    }
464
465    /// Clear the screen with a given Color.
466    pub fn clear(&mut self, color: [f32; 4]) {
467        let gl = &self.gl;
468        gl.clear_color(color[0], color[1], color[2], color[3]);
469        gl.clear(GL::COLOR_BUFFER_BIT);
470    }
471
472    /// Add a [`Component`] to the [`Renderer`].
473    pub fn add_component(&mut self, name: &'static str, component: Box<dyn Component>) {
474        self.components.insert(name, component);
475    }
476
477    /// (Re)Initialize the [`Components`](Component) of the [`Renderer`].
478    pub fn init_components(&mut self) {
479        for component in self.components.values_mut() {
480            component.init()
481        }
482    }
483
484    /// Update the [`Components`](Component) of the [`Renderer`].
485    pub fn update_components(&mut self, delta_time: f32) {
486        for component in self.components.values_mut() {
487            component.update(delta_time)
488        }
489    }
490
491    /// Get a [`Components`](Component) using a key, and ty to cast it to a given type.
492    pub fn get_component<T: 'static + Component>(&self, key: &'static str) -> Result<&T, String> {
493        self.components
494            .get(key)
495            .unwrap()
496            .as_any()
497            .downcast_ref::<T>()
498            .ok_or_else(|| String::from("Could not cast to type."))
499    }
500
501    /// Get a mutable [`Components`](Component) using a key, and ty to cast it to a given type.
502    pub fn get_mut_component<T: 'static + Component>(
503        &mut self,
504        key: &'static str,
505    ) -> Result<&mut T, String> {
506        self.components
507            .get_mut(key)
508            .unwrap()
509            .as_mut_any()
510            .downcast_mut::<T>()
511            .ok_or_else(|| String::from("Could not cast to type."))
512    }
513
514    /// Draw the [`Components`](Component) of the [`Renderer`].
515    pub fn draw_components(&mut self) {
516        let gl = &self.gl;
517        let mut layers: Vec<Vec<Quad>> = self
518            .components
519            .values()
520            .filter_map(|component| component.get_quads())
521            .collect();
522        for layer in layers.iter_mut() {
523            let mut mesh = Mesh::new(
524                gl,
525                Vec::with_capacity(layer.len() * 4),
526                Vec::with_capacity(layer.len() * 6),
527            );
528            for (id, quad) in layer.iter_mut().enumerate() {
529                let last: u32 = id as u32 * 4;
530                mesh.vertices.append(&mut quad.get_vertices());
531                let mut indices = vec![last, last + 2, last + 1, last, last + 3, last + 2];
532                mesh.indices.append(&mut indices);
533            }
534            mesh.setup(gl);
535            gl.draw_elements_with_i32(
536                GL::TRIANGLES,
537                mesh.indices.len() as i32,
538                GL::UNSIGNED_INT,
539                0,
540            );
541        }
542    }
543}