diff --git a/README.md b/README.md index 3222063..fffae6b 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,9 @@ The Goal of this repo is to make a simple engine like program in Rust It is mainly for learning purposes and should be the base for a later, more fleshed out Engine made in Rust (with the possibility to create logic in Julia) + + + + +The engine uses a left-handed y-up coordinate system + diff --git a/opengl_beginnings/.idea/vcs.xml b/opengl_beginnings/.idea/vcs.xml index 35eb1dd..62bd7a0 100644 --- a/opengl_beginnings/.idea/vcs.xml +++ b/opengl_beginnings/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/opengl_beginnings/src/custom.rs b/opengl_beginnings/src/custom.rs new file mode 100644 index 0000000..ddf41e4 --- /dev/null +++ b/opengl_beginnings/src/custom.rs @@ -0,0 +1 @@ +pub mod sphere_generator; \ No newline at end of file diff --git a/opengl_beginnings/src/custom/sphere_generator.rs b/opengl_beginnings/src/custom/sphere_generator.rs new file mode 100644 index 0000000..2405cf5 --- /dev/null +++ b/opengl_beginnings/src/custom/sphere_generator.rs @@ -0,0 +1,154 @@ +use crate::engine::mesh::Mesh; +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 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.copy_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}}} \ No newline at end of file diff --git a/opengl_beginnings/src/engine.rs b/opengl_beginnings/src/engine.rs index 7fc22f7..71e31a8 100644 --- a/opengl_beginnings/src/engine.rs +++ b/opengl_beginnings/src/engine.rs @@ -1,5 +1,5 @@ pub mod nebulix; pub mod time; pub mod input; -mod shader; -mod mesh; +pub mod shader; +pub mod mesh; diff --git a/opengl_beginnings/src/engine/mesh.rs b/opengl_beginnings/src/engine/mesh.rs index 39fdb54..86cd0c8 100644 --- a/opengl_beginnings/src/engine/mesh.rs +++ b/opengl_beginnings/src/engine/mesh.rs @@ -6,7 +6,7 @@ use nalgebra::Vector3; // Down the line the MeshRenderer should have a material. The material then holds the data that is needed by the shader (so the actual texture or data for variables inside the shader) // End Goal: Shader belongs to Material; Material belongs to MeshRenderer; Mesh belongs to MeshRenderer; MeshRenderer renders the mesh using the shader and data provided by the material -struct Mesh { +pub struct Mesh { vertices: Vec>, normals: Vec>, indices: Vec, diff --git a/opengl_beginnings/src/engine/nebulix.rs b/opengl_beginnings/src/engine/nebulix.rs index f6cd092..2349467 100644 --- a/opengl_beginnings/src/engine/nebulix.rs +++ b/opengl_beginnings/src/engine/nebulix.rs @@ -136,11 +136,6 @@ impl ApplicationHandler for Nebulix { } // Custom code HERE - // TODO: Next step is: - // - dynamically generate a mesh - // - use a shader to render it - // - and fly around in the scene - // After I confirmed that the above is working -> add textures gl_surface.swap_buffers(&gl_context).unwrap(); self.window.as_ref().unwrap().request_redraw(); diff --git a/opengl_beginnings/src/main.rs b/opengl_beginnings/src/main.rs index 6aaaa85..eef769f 100644 --- a/opengl_beginnings/src/main.rs +++ b/opengl_beginnings/src/main.rs @@ -4,7 +4,9 @@ use winit::event_loop::{ControlFlow, EventLoop}; mod camera; mod engine; +mod custom; +// TODO: This should actually not be needed because of the Type "Unit" from nalgebra! trait Extension { fn normalize_checked(&self) -> Self; fn normalize_checked_mut(&mut self);