use std::any::TypeId; 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 zerocopy::IntoBytes; use crate::mesh::Mesh; use crate::reflection::Reflection; use crate::shader::VertexSubset as ShaderVertexSubset; #[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_subset: &ShaderVertexSubset, ) -> Result { let vertex_arr = GlVertexArray::new(current_context); let vertex_buffer = GlBuffer::new(current_context); vertex_buffer .init( current_context, vertex_subset.layout.size() * mesh.vertices().len(), GlBufferUsage::Static, ) .map_err(Error::InitVertexBufferFailed)?; for (vertex_index, vertex) in mesh.vertices().iter().enumerate() { let vertex_bytes = vertex.as_bytes(); for vertex_subset_field in vertex_subset.fields.iter().flatten() { let vertex_buffer_offset = (vertex_subset.layout.size() * vertex_index) + vertex_subset_field.offset; let vertex_field_start_offset = vertex_subset_field.reflection.byte_offset; let vertex_field_size = vertex_subset_field.reflection.layout.size(); vertex_buffer .store_at_byte_offset( current_context, vertex_buffer_offset, &vertex_bytes[vertex_field_start_offset ..vertex_field_start_offset + vertex_field_size], ) .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: vertex_subset.layout.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_subset_field in vertex_subset.fields.iter().flatten() { let attrib_index: u32 = vertex_subset_field.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_subset_field.reflection.reflection { Reflection::Literal(_) => { if vertex_subset_field.reflection.type_id != TypeId::of::() { panic!("Unsupported vertex field data type"); } GlVertexArrayAttributeFormat { data_type: GlVertexArrayDataType::Float, count: 1, normalized: false, offset: vertex_subset_field.offset.try_into().unwrap(), } } Reflection::Array(array_vertex_field) => { let Reflection::Literal(array_vertex_field_item) = array_vertex_field.item_reflection else { panic!("Unsupported array item type in vertex field"); }; if array_vertex_field_item.type_id != TypeId::of::() { panic!("Unsupported array item type in vertex field"); } GlVertexArrayAttributeFormat { data_type: GlVertexArrayDataType::Float, count: array_vertex_field.length.try_into().unwrap(), normalized: false, offset: vertex_subset_field.offset.try_into().unwrap(), } } _ => panic!("Unsupported vertex field data type"), }, ); 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 .vertices() .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 initialize vertex buffer")] InitVertexBufferFailed(#[source] opengl_bindings::buffer::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), }