use std::collections::HashMap;
use std::ops::{Add, Div, Mul, Neg, Sub};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Vec3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vec3 {
pub const ZERO: Vec3 = Vec3 { x: 0.0, y: 0.0, z: 0.0 };
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
pub fn dot(self, rhs: Vec3) -> f32 {
self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
}
pub fn cross(self, rhs: Vec3) -> Vec3 {
Vec3::new(
self.y * rhs.z - self.z * rhs.y,
self.z * rhs.x - self.x * rhs.z,
self.x * rhs.y - self.y * rhs.x,
)
}
pub fn length_squared(self) -> f32 {
self.dot(self)
}
pub fn length(self) -> f32 {
self.length_squared().sqrt()
}
pub fn normalize(self) -> Vec3 {
self / self.length()
}
}
impl Add<Vec3> for Vec3 {
type Output = Vec3;
fn add(self, rhs: Vec3) -> Vec3 {
Vec3::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
}
}
impl Sub<Vec3> for Vec3 {
type Output = Vec3;
fn sub(self, rhs: Vec3) -> Vec3 {
Vec3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
}
}
impl Mul<f32> for Vec3 {
type Output = Vec3;
fn mul(self, rhs: f32) -> Vec3 {
Vec3::new(self.x * rhs, self.y * rhs, self.z * rhs)
}
}
impl Div<f32> for Vec3 {
type Output = Vec3;
fn div(self, rhs: f32) -> Vec3 {
Vec3::new(self.x / rhs, self.y / rhs, self.z / rhs)
}
}
impl Neg for Vec3 {
type Output = Vec3;
fn neg(self) -> Vec3 {
Vec3::new(-self.x, -self.y, -self.z)
}
}
pub struct Mesh3D {
pub vertices: Vec<Vec3>,
pub faces: Vec<[u32; 3]>,
}
fn midpoint(vertices: &mut Vec<Vec3>, cache: &mut HashMap<(u32, u32), u32>, a: u32, b: u32) -> u32 {
let key = if a < b { (a, b) } else { (b, a) };
if let Some(&idx) = cache.get(&key) {
return idx;
}
let mid = ((vertices[a as usize] + vertices[b as usize]) * 0.5).normalize();
let idx = vertices.len() as u32;
vertices.push(mid);
cache.insert(key, idx);
idx
}
pub fn icosphere(subdivisions: u32) -> Mesh3D {
let t = (1.0 + 5f32.sqrt()) / 2.0;
let mut vertices: Vec<Vec3> = [
(-1.0, t, 0.0), (1.0, t, 0.0), (-1.0, -t, 0.0), (1.0, -t, 0.0),
(0.0, -1.0, t), (0.0, 1.0, t), (0.0, -1.0, -t), (0.0, 1.0, -t),
(t, 0.0, -1.0), (t, 0.0, 1.0), (-t, 0.0, -1.0), (-t, 0.0, 1.0),
]
.into_iter()
.map(|(x, y, z)| Vec3::new(x, y, z).normalize())
.collect();
let mut faces: Vec<[u32; 3]> = vec![
[0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11],
[1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8],
[3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9],
[4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1],
];
for _ in 0..subdivisions {
let mut cache: HashMap<(u32, u32), u32> = HashMap::new();
let mut next_faces = Vec::with_capacity(faces.len() * 4);
for [a, b, c] in faces {
let ab = midpoint(&mut vertices, &mut cache, a, b);
let bc = midpoint(&mut vertices, &mut cache, b, c);
let ca = midpoint(&mut vertices, &mut cache, c, a);
next_faces.push([a, ab, ca]);
next_faces.push([ab, b, bc]);
next_faces.push([ca, bc, c]);
next_faces.push([ab, bc, ca]);
}
faces = next_faces;
}
Mesh3D { vertices, faces }
}