use crate::mesh::{Indices, Mesh};
use hexasphere::shapes::IcoSphere;
use thiserror::Error;
use wgpu::PrimitiveTopology;
#[derive(Debug, Clone, Copy)]
pub struct Icosphere {
pub radius: f32,
pub subdivisions: usize,
}
impl Default for Icosphere {
fn default() -> Self {
Self {
radius: 1.0,
subdivisions: 5,
}
}
}
#[derive(Debug, Clone, Error)]
pub enum FromIcosphereError {
#[error("Cannot create an icosphere of {subdivisions} subdivisions due to there being too many vertices being generated: {number_of_resulting_points}. (Limited to 65535 vertices or 79 subdivisions)")]
TooManyVertices {
subdivisions: usize,
number_of_resulting_points: usize,
},
}
impl TryFrom<Icosphere> for Mesh {
type Error = FromIcosphereError;
fn try_from(sphere: Icosphere) -> Result<Self, Self::Error> {
if sphere.subdivisions >= 80 {
let temp = sphere.subdivisions + 1;
let number_of_resulting_points = temp * temp * 10 + 2;
return Err(FromIcosphereError::TooManyVertices {
subdivisions: sphere.subdivisions,
number_of_resulting_points,
});
}
let generated = IcoSphere::new(sphere.subdivisions, |point| {
let inclination = point.y.acos();
let azimuth = point.z.atan2(point.x);
let norm_inclination = inclination / std::f32::consts::PI;
let norm_azimuth = 0.5 - (azimuth / std::f32::consts::TAU);
[norm_azimuth, norm_inclination]
});
let raw_points = generated.raw_points();
let points = raw_points
.iter()
.map(|&p| (p * sphere.radius).into())
.collect::<Vec<[f32; 3]>>();
let normals = raw_points
.iter()
.copied()
.map(Into::into)
.collect::<Vec<[f32; 3]>>();
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);
Ok(Mesh::new(PrimitiveTopology::TriangleList)
.with_indices(Some(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, points)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs))
}
}