use std::collections::HashMap; use crate::builder; use crate::data_types::dimens::Dimens3; use crate::math::calc_triangle_surface_normal; 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! { /// 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() } } impl CreationSpecBuilder { pub fn dimens(mut self, dimens: Dimens3) -> Self { self.width = dimens.width; self.height = dimens.height; self.depth = dimens.depth; self } } /// 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) -> Mesh { // TODO: Reimplement this mess 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() } #[derive(Debug, Default)] struct Data { faces: Vec, vertex_data: VertexData, } #[derive(Debug, Default)] struct VertexData { vertex_positions: Vec>, vertex_normals: Vec>, } impl Data { fn into_mesh(self) -> Mesh { 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::::with_capacity(self.faces.len() * 3); let mut added_face_vertices = HashMap::::new(); let mut face_location = FaceLocation::RightUp; let Self { faces, vertex_data } = self; for face in faces { 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; } 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_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"); indices.push(vertex_index); added_face_vertices.insert(face_vertex.clone(), vertex_index); } match face_location { FaceLocation::RightUp => face_location = FaceLocation::LeftDown, FaceLocation::LeftDown => face_location = FaceLocation::RightUp, } } Mesh::builder() .vertices(vertex_buf) .indices(indices) .build() } } #[derive(Debug)] struct Face { vertices: [FaceVertex; 3], } #[derive(Debug, PartialEq, Eq, Hash, Clone)] struct FaceVertex { pos_index: u32, normal_index: u32, } #[derive(Debug)] struct SidePositions { up_left: Vec3, up_right: Vec3, down_left: Vec3, down_right: Vec3, normal_calc_order: NormalCalcOrder, } 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, } } 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, } } 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, } } 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, } } 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, } } 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, } } } #[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, }, ], }); // 🮟 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, }, ], }); }