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 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 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}