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; use crate::util::DisplaySlice; 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, vertex_attr_props: Vec, last_used_vertex_attr_cnt: u32, pub index_buffer: Option>, 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 { 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 ({}) is too large. Must be less than {max_value}", 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_buffer, vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(), last_used_vertex_attr_cnt: 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_buffer, vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(), last_used_vertex_attr_cnt: 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(()); } 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> { let vertex_input_cnt = u32::try_from(shader_vertex_desc.inputs.len()) .expect("Shader has too many vertex inputs. Count does not fit into u32"); if self.last_used_vertex_attr_cnt > vertex_input_cnt { for index in vertex_input_cnt..self.last_used_vertex_attr_cnt { self.vertex_arr.disable_attrib(curr_gl_ctx, index); } } let mut used_vertex_attr_cnt = 0u32; for vertex_attr_props in &self.vertex_attr_props { let Some(vertex_input_desc) = shader_vertex_desc.inputs.iter().find(|vertex_input_desc| { vertex_input_desc .semantic_name .matches_vertex_label(&vertex_attr_props.label) }) else { continue; }; 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, ); used_vertex_attr_cnt += 1; } self.last_used_vertex_attr_cnt = used_vertex_attr_cnt; if used_vertex_attr_cnt as usize != shader_vertex_desc.inputs.len() { cold_path(); return Err(VertexAttrsUpdatingError::MissingVertexAttrs( shader_vertex_desc .inputs .iter() .filter_map(|vertex_input_desc| { if self.vertex_attr_props.iter().any(|props| { vertex_input_desc .semantic_name .matches_vertex_label(&props.label) }) { return None; } Some(vertex_input_desc.semantic_name.clone()) }) .collect(), )); } Ok(()) } pub fn destroy(&mut 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(s) for shader's vertex inputs: {}", DisplaySlice::new(.0) )] MissingVertexAttrs(Vec), } 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, } }