diff options
Diffstat (limited to 'engine/src/renderer/mod.rs')
-rw-r--r-- | engine/src/renderer/mod.rs | 354 |
1 files changed, 180 insertions, 174 deletions
diff --git a/engine/src/renderer/mod.rs b/engine/src/renderer/mod.rs index 839c0d3..304c38b 100644 --- a/engine/src/renderer/mod.rs +++ b/engine/src/renderer/mod.rs @@ -1,15 +1,22 @@ use std::collections::HashMap; -use std::ffi::{c_void, CString}; +use std::ffi::c_void; use std::process::abort; use cstr::cstr; -use glfw::WindowSize; +use ecs::component::local::Local; +use ecs::sole::Single; +use ecs::system::{Into as _, System}; +use ecs::{Component, Query}; +use tracing::warn; use crate::camera::Camera; use crate::color::Color; +use crate::data_types::dimens::Dimens; +use crate::event::{Present as PresentEvent, Start as StartEvent}; use crate::lighting::LightSource; +use crate::material::Material; use crate::matrix::Matrix; -use crate::object::Object; +use crate::mesh::Mesh; use crate::opengl::buffer::{ ArrayKind as ArrayBufferKind, Buffer, @@ -33,168 +40,184 @@ use crate::opengl::vertex_array::{PrimitiveKind, VertexArray}; use crate::opengl::{clear_buffers, enable, BufferClearMask, Capability}; use crate::projection::new_perspective; use crate::shader::Program as ShaderProgram; -use crate::texture::{Id as TextureId, Texture}; +use crate::texture::{Id as TextureId, Map as TextureMap, Texture}; +use crate::transform::Transform; use crate::vector::{Vec2, Vec3}; use crate::vertex::Vertex; +use crate::window::Window; -#[derive(Debug)] -pub struct Renderer<CameraT> +#[derive(Debug, Default)] +pub struct Extension {} + +impl ecs::extension::Extension for Extension { - camera: CameraT, - shader_programs: HashMap<u64, GlShaderProgram>, - textures: HashMap<TextureId, GlTexture>, + fn collect(self, mut collector: ecs::extension::Collector<'_>) + { + collector.add_system(StartEvent, initialize); + + collector.add_system( + PresentEvent, + render.into_system().initialize((GlObjects::default(),)), + ); + } } -impl<CameraT> Renderer<CameraT> -where - CameraT: Camera, +fn initialize(window: Single<Window>) { - pub fn new(window: &glfw::Window, camera: CameraT) -> Result<Self, Error> - { - gl::load_with(|symbol| { - let proc_name = unsafe { CString::from_vec_unchecked(symbol.into()) }; - - match window.get_proc_address(proc_name.as_c_str()) { - Ok(addr) => addr as *const c_void, - Err(err) => { - println!( - "FATAL ERROR: Failed to get adress of OpenGL function {}. {}", - symbol, err - ); - - abort(); - } - } - }); + window + .make_context_current() + .expect("Failed to make window context current"); + + gl::load_with(|symbol| match window.get_proc_address(symbol) { + Ok(addr) => addr as *const c_void, + Err(err) => { + println!( + "FATAL ERROR: Failed to get adress of OpenGL function {symbol}: {err}", + ); + + abort(); + } + }); - #[cfg(feature = "debug")] - Self::initialize_debug(); + #[cfg(feature = "debug")] + initialize_debug(); - let window_size = window.size().map_err(Error::GetWindowSizeFailed)?; + let window_size = window.size().expect("Failed to get window size"); - Self::set_viewport(&Vec2 { x: 0, y: 0 }, &window_size); + set_viewport(&Vec2 { x: 0, y: 0 }, window_size); - enable(Capability::DepthTest); + window.set_framebuffer_size_callback(|new_window_size| { + set_viewport(&Vec2::ZERO, new_window_size); + }); - Ok(Self { - camera, - shader_programs: HashMap::new(), - textures: HashMap::new(), - }) - } + enable(Capability::DepthTest); +} - pub fn render<'obj>( - &mut self, - objects: impl IntoIterator<Item = &'obj Object>, - light_source: Option<&LightSource>, - window_size: &WindowSize, - ) -> Result<(), Error> - { - clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH); - - for obj in objects { - let shader_program = self - .shader_programs - .entry(obj.shader().u64_hash()) - .or_insert_with(|| create_gl_shader_program(obj.shader()).unwrap()); - - shader_program.activate(|shader_program_curr_bound| { - apply_transformation_matrices( - obj, - shader_program, - &self.camera, - window_size, - &shader_program_curr_bound, - ); - - apply_light( - obj, - shader_program, - light_source, - &self.camera, - &shader_program_curr_bound, - ); - - for (texture_id, texture) in obj.textures() { - let gl_texture = self - .textures - .entry(*texture_id) - .or_insert_with(|| create_gl_texture(texture)); - - let texture_unit = TextureUnit::from_texture_id(*texture_id) - .ok_or(Error::TextureIdIsInvalidTextureUnit)?; - - set_active_texture_unit(texture_unit); - - gl_texture.bind(|_| {}); - } - - Self::draw_object(obj); - - Ok::<_, Error>(()) - })?; +fn render( + query: Query<(Mesh, TextureMap, ShaderProgram, Material, Transform)>, + light_source_query: Query<(LightSource,)>, + camera_query: Query<(Camera,)>, + window: Single<Window>, + mut gl_objects: Local<GlObjects>, +) +{ + let Some(camera) = camera_query.iter().find_map(|(camera,)| { + if !camera.current { + return None; } - Ok(()) - } + Some(camera) + }) else { + warn!("No current camera. Nothing will be rendered"); + return; + }; - pub fn set_viewport(position: &Vec2<u32>, size: &WindowSize) - { - crate::opengl::set_viewport(position, size); - } + let light_source = light_source_query + .iter() + .next() + .map(|(light_source,)| light_source); + + let GlObjects { + shader_programs: gl_shader_programs, + textures: gl_textures, + } = &mut *gl_objects; + + clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH); + + for (mesh, texture_map, shader_program, material, transform) in &query { + let shader_program = gl_shader_programs + .entry(shader_program.u64_hash()) + .or_insert_with(|| create_gl_shader_program(&shader_program).unwrap()); + + shader_program.activate(|shader_program_curr_bound| { + apply_transformation_matrices( + &transform, + shader_program, + &camera, + window.size().expect("Failed to get window size"), + &shader_program_curr_bound, + ); + + apply_light( + &material, + shader_program, + light_source.as_deref(), + &camera, + &shader_program_curr_bound, + ); + + for (texture_id, texture) in &texture_map.inner { + let gl_texture = gl_textures + .entry(*texture_id) + .or_insert_with(|| create_gl_texture(texture)); + + let texture_unit = TextureUnit::from_texture_id(*texture_id) + .unwrap_or_else(|| { + panic!("Texture id {texture_id} is a invalid texture unit"); + }); + + set_active_texture_unit(texture_unit); + + gl_texture.bind(|_| {}); + } - #[must_use] - pub fn camera(&self) -> &CameraT - { - &self.camera + draw_mesh(&mesh); + }); } +} - pub fn camera_mut(&mut self) -> &mut CameraT - { - &mut self.camera - } +#[derive(Debug, Default, Component)] +struct GlObjects +{ + shader_programs: HashMap<u64, GlShaderProgram>, + textures: HashMap<TextureId, GlTexture>, +} - #[cfg(feature = "debug")] - fn initialize_debug() - { - use crate::opengl::debug::{ - enable_debug_output, - set_debug_message_callback, - set_debug_message_control, - MessageIdsAction, - }; +fn set_viewport(position: &Vec2<u32>, size: Dimens<u32>) +{ + crate::opengl::set_viewport(position, size); +} - enable_debug_output(); +#[cfg(feature = "debug")] +fn initialize_debug() +{ + use crate::opengl::debug::{ + enable_debug_output, + set_debug_message_callback, + set_debug_message_control, + MessageIdsAction, + }; - set_debug_message_callback(opengl_debug_message_cb); + enable_debug_output(); - set_debug_message_control(None, None, None, &[], MessageIdsAction::Disable); - } + set_debug_message_callback(opengl_debug_message_cb); - fn draw_object(obj: &Object) - { - // TODO: Creating a new vertex buffer each draw is really dumb and slow this - // should be rethinked - let renderable = Renderable::new(obj.mesh().vertices(), obj.mesh().indices()); - - renderable.vertex_arr.bind(|vert_arr_curr_bound| { - if let Some(index_info) = &renderable.index_info { - VertexArray::draw_elements( - &vert_arr_curr_bound, - PrimitiveKind::Triangles, - 0, - index_info.cnt, - ); - } else { - VertexArray::draw_arrays( - &vert_arr_curr_bound, - PrimitiveKind::Triangles, - 0, - 3, - ); - } - }); - } + set_debug_message_control(None, None, None, &[], MessageIdsAction::Disable); +} + +fn draw_mesh(mesh: &Mesh) +{ + // TODO: Creating a new vertex buffer each draw is really dumb and slow this + // should be rethinked + let renderable = Renderable::new(mesh.vertices(), mesh.indices()); + + renderable.vertex_arr.bind(|vert_arr_curr_bound| { + if let Some(index_info) = &renderable.index_info { + VertexArray::draw_elements( + &vert_arr_curr_bound, + PrimitiveKind::Triangles, + 0, + index_info.cnt, + ); + } else { + VertexArray::draw_arrays( + &vert_arr_curr_bound, + PrimitiveKind::Triangles, + 0, + 3, + ); + } + }); } fn create_gl_texture(texture: &Texture) -> GlTexture @@ -204,7 +227,7 @@ fn create_gl_texture(texture: &Texture) -> GlTexture gl_texture.bind(|texture_curr_bound| { GlTexture::generate( &texture_curr_bound, - &texture.dimensions(), + texture.dimensions(), texture.image().as_bytes(), texture.pixel_data_format(), ); @@ -244,7 +267,7 @@ fn create_gl_shader_program( } #[derive(Debug)] -pub struct Renderable +struct Renderable { vertex_arr: VertexArray, @@ -255,7 +278,7 @@ pub struct Renderable impl Renderable { - pub fn new(vertices: &[Vertex], indices: Option<&[u32]>) -> Self + fn new(vertices: &[Vertex], indices: Option<&[u32]>) -> Self { let vertex_arr = VertexArray::new(); let vertex_buffer = Buffer::new(); @@ -291,35 +314,18 @@ impl Renderable } } -/// Renderer error. -#[derive(Debug, thiserror::Error)] -pub enum Error -{ - #[error("Failed to get window size")] - GetWindowSizeFailed(#[source] glfw::Error), - - #[error("Texture ID is a invalid texture unit")] - TextureIdIsInvalidTextureUnit, - - #[error(transparent)] - GlShader(#[from] GlShaderError), - - #[error("No shader program object was found for object")] - MissingShaderProgram, -} - fn apply_transformation_matrices( - object: &Object, + transform: &Transform, gl_shader_program: &GlShaderProgram, - camera: &impl Camera, - window_size: &WindowSize, + camera: &Camera, + window_size: Dimens<u32>, shader_program_curr_bound: &CurrentlyBound<GlShaderProgram>, ) { gl_shader_program.set_uniform_matrix_4fv( shader_program_curr_bound, cstr!("model"), - &object.transform().as_matrix(), + &transform.as_matrix(), ); let view = create_view(camera); @@ -346,10 +352,10 @@ fn apply_transformation_matrices( } fn apply_light( - obj: &Object, + material: &Material, gl_shader_program: &GlShaderProgram, light_source: Option<&LightSource>, - camera: &impl Camera, + camera: &Camera, shader_program_curr_bound: &CurrentlyBound<GlShaderProgram>, ) { @@ -395,41 +401,41 @@ fn apply_light( gl_shader_program.set_uniform_1i( shader_program_curr_bound, cstr!("material.ambient"), - obj.material().ambient_map().into_inner() as i32, + material.ambient_map().into_inner() as i32, ); #[allow(clippy::cast_possible_wrap)] gl_shader_program.set_uniform_1i( shader_program_curr_bound, cstr!("material.diffuse"), - obj.material().diffuse_map().into_inner() as i32, + material.diffuse_map().into_inner() as i32, ); #[allow(clippy::cast_possible_wrap)] gl_shader_program.set_uniform_1i( shader_program_curr_bound, cstr!("material.specular"), - obj.material().specular_map().into_inner() as i32, + material.specular_map().into_inner() as i32, ); gl_shader_program.set_uniform_1fv( shader_program_curr_bound, cstr!("material.shininess"), - obj.material().shininess(), + material.shininess(), ); gl_shader_program.set_uniform_vec_3fv( shader_program_curr_bound, cstr!("view_pos"), - &camera.position(), + &camera.position, ); } -fn create_view(camera: &impl Camera) -> Matrix<f32, 4, 4> +fn create_view(camera: &Camera) -> Matrix<f32, 4, 4> { let mut view = Matrix::new(); - view.look_at(&camera.position(), &camera.target(), &camera.global_up()); + view.look_at(&camera.position, &camera.target, &camera.global_up); view } |