extern crate gl; extern crate glfw; extern crate image; use self::camera::*; use self::gl::types::*; use self::glfw::{Action, Context, Key}; use cgmath::prelude::*; use cgmath::{perspective, vec3, Deg, Matrix4, Point3, Rad, Vector3}; use human_panic::setup_panic; use image::GenericImageView; use std::ffi::CString; use std::mem; use std::os::raw::c_void; use std::ptr; use std::sync::mpsc::Receiver; mod camera; mod consts; mod macros; mod shader; const CUBES_POS: [Vector3; 10] = [ vec3(0.0, 0.0, 0.0), vec3(2.0, 5.0, -15.0), vec3(-1.5, -2.2, -2.5), vec3(-3.8, -2.0, -12.3), vec3(2.4, -0.4, -3.5), vec3(-1.7, 3.0, -7.5), vec3(1.3, -2.0, -2.5), vec3(1.5, 2.0, -2.5), vec3(1.5, 0.2, -1.5), vec3(-1.3, 1.0, -1.5), ]; pub fn main() { setup_panic!(); let mut camera = Camera { Position: Point3::new(0.0, 0.0, 3.0), ..Camera::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_key_polling(true); window.set_framebuffer_size_polling(true); // gl: load all OpenGL function pointers // --------------------------------------- gl::load_with(|symbol| window.get_proc_address(symbol) as *const _); let (shader_object, vbo, vao, texture) = unsafe { gl::Enable(gl::DEPTH_TEST); let v_shader = CString::new(consts::VERTEX_SHADER_SRC.as_bytes()).unwrap(); let f_shader = CString::new(consts::FRAGMENT_SHADER_SRC.as_bytes()).unwrap(); let shader = crate::shader::Shader::new(v_shader, f_shader); // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------ // HINT: type annotation is crucial since default for float literals is f64 let vertices: [f32; 180] = [ -0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, -0.5, 0.5, 0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, -0.5, 1.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 1.0, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0, ]; let (mut vbo, mut vao) = (0, 0); gl::GenVertexArrays(1, &mut vao); gl::GenBuffers(1, &mut vbo); gl::BindVertexArray(vao); gl::BindBuffer(gl::ARRAY_BUFFER, vbo); gl::BufferData( gl::ARRAY_BUFFER, (vertices.len() * mem::size_of::()) as GLsizeiptr, &vertices[0] as *const f32 as *const c_void, gl::STATIC_DRAW, ); let stride = 5 * mem::size_of::() as GLsizei; // position attribute gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, stride, ptr::null()); gl::EnableVertexAttribArray(0); // texture coord attribute gl::VertexAttribPointer( 1, 2, gl::FLOAT, gl::FALSE, stride, (3 * mem::size_of::()) as *const c_void, ); gl::EnableVertexAttribArray(1); // uncomment this call to draw in wireframe polygons. // gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE); // ------------------------- let mut texture = 0; gl::GenTextures(1, &mut texture); gl::BindTexture(gl::TEXTURE_2D, texture); // all upcoming GL_TEXTURE_2D operations now have effect on this texture object // set the texture wrapping parameters gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32); // set texture wrapping to gl::REPEAT (default wrapping method) gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32); // set texture filtering parameters gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); // load image, create texture and generate mipmaps let img = image::open("resources/garlic_dog_space.jpg") .unwrap() .flipv(); let data = img.raw_pixels(); // let data = flipped let dimensions = (img.width(), img.height()); gl::TexImage2D( gl::TEXTURE_2D, 0, gl::RGB as i32, dimensions.0 as i32, dimensions.1 as i32, 0, gl::RGB, gl::UNSIGNED_BYTE, &data[0] as *const u8 as *const c_void, ); gl::GenerateMipmap(gl::TEXTURE_2D); (shader, vbo, vao, texture) }; // render loop // ----------- let color_r = 0.188; let color_g = 0.22; let color_b = 0.235; 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; // events // ----- process_events( &events, &mut first_mouse, &mut last_x, &mut last_y, &mut camera, ); // input // ----- process_input(&mut window, delta_time, &mut camera); // render // ------ unsafe { gl::ClearColor(color_r, color_g, color_b, 1.0); gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); gl::BindTexture(gl::TEXTURE_2D, texture); shader_object.useProgram(); // pass projection matrix to shader (note that in this case it could change every frame) let projection: Matrix4 = perspective( Deg(camera.Zoom), consts::SCR_WIDTH as f32 / consts::SCR_HEIGHT as f32, 0.1, 100.0, ); shader_object.setMat4(c_str!("projection"), &projection); // camera/view transformation let view = camera.get_view_matrix(); shader_object.setMat4(c_str!("view"), &view); gl::BindVertexArray(vao); for (i, position) in CUBES_POS.iter().enumerate() { // calculate the model matrix for each object and pass it to shader before drawing let mut model: Matrix4 = Matrix4::from_translation(*position); let angle = 20.0 * i as f32; model = model * Matrix4::from_axis_angle(vec3(1.0, 0.3, 0.5).normalize(), Deg(angle)); shader_object.setMat4(c_str!("model"), &model); gl::DrawArrays(gl::TRIANGLES, 0, 36); } } // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) // ------------------------------------------------------------------------------- window.swap_buffers(); glfw.poll_events(); } unsafe { gl::DeleteVertexArrays(1, &vao); gl::DeleteBuffers(1, &vbo); } } pub fn process_events( events: &Receiver<(f64, glfw::WindowEvent)>, first_mouse: &mut bool, last_x: &mut f32, last_y: &mut f32, camera: &mut Camera, ) { for (_, event) in glfw::flush_messages(events) { 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) => { 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) => { 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); } }