use std::alloc::Layout; use zerocopy::IntoBytes; use crate::data_types::dimens::Dimens3; use crate::mesh::vertex_buffer::{VertexAttrProperties, VertexLabel}; use crate::vector::Vec3; pub mod vertex_buffer; #[derive(Debug, Clone)] pub struct Mesh { vertex_buf: vertex_buffer::VertexBuffer, indices: Option>, } impl Mesh { pub fn cube(size: Dimens3) -> Self { let half_w = size.width / 2.0; let half_h = size.height / 2.0; let half_d = size.depth / 2.0; let vertices = [ // pos.x, pos.y, pos.z, texture_coords.x, texture_coords.y, normal.x, // normal.y, normal.z [half_w, half_h, half_d, 1.00, 1.00, 0.0, 1.00, 0.0], [-half_w, half_h, half_d, 0.0, 1.00, 0.0, 1.00, 0.0], [half_w, half_h, -half_d, 1.00, 0.0, 0.0, 1.00, 0.0], [-half_w, half_h, -half_d, 0.0, 0.0, 0.0, 1.00, 0.0], [half_w, -half_h, half_d, 1.00, 1.00, 0.0, -1.00, 0.0], [-half_w, -half_h, half_d, 0.0, 1.00, 0.0, -1.00, 0.0], [half_w, -half_h, -half_d, 1.00, 0.0, 0.0, -1.00, 0.0], [-half_w, -half_h, -half_d, 0.0, 0.0, 0.0, -1.00, 0.0], [-half_w, half_h, half_d, 1.00, 1.00, -1.00, 0.0, 0.0], [-half_w, half_h, -half_d, 0.0, 1.00, -1.00, 0.0, 0.0], [-half_w, -half_h, half_d, 1.00, 0.0, -1.00, 0.0, 0.0], [-half_w, -half_h, -half_d, 0.0, 0.0, -1.00, 0.0, 0.0], [half_w, half_h, half_d, 1.00, 1.00, 1.00, 0.0, 0.0], [half_w, half_h, -half_d, 0.0, 1.00, 1.00, 0.0, 0.0], [half_w, -half_h, half_d, 1.00, 0.0, 1.00, 0.0, 0.0], [half_w, -half_h, -half_d, 0.0, 0.0, 1.00, 0.0, 0.0], [half_w, half_h, -half_d, 1.00, 1.00, 0.0, 0.0, -1.00], [-half_w, half_h, -half_d, 0.0, 1.00, 0.0, 0.0, -1.00], [half_w, -half_h, -half_d, 1.00, 0.0, 0.0, 0.0, -1.00], [-half_w, -half_h, -half_d, 0.0, 0.0, 0.0, 0.0, -1.00], [half_w, half_h, half_d, 1.00, 1.00, 0.0, 0.0, 1.00], [-half_w, half_h, half_d, 0.0, 1.00, 0.0, 0.0, 1.00], [half_w, -half_h, half_d, 1.00, 0.0, 0.0, 0.0, 1.00], [-half_w, -half_h, half_d, 0.0, 0.0, 0.0, 0.0, 1.00], ]; let elements = [ 0, 1, 2, 1, 3, 2, 4, 5, 6, 5, 7, 6, 8, 9, 10, 9, 11, 10, 12, 13, 14, 13, 15, 14, 16, 17, 18, 17, 19, 18, 20, 21, 22, 21, 23, 22, ]; Mesh::builder() .vertices(unsafe { vertex_buffer::VertexBuffer::from_raw( &[ VertexAttrProperties { label: VertexLabel::Position, ty: VertexAttrType::Float32Array { length: 3 }, layout: Layout::new::<[f32; 3]>(), byte_offset: 0, }, VertexAttrProperties { label: VertexLabel::Uv, ty: VertexAttrType::Float32Array { length: 2 }, layout: Layout::new::<[f32; 2]>(), byte_offset: size_of::<[f32; 3]>(), }, VertexAttrProperties { label: VertexLabel::Normal, ty: VertexAttrType::Float32Array { length: 3 }, layout: Layout::new::<[f32; 3]>(), byte_offset: size_of::<[f32; 3]>() + size_of::<[f32; 2]>(), }, ], vertices.as_bytes().to_vec(), ) }) .indices(elements) .build() } pub fn builder() -> Builder { Builder::default() } pub fn vertex_buf(&self) -> &vertex_buffer::VertexBuffer { &self.vertex_buf } pub fn vertex_buf_mut(&mut self) -> &mut vertex_buffer::VertexBuffer { &mut self.vertex_buf } #[must_use] pub fn indices(&self) -> Option<&[u32]> { self.indices.as_deref() } #[must_use] pub fn indices_mut(&mut self) -> Option<&mut [u32]> { self.indices.as_deref_mut() } pub fn set_indices(&mut self, indices: impl IntoIterator) { let curr_indices = self.indices.get_or_insert_with(|| Vec::new()); curr_indices.clear(); curr_indices.extend(indices.into_iter()); } /// Finds the vertex positions that are furthest in every 3D direction. Keep in mind /// that this can be quite time-expensive if the mesh has many vertices. pub fn find_furthest_vertex_positions(&self) -> DirectionPositions { let mut pos_iter = self .vertex_buf() .iter::<[f32; 3]>(VertexLabel::Position) .map(|vertex_pos| Vec3::from(*vertex_pos)) .into_iter(); let first_pos = pos_iter.next().unwrap(); pos_iter .fold( FurthestPosAcc { up: FurthestPos::new(first_pos, &Vec3::UP), down: FurthestPos::new(first_pos, &Vec3::DOWN), left: FurthestPos::new(first_pos, &Vec3::LEFT), right: FurthestPos::new(first_pos, &Vec3::RIGHT), back: FurthestPos::new(first_pos, &Vec3::BACK), front: FurthestPos::new(first_pos, &Vec3::FRONT), }, |mut furthest_pos_acc, pos| { furthest_pos_acc.up.update_if_further(pos); furthest_pos_acc.down.update_if_further(pos); furthest_pos_acc.left.update_if_further(pos); furthest_pos_acc.right.update_if_further(pos); furthest_pos_acc.back.update_if_further(pos); furthest_pos_acc.front.update_if_further(pos); furthest_pos_acc }, ) .into() } } /// Mesh builder #[derive(Debug, Clone, Default)] pub struct Builder { vertex_buf: vertex_buffer::VertexBuffer, indices: Option>, } impl Builder { pub fn new() -> Self { Self::default() } pub fn vertices(mut self, vertices_bytes: vertex_buffer::VertexBuffer) -> Self { self.vertex_buf = vertices_bytes; self } pub fn indices(mut self, indices: impl IntoIterator) -> Self { self.indices = Some(indices.into_iter().collect()); self } pub fn build(self) -> Mesh { Mesh { vertex_buf: self.vertex_buf, indices: self.indices, } } } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum VertexAttrType { Float32, Float32Array { length: usize, }, } impl VertexAttrType { pub fn layout(&self) -> Layout { match self { Self::Float32 => Layout::new::(), Self::Float32Array { length } => Layout::array::(*length).unwrap(), } } } #[derive(Debug, Clone)] pub struct DirectionPositions { pub up: Vec3, pub down: Vec3, pub left: Vec3, pub right: Vec3, pub back: Vec3, pub front: Vec3, } impl<'mesh> From> for DirectionPositions { fn from(acc: FurthestPosAcc<'mesh>) -> Self { Self { up: acc.up.pos, down: acc.down.pos, left: acc.left.pos, right: acc.right.pos, back: acc.back.pos, front: acc.front.pos, } } } #[derive(Debug)] struct FurthestPosAcc<'mesh> { up: FurthestPos<'mesh>, down: FurthestPos<'mesh>, left: FurthestPos<'mesh>, right: FurthestPos<'mesh>, back: FurthestPos<'mesh>, front: FurthestPos<'mesh>, } #[derive(Debug)] struct FurthestPos<'mesh> { pos: Vec3, dot_prod: f32, direction: &'mesh Vec3, } impl<'mesh> FurthestPos<'mesh> { fn new(pos: Vec3, direction: &'mesh Vec3) -> Self { Self { pos, dot_prod: direction.dot(&pos), direction, } } fn update_if_further(&mut self, point: Vec3) { let point_dot_prod = self.direction.dot(&point); if point_dot_prod > self.dot_prod { self.pos = point; self.dot_prod = point_dot_prod; } } }