diff options
Diffstat (limited to 'engine/src/rendering/backend/opengl/graphics_mesh.rs')
| -rw-r--r-- | engine/src/rendering/backend/opengl/graphics_mesh.rs | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/engine/src/rendering/backend/opengl/graphics_mesh.rs b/engine/src/rendering/backend/opengl/graphics_mesh.rs new file mode 100644 index 0000000..899f50f --- /dev/null +++ b/engine/src/rendering/backend/opengl/graphics_mesh.rs @@ -0,0 +1,263 @@ +use std::hint::cold_path; + +use opengl_bindings::buffer::{Buffer as GlBuffer, Usage as GlBufferUsage}; +use opengl_bindings::vertex_array::{ + AttributeFormat as GlVertexArrayAttributeFormat, + BindVertexBufferError as GlVertexArrayBindVertexBufferError, + DataType as GlVertexArrayDataType, + VertexArray as GlVertexArray, + VertexBufferSpec as GlVertexArrayVertexBufferSpec, +}; +use opengl_bindings::MaybeCurrentContextWithFns as GlCurrentContextWithFns; + +use crate::mesh::vertex_buffer::VertexAttrProperties as MeshVertexAttrProperties; +use crate::mesh::{Mesh, VertexAttrType}; +use crate::rendering::shader::{ + VertexDescription as ShaderVertexDescription, + VertexInputSemName, +}; +use crate::rendering::MeshUsage; + +const VERTEX_BUF_BINDING_INDEX: u32 = 0; + +#[derive(Debug)] +pub struct GraphicsMesh +{ + /// Vertex and index buffer has to live as long as the vertex array + vertex_buffer: GlBuffer<u8>, + vertex_attr_props: Vec<MeshVertexAttrProperties>, + last_max_vertex_attr_index: u32, + pub index_buffer: Option<GlBuffer<u32>>, + pub element_cnt: u32, + pub vertex_arr: GlVertexArray, +} + +impl GraphicsMesh +{ + #[tracing::instrument(skip_all)] + pub fn new( + current_context: &GlCurrentContextWithFns, + mesh: &Mesh, + mesh_usage: MeshUsage, + ) -> Result<Self, Error> + { + let buffer_usage = mesh_usage_to_gl_buffer_usage(mesh_usage); + + let vertex_arr = GlVertexArray::new(current_context); + let vertex_buffer = GlBuffer::new(current_context); + + vertex_buffer + .store(current_context, mesh.vertex_buf().as_bytes(), buffer_usage) + .map_err(Error::StoreVerticesFailed)?; + + if let Err(err) = vertex_arr.bind_vertex_buffer( + current_context, + VERTEX_BUF_BINDING_INDEX, + &vertex_buffer, + GlVertexArrayVertexBufferSpec { + offset: 0, + vertex_size: mesh.vertex_buf().vertex_size(), + }, + ) { + match err { + GlVertexArrayBindVertexBufferError::OffsetValueTooLarge { + value: _, + max_value: _, + } => unreachable!(), + GlVertexArrayBindVertexBufferError::VertexSizeValueTooLarge { + value, + max_value, + } => { + panic!( + "Size of vertex ({value}) is too large. Must be less than {max_value}" + ); + } + } + } + + if let Some(indices) = mesh.indices() { + let index_buffer = GlBuffer::new(current_context); + + index_buffer + .store(current_context, indices, buffer_usage) + .map_err(Error::StoreIndicesFailed)?; + + vertex_arr.bind_element_buffer(current_context, &index_buffer); + + return Ok(Self { + vertex_buffer, + vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(), + last_max_vertex_attr_index: 0, + index_buffer: Some(index_buffer), + element_cnt: indices + .len() + .try_into() + .expect("Mesh index count does not fit into a 32-bit unsigned int"), + vertex_arr, + }); + } + + Ok(Self { + vertex_buffer, + vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(), + last_max_vertex_attr_index: 0, + index_buffer: None, + element_cnt: mesh + .vertex_buf() + .len() + .try_into() + .expect("Mesh vertex count does not fit into a 32-bit unsigned int"), + vertex_arr, + }) + } + + pub fn update( + &mut self, + current_context: &GlCurrentContextWithFns, + mesh: &Mesh, + mesh_usage: MeshUsage, + ) -> Result<(), Error> + { + let buffer_usage = mesh_usage_to_gl_buffer_usage(mesh_usage); + + self.vertex_buffer + .store(current_context, mesh.vertex_buf().as_bytes(), buffer_usage) + .map_err(Error::StoreVerticesFailed)?; + + if let Some(indices) = mesh.indices() { + let index_buffer = self + .index_buffer + .get_or_insert_with(|| GlBuffer::new(current_context)); + + index_buffer + .store(current_context, indices, buffer_usage) + .map_err(Error::StoreIndicesFailed)?; + + self.vertex_arr + .bind_element_buffer(current_context, index_buffer); + + self.element_cnt = indices + .len() + .try_into() + .expect("Mesh index count does not fit into a 32-bit unsigned int"); + + return Ok(()); + } + + if let Some(index_buffer) = self.index_buffer.take() { + index_buffer.delete(current_context); + self.vertex_arr.unbind_element_buffer(current_context); + } + + self.element_cnt = mesh + .vertex_buf() + .len() + .try_into() + .expect("Mesh vertex count does not fit into a 32-bit unsigned int"); + + Ok(()) + } + + pub fn update_vertex_attrs_for_shader( + &mut self, + curr_gl_ctx: &GlCurrentContextWithFns, + shader_vertex_desc: &ShaderVertexDescription, + ) -> Result<(), VertexAttrsUpdatingError> + { + for index in 0..self.last_max_vertex_attr_index { + self.vertex_arr.disable_attrib(curr_gl_ctx, index); + } + + let mut max_vertex_attr_index = 0u32; + + for vertex_input_desc in &*shader_vertex_desc.inputs { + let Some(vertex_attr_props) = + self.vertex_attr_props.iter().find(|vertex_attr_props| { + vertex_input_desc + .semantic_name + .matches_vertex_label(&vertex_attr_props.label) + }) + else { + cold_path(); + return Err(VertexAttrsUpdatingError::MissingVertexAttr( + vertex_input_desc.semantic_name.clone(), + )); + }; + + let attrib_index: u32 = vertex_input_desc.index.try_into().unwrap(); + + self.vertex_arr.enable_attrib(curr_gl_ctx, attrib_index); + + self.vertex_arr.set_attrib_format( + curr_gl_ctx, + attrib_index, + match &vertex_attr_props.ty { + VertexAttrType::Float32 => GlVertexArrayAttributeFormat { + data_type: GlVertexArrayDataType::Float, + count: 1, + normalized: false, + offset: vertex_attr_props.byte_offset.try_into().unwrap(), + }, + VertexAttrType::Float32Array { length } => { + GlVertexArrayAttributeFormat { + data_type: GlVertexArrayDataType::Float, + count: (*length).try_into().unwrap(), + normalized: false, + offset: vertex_attr_props.byte_offset.try_into().unwrap(), + } + } + }, + ); + + self.vertex_arr.set_attrib_vertex_buf_binding( + curr_gl_ctx, + attrib_index, + VERTEX_BUF_BINDING_INDEX, + ); + + max_vertex_attr_index = max_vertex_attr_index.max(attrib_index); + } + + self.last_max_vertex_attr_index = max_vertex_attr_index; + + Ok(()) + } + + pub fn destroy(&self, curr_gl_ctx: &GlCurrentContextWithFns) + { + self.vertex_arr.delete(curr_gl_ctx); + self.vertex_buffer.delete(curr_gl_ctx); + + if let Some(index_buffer) = &self.index_buffer { + index_buffer.delete(curr_gl_ctx); + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum Error +{ + #[error("Failed to store vertices in vertex buffer")] + StoreVerticesFailed(#[source] opengl_bindings::buffer::Error), + + #[error("Failed to store indices in index buffer")] + StoreIndicesFailed(#[source] opengl_bindings::buffer::Error), +} + +#[derive(Debug, thiserror::Error)] +pub enum VertexAttrsUpdatingError +{ + #[error( + "Mesh is missing equivalent vertex attribute for shader's vertex input: {0}" + )] + MissingVertexAttr(VertexInputSemName), +} + +fn mesh_usage_to_gl_buffer_usage(mesh_usage: MeshUsage) -> GlBufferUsage +{ + match mesh_usage { + MeshUsage::Stream => GlBufferUsage::Stream, + MeshUsage::Static => GlBufferUsage::Static, + MeshUsage::Dynamic => GlBufferUsage::Dynamic, + } +} |
