Skip to main content

flatbox_render/
renderer.rs

1use std::collections::hash_map::{HashMap, Entry};
2use std::any::TypeId;
3use std::fmt::Debug;
4use std::marker::PhantomData;
5
6use flatbox_core::{
7    logger::{warn, error},
8    math::transform::Transform,
9};
10use pretty_type_name::pretty_type_name;
11
12#[cfg(feature = "context")]
13use crate::context::Context;
14use crate::glenum_wrapper;
15use crate::pbr::texture::Order;
16use crate::{
17    error::RenderError,
18    hal::shader::{GraphicsPipeline, Shader, ShaderType},
19    pbr::{
20        material::Material,
21        model::Model,
22        camera::Camera,
23    },
24};
25
26#[allow(unused_imports)]
27use crate::hal::buffer::VertexArray;
28
29glenum_wrapper! {
30    wrapper: Capability,
31    variants: [
32        ScissorTest,
33        CullFace,
34        DepthTest,
35        Blend
36    ]
37}
38
39glenum_wrapper! {
40    wrapper: ColorBlendEquation,
41    variants: [
42        FuncAdd,
43        FuncSubtract,
44        FuncReverseSubtract
45    ]
46}
47
48glenum_wrapper! {
49    wrapper: ColorBlendMode,
50    variants: [
51        Zero,
52        One,
53        SrcColor,
54        OneMinusSrcColor,
55        SrcAlpha,
56        OneMinusSrcAlpha,
57        DstAlpha,
58        OneMinusDstAlpha,
59        DstColor,
60        OneMinusDstColor,
61        SrcAlphaSaturate,
62        ConstantColor,
63        OneMinusConstantColor,
64        ConstantAlpha,
65        OneMinusConstantAlpha
66    ]
67}
68
69#[repr(C)]
70#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
71pub struct WindowExtent {
72    pub x: f32,
73    pub y: f32,
74    pub width: f32,
75    pub height: f32,
76}
77
78impl WindowExtent {
79    pub fn new(width: f32, height: f32) -> WindowExtent {
80        WindowExtent { x: 0.0, y: 0.0, width, height }
81    }
82
83    pub fn to_aspect(&self) -> f32 {
84        self.width / self.height
85    }
86}
87
88impl From<WindowExtent> for [u32; 2] {
89    fn from(e: WindowExtent) -> Self {
90        [e.width as u32, e.height as u32]
91    }
92}
93
94pub type GraphicsPipelines = HashMap<TypeId, GraphicsPipeline>;
95
96pub struct Renderer {
97    graphics_pipelines: GraphicsPipelines,
98    extent: WindowExtent,
99    commands_history: RenderCommandsHistory,
100}
101
102#[cfg(not(feature = "context"))]
103use crate::hal::GlInitFunction;
104
105impl Renderer {
106    #[cfg(not(feature = "context"))]
107    pub fn init<F: GlInitFunction>(init_function: F) -> Renderer {
108        gl::load_with(init_function);
109
110        Renderer {
111            graphics_pipelines: GraphicsPipelines::new(),
112            extent: WindowExtent::new(800.0, 600.0),
113            commands_history: RenderCommandsHistory::new(50),
114        }
115    }
116
117    #[cfg(feature = "context")]
118    pub fn init(context: &Context) -> Result<Renderer, RenderError> {
119        gl::load_with(|addr| context.get_proc_address(addr));
120
121        Ok(Renderer {
122            graphics_pipelines: GraphicsPipelines::new(),
123            extent: WindowExtent::new(800.0, 600.0),
124            commands_history: RenderCommandsHistory::new(50),
125        })
126    }
127
128    pub fn extent(&self) -> WindowExtent {
129        self.extent
130    }
131
132    pub fn set_extent(&mut self, extent: WindowExtent) {
133        self.extent = extent;
134        unsafe { gl::Viewport(
135            self.extent.x as i32, 
136            self.extent.y as i32, 
137            self.extent.width as i32, 
138            self.extent.height as i32,
139        ); }
140    }
141
142    pub fn get_pipeline<M: Material>(&self) -> Result<&GraphicsPipeline, RenderError> {
143        self.graphics_pipelines.get(&TypeId::of::<M>()).ok_or(RenderError::MaterialNotBound(pretty_type_name::<M>().to_string()))
144    }
145
146    pub fn bind_material<M: Material>(&mut self) {
147        let material_type = TypeId::of::<M>();
148        
149        if let Entry::Vacant(e) = self.graphics_pipelines.entry(material_type) {
150            let vertex_shader = Shader::new_from_source(M::vertex_shader(), ShaderType::VertexShader)
151                .expect("Cannot compile vertex shader");
152
153            let fragment_shader = Shader::new_from_source(M::fragment_shader(), ShaderType::FragmentShader)
154                .expect("Cannot compile fragment shader");
155
156            let pipeline = GraphicsPipeline::new(&[vertex_shader, fragment_shader]).expect("Cannot initialize graphics pipeline");
157            e.insert(pipeline);
158        } else {
159            error!("Material type `{}` is already bound", pretty_type_name::<M>());
160        }
161    }
162
163    pub fn execute(&mut self, command: &mut dyn RenderCommand) -> Result<(), RenderError> {
164        self.commands_history.push(command);
165        command.execute(self)
166    }
167
168    pub fn history(&self) -> &RenderCommandsHistory {
169        &self.commands_history
170    }
171}
172
173#[derive(Clone)]
174pub struct RenderCommandsHistory{
175    cache: Vec<String>,
176    max_capacity: usize,
177}
178
179impl RenderCommandsHistory {
180    pub fn new(max_capacity: usize) -> Self {
181        Self {
182            cache: Vec::new(),
183            max_capacity,
184        }
185    }
186
187    pub fn push(&mut self, command: &mut dyn RenderCommand) {
188        if self.cache.len() >= self.max_capacity {
189            self.cache.remove(0);
190        }
191        self.cache.push(command.name());
192    }
193
194    pub fn get(&self, index: usize) -> Option<&str> {
195        self.cache.get(index).map(|s| s.as_str())
196    }
197
198    pub fn len(&self) -> usize {
199        self.cache.len()
200    }
201
202    pub fn is_empty(&self) -> bool {
203        self.len() == 0
204    }
205}
206
207impl Debug for RenderCommandsHistory {
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209        f.debug_list()
210            .entries(&self.cache)
211            .finish()
212    }
213}
214
215pub trait RenderCommand {
216    fn execute(&mut self, renderer: &mut Renderer) -> Result<(), RenderError>;
217
218    fn name(&self) -> String { pretty_type_name::<Self>() }
219}
220
221pub struct ClearCommand(pub f32, pub f32, pub f32);
222
223impl RenderCommand for ClearCommand {
224    fn execute(&mut self, renderer: &mut Renderer) -> Result<(), RenderError> {
225        unsafe { gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); }
226
227        renderer.execute(&mut EnableCommand(Capability::Blend))?;
228        renderer.execute(&mut EnableCommand(Capability::DepthTest))?;
229
230        unsafe {
231            gl::ClearColor(self.0, self.1, self.2, 1.0);
232            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
233        }
234
235        Ok(())
236    }
237}
238
239pub struct EnableCommand(pub Capability);
240
241impl RenderCommand for EnableCommand {
242    fn execute(&mut self, _: &mut Renderer) -> Result<(), RenderError> {
243        unsafe { gl::Enable(self.0 as u32); }
244        Ok(())
245    }
246}
247
248pub struct DisableCommand(pub Capability);
249
250impl RenderCommand for DisableCommand {
251    fn execute(&mut self, _: &mut Renderer) -> Result<(), RenderError> {
252        unsafe { gl::Disable(self.0 as u32); }
253        Ok(())
254    }
255}
256
257pub struct BlendEquationSeparateCommand(pub ColorBlendEquation, pub ColorBlendEquation);
258
259impl RenderCommand for BlendEquationSeparateCommand {
260    fn execute(&mut self, _: &mut Renderer) -> Result<(), RenderError> {
261        unsafe { gl::BlendEquationSeparate(self.0 as u32, self.1 as u32); }
262        Ok(())
263    }
264}
265
266pub struct BlendFuncSeparateCommand(pub ColorBlendMode, pub ColorBlendMode, pub ColorBlendMode, pub ColorBlendMode);
267
268impl RenderCommand for BlendFuncSeparateCommand {
269    fn execute(&mut self, _: &mut Renderer) -> Result<(), RenderError> {
270        unsafe { gl::BlendFuncSeparate(
271            self.0 as u32,
272            self.1 as u32,
273            self.2 as u32,
274            self.3 as u32,
275        ); }
276
277        Ok(())
278    }
279}
280
281pub struct ScissorCommand(pub WindowExtent);
282
283impl RenderCommand for ScissorCommand {
284    fn execute(&mut self, _: &mut Renderer) -> Result<(), RenderError> {
285        unsafe { gl::Scissor(
286            self.0.x as i32,
287            self.0.y as i32,
288            self.0.width as i32,
289            self.0.height as i32,
290        ); }
291        Ok(())
292    }
293}
294
295pub struct ColorMaskCommand(pub bool, pub bool, pub bool, pub bool);
296
297impl RenderCommand for ColorMaskCommand {
298    fn execute(&mut self, _: &mut Renderer) -> Result<(), RenderError> {
299        unsafe { gl::ColorMask(
300            self.0 as u8, 
301            self.1 as u8, 
302            self.2 as u8, 
303            self.3 as u8
304        ); }
305        Ok(())
306    }
307}
308
309pub struct ActivateTextureRawCommand(Order);
310
311impl ActivateTextureRawCommand {
312    ///
313    /// # Safety
314    /// [`GraphicsPipeline`]'s sampler with the given order must be set via [`GraphicsPipeline::set_int`] method
315    pub unsafe fn new(order: Order) -> Self {
316        ActivateTextureRawCommand(order)
317    } 
318}
319
320impl RenderCommand for ActivateTextureRawCommand {
321    fn execute(&mut self, _: &mut Renderer) -> Result<(), RenderError> {
322        unsafe { gl::ActiveTexture(self.0 as u32); }
323        Ok(())
324    }
325}
326
327pub struct DrawTrianglesCommand(usize);
328
329impl DrawTrianglesCommand {
330    ///
331    /// # Safety
332    /// A valid [`VertexArray`] has to be bound
333    /// Valid index and vertex buffers have to be bound
334    pub unsafe fn new(indices_count: usize) -> Self {
335        DrawTrianglesCommand(indices_count)
336    }
337}
338
339impl RenderCommand for DrawTrianglesCommand {
340    fn execute(&mut self, _: &mut Renderer) -> Result<(), RenderError> {
341        unsafe { gl::DrawElements(
342            gl::TRIANGLES, 
343            self.0 as i32, 
344            gl::UNSIGNED_INT, 
345            std::ptr::null()
346        ); }
347        Ok(())
348    }
349} 
350
351#[derive(Debug)]
352pub struct RenderCameraCommand<'a, M: Material> {
353    camera: &'a mut Camera,
354    transform: &'a Transform,
355    __phantom_data: PhantomData<M>,
356}
357
358impl<'a, M: Material> RenderCameraCommand<'a, M> {
359    pub fn new(camera: &'a mut Camera, transform: &'a Transform) -> RenderCameraCommand<'a, M> {
360        Self { camera, transform, __phantom_data: PhantomData }
361    }
362}
363
364impl<'a, M: Material> RenderCommand for RenderCameraCommand<'a, M> {
365    fn execute(&mut self, renderer: &mut Renderer) -> Result<(), RenderError> {
366        let pipeline = renderer.get_pipeline::<M>()?;
367
368        if !self.camera.is_active() {
369            warn!("Camera being rendered is not active");
370        }
371
372        self.camera.set_aspect(renderer.extent().to_aspect());
373        self.camera.update_buffer(pipeline, self.transform);
374                
375        Ok(())
376    }
377}
378
379#[derive(Debug)]
380pub struct PrepareModelCommand<'a, M> {
381    model: &'a mut Model,
382    material: &'a M,
383}
384
385impl<'a, M: Material> PrepareModelCommand<'a, M> {
386    pub fn new(model: &'a mut Model, material: &'a M) -> PrepareModelCommand<'a, M> {
387        Self { model, material }
388    }
389}
390
391impl<'a, M: Material> RenderCommand for PrepareModelCommand<'a, M> {
392    fn execute(&mut self, renderer: &mut Renderer) -> Result<(), RenderError> {
393        let Some(ref mut mesh) = self.model.mesh else { return Ok(()) };
394
395        if mesh.prepared { return Ok(()); }
396
397        println!("not prepaired");
398
399        let pipeline = renderer.get_pipeline::<M>()?;
400        mesh.setup(pipeline);
401
402        pipeline.apply();
403        self.material.setup_pipeline(pipeline);
404
405        mesh.prepared = true;
406
407        Ok(())
408    }
409}
410
411#[derive(Debug)]
412pub struct DrawModelCommand<'a, M> {
413    model: &'a Model,
414    material: &'a M,
415    transform: &'a Transform,
416}
417
418impl<'a, M: Material> DrawModelCommand<'a, M> {
419    pub fn new(
420        model: &'a Model, 
421        material: &'a M,
422        transform: &'a Transform,
423    ) -> DrawModelCommand<'a, M> {
424        Self { model, material, transform }
425    }
426}
427
428impl<'a, M: Material> RenderCommand for DrawModelCommand<'a, M> {
429    fn execute(&mut self, renderer: &mut Renderer) -> Result<(), RenderError> {
430        let Some(ref mesh) = self.model.mesh else { return Ok(()) };
431
432        if !mesh.prepared {
433            return Err(RenderError::ModelNotPrepared);
434        }
435
436        let pipeline = renderer.get_pipeline::<M>()?;
437
438        self.material.setup_pipeline(pipeline);
439        
440        let (model, inversed) = self.transform.to_matrices();
441        
442        pipeline.apply();
443        pipeline.set_mat4("model", &model);
444        pipeline.set_mat4("inversed", &inversed);
445    
446        mesh.vertex_array.bind();
447
448        unsafe { renderer.execute(&mut DrawTrianglesCommand::new(mesh.index_data.len()))?; }
449
450        Ok(())
451    }
452}