use super::{Indices, Mesh}; use crate::pipeline::PrimitiveTopology; use bevy_math::*; use hexasphere::shapes::IcoSphere; pub struct Cube { pub size: f32, } impl Cube { pub fn new(size: f32) -> Cube { Cube { size } } } impl Default for Cube { fn default() -> Self { Cube { size: 1.0 } } } impl From for Mesh { fn from(cube: Cube) -> Self { Box::new(cube.size, cube.size, cube.size).into() } } pub struct Box { pub min_x: f32, pub max_x: f32, pub min_y: f32, pub max_y: f32, pub min_z: f32, pub max_z: f32, } impl Box { pub fn new(x_length: f32, y_length: f32, z_length: f32) -> Box { Box { max_x: x_length / 2.0, min_x: -x_length / 2.0, max_y: y_length / 2.0, min_y: -y_length / 2.0, max_z: z_length / 2.0, min_z: -z_length / 2.0, } } } impl Default for Box { fn default() -> Self { Box::new(2.0, 1.0, 1.0) } } impl From for Mesh { fn from(sp: Box) -> Self { let vertices = &[ // Top ([sp.min_x, sp.min_y, sp.max_z], [0., 0., 1.0], [0., 0.]), ([sp.max_x, sp.min_y, sp.max_z], [0., 0., 1.0], [1.0, 0.]), ([sp.max_x, sp.max_y, sp.max_z], [0., 0., 1.0], [1.0, 1.0]), ([sp.min_x, sp.max_y, sp.max_z], [0., 0., 1.0], [0., 1.0]), // Bottom ([sp.min_x, sp.max_y, sp.min_z], [0., 0., -1.0], [1.0, 0.]), ([sp.max_x, sp.max_y, sp.min_z], [0., 0., -1.0], [0., 0.]), ([sp.max_x, sp.min_y, sp.min_z], [0., 0., -1.0], [0., 1.0]), ([sp.min_x, sp.min_y, sp.min_z], [0., 0., -1.0], [1.0, 1.0]), // Right ([sp.max_x, sp.min_y, sp.min_z], [1.0, 0., 0.], [0., 0.]), ([sp.max_x, sp.max_y, sp.min_z], [1.0, 0., 0.], [1.0, 0.]), ([sp.max_x, sp.max_y, sp.max_z], [1.0, 0., 0.], [1.0, 1.0]), ([sp.max_x, sp.min_y, sp.max_z], [1.0, 0., 0.], [0., 1.0]), // Left ([sp.min_x, sp.min_y, sp.max_z], [-1.0, 0., 0.], [1.0, 0.]), ([sp.min_x, sp.max_y, sp.max_z], [-1.0, 0., 0.], [0., 0.]), ([sp.min_x, sp.max_y, sp.min_z], [-1.0, 0., 0.], [0., 1.0]), ([sp.min_x, sp.min_y, sp.min_z], [-1.0, 0., 0.], [1.0, 1.0]), // Front ([sp.max_x, sp.max_y, sp.min_z], [0., 1.0, 0.], [1.0, 0.]), ([sp.min_x, sp.max_y, sp.min_z], [0., 1.0, 0.], [0., 0.]), ([sp.min_x, sp.max_y, sp.max_z], [0., 1.0, 0.], [0., 1.0]), ([sp.max_x, sp.max_y, sp.max_z], [0., 1.0, 0.], [1.0, 1.0]), // Back ([sp.max_x, sp.min_y, sp.max_z], [0., -1.0, 0.], [0., 0.]), ([sp.min_x, sp.min_y, sp.max_z], [0., -1.0, 0.], [1.0, 0.]), ([sp.min_x, sp.min_y, sp.min_z], [0., -1.0, 0.], [1.0, 1.0]), ([sp.max_x, sp.min_y, sp.min_z], [0., -1.0, 0.], [0., 1.0]), ]; let mut positions = Vec::with_capacity(24); let mut normals = Vec::with_capacity(24); let mut uvs = Vec::with_capacity(24); for (position, normal, uv) in vertices.iter() { positions.push(*position); normals.push(*normal); uvs.push(*uv); } let indices = Indices::U32(vec![ 0, 1, 2, 2, 3, 0, // top 4, 5, 6, 6, 7, 4, // bottom 8, 9, 10, 10, 11, 8, // right 12, 13, 14, 14, 15, 12, // left 16, 17, 18, 18, 19, 16, // front 20, 21, 22, 22, 23, 20, // back ]); let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh.set_indices(Some(indices)); mesh } } /// A rectangle on the XY plane. #[derive(Debug)] pub struct Quad { /// Full width and height of the rectangle. pub size: Vec2, /// Flips the texture coords of the resulting vertices. pub flip: bool, } impl Quad { pub fn new(size: Vec2) -> Self { Self { size, flip: false } } pub fn flipped(size: Vec2) -> Self { Self { size, flip: true } } } impl From for Mesh { fn from(quad: Quad) -> Self { let extent_x = quad.size.x / 2.0; let extent_y = quad.size.y / 2.0; let north_west = vec2(-extent_x, extent_y); let north_east = vec2(extent_x, extent_y); let south_west = vec2(-extent_x, -extent_y); let south_east = vec2(extent_x, -extent_y); let vertices = if quad.flip { [ ( [south_east.x, south_east.y, 0.0], [0.0, 0.0, 1.0], [1.0, 1.0], ), ( [north_east.x, north_east.y, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0], ), ( [north_west.x, north_west.y, 0.0], [0.0, 0.0, 1.0], [0.0, 0.0], ), ( [south_west.x, south_west.y, 0.0], [0.0, 0.0, 1.0], [0.0, 1.0], ), ] } else { [ ( [south_west.x, south_west.y, 0.0], [0.0, 0.0, 1.0], [0.0, 1.0], ), ( [north_west.x, north_west.y, 0.0], [0.0, 0.0, 1.0], [0.0, 0.0], ), ( [north_east.x, north_east.y, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0], ), ( [south_east.x, south_east.y, 0.0], [0.0, 0.0, 1.0], [1.0, 1.0], ), ] }; let indices = Indices::U32(vec![0, 2, 1, 0, 3, 2]); let mut positions = Vec::<[f32; 3]>::new(); let mut normals = Vec::<[f32; 3]>::new(); let mut uvs = Vec::<[f32; 2]>::new(); for (position, normal, uv) in vertices.iter() { positions.push(*position); normals.push(*normal); uvs.push(*uv); } let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.set_indices(Some(indices)); mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh } } /// A square on the XZ plane. #[derive(Debug)] pub struct Plane { /// The total side length of the square. pub size: f32, } impl From for Mesh { fn from(plane: Plane) -> Self { let extent = plane.size / 2.0; let vertices = [ ([extent, 0.0, -extent], [0.0, 1.0, 0.0], [1.0, 1.0]), ([extent, 0.0, extent], [0.0, 1.0, 0.0], [1.0, 0.0]), ([-extent, 0.0, extent], [0.0, 1.0, 0.0], [0.0, 0.0]), ([-extent, 0.0, -extent], [0.0, 1.0, 0.0], [0.0, 1.0]), ]; let indices = Indices::U32(vec![0, 2, 1, 0, 3, 2]); let mut positions = Vec::new(); let mut normals = Vec::new(); let mut uvs = Vec::new(); for (position, normal, uv) in vertices.iter() { positions.push(*position); normals.push(*normal); uvs.push(*uv); } let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.set_indices(Some(indices)); mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh } } /// A sphere made from a subdivided Icosahedron. #[derive(Debug)] pub struct Icosphere { /// The radius of the sphere. pub radius: f32, /// The number of subdivisions applied. pub subdivisions: usize, } impl Default for Icosphere { fn default() -> Self { Self { radius: 1.0, subdivisions: 5, } } } impl From for Mesh { fn from(sphere: Icosphere) -> Self { if sphere.subdivisions >= 80 { // https://oeis.org/A005901 let subdivisions = sphere.subdivisions + 1; let number_of_resulting_points = (subdivisions * subdivisions * 10) + 2; panic!( "Cannot create an icosphere of {} subdivisions due to there being too many vertices being generated: {}. (Limited to 65535 vertices or 79 subdivisions)", sphere.subdivisions, number_of_resulting_points ); } let generated = IcoSphere::new(sphere.subdivisions, |point| { let inclination = point.z.acos(); let azumith = point.y.atan2(point.x); let norm_inclination = 1.0 - (inclination / std::f32::consts::PI); let norm_azumith = (azumith / std::f32::consts::PI) * 0.5; [norm_inclination, norm_azumith] }); let raw_points = generated.raw_points(); let points = raw_points .iter() .map(|&p| (p * sphere.radius).into()) .collect::>(); let normals = raw_points .iter() .copied() .map(Into::into) .collect::>(); let uvs = generated.raw_data().to_owned(); let mut indices = Vec::with_capacity(generated.indices_per_main_triangle() * 20); for i in 0..20 { generated.get_indices(i, &mut indices); } let indices = Indices::U32(indices); let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.set_indices(Some(indices)); mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, points); mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh } }