diff --git a/README.md b/README.md new file mode 100644 index 0000000..4bda95f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +[![builds.sr.ht status](https://builds.sr.ht/~leinnan/doppler/commits/.build.yml.svg)](https://builds.sr.ht/~leinnan/doppler/commits/.build.yml?) + +My OpenGL "engine". WIP \ No newline at end of file diff --git a/src/gaia/bg_info.rs b/src/gaia/bg_info.rs index 9f17d84..966b857 100644 --- a/src/gaia/bg_info.rs +++ b/src/gaia/bg_info.rs @@ -4,7 +4,7 @@ use imgui_inspect_derive::Inspect; #[derive(Inspect)] pub struct BgInfo { #[inspect_slider(min_value = 0.0, max_value = 1.0)] - pub r : f32, + pub r: f32, #[inspect_slider(min_value = 0.0, max_value = 1.0)] pub g: f32, #[inspect_slider(min_value = 0.0, max_value = 1.0)] @@ -13,10 +13,10 @@ pub struct BgInfo { impl Default for BgInfo { fn default() -> Self { - BgInfo{ + BgInfo { r: 0.1, g: 0.2, - b: 0.4 + b: 0.4, } } -} \ No newline at end of file +} diff --git a/src/gaia/camera.rs b/src/gaia/camera.rs index bb00ab4..2223b05 100644 --- a/src/gaia/camera.rs +++ b/src/gaia/camera.rs @@ -29,7 +29,7 @@ const ZOOM: f32 = 45.0; pub struct Camera { // Camera Attributes - pub Position: Point3, + pub position: Point3, pub Front: Vector3, pub Up: Vector3, pub Right: Vector3, @@ -46,7 +46,7 @@ pub struct Camera { impl Default for Camera { fn default() -> Camera { let mut camera = Camera { - Position: Point3::new(0.0, 0.0, 0.0), + position: Point3::new(0.0, 0.0, 0.0), Front: vec3(0.0, 0.0, -1.0), Up: Vector3::zero(), // initialized later Right: Vector3::zero(), // initialized later @@ -65,23 +65,23 @@ impl Default for Camera { impl Camera { /// Returns the view matrix calculated using Eular Angles and the LookAt Matrix pub fn get_view_matrix(&self) -> Matrix4 { - Matrix4::look_at(self.Position, self.Position + self.Front, self.Up) + Matrix4::look_at(self.position, self.position + self.Front, self.Up) } /// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems) pub fn process_keyboard(&mut self, direction: Camera_Movement, deltaTime: f32) { let velocity = self.MovementSpeed * deltaTime; if direction == FORWARD { - self.Position += self.Front * velocity; + self.position += self.Front * velocity; } if direction == BACKWARD { - self.Position += -(self.Front * velocity); + self.position += -(self.Front * velocity); } if direction == LEFT { - self.Position += -(self.Right * velocity); + self.position += -(self.Right * velocity); } if direction == RIGHT { - self.Position += self.Right * velocity; + self.position += self.Right * velocity; } } diff --git a/src/gaia/engine.rs b/src/gaia/engine.rs new file mode 100644 index 0000000..d9eae62 --- /dev/null +++ b/src/gaia/engine.rs @@ -0,0 +1,265 @@ +use crate::gaia::bg_info::BgInfo; +use crate::gaia::camera::*; +use crate::gaia::consts; +use crate::gaia::*; +use cgmath::{perspective, vec3, Deg, Matrix4, Point3, Rad, Vector3}; +use imgui_glfw_rs::glfw; +use imgui_glfw_rs::glfw::{Action, Context, Key}; +use imgui_glfw_rs::imgui; +use imgui_glfw_rs::ImguiGLFW; +use imgui_inspect::InspectArgsStruct; + +pub struct Engine { + pub camera: Camera, + pub bg_info: BgInfo, + pub window: imgui_glfw_rs::glfw::Window, + pub window_size: (f32, f32), + pub events: std::sync::mpsc::Receiver<(f64, imgui_glfw_rs::glfw::WindowEvent)>, + pub glfw: imgui_glfw_rs::glfw::Glfw, + pub imgui: imgui::Context, + pub imgui_glfw: ImguiGLFW, + pub shader: shader::Shader, + pub models: Vec, +} + +impl Engine { + pub fn run(&mut self) { + let mut first_mouse = true; + let mut last_x: f32 = consts::SCR_WIDTH as f32 / 2.0; + let mut last_y: f32 = consts::SCR_HEIGHT as f32 / 2.0; + + // timing + let mut delta_time: f32; // time between current frame and last frame + let mut last_frame: f32 = 0.0; + { + // build and compile shaders + // ------------------------- + self.shader = shader::Shader::from_file( + "resources/shaders/model_loading.vs", + "resources/shaders/model_loading.fs", + ); + + // load models + // ----------- + for _ in 0..10 { + self.models.push(model::Model::new("resources/objects/nanosuit/nanosuit.obj")); + } + }; + // render loop + // ----------- + while !self.window.should_close() { + // per-frame time logic + // -------------------- + let cur_frame = self.glfw.get_time() as f32; + delta_time = cur_frame - last_frame; + last_frame = cur_frame; + + // input + // ----- + let skip_input = + self.imgui.io().want_capture_mouse || self.imgui.io().want_capture_keyboard; + if !skip_input { + self.process_input(delta_time); + } + + // render + // ------ + unsafe { + gl::ClearColor(self.bg_info.r, self.bg_info.g, self.bg_info.b, 1.0); + gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); + + // don't forget to enable shader before setting uniforms + self.shader.useProgram(); + + // view/projection transformations + let projection: Matrix4 = perspective( + Deg(self.camera.Zoom), + self.window_size.0 / self.window_size.1, + 0.1, + 100.0, + ); + let view = self.camera.get_view_matrix(); + self.shader.setMat4(c_str!("projection"), &projection); + self.shader.setMat4(c_str!("view"), &view); + + let mut i = 0; + for m in &self.models { + // render the loaded model + let mut model = Matrix4::::from_translation(vec3(0.0, -1.75, -1.25 * (i as f32))); // translate it down so it's at the center of the scene + model = model * Matrix4::from_scale(0.2); // it's a bit too big for our scene, so scale it down + self.shader.setMat4(c_str!("model"), &model); + m.Draw(&self.shader); + i = i + 1; + } + } + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + + let ui = self.imgui_glfw.frame(&mut self.window, &mut self.imgui); + + { + use imgui::*; + Window::new(im_str!("Hello world")) + .size([300.0, 110.0], Condition::FirstUseEver) + .build(&ui, || { + ui.text(im_str!("Hello world!")); + ui.text(im_str!("こんにちは世界!")); + ui.text(im_str!("This...is...imgui-rs!")); + ui.separator(); + ui.text(format!("Mouse position: ({:.1},{:.1})", last_x, last_y)); + // let selected = vec![&self.bg_info]; + // >::render( + // &selected, + // "Example Struct - Read Only", + // &ui, + // &InspectArgsStruct::default(), + // ); + // let mut selected_mut = vec![&mut self.bg_info]; + // >::render_mut( + // &mut selected_mut, + // "Example Struct - Writable", + // &ui, + // &InspectArgsStruct::default(), + // ); + }); + } + + self.imgui_glfw.draw(ui, &mut self.window); + self.window.swap_buffers(); + self.glfw.poll_events(); + // events + // ----- + self.process_events(&mut first_mouse, &mut last_x, &mut last_y, skip_input); + } + } + + pub fn process_input(&mut self, delta_time: f32) { + if self.window.get_key(Key::Escape) == Action::Press { + self.window.set_should_close(true) + } + if self.window.get_key(Key::W) == Action::Press { + self.camera + .process_keyboard(Camera_Movement::FORWARD, delta_time); + } + if self.window.get_key(Key::S) == Action::Press { + self.camera + .process_keyboard(Camera_Movement::BACKWARD, delta_time); + } + if self.window.get_key(Key::A) == Action::Press { + self.camera + .process_keyboard(Camera_Movement::LEFT, delta_time); + } + if self.window.get_key(Key::D) == Action::Press { + self.camera + .process_keyboard(Camera_Movement::RIGHT, delta_time); + } + self.camera + .enable_mouse_movement(self.window.get_key(Key::LeftControl) != Action::Press); + } + + pub fn process_events( + &mut self, + first_mouse: &mut bool, + last_x: &mut f32, + last_y: &mut f32, + skip_input: bool, + ) { + for (_, event) in glfw::flush_messages(&self.events) { + self.imgui_glfw.handle_event(&mut self.imgui, &event); + match event { + glfw::WindowEvent::FramebufferSize(width, height) => { + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + unsafe { gl::Viewport(0, 0, width, height) } + self.window_size = (width as f32, height as f32); + } + glfw::WindowEvent::CursorPos(xpos, ypos) => { + if skip_input { + return; + } + let (xpos, ypos) = (xpos as f32, ypos as f32); + if *first_mouse { + *last_x = xpos; + *last_y = ypos; + *first_mouse = false; + } + + let xoffset = xpos - *last_x; + let yoffset = *last_y - ypos; // reversed since y-coordinates go from bottom to top + + *last_x = xpos; + *last_y = ypos; + + self.camera.process_mouse_movement(xoffset, yoffset, true); + } + glfw::WindowEvent::Scroll(_xoffset, yoffset) => { + if skip_input { + return; + } + self.camera.process_mouse_scroll(yoffset as f32); + } + _ => {} + } + } + } +} + +impl Default for Engine { + fn default() -> Self { + // glfw: initialize and configure + // ------------------------------ + let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap(); + glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3)); + glfw.window_hint(glfw::WindowHint::OpenGlProfile( + glfw::OpenGlProfileHint::Core, + )); + #[cfg(target_os = "macos")] + glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true)); + + // glfw window creation + // -------------------- + let (mut window, events) = glfw + .create_window( + consts::SCR_WIDTH, + consts::SCR_HEIGHT, + "chRustedGL", + glfw::WindowMode::Windowed, + ) + .expect("Failed to create GLFW window"); + + window.make_current(); + window.set_all_polling(true); + window.set_framebuffer_size_polling(true); + window.set_cursor_pos_polling(true); + window.set_scroll_polling(true); + + // tell GLFW to capture our mouse + window.set_cursor_mode(glfw::CursorMode::Disabled); + // gl: load all OpenGL function pointers + // --------------------------------------- + gl::load_with(|symbol| window.get_proc_address(symbol) as *const _); + + let mut imgui = imgui::Context::create(); + let mut imgui_glfw = ImguiGLFW::new(&mut imgui, &mut window); + // configure global opengl state + // ----------------------------- + unsafe{gl::Enable(gl::DEPTH_TEST);} + + Engine { + camera: Camera { + position: Point3::new(0.0, 0.0, 3.0), + ..Camera::default() + }, + bg_info: BgInfo::default(), + window: window, + window_size: (consts::SCR_WIDTH as f32, consts::SCR_HEIGHT as f32), + events: events, + glfw: glfw, + imgui: imgui, + imgui_glfw: imgui_glfw, + shader: shader::Shader::default(), + models: vec![], + } + } +} diff --git a/src/gaia/mesh.rs b/src/gaia/mesh.rs index 27eb5d3..9198a57 100644 --- a/src/gaia/mesh.rs +++ b/src/gaia/mesh.rs @@ -6,11 +6,11 @@ use std::mem::size_of; use std::os::raw::c_void; use std::ptr; -use cgmath::{ Vector3, Vector2 }; use cgmath::prelude::*; +use cgmath::{Vector2, Vector3}; use gl; -use crate::shader::Shader; +use crate::gaia::shader::Shader; // NOTE: without repr(C) the compiler may reorder the fields or use different padding/alignment than C. // Depending on how you pass the data to OpenGL, this may be bad. In this case it's not strictly @@ -18,7 +18,7 @@ use crate::shader::Shader; #[repr(C)] pub struct Vertex { // position - pub Position: Vector3, + pub position: Vector3, // normal pub Normal: Vector3, // texCoords @@ -32,7 +32,7 @@ pub struct Vertex { impl Default for Vertex { fn default() -> Self { Vertex { - Position: Vector3::zero(), + position: Vector3::zero(), Normal: Vector3::zero(), TexCoords: Vector2::zero(), Tangent: Vector3::zero(), @@ -63,8 +63,12 @@ pub struct Mesh { impl Mesh { pub fn new(vertices: Vec, indices: Vec, textures: Vec) -> Mesh { let mut mesh = Mesh { - vertices, indices, textures, - VAO: 0, VBO: 0, EBO: 0 + vertices, + indices, + textures, + VAO: 0, + VBO: 0, + EBO: 0, }; // now that we have all the required data, set the vertex buffers and its attribute pointers. @@ -75,19 +79,19 @@ impl Mesh { /// render the mesh pub unsafe fn Draw(&self, shader: &Shader) { // bind appropriate textures - let mut diffuseNr = 0; + let mut diffuseNr = 0; let mut specularNr = 0; - let mut normalNr = 0; - let mut heightNr = 0; + let mut normalNr = 0; + let mut heightNr = 0; for (i, texture) in self.textures.iter().enumerate() { gl::ActiveTexture(gl::TEXTURE0 + i as u32); // active proper texture unit before binding - // retrieve texture number (the N in diffuse_textureN) + // retrieve texture number (the N in diffuse_textureN) let name = &texture.type_; let number = match name.as_str() { "texture_diffuse" => { diffuseNr += 1; diffuseNr - }, + } "texture_specular" => { specularNr += 1; specularNr @@ -100,18 +104,26 @@ impl Mesh { heightNr += 1; heightNr } - _ => panic!("unknown texture type") + _ => panic!("unknown texture type"), }; // now set the sampler to the correct texture unit let sampler = CString::new(format!("{}{}", name, number)).unwrap(); - gl::Uniform1i(gl::GetUniformLocation(shader.ID, sampler.as_ptr()), i as i32); + gl::Uniform1i( + gl::GetUniformLocation(shader.ID, sampler.as_ptr()), + i as i32, + ); // and finally bind the texture gl::BindTexture(gl::TEXTURE_2D, texture.id); } // draw mesh gl::BindVertexArray(self.VAO); - gl::DrawElements(gl::TRIANGLES, self.indices.len() as i32, gl::UNSIGNED_INT, ptr::null()); + gl::DrawElements( + gl::TRIANGLES, + self.indices.len() as i32, + gl::UNSIGNED_INT, + ptr::null(), + ); gl::BindVertexArray(0); // always good practice to set everything back to defaults once configured. @@ -143,19 +155,54 @@ impl Mesh { let size = size_of::() as i32; // vertex Positions gl::EnableVertexAttribArray(0); - gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, Position) as *const c_void); + gl::VertexAttribPointer( + 0, + 3, + gl::FLOAT, + gl::FALSE, + size, + offset_of!(Vertex, position) as *const c_void, + ); // vertex normals gl::EnableVertexAttribArray(1); - gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, Normal) as *const c_void); + gl::VertexAttribPointer( + 1, + 3, + gl::FLOAT, + gl::FALSE, + size, + offset_of!(Vertex, Normal) as *const c_void, + ); // vertex texture coords gl::EnableVertexAttribArray(2); - gl::VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, TexCoords) as *const c_void); + gl::VertexAttribPointer( + 2, + 2, + gl::FLOAT, + gl::FALSE, + size, + offset_of!(Vertex, TexCoords) as *const c_void, + ); // vertex tangent gl::EnableVertexAttribArray(3); - gl::VertexAttribPointer(3, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, Tangent) as *const c_void); + gl::VertexAttribPointer( + 3, + 3, + gl::FLOAT, + gl::FALSE, + size, + offset_of!(Vertex, Tangent) as *const c_void, + ); // vertex bitangent gl::EnableVertexAttribArray(4); - gl::VertexAttribPointer(4, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, Bitangent) as *const c_void); + gl::VertexAttribPointer( + 4, + 3, + gl::FLOAT, + gl::FALSE, + size, + offset_of!(Vertex, Bitangent) as *const c_void, + ); gl::BindVertexArray(0); } diff --git a/src/gaia/mod.rs b/src/gaia/mod.rs index 5440bc8..b26d114 100644 --- a/src/gaia/mod.rs +++ b/src/gaia/mod.rs @@ -3,7 +3,8 @@ pub mod consts; pub mod utils; #[macro_use] pub mod macros; -pub mod shader; -pub mod model; +pub mod bg_info; +pub mod engine; pub mod mesh; -pub mod bg_info; \ No newline at end of file +pub mod model; +pub mod shader; diff --git a/src/gaia/model.rs b/src/gaia/model.rs index 343c762..4a6cad5 100644 --- a/src/gaia/model.rs +++ b/src/gaia/model.rs @@ -11,15 +11,15 @@ use image::DynamicImage::*; use image::GenericImage; use tobj; -use crate::mesh::{ Mesh, Texture, Vertex }; -use crate::shader::Shader; -use crate::utils::*; +use crate::gaia::mesh::{Mesh, Texture, Vertex}; +use crate::gaia::shader::Shader; +use crate::gaia::utils::*; // #[derive(Default)] pub struct Model { /* Model Data */ pub meshes: Vec, - pub textures_loaded: Vec, // stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once. + pub textures_loaded: Vec, // stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once. directory: String, } @@ -27,10 +27,15 @@ impl Model { /// constructor, expects a filepath to a 3D model. pub fn new(path: &str) -> Model { let pathObj = Path::new(path); - let mut model = Model{ + let mut model = Model { meshes: Vec::::new(), textures_loaded: Vec::::new(), - directory: pathObj.parent().unwrap_or_else(|| Path::new("")).to_str().unwrap().into() + directory: pathObj + .parent() + .unwrap_or_else(|| Path::new("")) + .to_str() + .unwrap() + .into(), }; model.loadModel(path); model @@ -38,7 +43,9 @@ impl Model { pub fn Draw(&self, shader: &Shader) { for mesh in &self.meshes { - unsafe { mesh.Draw(shader); } + unsafe { + mesh.Draw(shader); + } } } @@ -48,7 +55,12 @@ impl Model { println!("Started loading model from path: {}", path.display()); // retrieve the directory path of the filepath - self.directory = path.parent().unwrap_or_else(|| Path::new("")).to_str().unwrap().into(); + self.directory = path + .parent() + .unwrap_or_else(|| Path::new("")) + .to_str() + .unwrap() + .into(); let obj = tobj::load_obj(path, true); let (models, materials) = obj.unwrap(); @@ -63,9 +75,9 @@ impl Model { let (p, n, t) = (&mesh.positions, &mesh.normals, &mesh.texcoords); for i in 0..num_vertices { vertices.push(Vertex { - Position: vec3(p[i*3], p[i*3+1], p[i*3+2]), - Normal: vec3(n[i*3], n[i*3+1], n[i*3+2]), - TexCoords: vec2(t[i*2], t[i*2+1]), + position: vec3(p[i * 3], p[i * 3 + 1], p[i * 3 + 2]), + Normal: vec3(n[i * 3], n[i * 3 + 1], n[i * 3 + 2]), + TexCoords: vec2(t[i * 2], t[i * 2 + 1]), ..Vertex::default() }) } @@ -77,17 +89,20 @@ impl Model { // 1. diffuse map if !material.diffuse_texture.is_empty() { - let texture = self.loadMaterialTexture(&material.diffuse_texture, "texture_diffuse"); + let texture = + self.loadMaterialTexture(&material.diffuse_texture, "texture_diffuse"); textures.push(texture); } // 2. specular map if !material.specular_texture.is_empty() { - let texture = self.loadMaterialTexture(&material.specular_texture, "texture_specular"); + let texture = + self.loadMaterialTexture(&material.specular_texture, "texture_specular"); textures.push(texture); } // 3. normal map if !material.normal_texture.is_empty() { - let texture = self.loadMaterialTexture(&material.normal_texture, "texture_normal"); + let texture = + self.loadMaterialTexture(&material.normal_texture, "texture_normal"); textures.push(texture); } // NOTE: no height maps @@ -96,7 +111,6 @@ impl Model { self.meshes.push(Mesh::new(vertices, indices, textures)); } println!("Finished loading model from path: {}", path.display()); - } fn loadMaterialTexture(&mut self, path: &str, typeName: &str) -> Texture { @@ -108,12 +122,11 @@ impl Model { } let texture = Texture { - id: unsafe { loadTextureFromDir(path, &self.directory) }, + id: unsafe { load_texture_from_dir(path, &self.directory) }, type_: typeName.into(), - path: path.into() + path: path.into(), }; self.textures_loaded.push(texture.clone()); texture } } - diff --git a/src/gaia/shader.rs b/src/gaia/shader.rs index 5577f90..8f2473b 100644 --- a/src/gaia/shader.rs +++ b/src/gaia/shader.rs @@ -8,9 +8,9 @@ use std::str; use gl; use gl::types::*; +use crate::gaia::consts; use cgmath::prelude::*; use cgmath::{Matrix, Matrix4, Vector3}; -use crate::gaia::consts; pub struct Shader { pub ID: u32, diff --git a/src/gaia/utils.rs b/src/gaia/utils.rs index feaefa3..4c718b3 100644 --- a/src/gaia/utils.rs +++ b/src/gaia/utils.rs @@ -9,11 +9,11 @@ use image::DynamicImage::*; use image::GenericImage; use image::*; -pub unsafe fn loadTexture(path: &str) -> u32 { +pub unsafe fn load_texture(path: &str) -> u32 { println!("Loading texture from path: {}", path); - let mut textureID = 0; + let mut id = 0; - gl::GenTextures(1, &mut textureID); + gl::GenTextures(1, &mut id); let img = image::open(&Path::new(path)).expect("Texture failed to load"); let format = match img { ImageLuma8(_) => gl::RED, @@ -26,7 +26,7 @@ pub unsafe fn loadTexture(path: &str) -> u32 { let data = img.raw_pixels(); let dim = img.dimensions(); - gl::BindTexture(gl::TEXTURE_2D, textureID); + gl::BindTexture(gl::TEXTURE_2D, id); gl::TexImage2D( gl::TEXTURE_2D, 0, @@ -49,11 +49,11 @@ pub unsafe fn loadTexture(path: &str) -> u32 { ); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); - textureID + id } -pub unsafe fn loadTextureFromDir(filename: &str, directory: &str) -> u32 { +pub unsafe fn load_texture_from_dir(filename: &str, directory: &str) -> u32 { let fullpath = format!("{}/{}", directory, filename); - loadTexture(&fullpath) -} \ No newline at end of file + load_texture(&fullpath) +} diff --git a/src/main.rs b/src/main.rs index c04778d..66906db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,255 +1,15 @@ +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + extern crate gl; -extern crate imgui_glfw_rs; extern crate image; -// Use the reexported glfw crate to avoid version conflicts. -use imgui_glfw_rs::glfw; -// Use the reexported imgui crate to avoid version conflicts. -use imgui_glfw_rs::imgui; - -use imgui_glfw_rs::ImguiGLFW; -use imgui_inspect::InspectArgsStruct; -use self::gl::types::*; -use imgui_glfw_rs::glfw::{Action, Context, Key}; -use cgmath::prelude::*; -use cgmath::{perspective, vec3, Deg, Matrix4, Point3, Rad, Vector3}; +extern crate imgui_glfw_rs; use human_panic::setup_panic; - -#[macro_use] mod gaia; -use crate::gaia::camera::*; -use crate::gaia::*; -use crate::gaia::bg_info::BgInfo; +use crate::gaia::engine::Engine; pub fn main() { setup_panic!(); - let mut camera = Camera { - Position: Point3::new(0.0, 0.0, 3.0), - ..Camera::default() - }; + let mut engine = Engine::default(); - let mut first_mouse = true; - let mut last_x: f32 = consts::SCR_WIDTH as f32 / 2.0; - let mut last_y: f32 = consts::SCR_HEIGHT as f32 / 2.0; - - // timing - let mut delta_time: f32; // time between current frame and last frame - let mut last_frame: f32 = 0.0; - // glfw: initialize and configure - // ------------------------------ - let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap(); - glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3)); - glfw.window_hint(glfw::WindowHint::OpenGlProfile( - glfw::OpenGlProfileHint::Core, - )); - #[cfg(target_os = "macos")] - glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true)); - - // glfw window creation - // -------------------- - let (mut window, events) = glfw - .create_window( - consts::SCR_WIDTH, - consts::SCR_HEIGHT, - "chRustedGL", - glfw::WindowMode::Windowed, - ) - .expect("Failed to create GLFW window"); - - window.make_current(); - window.set_all_polling(true); - window.set_framebuffer_size_polling(true); - window.set_cursor_pos_polling(true); - window.set_scroll_polling(true); - - // tell GLFW to capture our mouse - window.set_cursor_mode(glfw::CursorMode::Disabled); - - // gl: load all OpenGL function pointers - // --------------------------------------- - gl::load_with(|symbol| window.get_proc_address(symbol) as *const _); - - - let (ourShader, ourModel) = unsafe { - // configure global opengl state - // ----------------------------- - gl::Enable(gl::DEPTH_TEST); - - // build and compile shaders - // ------------------------- - let ourShader = shader::Shader::from_file( - "resources/shaders/model_loading.vs", - "resources/shaders/model_loading.fs"); - - // load models - // ----------- - let ourModel = model::Model::new("resources/objects/nanosuit/nanosuit.obj"); - - // draw in wireframe - // gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE); - - (ourShader, ourModel) - }; - - - let mut imgui = imgui::Context::create(); - - let mut imgui_glfw = ImguiGLFW::new(&mut imgui, &mut window); - - // render loop - // ----------- - let mut bg = BgInfo::default(); - while !window.should_close() { - // per-frame time logic - // -------------------- - let cur_frame = glfw.get_time() as f32; - delta_time = cur_frame - last_frame; - last_frame = cur_frame; - - // input - // ----- - let skip_input = imgui.io().want_capture_mouse || imgui.io().want_capture_keyboard; - if !skip_input { - process_input(&mut window, delta_time, &mut camera); - } - - // render - // ------ - unsafe { - gl::ClearColor(bg.r, bg.g, bg.b, 1.0); - gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); - - // don't forget to enable shader before setting uniforms - ourShader.useProgram(); - - // view/projection transformations - let projection: Matrix4 = perspective(Deg(camera.Zoom), consts::SCR_WIDTH as f32 / consts::SCR_HEIGHT as f32, 0.1, 100.0); - let view = camera.get_view_matrix(); - ourShader.setMat4(c_str!("projection"), &projection); - ourShader.setMat4(c_str!("view"), &view); - - // render the loaded model - let mut model = Matrix4::::from_translation(vec3(0.0, -1.75, 0.0)); // translate it down so it's at the center of the scene - model = model * Matrix4::from_scale(0.2); // it's a bit too big for our scene, so scale it down - ourShader.setMat4(c_str!("model"), &model); - ourModel.Draw(&ourShader); - } - - // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) - // ------------------------------------------------------------------------------- - - let ui = imgui_glfw.frame(&mut window, &mut imgui); - - { - use imgui::*; - Window::new(im_str!("Hello world")) - .size([300.0, 110.0], Condition::FirstUseEver) - .build(&ui, || { - ui.text(im_str!("Hello world!")); - ui.text(im_str!("こんにちは世界!")); - ui.text(im_str!("This...is...imgui-rs!")); - ui.separator(); - ui.text(format!( - "Mouse Position: ({:.1},{:.1})", - last_x, last_y - )); - let selected = vec![&bg]; - >::render( - &selected, - "Example Struct - Read Only", - &ui, - &InspectArgsStruct::default(), - ); - let mut selected_mut = vec![&mut bg]; - >::render_mut( - &mut selected_mut, - "Example Struct - Writable", - &ui, - &InspectArgsStruct::default(), - ); - }); - } - - imgui_glfw.draw(ui, &mut window); - window.swap_buffers(); - glfw.poll_events(); - // events - // ----- - - for (_, event) in glfw::flush_messages(&events) { - imgui_glfw.handle_event(&mut imgui, &event); - process_events( - event, - &mut first_mouse, - &mut last_x, - &mut last_y, - &mut camera, - skip_input - ); - } - } -} - -pub fn process_events( - event: imgui_glfw_rs::glfw::WindowEvent, - first_mouse: &mut bool, - last_x: &mut f32, - last_y: &mut f32, - camera: &mut Camera, - skip_input: bool -) { - match event { - glfw::WindowEvent::FramebufferSize(width, height) => { - // make sure the viewport matches the new window dimensions; note that width and - // height will be significantly larger than specified on retina displays. - unsafe { gl::Viewport(0, 0, width, height) } - } - glfw::WindowEvent::CursorPos(xpos, ypos) => { - if skip_input { return; } - let (xpos, ypos) = (xpos as f32, ypos as f32); - if *first_mouse { - *last_x = xpos; - *last_y = ypos; - *first_mouse = false; - } - - let xoffset = xpos - *last_x; - let yoffset = *last_y - ypos; // reversed since y-coordinates go from bottom to top - - *last_x = xpos; - *last_y = ypos; - - camera.process_mouse_movement(xoffset, yoffset, true); - } - glfw::WindowEvent::Scroll(_xoffset, yoffset) => { - if skip_input { return; } - camera.process_mouse_scroll(yoffset as f32); - } - _ => {} - } -} - -/// Input processing function as introduced in 1.7.4 (Camera Class) and used in -/// most later tutorials -pub fn process_input(window: &mut glfw::Window, delta_time: f32, camera: &mut Camera) { - if window.get_key(Key::Escape) == Action::Press { - window.set_should_close(true) - } - - if window.get_key(Key::W) == Action::Press { - camera.process_keyboard(Camera_Movement::FORWARD, delta_time); - } - if window.get_key(Key::S) == Action::Press { - camera.process_keyboard(Camera_Movement::BACKWARD, delta_time); - } - if window.get_key(Key::A) == Action::Press { - camera.process_keyboard(Camera_Movement::LEFT, delta_time); - } - if window.get_key(Key::D) == Action::Press { - camera.process_keyboard(Camera_Movement::RIGHT, delta_time); - } - camera.enable_mouse_movement(window.get_key(Key::LeftControl) != Action::Press); + engine.run(); }