diff options
| author | HampusM <hampus@hampusmat.com> | 2026-04-14 16:22:44 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-04-14 16:22:44 +0200 |
| commit | 484e2295a2fae589c7b5c9ae05aba57867ae681a (patch) | |
| tree | ef1750db7281676ae7b96d1d3135eb8a291d7498 /engine/src/mesh | |
| parent | 36fa6811c1c8171008bde10127a31be9614d6de8 (diff) | |
refactor(engine): make mesh vertices dynamic
Diffstat (limited to 'engine/src/mesh')
| -rw-r--r-- | engine/src/mesh/cube.rs | 209 | ||||
| -rw-r--r-- | engine/src/mesh/vertex_buffer.rs | 247 |
2 files changed, 326 insertions, 130 deletions
diff --git a/engine/src/mesh/cube.rs b/engine/src/mesh/cube.rs index fe45a4a..dba6473 100644 --- a/engine/src/mesh/cube.rs +++ b/engine/src/mesh/cube.rs @@ -1,7 +1,14 @@ +use std::collections::HashMap; + use crate::builder; use crate::data_types::dimens::Dimens3; use crate::math::calc_triangle_surface_normal; -use crate::mesh::{Mesh, Vertex}; +use crate::mesh::vertex_buffer::{ + NamedVertexAttr, + VertexAttrInfo, + VertexBuffer as MeshVertexBuffer, +}; +use crate::mesh::{Mesh, POSITION_VERTEX_ATTRIB_NAME, VertexAttrType}; use crate::vector::{Vec2, Vec3}; builder! { @@ -39,29 +46,6 @@ impl CreationSpecBuilder } } -/// Describes a single side of a cube (obviously). -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum Side -{ - /// +Z - Front, - - /// -Z - Back, - - /// -X - Left, - - /// +X - Right, - - /// +Y - Top, - - /// -Y - Bottom, -} - /// Describes what location on a side of a cube a face is. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum FaceLocation @@ -77,11 +61,10 @@ pub enum FaceLocation /// /// By default, the texture coordinates are arranged so that the full texture is visible /// on every side. This can be changed inside of the `face_cb` function. -pub fn create( - creation_spec: CreationSpec, - face_cb: impl FnMut(FaceVertices, Side, FaceLocation) -> FaceVertices, -) -> Mesh +pub fn create(creation_spec: CreationSpec) -> Mesh { + // TODO: Reimplement this mess + let mut data = Data::default(); create_side(&SidePositions::new_top(&creation_spec), &mut data); @@ -91,7 +74,7 @@ pub fn create( create_side(&SidePositions::new_back(&creation_spec), &mut data); create_side(&SidePositions::new_front(&creation_spec), &mut data); - data.into_mesh(face_cb) + data.into_mesh() } #[derive(Debug, Default)] @@ -110,88 +93,55 @@ struct VertexData impl Data { - fn into_mesh( - self, - mut face_cb: impl FnMut(FaceVertices, Side, FaceLocation) -> FaceVertices, - ) -> Mesh + fn into_mesh(self) -> Mesh { - let mut vertices = Vec::<Vertex>::with_capacity(self.faces.len() * 3); + let mut vertex_buf = MeshVertexBuffer::with_capacity( + &[ + VertexAttrInfo { + name: POSITION_VERTEX_ATTRIB_NAME.into(), + ty: VertexAttrType::Float32Array { length: 3 }, + }, + VertexAttrInfo { + name: "texture_coords".into(), + ty: VertexAttrType::Float32Array { length: 2 }, + }, + VertexAttrInfo { + name: "normal".into(), + ty: VertexAttrType::Float32Array { length: 3 }, + }, + ], + self.faces.len() * 3, + ); + let mut indices = Vec::<u32>::with_capacity(self.faces.len() * 3); + let mut added_face_vertices = HashMap::<FaceVertex, u32>::new(); + let mut face_location = FaceLocation::RightUp; let Self { faces, vertex_data } = self; for face in faces { - let side = face.side; - - let face_vertices = face_cb( - FaceVertices::new(face, &vertex_data) - .with_full_per_side_tex_coords(face_location), - side, - face_location, - ); - - for vertex in face_vertices.vertices { - if let Some((prev_vertex_index, _)) = vertices - .iter() - .enumerate() - .find(|(_, prev_vertex)| *prev_vertex == &vertex) - { - indices - .push(u32::try_from(prev_vertex_index).expect( - "Vertex index does not fit into 32-bit unsigned int", - )); - + let face_texture_coords = match face_location { + FaceLocation::RightUp => [ + Vec2 { x: 1.0, y: 1.0 }, + Vec2 { x: 0.0, y: 1.0 }, + Vec2 { x: 1.0, y: 0.0 }, + ], + FaceLocation::LeftDown => [ + Vec2 { x: 0.0, y: 1.0 }, + Vec2 { x: 0.0, y: 0.0 }, + Vec2 { x: 1.0, y: 0.0 }, + ], + }; + + for (face_vertex, vertex_uv) in face.vertices.iter().zip(face_texture_coords) + { + if let Some(vertex_index) = added_face_vertices.get(face_vertex) { + indices.push(*vertex_index); continue; } - vertices.push(vertex); - - let vertex_index = u32::try_from(vertices.len() - 1) - .expect("Vertex index does not fit into 32-bit unsigned int"); - - indices.push(vertex_index); - } - - match face_location { - FaceLocation::RightUp => face_location = FaceLocation::LeftDown, - FaceLocation::LeftDown => face_location = FaceLocation::RightUp, - } - } - - Mesh::new(vertices, Some(indices)) - } -} - -/// The vertices of a single face of a cube. -#[derive(Debug, Default, Clone)] -pub struct FaceVertices -{ - /// The three vertices of a face in counter-clockwise order. - /// - /// Order when [`FaceLocation::RightUp`]: - /// ```text - /// ₂ ₁ - /// 🮝 - /// ³ - /// ``` - /// - /// Order when [`FaceLocation::LeftDown`]: - /// ```text - /// ₁ - /// 🮟 - /// ² ³ - /// ``` - pub vertices: [Vertex; 3], -} - -impl FaceVertices -{ - fn new(face: Face, vertex_data: &VertexData) -> Self - { - Self { - vertices: face.vertices.map(|face_vertex| { let vertex_pos = vertex_data .vertex_positions .get(face_vertex.pos_index as usize) @@ -204,30 +154,39 @@ impl FaceVertices .expect("Vertex normal index is out of bounds") .clone(); - Vertex::builder() - .pos(vertex_pos) - .normal(vertex_normal) - .build() - }), - } - } + vertex_buf.push(( + NamedVertexAttr { + name: POSITION_VERTEX_ATTRIB_NAME, + value: vertex_pos.into_array(), + }, + NamedVertexAttr { + name: "texture_coords", + value: vertex_uv.into_array(), + }, + NamedVertexAttr { + name: "normal", + value: vertex_normal.into_array(), + }, + )); + + let vertex_index = u32::try_from(vertex_buf.len() - 1) + .expect("Vertex index does not fit into 32-bit unsigned int"); - fn with_full_per_side_tex_coords(mut self, face_location: FaceLocation) -> Self - { - match face_location { - FaceLocation::RightUp => { - self.vertices[0].texture_coords = Vec2 { x: 1.0, y: 1.0 }.into(); - self.vertices[1].texture_coords = Vec2 { x: 0.0, y: 1.0 }.into(); - self.vertices[2].texture_coords = Vec2 { x: 1.0, y: 0.0 }.into(); + indices.push(vertex_index); + + added_face_vertices.insert(face_vertex.clone(), vertex_index); } - FaceLocation::LeftDown => { - self.vertices[0].texture_coords = Vec2 { x: 0.0, y: 1.0 }.into(); - self.vertices[1].texture_coords = Vec2 { x: 0.0, y: 0.0 }.into(); - self.vertices[2].texture_coords = Vec2 { x: 1.0, y: 0.0 }.into(); + + match face_location { + FaceLocation::RightUp => face_location = FaceLocation::LeftDown, + FaceLocation::LeftDown => face_location = FaceLocation::RightUp, } - }; + } - self + Mesh::builder() + .vertices(vertex_buf) + .indices(indices) + .build() } } @@ -235,7 +194,6 @@ impl FaceVertices struct Face { vertices: [FaceVertex; 3], - side: Side, } #[derive(Debug, PartialEq, Eq, Hash, Clone)] @@ -253,7 +211,6 @@ struct SidePositions down_left: Vec3<f32>, down_right: Vec3<f32>, normal_calc_order: NormalCalcOrder, - side: Side, } impl SidePositions @@ -278,7 +235,6 @@ impl SidePositions down_left: Vec3 { x: up_left.x, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::Clockwise, - side: Side::Top, } } @@ -302,7 +258,6 @@ impl SidePositions down_left: Vec3 { x: up_left.x, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::CounterClockwise, - side: Side::Bottom, } } @@ -326,7 +281,6 @@ impl SidePositions down_left: Vec3 { z: up_left.z, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::CounterClockwise, - side: Side::Left, } } @@ -350,7 +304,6 @@ impl SidePositions down_left: Vec3 { z: up_left.z, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::Clockwise, - side: Side::Right, } } @@ -374,7 +327,6 @@ impl SidePositions down_left: Vec3 { x: up_left.x, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::Clockwise, - side: Side::Back, } } @@ -398,7 +350,6 @@ impl SidePositions down_left: Vec3 { x: up_left.x, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::CounterClockwise, - side: Side::Front, } } } @@ -469,7 +420,6 @@ fn create_side(side_positions: &SidePositions, data: &mut Data) normal_index: top_normal_index as u32, }, ], - side: side_positions.side, }); // 🮟 @@ -488,6 +438,5 @@ fn create_side(side_positions: &SidePositions, data: &mut Data) normal_index: top_normal_index as u32, }, ], - side: side_positions.side, }); } 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); +}); |
