summaryrefslogtreecommitdiff
path: root/engine/src/mesh
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/mesh')
-rw-r--r--engine/src/mesh/cube.rs480
-rw-r--r--engine/src/mesh/vertex_buffer.rs294
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);
+});