use opengl_bindings::CurrentContextWithFns as GlCurrentContextWithFns; 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 crate::mesh::{Mesh, VertexAttrType}; use crate::shader::VertexDescription as ShaderVertexDescription; #[derive(Debug)] pub struct GraphicsMesh { /// Vertex and index buffer has to live as long as the vertex array vertex_buffer: GlBuffer, 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, vertex_desc: &ShaderVertexDescription, ) -> Result { let vertex_arr = GlVertexArray::new(current_context); let vertex_buffer = GlBuffer::new(current_context); vertex_buffer .store( current_context, mesh.vertex_buf().as_bytes(), GlBufferUsage::Static, ) .map_err(Error::StoreVerticesFailed)?; let vertex_buf_binding_index = 0; 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 ); } } } for vertex_attr_props in mesh.vertex_buf().vertex_attr_props() { let vertex_field_desc = vertex_desc .fields .iter() .find(|vertex_field_desc| { *vertex_field_desc.name == vertex_attr_props.name }) .unwrap(); let attrib_index: u32 = vertex_field_desc.varying_input_offset.try_into().unwrap(); vertex_arr.enable_attrib(current_context, attrib_index); vertex_arr.set_attrib_format( current_context, 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(), } } }, ); vertex_arr.set_attrib_vertex_buf_binding( current_context, attrib_index, vertex_buf_binding_index, ); } if let Some(indices) = mesh.indices() { let index_buffer = GlBuffer::new(current_context); index_buffer .store(current_context, indices, GlBufferUsage::Static) .map_err(Error::StoreIndicesFailed)?; vertex_arr.bind_element_buffer(current_context, &index_buffer); return Ok(Self { vertex_buffer: vertex_buffer, 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, 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 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), }