diff options
Diffstat (limited to 'engine/src/mesh')
| -rw-r--r-- | engine/src/mesh/cube.rs | 480 | ||||
| -rw-r--r-- | engine/src/mesh/vertex_buffer.rs | 294 |
2 files changed, 294 insertions, 480 deletions
diff --git a/engine/src/mesh/cube.rs b/engine/src/mesh/cube.rs deleted file mode 100644 index 6c9d381..0000000 --- a/engine/src/mesh/cube.rs +++ /dev/null @@ -1,480 +0,0 @@ -use crate::math::calc_triangle_surface_normal; -use crate::mesh::{Mesh, Vertex}; -use crate::util::builder; -use crate::vector::{Vec2, Vec3}; - -builder! { -/// Cube mesh creation specification. -#[builder(name = CreationSpecBuilder, derives = (Debug, Default))] -#[derive(Debug, Default)] -#[non_exhaustive] -pub struct CreationSpec -{ - pub width: f32, - pub height: f32, - pub depth: f32, -} -} - -impl CreationSpec -{ - /// Returns a new `CreationSpec` builder. - #[must_use] - pub fn builder() -> CreationSpecBuilder - { - CreationSpecBuilder::default() - } -} - -/// 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 -{ - /// 🮝 - RightUp, - - /// 🮟 - LeftDown, -} - -/// Creates a cube mesh. -/// -/// 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 -{ - let mut data = Data::default(); - - create_side(&SidePositions::new_top(&creation_spec), &mut data); - create_side(&SidePositions::new_bottom(&creation_spec), &mut data); - create_side(&SidePositions::new_left(&creation_spec), &mut data); - create_side(&SidePositions::new_right(&creation_spec), &mut data); - create_side(&SidePositions::new_back(&creation_spec), &mut data); - create_side(&SidePositions::new_front(&creation_spec), &mut data); - - data.into_mesh(face_cb) -} - -#[derive(Debug, Default)] -struct Data -{ - faces: Vec<Face>, - vertex_data: VertexData, -} - -#[derive(Debug, Default)] -struct VertexData -{ - vertex_positions: Vec<Vec3<f32>>, - vertex_normals: Vec<Vec3<f32>>, -} - -impl Data -{ - fn into_mesh( - self, - mut face_cb: impl FnMut(FaceVertices, Side, FaceLocation) -> FaceVertices, - ) -> Mesh - { - let mut vertices = Vec::<Vertex>::with_capacity(self.faces.len() * 3); - let mut indices = Vec::<u32>::with_capacity(self.faces.len() * 3); - - 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", - )); - - 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) - .expect("Vertex position index is out of bounds") - .clone(); - - let vertex_normal = vertex_data - .vertex_normals - .get(face_vertex.normal_index as usize) - .expect("Vertex normal index is out of bounds") - .clone(); - - Vertex::builder() - .pos(vertex_pos) - .normal(vertex_normal) - .build() - }), - } - } - - 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 }; - self.vertices[1].texture_coords = Vec2 { x: 0.0, y: 1.0 }; - self.vertices[2].texture_coords = Vec2 { x: 1.0, y: 0.0 }; - } - FaceLocation::LeftDown => { - self.vertices[0].texture_coords = Vec2 { x: 0.0, y: 1.0 }; - self.vertices[1].texture_coords = Vec2 { x: 0.0, y: 0.0 }; - self.vertices[2].texture_coords = Vec2 { x: 1.0, y: 0.0 }; - } - }; - - self - } -} - -#[derive(Debug)] -struct Face -{ - vertices: [FaceVertex; 3], - side: Side, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -struct FaceVertex -{ - pos_index: u32, - normal_index: u32, -} - -#[derive(Debug)] -struct SidePositions -{ - up_left: Vec3<f32>, - up_right: Vec3<f32>, - down_left: Vec3<f32>, - down_right: Vec3<f32>, - normal_calc_order: NormalCalcOrder, - side: Side, -} - -impl SidePositions -{ - fn new_top(creation_spec: &CreationSpec) -> Self - { - let up_left = Vec3 { - x: -(creation_spec.width / 2.0), - y: creation_spec.height / 2.0, - z: creation_spec.depth / 2.0, - }; - - let down_right = Vec3 { - x: creation_spec.width / 2.0, - y: creation_spec.height / 2.0, - z: -(creation_spec.depth / 2.0), - }; - - Self { - up_left, - up_right: Vec3 { x: down_right.x, ..up_left.clone() }, - down_left: Vec3 { x: up_left.x, ..down_right.clone() }, - down_right, - normal_calc_order: NormalCalcOrder::Clockwise, - side: Side::Top, - } - } - - fn new_bottom(creation_spec: &CreationSpec) -> Self - { - let up_left = Vec3 { - x: -(creation_spec.width / 2.0), - y: -creation_spec.height / 2.0, - z: creation_spec.depth / 2.0, - }; - - let down_right = Vec3 { - x: creation_spec.width / 2.0, - y: -creation_spec.height / 2.0, - z: -(creation_spec.depth / 2.0), - }; - - Self { - up_left, - up_right: Vec3 { x: down_right.x, ..up_left.clone() }, - down_left: Vec3 { x: up_left.x, ..down_right.clone() }, - down_right, - normal_calc_order: NormalCalcOrder::CounterClockwise, - side: Side::Bottom, - } - } - - fn new_left(creation_spec: &CreationSpec) -> Self - { - let up_left = Vec3 { - x: -(creation_spec.width / 2.0), - y: creation_spec.height / 2.0, - z: -(creation_spec.depth / 2.0), - }; - - let down_right = Vec3 { - x: -(creation_spec.width / 2.0), - y: -(creation_spec.height / 2.0), - z: creation_spec.depth / 2.0, - }; - - Self { - up_left, - up_right: Vec3 { z: down_right.z, ..up_left.clone() }, - down_left: Vec3 { z: up_left.z, ..down_right.clone() }, - down_right, - normal_calc_order: NormalCalcOrder::CounterClockwise, - side: Side::Left, - } - } - - fn new_right(creation_spec: &CreationSpec) -> Self - { - let up_left = Vec3 { - x: (creation_spec.width / 2.0), - y: creation_spec.height / 2.0, - z: -(creation_spec.depth / 2.0), - }; - - let down_right = Vec3 { - x: (creation_spec.width / 2.0), - y: -(creation_spec.height / 2.0), - z: creation_spec.depth / 2.0, - }; - - Self { - up_left, - up_right: Vec3 { z: down_right.z, ..up_left.clone() }, - down_left: Vec3 { z: up_left.z, ..down_right.clone() }, - down_right, - normal_calc_order: NormalCalcOrder::Clockwise, - side: Side::Right, - } - } - - fn new_back(creation_spec: &CreationSpec) -> Self - { - let up_left = Vec3 { - x: -(creation_spec.width / 2.0), - y: creation_spec.height / 2.0, - z: -creation_spec.depth / 2.0, - }; - - let down_right = Vec3 { - x: creation_spec.width / 2.0, - y: -(creation_spec.height / 2.0), - z: -creation_spec.depth / 2.0, - }; - - Self { - up_left, - up_right: Vec3 { x: down_right.x, ..up_left.clone() }, - down_left: Vec3 { x: up_left.x, ..down_right.clone() }, - down_right, - normal_calc_order: NormalCalcOrder::Clockwise, - side: Side::Back, - } - } - - fn new_front(creation_spec: &CreationSpec) -> Self - { - let up_left = Vec3 { - x: -(creation_spec.width / 2.0), - y: creation_spec.height / 2.0, - z: creation_spec.depth / 2.0, - }; - - let down_right = Vec3 { - x: creation_spec.width / 2.0, - y: -(creation_spec.height / 2.0), - z: creation_spec.depth / 2.0, - }; - - Self { - up_left, - up_right: Vec3 { x: down_right.x, ..up_left.clone() }, - down_left: Vec3 { x: up_left.x, ..down_right.clone() }, - down_right, - normal_calc_order: NormalCalcOrder::CounterClockwise, - side: Side::Front, - } - } -} - -#[derive(Debug)] -enum NormalCalcOrder -{ - Clockwise, - CounterClockwise, -} - -fn create_side(side_positions: &SidePositions, data: &mut Data) -{ - let normal = match side_positions.normal_calc_order { - NormalCalcOrder::Clockwise => calc_triangle_surface_normal( - &side_positions.up_left, - &side_positions.up_right, - &side_positions.down_left, - ), - NormalCalcOrder::CounterClockwise => calc_triangle_surface_normal( - &side_positions.up_left, - &side_positions.down_left, - &side_positions.up_right, - ), - }; - - data.vertex_data.vertex_normals.push(normal); - - let top_normal_index = data.vertex_data.vertex_normals.len() - 1; - - data.vertex_data - .vertex_positions - .push(side_positions.up_right); - - let up_right_pos_index = data.vertex_data.vertex_positions.len() - 1; - - data.vertex_data - .vertex_positions - .push(side_positions.up_left); - - let up_left_pos_index = data.vertex_data.vertex_positions.len() - 1; - - data.vertex_data - .vertex_positions - .push(side_positions.down_left); - - let down_left_pos_index = data.vertex_data.vertex_positions.len() - 1; - - data.vertex_data - .vertex_positions - .push(side_positions.down_right); - - let down_right_pos_index = data.vertex_data.vertex_positions.len() - 1; - - // 🮝 - data.faces.push(Face { - vertices: [ - FaceVertex { - pos_index: up_right_pos_index as u32, - normal_index: top_normal_index as u32, - }, - FaceVertex { - pos_index: up_left_pos_index as u32, - normal_index: top_normal_index as u32, - }, - FaceVertex { - pos_index: down_right_pos_index as u32, - normal_index: top_normal_index as u32, - }, - ], - side: side_positions.side, - }); - - // 🮟 - data.faces.push(Face { - vertices: [ - FaceVertex { - pos_index: up_left_pos_index as u32, - normal_index: top_normal_index as u32, - }, - FaceVertex { - pos_index: down_left_pos_index as u32, - normal_index: top_normal_index as u32, - }, - FaceVertex { - pos_index: down_right_pos_index as u32, - 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..64edc58 --- /dev/null +++ b/engine/src/mesh/vertex_buffer.rs @@ -0,0 +1,294 @@ +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<Value: VertexAttrValue> +{ + pub label: VertexLabel, + pub value: Value, +} + +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct VertexAttrProperties +{ + pub label: VertexLabel, + pub ty: VertexAttrType, + pub layout: Layout, + pub byte_offset: usize, +} + +#[derive(Debug)] +pub struct VertexAttrInfo +{ + pub label: VertexLabel, + pub ty: VertexAttrType, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum VertexLabel +{ + Position, + Normal, + + /// Texture coordinate with (0, 0) being at the top left. + UvFromTopLeft, + Color, + Other(Cow<'static, str>), +} + +#[derive(Debug, Clone, Default)] +pub struct VertexBuffer +{ + buf: Vec<u8>, + vertex_size: usize, + vertex_attr_props: Vec<VertexAttrProperties>, +} + +impl VertexBuffer +{ + #[must_use] + pub unsafe fn from_raw( + vertex_attr_props: &[VertexAttrProperties], + buffer: Vec<u8>, + ) -> Self + { + let last_vertex_attr_props = vertex_attr_props + .iter() + .max_by_key(|props| props.byte_offset) + .expect("No vertex attribute properties are given"); + + let vertex_size = + last_vertex_attr_props.byte_offset + last_vertex_attr_props.layout.size(); + + Self { + buf: buffer, + vertex_size, + vertex_attr_props: vertex_attr_props.to_vec(), + } + } + + #[must_use] + pub fn with_capacity(vertex_attrs: &[VertexAttrInfo], capacity: usize) -> Self + { + let mut vertex_attr_props = vertex_attrs + .iter() + .map(|VertexAttrInfo { label, ty }| VertexAttrProperties { + label: label.clone(), + ty: ty.clone(), + layout: ty.layout(), + byte_offset: 0, + }) + .collect::<Vec<_>>(); + + let mut vertex_layout = Layout::new::<()>(); + + for VertexAttrProperties { + 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<VertexAttrs: NamedVertexAttrs>(&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_label, 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.label == vertex_label) + .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); + } + } + + #[must_use] + pub fn vertex_attr_props(&self) -> &[VertexAttrProperties] + { + &self.vertex_attr_props + } + + #[must_use] + pub fn len(&self) -> usize + { + assert_eq!(self.buf.len() % self.vertex_size, 0, "Invalid length"); + + self.buf.len() / self.vertex_size + } + + #[must_use] + pub fn is_empty(&self) -> bool + { + self.buf.is_empty() + } + + #[must_use] + pub fn vertex_size(&self) -> usize + { + self.vertex_size + } + + #[must_use] + pub fn as_bytes(&self) -> &[u8] + { + &self.buf + } + + pub fn clear(&mut self) + { + self.buf.clear(); + } + + #[must_use] + pub fn iter<VertexAttr: VertexAttrValue>( + &self, + vertex_label: VertexLabel, + ) -> Iter<'_, VertexAttr> + { + let vertex_attr_props = self + .vertex_attr_props + .iter() + .find(|vertex_attr_props| vertex_attr_props.label == vertex_label) + .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 +{ + fn vertex_attr_cnt(&self) -> usize; + + fn vertex_attrs(&self) + -> impl Iterator<Item = (&VertexLabel, &[u8], VertexAttrType)>; +} + +macro_rules! impl_named_vertex_attrs { + ($cnt: tt) => { + seq!(I in 0..$cnt { + impl<#(VertexAttr~I: VertexAttrValue,)*> + NamedVertexAttrs for (#(NamedVertexAttr<VertexAttr~I>,)*) + { + fn vertex_attr_cnt(&self) -> usize + { + $cnt + } + + fn vertex_attrs(&self) + -> impl Iterator<Item = (&VertexLabel, &[u8], VertexAttrType)> + { + [#( + (&self.I.label, self.I.value.as_bytes(), VertexAttr~I::ty()), + )*].into_iter() + } + } + }); + }; +} + +seq!(I in 0..16 { + impl_named_vertex_attrs!(I); +}); |
