diff options
Diffstat (limited to 'engine/src/renderer/opengl.rs')
-rw-r--r-- | engine/src/renderer/opengl.rs | 283 |
1 files changed, 153 insertions, 130 deletions
diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs index a353c6a..c44a479 100644 --- a/engine/src/renderer/opengl.rs +++ b/engine/src/renderer/opengl.rs @@ -2,11 +2,14 @@ use std::collections::HashMap; use std::ffi::{c_void, CString}; +use std::io::{Error as IoError, ErrorKind as IoErrorKind}; use std::ops::Deref; +use std::path::Path; use std::process::abort; use ecs::actions::Actions; use ecs::component::local::Local; +use ecs::phase::{PRESENT as PRESENT_PHASE, START as START_PHASE}; use ecs::query::options::{Not, With}; use ecs::sole::Single; use ecs::system::{Into as _, System}; @@ -16,16 +19,27 @@ use crate::camera::{Active as ActiveCamera, Camera}; use crate::color::Color; use crate::data_types::dimens::Dimens; use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig}; -use crate::event::{Present as PresentEvent, Start as StartEvent}; use crate::lighting::{DirectionalLight, GlobalLight, PointLight}; use crate::material::{Flags as MaterialFlags, Material}; use crate::matrix::Matrix; use crate::mesh::Mesh; use crate::opengl::buffer::{Buffer, Usage as BufferUsage}; -#[cfg(feature = "debug")] -use crate::opengl::debug::{MessageSeverity, MessageSource, MessageType}; +use crate::opengl::debug::{ + enable_debug_output, + set_debug_message_callback, + set_debug_message_control, + MessageIdsAction, + MessageSeverity, + MessageSource, + MessageType, +}; +use crate::opengl::glsl::{ + preprocess as glsl_preprocess, + PreprocessingError as GlslPreprocessingError, +}; use crate::opengl::shader::{ Error as GlShaderError, + Kind as ShaderKind, Program as GlShaderProgram, Shader as GlShader, }; @@ -39,38 +53,44 @@ use crate::opengl::vertex_array::{ PrimitiveKind, VertexArray, }; -use crate::opengl::{clear_buffers, enable, BufferClearMask, Capability}; -use crate::projection::{new_perspective_matrix, Projection}; -use crate::shader::Program as ShaderProgram; +use crate::opengl::{ + clear_buffers, + enable, + get_context_flags as get_opengl_context_flags, + BufferClearMask, + Capability, + ContextFlags, +}; +use crate::projection::{ClipVolume, Projection}; use crate::texture::{Id as TextureId, Texture}; use crate::transform::{Position, Scale}; -use crate::util::NeverDrop; +use crate::util::{defer, Defer, RefOrValue}; use crate::vector::{Vec2, Vec3}; use crate::vertex::{AttributeComponentType, Vertex}; use crate::window::Window; -type RenderableEntity = ( - Mesh, - ShaderProgram, - Material, - Option<MaterialFlags>, - Option<Position>, - Option<Scale>, - Option<DrawFlags>, - Option<GlObjects>, +type RenderableEntity<'a> = ( + &'a Mesh, + &'a Material, + &'a Option<MaterialFlags>, + &'a Option<Position>, + &'a Option<Scale>, + &'a Option<DrawFlags>, + &'a Option<GlObjects>, ); #[derive(Debug, Default)] +#[non_exhaustive] pub struct Extension {} impl ecs::extension::Extension for Extension { fn collect(self, mut collector: ecs::extension::Collector<'_>) { - collector.add_system(StartEvent, initialize); + collector.add_system(*START_PHASE, initialize); collector.add_system( - PresentEvent, + *PRESENT_PHASE, render .into_system() .initialize((GlobalGlObjects::default(),)), @@ -95,8 +115,9 @@ fn initialize(window: Single<Window>) } }); - #[cfg(feature = "debug")] - initialize_debug(); + if get_opengl_context_flags().contains(ContextFlags::DEBUG) { + initialize_debug(); + } let window_size = window.size().expect("Failed to get window size"); @@ -112,10 +133,10 @@ fn initialize(window: Single<Window>) #[allow(clippy::too_many_arguments)] fn render( - query: Query<RenderableEntity, Not<With<NoDraw>>>, - point_light_query: Query<(PointLight,)>, - directional_lights: Query<(DirectionalLight,)>, - camera_query: Query<(Camera, Position, ActiveCamera)>, + query: Query<RenderableEntity<'_>, Not<With<NoDraw>>>, + point_light_query: Query<(&PointLight,)>, + directional_lights: Query<(&DirectionalLight,)>, + camera_query: Query<(&Camera, &Position, &ActiveCamera)>, window: Single<Window>, global_light: Single<GlobalLight>, mut gl_objects: Local<GlobalGlObjects>, @@ -123,7 +144,6 @@ fn render( ) { let Some((camera, camera_pos, _)) = camera_query.iter().next() else { - #[cfg(feature = "debug")] tracing::warn!("No current camera. Nothing will be rendered"); return; }; @@ -136,52 +156,35 @@ fn render( let directional_lights = directional_lights.iter().collect::<Vec<_>>(); let GlobalGlObjects { - shader_programs: gl_shader_programs, + shader_program, textures: gl_textures, } = &mut *gl_objects; + let shader_program = + shader_program.get_or_insert_with(|| create_default_shader_program().unwrap()); + clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH); for ( - entity_index, - ( - mesh, - shader_program, - material, - material_flags, - position, - scale, - draw_flags, - gl_objects, - ), - ) in query.iter().enumerate() + euid, + (mesh, material, material_flags, position, scale, draw_flags, gl_objects), + ) in query.iter_with_euids() { let material_flags = material_flags .map(|material_flags| material_flags.clone()) .unwrap_or_default(); - let shader_program = gl_shader_programs - .entry(shader_program.u64_hash()) - .or_insert_with(|| create_gl_shader_program(&shader_program).unwrap()); - - let new_gl_objects; - - let gl_objects = if let Some(gl_objects) = gl_objects.as_deref() { - gl_objects - } else { - // TODO: Account for when meshes are changed - let gl_objects = GlObjects::new(&mesh); - - new_gl_objects = Some(gl_objects.clone()); - - actions.add_components( - query.get_entity_uid(entity_index).unwrap(), - (gl_objects,), - ); - - &*new_gl_objects.unwrap() + let gl_objs = match gl_objects.as_deref() { + Some(gl_objs) => RefOrValue::Ref(gl_objs), + None => RefOrValue::Value(Some(GlObjects::new(&mesh))), }; + defer!(|gl_objs| { + if let RefOrValue::Value(opt_gl_objs) = gl_objs { + actions.add_components(euid, (opt_gl_objs.take().unwrap(),)); + }; + }); + apply_transformation_matrices( Transformation { position: position.map(|pos| *pos).unwrap_or_default().position, @@ -207,15 +210,12 @@ fn render( &camera_pos, ); - for texture in &material.textures { + for (index, texture) in material.textures.iter().enumerate() { 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 {} is a invalid texture unit", texture.id()); - }); + let texture_unit = TextureUnit::from_num(index).expect("Too many textures"); set_active_texture_unit(texture_unit); @@ -231,7 +231,7 @@ fn render( ); } - draw_mesh(gl_objects); + draw_mesh(gl_objs.get().unwrap()); if draw_flags.is_some() { let default_polygon_mode_config = PolygonModeConfig::default(); @@ -247,7 +247,7 @@ fn render( #[derive(Debug, Default, Component)] struct GlobalGlObjects { - shader_programs: HashMap<u64, GlShaderProgram>, + shader_program: Option<GlShaderProgram>, textures: HashMap<TextureId, GlTexture>, } @@ -256,20 +256,11 @@ fn set_viewport(position: Vec2<u32>, size: Dimens<u32>) crate::opengl::set_viewport(position, size); } -#[cfg(feature = "debug")] fn initialize_debug() { - use crate::opengl::debug::{ - enable_debug_output, - set_debug_message_callback, - set_debug_message_control, - MessageIdsAction, - }; - enable_debug_output(); set_debug_message_callback(opengl_debug_message_cb); - set_debug_message_control(None, None, None, &[], MessageIdsAction::Disable); } @@ -278,9 +269,9 @@ fn draw_mesh(gl_objects: &GlObjects) gl_objects.vertex_arr.bind(); if gl_objects.index_buffer.is_some() { - VertexArray::draw_elements(PrimitiveKind::Triangles, 0, gl_objects.index_cnt); + VertexArray::draw_elements(PrimitiveKind::Triangles, 0, gl_objects.element_cnt); } else { - VertexArray::draw_arrays(PrimitiveKind::Triangles, 0, 3); + VertexArray::draw_arrays(PrimitiveKind::Triangles, 0, gl_objects.element_cnt); } } @@ -299,51 +290,84 @@ fn create_gl_texture(texture: &Texture) -> GlTexture gl_texture } -fn create_gl_shader_program( - shader_program: &ShaderProgram, -) -> Result<GlShaderProgram, GlShaderError> +const VERTEX_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex.glsl"); +const FRAGMENT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/fragment.glsl"); + +const VERTEX_DATA_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex_data.glsl"); +const LIGHT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/light.glsl"); + +fn create_default_shader_program() -> Result<GlShaderProgram, CreateShaderError> { - let gl_shaders = shader_program - .shaders() - .iter() - .map(|shader| { - let gl_shader = GlShader::new(shader.kind()); + let mut vertex_shader = GlShader::new(ShaderKind::Vertex); - gl_shader.set_source(shader.source())?; - gl_shader.compile()?; + vertex_shader.set_source(&*glsl_preprocess( + VERTEX_GLSL_SHADER_SRC, + &get_glsl_shader_content, + )?)?; - Ok(gl_shader) - }) - .collect::<Result<Vec<_>, _>>()?; + vertex_shader.compile()?; - let gl_shader_program = GlShaderProgram::new(); + let mut fragment_shader = GlShader::new(ShaderKind::Fragment); - for gl_shader in &gl_shaders { - gl_shader_program.attach(gl_shader); - } + fragment_shader.set_source(&*glsl_preprocess( + FRAGMENT_GLSL_SHADER_SRC, + &get_glsl_shader_content, + )?)?; + + fragment_shader.compile()?; + + let mut gl_shader_program = GlShaderProgram::new(); + + gl_shader_program.attach(&vertex_shader); + gl_shader_program.attach(&fragment_shader); gl_shader_program.link()?; Ok(gl_shader_program) } +#[derive(Debug, thiserror::Error)] +enum CreateShaderError +{ + #[error(transparent)] + ShaderError(#[from] GlShaderError), + + #[error(transparent)] + PreprocessingError(#[from] GlslPreprocessingError), +} + +fn get_glsl_shader_content(path: &Path) -> Result<Vec<u8>, std::io::Error> +{ + if path == Path::new("vertex_data.glsl") { + return Ok(VERTEX_DATA_GLSL_SHADER_SRC.as_bytes().to_vec()); + } + + if path == Path::new("light.glsl") { + return Ok(LIGHT_GLSL_SHADER_SRC.as_bytes().to_vec()); + } + + Err(IoError::new( + IoErrorKind::NotFound, + format!("Content for shader file {} not found", path.display()), + )) +} + #[derive(Debug, Component)] struct GlObjects { /// Vertex and index buffer has to live as long as the vertex array - vertex_buffer: Buffer<Vertex>, + _vertex_buffer: Buffer<Vertex>, index_buffer: Option<Buffer<u32>>, - index_cnt: u32, + element_cnt: u32, vertex_arr: VertexArray, } impl GlObjects { - #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] + #[tracing::instrument(skip_all)] fn new(mesh: &Mesh) -> Self { - #[cfg(feature = "debug")] tracing::trace!( "Creating vertex array, vertex buffer{}", if mesh.indices().is_some() { @@ -387,37 +411,27 @@ impl GlObjects vertex_arr.bind_element_buffer(&index_buffer); return Self { - vertex_buffer, + _vertex_buffer: vertex_buffer, index_buffer: Some(index_buffer), - index_cnt: indices.len().try_into().unwrap(), + element_cnt: indices + .len() + .try_into() + .expect("Mesh index count does not fit into a 32-bit unsigned int"), vertex_arr, }; } Self { - vertex_buffer, + _vertex_buffer: vertex_buffer, index_buffer: None, - index_cnt: 0, + element_cnt: mesh + .vertices() + .len() + .try_into() + .expect("Mesh vertex count does not fit into a 32-bit unsigned int"), vertex_arr, } } - - pub fn clone(&self) -> NeverDrop<Self> - { - NeverDrop::new(Self { - // SAFETY: The vertex buffer will never become dropped (NeverDrop ensures it) - vertex_buffer: unsafe { self.vertex_buffer.clone_weak() }, - index_buffer: self - .index_buffer - .as_ref() - // SAFETY: The index buffer will never become dropped (NeverDrop ensures - // it) - .map(|index_buffer| unsafe { index_buffer.clone_weak() }), - index_cnt: self.index_cnt, - // SAFETY: The vertex array will never become dropped (NeverDrop ensures it) - vertex_arr: unsafe { self.vertex_arr.clone_unsafe() }, - }) - } } fn apply_transformation_matrices( @@ -431,19 +445,22 @@ fn apply_transformation_matrices( gl_shader_program .set_uniform_matrix_4fv(c"model", &create_transformation_matrix(transformation)); - let view = create_view(camera, camera_pos); + let view_matrix = create_view_matrix(camera, &camera_pos.position); - gl_shader_program.set_uniform_matrix_4fv(c"view", &view); + gl_shader_program.set_uniform_matrix_4fv(c"view", &view_matrix); #[allow(clippy::cast_precision_loss)] - let projection = match &camera.projection { - Projection::Perspective(perspective) => new_perspective_matrix( - perspective, + let proj_matrix = match &camera.projection { + Projection::Perspective(perspective_proj) => perspective_proj.to_matrix_rh( window_size.width as f32 / window_size.height as f32, + ClipVolume::NegOneToOne, ), + Projection::Orthographic(orthographic_proj) => { + orthographic_proj.to_matrix_rh(&camera_pos.position, ClipVolume::NegOneToOne) + } }; - gl_shader_program.set_uniform_matrix_4fv(c"projection", &projection); + gl_shader_program.set_uniform_matrix_4fv(c"projection", &proj_matrix); } fn apply_light<PointLightHolder>( @@ -532,22 +549,29 @@ fn apply_light<PointLightHolder>( gl_shader_program .set_uniform_vec_3fv(c"material.specular", &material.specular.clone().into()); + let texture_map = material + .textures + .iter() + .enumerate() + .map(|(index, texture)| (texture.id(), index)) + .collect::<HashMap<_, _>>(); + #[allow(clippy::cast_possible_wrap)] gl_shader_program.set_uniform_1i( c"material.ambient_map", - material.ambient_map.into_inner() as i32, + *texture_map.get(&material.ambient_map).unwrap() as i32, ); #[allow(clippy::cast_possible_wrap)] gl_shader_program.set_uniform_1i( c"material.diffuse_map", - material.diffuse_map.into_inner() as i32, + *texture_map.get(&material.diffuse_map).unwrap() as i32, ); #[allow(clippy::cast_possible_wrap)] gl_shader_program.set_uniform_1i( c"material.specular_map", - material.specular_map.into_inner() as i32, + *texture_map.get(&material.specular_map).unwrap() as i32, ); gl_shader_program.set_uniform_1fv(c"material.shininess", material.shininess); @@ -649,16 +673,15 @@ fn create_light_uniform_name( } } -fn create_view(camera: &Camera, camera_pos: &Position) -> Matrix<f32, 4, 4> +fn create_view_matrix(camera: &Camera, camera_pos: &Vec3<f32>) -> Matrix<f32, 4, 4> { let mut view = Matrix::new(); - view.look_at(&camera_pos.position, &camera.target, &camera.global_up); + view.look_at(&camera_pos, &camera.target, &camera.global_up); view } -#[cfg(feature = "debug")] #[tracing::instrument(skip_all)] fn opengl_debug_message_cb( source: MessageSource, |