use crate::math::calc_triangle_surface_normal; use crate::mesh::Mesh; use crate::util::builder; use crate::vector::Vec3; use crate::vertex::{Builder as VertexBuilder, Vertex}; 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() } } #[derive(Debug)] pub enum Side { Front, Back, Left, Right, Top, Bottom, } #[derive(Debug)] pub enum Corner { TopRight, TopLeft, BottomRight, BottomLeft, } /// Creates a cube mesh. pub fn create( creation_spec: CreationSpec, vertex_builder_cb: impl Fn(VertexBuilder, Side, Corner) -> VertexBuilder, ) -> Mesh { let mut vertices = [const { None }; VertexIndex::VARIANT_CNT]; create_front(&creation_spec, &mut vertices, &vertex_builder_cb); create_back(&creation_spec, &mut vertices, &vertex_builder_cb); create_right(&creation_spec, &mut vertices, &vertex_builder_cb); create_left(&creation_spec, &mut vertices, &vertex_builder_cb); create_top(&creation_spec, &mut vertices, &vertex_builder_cb); create_bottom(&creation_spec, &mut vertices, &vertex_builder_cb); let front = [ // 🮝 VertexIndex::FrontTopRight, VertexIndex::FrontBottomRight, VertexIndex::FrontTopLeft, // // 🮟 VertexIndex::FrontBottomRight, VertexIndex::FrontBottomLeft, VertexIndex::FrontTopLeft, ]; let back = [ // 🮝 VertexIndex::BackTopRight, VertexIndex::BackBottomRight, VertexIndex::BackTopLeft, // // 🮟 VertexIndex::BackBottomRight, VertexIndex::BackBottomLeft, VertexIndex::BackTopLeft, ]; let right = [ // 🮝 VertexIndex::RightBackTop, VertexIndex::RightBackBottom, VertexIndex::RightFrontTop, // // 🮟 VertexIndex::RightBackBottom, VertexIndex::RightFrontBottom, VertexIndex::RightFrontTop, ]; let left = [ // 🮝 VertexIndex::LeftBackTop, VertexIndex::LeftBackBottom, VertexIndex::LeftFrontTop, // // 🮟 VertexIndex::LeftBackBottom, VertexIndex::LeftFrontBottom, VertexIndex::LeftFrontTop, ]; let top = [ // 🮝 VertexIndex::TopBackRight, VertexIndex::TopBackLeft, VertexIndex::TopFrontRight, // // 🮟 VertexIndex::TopBackLeft, VertexIndex::TopFrontLeft, VertexIndex::TopFrontRight, ]; let bottom = [ // 🮝 VertexIndex::BottomBackRight, VertexIndex::BottomBackLeft, VertexIndex::BottomFrontRight, // // 🮟 VertexIndex::BottomBackLeft, VertexIndex::BottomFrontLeft, VertexIndex::BottomFrontRight, ]; let indices = [front, back, right, left, top, bottom]; Mesh::new( vertices.map(Option::unwrap).to_vec(), Some( indices .into_iter() .flatten() .map(|index| index as u32) .collect(), ), ) } macro_rules! one { ($tt: tt) => { 1 }; } macro_rules! enum_with_variant_cnt { ( $(#[$attr: meta])* enum $name: ident { $($variant: ident,)* } ) => { $(#[$attr])* enum $name { $($variant,)* } impl $name { const VARIANT_CNT: usize = 0 $(+ one!($variant))*; } }; } enum_with_variant_cnt! { #[repr(u32)] enum VertexIndex { FrontTopRight, FrontBottomRight, FrontBottomLeft, FrontTopLeft, BackTopRight, BackBottomRight, BackBottomLeft, BackTopLeft, RightBackTop, RightBackBottom, RightFrontTop, RightFrontBottom, LeftBackTop, LeftBackBottom, LeftFrontTop, LeftFrontBottom, TopBackRight, TopBackLeft, TopFrontRight, TopFrontLeft, BottomBackRight, BottomBackLeft, BottomFrontRight, BottomFrontLeft, } } fn create_front( creation_spec: &CreationSpec, vertices: &mut [Option], vertex_builder_cb: &impl Fn(VertexBuilder, Side, Corner) -> VertexBuilder, ) { let front_top_right_pos = Vec3 { x: creation_spec.width / 2.0, y: creation_spec.height / 2.0, z: -(creation_spec.depth / 2.0), }; let front_bottom_right_pos = Vec3 { x: creation_spec.width / 2.0, y: -(creation_spec.height / 2.0), z: -(creation_spec.depth / 2.0), }; let front_bottom_left_pos = Vec3 { x: -(creation_spec.width / 2.0), y: -(creation_spec.height / 2.0), z: -(creation_spec.depth / 2.0), }; let front_top_left_pos = Vec3 { x: -(creation_spec.width / 2.0), y: creation_spec.height / 2.0, z: -(creation_spec.depth / 2.0), }; let front_normal = calc_triangle_surface_normal( &front_top_right_pos, &front_bottom_right_pos, &front_top_left_pos, ); vertices[VertexIndex::FrontTopRight as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(front_top_right_pos) .normal(front_normal), Side::Front, Corner::TopRight, ) .build(), ); vertices[VertexIndex::FrontBottomRight as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(front_bottom_right_pos) .normal(front_normal), Side::Front, Corner::BottomRight, ) .build(), ); vertices[VertexIndex::FrontBottomLeft as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(front_bottom_left_pos) .normal(front_normal), Side::Front, Corner::BottomLeft, ) .build(), ); vertices[VertexIndex::FrontTopLeft as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(front_top_left_pos) .normal(front_normal), Side::Front, Corner::TopLeft, ) .build(), ); } fn create_back( creation_spec: &CreationSpec, vertices: &mut [Option], vertex_builder_cb: &impl Fn(VertexBuilder, Side, Corner) -> VertexBuilder, ) { let back_top_right_pos = Vec3 { x: creation_spec.width / 2.0, y: creation_spec.height / 2.0, z: creation_spec.depth / 2.0, }; let back_bottom_right_pos = Vec3 { x: creation_spec.width / 2.0, y: -(creation_spec.height / 2.0), z: creation_spec.depth / 2.0, }; let back_bottom_left_pos = Vec3 { x: -(creation_spec.width / 2.0), y: -(creation_spec.height / 2.0), z: creation_spec.depth / 2.0, }; let back_top_left_pos = Vec3 { x: -(creation_spec.width / 2.0), y: creation_spec.height / 2.0, z: creation_spec.depth / 2.0, }; let back_normal = -calc_triangle_surface_normal( &back_top_right_pos, &back_bottom_right_pos, &back_top_left_pos, ); vertices[VertexIndex::BackTopRight as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(back_top_right_pos) .normal(back_normal), Side::Back, Corner::TopRight, ) .build(), ); vertices[VertexIndex::BackBottomRight as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(back_bottom_right_pos) .normal(back_normal), Side::Back, Corner::BottomRight, ) .build(), ); vertices[VertexIndex::BackBottomLeft as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(back_bottom_left_pos) .normal(back_normal), Side::Back, Corner::BottomLeft, ) .build(), ); vertices[VertexIndex::BackTopLeft as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(back_top_left_pos) .normal(back_normal), Side::Back, Corner::TopLeft, ) .build(), ); } fn create_right( creation_spec: &CreationSpec, vertices: &mut [Option], vertex_builder_cb: &impl Fn(VertexBuilder, Side, Corner) -> VertexBuilder, ) { let right_back_top_pos = Vec3 { x: creation_spec.width / 2.0, y: creation_spec.height / 2.0, z: creation_spec.depth / 2.0, }; let right_back_bottom_pos = Vec3 { x: creation_spec.width / 2.0, y: -(creation_spec.height / 2.0), z: creation_spec.depth / 2.0, }; let right_front_top_pos = Vec3 { x: creation_spec.width / 2.0, y: creation_spec.height / 2.0, z: -(creation_spec.depth / 2.0), }; let right_front_bottom_pos = Vec3 { x: creation_spec.width / 2.0, y: -(creation_spec.height / 2.0), z: -(creation_spec.depth / 2.0), }; let right_normal = calc_triangle_surface_normal( &right_back_top_pos, &right_back_bottom_pos, &right_front_top_pos, ); vertices[VertexIndex::RightBackTop as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(right_back_top_pos) .normal(right_normal), Side::Right, Corner::TopLeft, ) .build(), ); vertices[VertexIndex::RightBackBottom as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(right_back_bottom_pos) .normal(right_normal), Side::Right, Corner::BottomLeft, ) .build(), ); vertices[VertexIndex::RightFrontTop as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(right_front_top_pos) .normal(right_normal), Side::Right, Corner::TopRight, ) .build(), ); vertices[VertexIndex::RightFrontBottom as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(right_front_bottom_pos) .normal(right_normal), Side::Right, Corner::BottomRight, ) .build(), ); } fn create_left( creation_spec: &CreationSpec, vertices: &mut [Option], vertex_builder_cb: &impl Fn(VertexBuilder, Side, Corner) -> VertexBuilder, ) { let left_back_top_pos = Vec3 { x: -(creation_spec.width / 2.0), y: creation_spec.height / 2.0, z: creation_spec.depth / 2.0, }; let left_back_bottom_pos = Vec3 { x: -(creation_spec.width / 2.0), y: -(creation_spec.height / 2.0), z: creation_spec.depth / 2.0, }; let left_front_top_pos = Vec3 { x: -(creation_spec.width / 2.0), y: creation_spec.height / 2.0, z: -(creation_spec.depth / 2.0), }; let left_front_bottom_pos = Vec3 { x: -(creation_spec.width / 2.0), y: -(creation_spec.height / 2.0), z: -(creation_spec.depth / 2.0), }; let left_normal = -calc_triangle_surface_normal( &left_back_top_pos, &left_back_bottom_pos, &left_front_top_pos, ); vertices[VertexIndex::LeftBackTop as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(left_back_top_pos) .normal(left_normal), Side::Left, Corner::TopRight, ) .build(), ); vertices[VertexIndex::LeftBackBottom as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(left_back_bottom_pos) .normal(left_normal), Side::Left, Corner::BottomRight, ) .build(), ); vertices[VertexIndex::LeftFrontTop as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(left_front_top_pos) .normal(left_normal), Side::Left, Corner::TopLeft, ) .build(), ); vertices[VertexIndex::LeftFrontBottom as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(left_front_bottom_pos) .normal(left_normal), Side::Left, Corner::BottomLeft, ) .build(), ); } fn create_top( creation_spec: &CreationSpec, vertices: &mut [Option], vertex_builder_cb: &impl Fn(VertexBuilder, Side, Corner) -> VertexBuilder, ) { let top_back_right_pos = Vec3 { x: creation_spec.width / 2.0, y: creation_spec.height / 2.0, z: creation_spec.depth / 2.0, }; let top_back_left_pos = Vec3 { x: -(creation_spec.width / 2.0), y: creation_spec.height / 2.0, z: creation_spec.depth / 2.0, }; let top_front_left_pos = Vec3 { x: -(creation_spec.width / 2.0), y: creation_spec.height / 2.0, z: -(creation_spec.depth / 2.0), }; let top_front_right_pos = Vec3 { x: creation_spec.width / 2.0, y: creation_spec.height / 2.0, z: -(creation_spec.depth / 2.0), }; let top_normal = -calc_triangle_surface_normal( &top_back_right_pos, &top_back_left_pos, &top_front_right_pos, ); vertices[VertexIndex::TopBackRight as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(top_back_right_pos) .normal(top_normal), Side::Top, Corner::TopRight, ) .build(), ); vertices[VertexIndex::TopBackLeft as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(top_back_left_pos) .normal(top_normal), Side::Top, Corner::TopLeft, ) .build(), ); vertices[VertexIndex::TopFrontLeft as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(top_front_left_pos) .normal(top_normal), Side::Top, Corner::BottomLeft, ) .build(), ); vertices[VertexIndex::TopFrontRight as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(top_front_right_pos) .normal(top_normal), Side::Top, Corner::BottomRight, ) .build(), ); } fn create_bottom( creation_spec: &CreationSpec, vertices: &mut [Option], vertex_builder_cb: &impl Fn(VertexBuilder, Side, Corner) -> VertexBuilder, ) { let bottom_back_right_pos = Vec3 { x: creation_spec.width / 2.0, y: -(creation_spec.height / 2.0), z: (creation_spec.depth / 2.0), }; let bottom_back_left_pos = Vec3 { x: -(creation_spec.width / 2.0), y: -(creation_spec.height / 2.0), z: creation_spec.depth / 2.0, }; let bottom_front_right_pos = Vec3 { x: creation_spec.width / 2.0, y: -(creation_spec.height / 2.0), z: -(creation_spec.depth / 2.0), }; let bottom_front_left_pos = Vec3 { x: -(creation_spec.width / 2.0), y: -(creation_spec.height / 2.0), z: -(creation_spec.depth / 2.0), }; let bottom_normal = calc_triangle_surface_normal( &bottom_back_right_pos, &bottom_back_left_pos, &bottom_front_right_pos, ); vertices[VertexIndex::BottomBackRight as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(bottom_back_right_pos) .normal(bottom_normal), Side::Bottom, Corner::BottomRight, ) .build(), ); vertices[VertexIndex::BottomBackLeft as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(bottom_back_left_pos) .normal(bottom_normal), Side::Bottom, Corner::BottomLeft, ) .build(), ); vertices[VertexIndex::BottomFrontRight as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(bottom_front_right_pos) .normal(bottom_normal), Side::Bottom, Corner::TopRight, ) .build(), ); vertices[VertexIndex::BottomFrontLeft as usize] = Some( vertex_builder_cb( VertexBuilder::default() .pos(bottom_front_left_pos) .normal(bottom_normal), Side::Bottom, Corner::TopLeft, ) .build(), ); }