163 lines
6.3 KiB
Rust
163 lines
6.3 KiB
Rust
use crate::engine::mesh::Mesh;
|
|
use glow::Context;
|
|
use nalgebra::Vector3;
|
|
|
|
pub struct OctaSphere {
|
|
mesh: Option<Mesh>,
|
|
resolution: i32,
|
|
generator: OctaSphereGenerator,
|
|
}
|
|
|
|
impl OctaSphere {
|
|
pub fn new(resolution: i32) -> Result<Self, String> {
|
|
match resolution {
|
|
r if r >= 0 => Ok(Self
|
|
{
|
|
resolution,
|
|
generator: OctaSphereGenerator::new(resolution),
|
|
mesh: None,
|
|
}),
|
|
_ => Err(String::from("Resolution of an octa sphere must be positive"))
|
|
}
|
|
}
|
|
|
|
pub fn render(&mut self, gl: &Context) {
|
|
match self.mesh.as_mut() {
|
|
None => {self.generate_sphere(); self.render(gl)},
|
|
Some(mut mesh) => {mesh.render(gl)}
|
|
}
|
|
}
|
|
|
|
fn generate_sphere(&mut self) {
|
|
let (vertices, indices) = self.generator.generate_sphere();
|
|
self.mesh = Some(Mesh::new(vertices, indices))
|
|
}
|
|
}
|
|
|
|
struct OctaSphereGenerator {
|
|
resolution: i32,
|
|
}
|
|
|
|
impl OctaSphereGenerator {
|
|
fn new(resolution: i32) -> Self {
|
|
Self {
|
|
resolution,
|
|
}
|
|
}
|
|
|
|
// Thanks Sebastian Lague
|
|
fn generate_sphere(&self) -> (Vec<Vector3<f32>>, Vec<u32>) {
|
|
// Indices of the vertex pairs that make up each of the initial 12 edges
|
|
let vertex_pairs = [0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 2, 3, 3, 4, 4, 1, 5, 1, 5, 2, 5, 3, 5, 4];
|
|
// Indices of the edge triplets that make up the initial 8 faces
|
|
let edge_triplets = [0, 1, 4, 1, 2, 5, 2, 3, 6, 3, 0, 7, 8, 9, 4, 9, 10, 5, 10, 11, 6, 11, 8, 7];
|
|
// The six initial vertices up left back right forward down
|
|
let base_vertices = [Vector3::y_axis().into_inner(), -Vector3::x_axis().into_inner(), -Vector3::z_axis().into_inner(), Vector3::x_axis().into_inner(), Vector3::z_axis().into_inner(), -Vector3::y_axis().into_inner()];
|
|
|
|
let num_divisions = self.resolution;
|
|
let num_vertices_per_face = ((num_divisions + 3) * (num_divisions + 3) - (num_divisions + 3)) / 2;
|
|
let num_vertices = num_vertices_per_face * 8 - (num_divisions + 2) * 12 + 6;
|
|
let num_tris_per_face = (num_divisions + 1) * (num_divisions + 1);
|
|
|
|
|
|
let mut vertices = Vec::<Vector3<f32>>::with_capacity(num_vertices as usize);
|
|
let mut indicies = Vec::<u32>::with_capacity((num_tris_per_face * 8 * 3) as usize);
|
|
vertices.extend_from_slice(&base_vertices);
|
|
|
|
// Create 12 edges, with n vertices added along them (n = numDivisions)
|
|
let mut edges = Vec::<Edge>::with_capacity(12);
|
|
for i in (0..vertex_pairs.len()).step_by(2) {
|
|
let start_vertex = vertices[vertex_pairs[i]];
|
|
let end_vertex = vertices[vertex_pairs[i + 1]];
|
|
|
|
let mut edge_vertex_indices = Vec::<u32>::with_capacity((num_divisions + 2) as usize);
|
|
edge_vertex_indices[0] = vertex_pairs[i] as u32;
|
|
|
|
// Add vertices along edge
|
|
for division_index in 0..num_divisions {
|
|
let t = (division_index as f32 + 1.0) / (num_divisions as f32 + 1.0);
|
|
edge_vertex_indices[(division_index + 1) as usize] = vertices.len() as u32;
|
|
vertices.push(start_vertex.slerp(&end_vertex, t));
|
|
}
|
|
|
|
edge_vertex_indices[(num_divisions + 1) as usize] = vertex_pairs[i + 1] as u32;
|
|
let edge_index = i / 2;
|
|
edges[edge_index] = Edge::new(edge_vertex_indices)
|
|
}
|
|
|
|
// Create faces
|
|
for i in (0..edge_triplets.len()).step_by(3) {
|
|
let face_index = i / 3;
|
|
let reverse = face_index >= 4;
|
|
self.create_face(&edges[edge_triplets[i]], &edges[edge_triplets[i + 1]], &edges[edge_triplets[i + 2]], reverse, num_vertices_per_face, &mut vertices, &mut indicies);
|
|
}
|
|
|
|
|
|
(vertices, indicies)
|
|
}
|
|
|
|
fn create_face(&self, side_a: &Edge, side_b: &Edge, bottom: &Edge, reverse: bool, num_vertices_per_face: i32, vertices: &mut Vec<Vector3<f32>>, indices: &mut Vec<u32>) {
|
|
let num_points_in_edge = side_a.vertex_indices.len();
|
|
let mut vertex_map = Vec::<u32>::with_capacity(num_vertices_per_face as usize);
|
|
vertex_map.push(side_a.vertex_indices[0]); // top of triangle
|
|
|
|
for i in 1..num_points_in_edge-1 {
|
|
// Side A vertex
|
|
vertex_map.push(side_a.vertex_indices[i]);
|
|
|
|
//add vertices between side a and b
|
|
let side_a_vertex = vertices[(side_a.vertex_indices[i]) as usize];
|
|
let side_b_vertex = vertices[(side_b.vertex_indices[i]) as usize];
|
|
let num_inner_points = i - 1;
|
|
for j in 0..num_inner_points {
|
|
let t = (j as f32 + 1.0) / (num_inner_points as f32 + 1.0);
|
|
vertex_map.push(vertices.len() as u32);
|
|
vertices.push(side_a_vertex.slerp(&side_b_vertex, t));
|
|
}
|
|
|
|
// Side B vertex
|
|
vertex_map.push(side_b.vertex_indices[i]);
|
|
}
|
|
|
|
// add bottom edge vertices
|
|
for i in 0..num_points_in_edge {
|
|
vertex_map.push(bottom.vertex_indices[i]);
|
|
}
|
|
|
|
// Triangulate
|
|
let num_rows = self.resolution + 1;
|
|
for row in 0..num_rows {
|
|
// vertices down left edge follow quadratic sequence: 0, 1, 3, 6, 10, 15...
|
|
// the nth term can be calculated with: (n^2 - n)/2
|
|
let top_vertex = (((row + 1) * (row + 1) - row - 1) / 2) as usize;
|
|
let bottom_vertex = (((row + 2) * (row + 2) + row - 2) / 2) as usize;
|
|
|
|
let num_triangles_in_row = 1 + 2 * row;
|
|
for column in 0..num_triangles_in_row {
|
|
let v0;
|
|
let v1;
|
|
let v2;
|
|
|
|
if column % 2 == 0 {
|
|
v0 = top_vertex;
|
|
v1 = bottom_vertex + 1;
|
|
v2 = bottom_vertex;
|
|
} else {
|
|
v0 = top_vertex;
|
|
v1 = bottom_vertex;
|
|
v2 = top_vertex + 1;
|
|
}
|
|
|
|
indices.push(vertex_map[v0]);
|
|
indices.push(vertex_map[if reverse {v2} else {v1}]);
|
|
indices.push(vertex_map[if reverse {v1} else {v2}]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Edge {
|
|
vertex_indices: Vec<u32>,
|
|
}
|
|
|
|
impl Edge {fn new(vertex_indices: Vec<u32>) -> Self {Self{vertex_indices}}} |