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; 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, 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_field_desc_cnt = u32::try_from(shader_vertex_desc.fields.len()) .expect("Shader has too many vertex fields. Count does not fit into u32"); if self.last_used_vertex_attr_cnt > vertex_field_desc_cnt { for index in vertex_field_desc_cnt..self.last_used_vertex_attr_cnt { self.vertex_arr.disable_attrib(curr_gl_ctx, index); } } let mut used_vertex_attr_cnt = 0u32; let mut last_attr_index: Option = None; for vertex_attr_props in &self.vertex_attr_props { let Some(vertex_field_desc) = shader_vertex_desc.fields.iter().find(|vertex_field_desc| { *vertex_field_desc.name == vertex_attr_props.name }) else { continue; }; let attrib_index: u32 = vertex_field_desc.varying_input_offset.try_into().unwrap(); if let Some(last_attr_index) = last_attr_index { if last_attr_index.wrapping_add(1) != attrib_index { cold_path(); return Err( VertexAttrsUpdatingError::VertexAttrIndicesNotConsecutive, ); } } else if attrib_index != 0 { cold_path(); return Err(VertexAttrsUpdatingError::FirstVertexAttrIndexNotZero { vertex_attr_name: vertex_attr_props.name.to_string().into_boxed_str(), unexpected_index: attrib_index, }); } last_attr_index = Some(attrib_index); 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.fields.len() { cold_path(); return Err(VertexAttrsUpdatingError::MissingVertexAttrs( shader_vertex_desc .fields .iter() .filter_map(|vertex_field_desc| { if self .vertex_attr_props .iter() .any(|prop| prop.name == *vertex_field_desc.name) { return None; } Some(vertex_field_desc.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 vertex attribute(s) required by shader: {0:?}")] MissingVertexAttrs(Vec>), #[error("Shader's vertex attribute indices are not consecutive")] VertexAttrIndicesNotConsecutive, #[error( "Shader's first vertex attribute ({vertex_attr_name}) index is not 0, is {}", unexpected_index )] FirstVertexAttrIndexNotZero { vertex_attr_name: Box, unexpected_index: u32, }, } 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, } }