diff options
Diffstat (limited to 'engine/src/mesh/vertex_buffer.rs')
| -rw-r--r-- | engine/src/mesh/vertex_buffer.rs | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/engine/src/mesh/vertex_buffer.rs b/engine/src/mesh/vertex_buffer.rs new file mode 100644 index 0000000..3e3c467 --- /dev/null +++ b/engine/src/mesh/vertex_buffer.rs @@ -0,0 +1,247 @@ +use std::alloc::Layout; +use std::borrow::Cow; +use std::marker::PhantomData; + +use seq_macro::seq; +use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; + +use crate::mesh::VertexAttrType; + +pub trait VertexAttrValue: + IntoBytes + FromBytes + KnownLayout + Immutable + 'static +{ + fn ty() -> VertexAttrType; +} + +impl VertexAttrValue for f32 +{ + fn ty() -> VertexAttrType + { + VertexAttrType::Float32 + } +} + +impl<const LEN: usize> VertexAttrValue for [f32; LEN] +{ + fn ty() -> VertexAttrType + { + VertexAttrType::Float32Array { length: LEN } + } +} + +#[derive(Debug)] +pub struct NamedVertexAttr<'name, Value: VertexAttrValue> +{ + pub name: &'name str, + pub value: Value, +} + +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct VertexAttrProperties +{ + pub name: Cow<'static, str>, + pub ty: VertexAttrType, + pub layout: Layout, + pub byte_offset: usize, +} + +#[derive(Debug)] +pub struct VertexAttrInfo +{ + pub name: Cow<'static, str>, + pub ty: VertexAttrType, +} + +#[derive(Debug, Clone, Default)] +pub struct VertexBuffer +{ + buf: Vec<u8>, + vertex_size: usize, + vertex_attr_props: Vec<VertexAttrProperties>, +} + +impl VertexBuffer +{ + pub fn with_capacity(vertex_attrs: &[VertexAttrInfo], capacity: usize) -> Self + { + let mut vertex_attr_props = vertex_attrs + .iter() + .map(|VertexAttrInfo { name, ty }| VertexAttrProperties { + name: name.clone(), + ty: ty.clone(), + layout: ty.layout(), + byte_offset: 0, + }) + .collect::<Vec<_>>(); + + let mut vertex_layout = Layout::new::<()>(); + + for VertexAttrProperties { + name: _, + ty: _, + layout: vertex_attr_layout, + byte_offset: vertex_attr_byte_offset, + } in &mut vertex_attr_props + { + let (new_struct_layout, byte_offset) = + vertex_layout.extend(*vertex_attr_layout).unwrap(); + + *vertex_attr_byte_offset = byte_offset; + vertex_layout = new_struct_layout; + } + + let vertex_layout = vertex_layout.pad_to_align(); + + Self { + buf: Vec::with_capacity(vertex_layout.size() * capacity), + vertex_size: vertex_layout.size(), + vertex_attr_props, + } + } + + pub fn push<'name, VertexAttrs: NamedVertexAttrs<'name>>( + &mut self, + vertex: VertexAttrs, + ) + { + assert_eq!( + self.vertex_attr_props.len(), + vertex.vertex_attr_cnt(), + "Vertex has incorrect amount of attributes" + ); + + if self.buf.spare_capacity_mut().len() < self.vertex_size { + self.buf.reserve_exact(self.vertex_size * (self.len() / 2)); + } + + let spare_capacity = self.buf.spare_capacity_mut(); + + let vertex_attrs = vertex.vertex_attrs(); + + for (vertex_attr_name, vertex_attr_bytes, vertex_attr_ty) in vertex_attrs { + let vertex_attr_props = self + .vertex_attr_props + .iter() + .find(|vertex_attr_props| vertex_attr_props.name == vertex_attr_name) + .unwrap(); + + assert_eq!(vertex_attr_ty, vertex_attr_props.ty); + + let start_offset = vertex_attr_props.byte_offset; + + let end_offset = start_offset + vertex_attr_props.layout.size(); + + spare_capacity[start_offset..end_offset] + .write_copy_of_slice(vertex_attr_bytes); + } + + unsafe { + self.buf.set_len(self.buf.len() + self.vertex_size); + } + } + + pub fn vertex_attr_props(&self) -> &[VertexAttrProperties] + { + &self.vertex_attr_props + } + + pub fn len(&self) -> usize + { + assert_eq!(self.buf.len() % self.vertex_size, 0, "Invalid length"); + + self.buf.len() / self.vertex_size + } + + pub fn vertex_size(&self) -> usize + { + self.vertex_size + } + + pub fn as_bytes(&self) -> &[u8] + { + &self.buf + } + + pub fn iter<VertexAttr: VertexAttrValue>( + &self, + vertex_attr_name: &str, + ) -> Iter<'_, VertexAttr> + { + let vertex_attr_props = self + .vertex_attr_props + .iter() + .find(|vertex_attr_props| vertex_attr_props.name == vertex_attr_name) + .unwrap(); + + assert_eq!(VertexAttr::ty(), vertex_attr_props.ty); + + Iter { + buf: self, + vertex_attr_props, + curr_index: 0, + _pd: PhantomData, + } + } +} + +pub struct Iter<'a, VertexAttr: VertexAttrValue> +{ + buf: &'a VertexBuffer, + vertex_attr_props: &'a VertexAttrProperties, + curr_index: usize, + _pd: PhantomData<VertexAttr>, +} + +impl<'a, VertexAttr: VertexAttrValue> Iterator for Iter<'a, VertexAttr> +{ + type Item = &'a VertexAttr; + + fn next(&mut self) -> Option<Self::Item> + { + let start_offset = + (self.buf.vertex_size * self.curr_index) + self.vertex_attr_props.byte_offset; + + let end_offset = start_offset + self.vertex_attr_props.layout.size(); + + let bytes = self.buf.buf.get(start_offset..end_offset)?; + + self.curr_index += 1; + + Some(VertexAttr::ref_from_bytes(bytes).unwrap()) + } +} + +pub trait NamedVertexAttrs<'name> +{ + fn vertex_attr_cnt(&self) -> usize; + + fn vertex_attrs(&self) -> impl Iterator<Item = (&'name str, &[u8], VertexAttrType)>; +} + +macro_rules! impl_named_vertex_attrs { + ($cnt: tt) => { + seq!(I in 0..$cnt { + impl<'name, #(VertexAttr~I: VertexAttrValue,)*> + NamedVertexAttrs<'name> for (#(NamedVertexAttr<'name, VertexAttr~I>,)*) + { + fn vertex_attr_cnt(&self) -> usize + { + $cnt + } + + fn vertex_attrs(&self) + -> impl Iterator<Item = (&'name str, &[u8], VertexAttrType)> + { + [#( + (self.I.name, self.I.value.as_bytes(), VertexAttr~I::ty()), + )*].into_iter() + } + } + }); + }; +} + +seq!(I in 0..16 { + impl_named_vertex_attrs!(I); +}); |
