use crate::engine::mesh::Mesh; use glow::Context; use nalgebra::Vector3; pub struct OctaSphere { mesh: Option, resolution: i32, generator: OctaSphereGenerator, } impl OctaSphere { pub fn new(resolution: i32) -> Result { 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>, Vec) { // 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::>::with_capacity(num_vertices as usize); let mut indicies = Vec::::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::::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::::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>, indices: &mut Vec) { let num_points_in_edge = side_a.vertex_indices.len(); let mut vertex_map = Vec::::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, } impl Edge {fn new(vertex_indices: Vec) -> Self {Self{vertex_indices}}}