use crate::math::{Vec2, Vec3};
use crate::mesh::{Mesh, MeshData, Vertex};
use crate::{Assets, Catalog};
const HALF: f32 = 0.5;
const QUAD_INDICES: [u32; 6] = [0, 1, 2, 0, 2, 3];
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Cube;
impl Catalog for Cube {
fn catalog() -> Vec<Self> {
vec![Self]
}
}
impl Mesh for Cube {
fn build(&self, _assets: &Assets) -> MeshData {
let faces = [
(Vec3::X, Vec3::NEG_Z, Vec3::Y),
(Vec3::NEG_X, Vec3::Z, Vec3::Y),
(Vec3::Y, Vec3::X, Vec3::NEG_Z),
(Vec3::NEG_Y, Vec3::X, Vec3::Z),
(Vec3::Z, Vec3::X, Vec3::Y),
(Vec3::NEG_Z, Vec3::NEG_X, Vec3::Y),
];
let vertices = faces
.iter()
.flat_map(|&(normal, right, up)| square(normal * HALF, normal, right, up))
.collect();
let indices = (0..faces.len() as u32)
.flat_map(|face| QUAD_INDICES.map(|index| face * 4 + index))
.collect();
MeshData::new(vertices, indices)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Plane;
impl Catalog for Plane {
fn catalog() -> Vec<Self> {
vec![Self]
}
}
impl Mesh for Plane {
fn build(&self, _assets: &Assets) -> MeshData {
square_mesh(Vec3::Y, Vec3::X, Vec3::NEG_Z)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Quad;
impl Catalog for Quad {
fn catalog() -> Vec<Self> {
vec![Self]
}
}
impl Mesh for Quad {
fn build(&self, _assets: &Assets) -> MeshData {
square_mesh(Vec3::Z, Vec3::X, Vec3::Y)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Sphere {
pub subdivisions: u32,
}
impl Catalog for Sphere {
fn catalog() -> Vec<Self> {
(0..=3).map(|subdivisions| Self { subdivisions }).collect()
}
}
impl Mesh for Sphere {
fn build(&self, _assets: &Assets) -> MeshData {
let segments = 4 * (self.subdivisions + 1);
let rings = 2 * (self.subdivisions + 1);
let mut vertices = Vec::with_capacity(((rings + 1) * (segments + 1)) as usize);
for ring in 0..=rings {
let latitude = core::f32::consts::PI * ring as f32 / rings as f32;
let (radius, height) = latitude.sin_cos();
for segment in 0..=segments {
let longitude = core::f32::consts::TAU * segment as f32 / segments as f32;
let (ahead, right) = longitude.sin_cos();
let normal = Vec3::new(radius * right, height, radius * ahead);
vertices.push(Vertex::new(
normal * HALF,
normal,
Vec2::new(segment as f32 / segments as f32, ring as f32 / rings as f32),
));
}
}
let corner = |ring: u32, segment: u32| ring * (segments + 1) + segment;
let mut indices = Vec::with_capacity((rings * segments * 6) as usize);
for ring in 0..rings {
for segment in 0..segments {
let (here, next) = (corner(ring, segment), corner(ring, segment + 1));
let (under, under_next) =
(corner(ring + 1, segment), corner(ring + 1, segment + 1));
let touches_north_pole = ring == 0;
let touches_south_pole = ring + 1 == rings;
if !touches_north_pole {
indices.extend([here, next, under_next]);
}
if !touches_south_pole {
indices.extend([here, under_next, under]);
}
}
}
MeshData::new(vertices, indices)
}
}
fn square(center: Vec3, normal: Vec3, right: Vec3, up: Vec3) -> [Vertex; 4] {
let (right, up) = (right * HALF, up * HALF);
[
Vertex::new(center - right - up, normal, Vec2::new(0.0, 1.0)),
Vertex::new(center + right - up, normal, Vec2::new(1.0, 1.0)),
Vertex::new(center + right + up, normal, Vec2::new(1.0, 0.0)),
Vertex::new(center - right + up, normal, Vec2::new(0.0, 0.0)),
]
}
fn square_mesh(normal: Vec3, right: Vec3, up: Vec3) -> MeshData {
MeshData::new(
square(Vec3::ZERO, normal, right, up).to_vec(),
QUAD_INDICES.to_vec(),
)
}