#![allow(non_upper_case_globals)] #![allow(non_snake_case)] extern crate glfw; use self::glfw::{Context, Key, Action}; extern crate gl; use self::gl::types::*; use std::sync::mpsc::Receiver; use std::ptr; use std::mem; use std::os::raw::c_void; use std::path::Path; use std::ffi::CStr; use shader::Shader; use image; use image::GenericImage; use cgmath::{Matrix4, vec3, Deg, Rad, perspective}; use cgmath::prelude::*; // settings const SCR_WIDTH: u32 = 800; const SCR_HEIGHT: u32 = 600; unsafe fn glCheckError_(file: &str, line: u32) -> u32 { let mut errorCode = gl::GetError(); while errorCode != gl::NO_ERROR { let error = match errorCode { gl::INVALID_ENUM => "INVALID_ENUM", gl::INVALID_VALUE => "INVALID_VALUE", gl::INVALID_OPERATION => "INVALID_OPERATION", gl::STACK_OVERFLOW => "STACK_OVERFLOW", gl::STACK_UNDERFLOW => "STACK_UNDERFLOW", gl::OUT_OF_MEMORY => "OUT_OF_MEMORY", gl::INVALID_FRAMEBUFFER_OPERATION => "INVALID_FRAMEBUFFER_OPERATION", _ => "unknown GL error code" }; println!("{} | {} ({})", error, file, line); errorCode = gl::GetError(); } errorCode } macro_rules! glCheckError { () => ( glCheckError_(file!(), line!()) ) } extern "system" fn glDebugOutput(source: gl::types::GLenum, type_: gl::types::GLenum, id: gl::types::GLuint, severity: gl::types::GLenum, _length: gl::types::GLsizei, message: *const gl::types::GLchar, _userParam: *mut c_void) { if id == 131169 || id == 131185 || id == 131218 || id == 131204 { // ignore these non-significant error codes return } println!("---------------"); let message = unsafe { CStr::from_ptr(message).to_str().unwrap() }; println!("Debug message ({}): {}", id, message); match source { gl::DEBUG_SOURCE_API => println!("Source: API"), gl::DEBUG_SOURCE_WINDOW_SYSTEM => println!("Source: Window System"), gl::DEBUG_SOURCE_SHADER_COMPILER => println!("Source: Shader Compiler"), gl::DEBUG_SOURCE_THIRD_PARTY => println!("Source: Third Party"), gl::DEBUG_SOURCE_APPLICATION => println!("Source: Application"), gl::DEBUG_SOURCE_OTHER => println!("Source: Other"), _ => println!("Source: Unknown enum value") } match type_ { gl::DEBUG_TYPE_ERROR => println!("Type: Error"), gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR => println!("Type: Deprecated Behaviour"), gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR => println!("Type: Undefined Behaviour"), gl::DEBUG_TYPE_PORTABILITY => println!("Type: Portability"), gl::DEBUG_TYPE_PERFORMANCE => println!("Type: Performance"), gl::DEBUG_TYPE_MARKER => println!("Type: Marker"), gl::DEBUG_TYPE_PUSH_GROUP => println!("Type: Push Group"), gl::DEBUG_TYPE_POP_GROUP => println!("Type: Pop Group"), gl::DEBUG_TYPE_OTHER => println!("Type: Other"), _ => println!("Type: Unknown enum value") } match severity { gl::DEBUG_SEVERITY_HIGH => println!("Severity: high"), gl::DEBUG_SEVERITY_MEDIUM => println!("Severity: medium"), gl::DEBUG_SEVERITY_LOW => println!("Severity: low"), gl::DEBUG_SEVERITY_NOTIFICATION => println!("Severity: notification"), _ => println!("Severity: Unknown enum value") } } #[allow(non_snake_case)] pub fn main_7_1() { // 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)); glfw.window_hint(glfw::WindowHint::OpenGlDebugContext(true)); // comment this line in a release build! #[cfg(target_os = "macos")] glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true)); // glfw window creation // -------------------- let (mut window, events) = glfw.create_window(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", glfw::WindowMode::Windowed) .expect("Failed to create GLFW window"); window.make_current(); window.set_key_polling(true); window.set_framebuffer_size_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 (shader, cubeVAO, texture) = unsafe { // enable OpenGL debug context if context allows for debug context let mut flags = 0; gl::GetIntegerv(gl::CONTEXT_FLAGS, &mut flags); if flags as u32 & gl::CONTEXT_FLAG_DEBUG_BIT != 0 { gl::Enable(gl::DEBUG_OUTPUT); gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS); // makes sure errors are displayed synchronously gl::DebugMessageCallback(glDebugOutput, ptr::null()); gl::DebugMessageControl(gl::DONT_CARE, gl::DONT_CARE, gl::DONT_CARE, 0, ptr::null(), gl::TRUE); } else { println!("Debug Context not active! Check if your driver supports the extension.") } // configure global opengl state // ----------------------------- gl::Enable(gl::DEPTH_TEST); gl::Enable(gl::CULL_FACE); // OpenGL initial state let shader = Shader::new( "src/_7_in_practice/shaders/debugging.vs", "src/_7_in_practice/shaders/debugging.fs"); // configure 3D cube let (mut cubeVAO, mut cubeVBO) = (0, 0); let vertices: [f32; 180] = [ // back face -0.5, -0.5, -0.5, 0.0, 0.0, // Bottom-left 0.5, 0.5, -0.5, 1.0, 1.0, // top-right 0.5, -0.5, -0.5, 1.0, 0.0, // bottom-right 0.5, 0.5, -0.5, 1.0, 1.0, // top-right -0.5, -0.5, -0.5, 0.0, 0.0, // bottom-left -0.5, 0.5, -0.5, 0.0, 1.0, // top-left // front face -0.5, -0.5, 0.5, 0.0, 0.0, // bottom-left 0.5, -0.5, 0.5, 1.0, 0.0, // bottom-right 0.5, 0.5, 0.5, 1.0, 1.0, // top-right 0.5, 0.5, 0.5, 1.0, 1.0, // top-right -0.5, 0.5, 0.5, 0.0, 1.0, // top-left -0.5, -0.5, 0.5, 0.0, 0.0, // bottom-left // left face -0.5, 0.5, 0.5, -1.0, 0.0, // top-right -0.5, 0.5, -0.5, -1.0, 1.0, // top-left -0.5, -0.5, -0.5, -0.0, 1.0, // bottom-left -0.5, -0.5, -0.5, -0.0, 1.0, // bottom-left -0.5, -0.5, 0.5, -0.0, 0.0, // bottom-right -0.5, 0.5, 0.5, -1.0, 0.0, // top-right // right face 0.5, 0.5, 0.5, 1.0, 0.0, // top-left 0.5, -0.5, -0.5, 0.0, 1.0, // bottom-right 0.5, 0.5, -0.5, 1.0, 1.0, // top-right 0.5, -0.5, -0.5, 0.0, 1.0, // bottom-right 0.5, 0.5, 0.5, 1.0, 0.0, // top-left 0.5, -0.5, 0.5, 0.0, 0.0, // bottom-left // bottom face -0.5, -0.5, -0.5, 0.0, 1.0, // top-right 0.5, -0.5, -0.5, 1.0, 1.0, // top-left 0.5, -0.5, 0.5, 1.0, 0.0, // bottom-left 0.5, -0.5, 0.5, 1.0, 0.0, // bottom-left -0.5, -0.5, 0.5, 0.0, 0.0, // bottom-right -0.5, -0.5, -0.5, 0.0, 1.0, // top-right // top face -0.5, 0.5, -0.5, 0.0, 1.0, // top-left 0.5, 0.5, 0.5, 1.0, 0.0, // bottom-right 0.5, 0.5, -0.5, 1.0, 1.0, // top-right 0.5, 0.5, 0.5, 1.0, 0.0, // bottom-right -0.5, 0.5, -0.5, 0.0, 1.0, // top-left -0.5, 0.5, 0.5, 0.0, 0.0 // bottom-left ]; gl::GenVertexArrays(1, &mut cubeVAO); gl::GenBuffers(1, &mut cubeVBO); // fill buffer gl::BindBuffer(gl::ARRAY_BUFFER, cubeVBO); gl::BufferData(gl::ARRAY_BUFFER, (vertices.len() * mem::size_of::<GLfloat>()) as GLsizeiptr, &vertices[0] as *const f32 as *const c_void, gl::STATIC_DRAW); // link vertex attributes gl::BindVertexArray(cubeVAO); gl::EnableVertexAttribArray(0); let stride = 5 * mem::size_of::<GLfloat>() as GLsizei; gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, stride, ptr::null()); gl::EnableVertexAttribArray(1); gl::VertexAttribPointer(1, 2, gl::FLOAT, gl::FALSE, stride, (3 * mem::size_of::<GLfloat>()) as *const c_void); gl::BindBuffer(gl::ARRAY_BUFFER, 0); gl::BindVertexArray(0); // load cube texture let mut texture = 0; gl::GenTextures(1, &mut texture); gl::BindTexture(gl::TEXTURE_2D, texture); let img = image::open(&Path::new("resources/textures/wood.png")).expect("Failed to load texture"); let data = img.raw_pixels(); gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB as i32, img.width() as i32, img.height() as i32, 0, gl::RGB, gl::UNSIGNED_BYTE, &data[0] as *const u8 as *const c_void); gl::GenerateMipmap(gl::TEXTURE_2D); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); // set up projection matrix let projection: Matrix4<f32> = perspective(Deg(45.0), SCR_WIDTH as f32 / SCR_HEIGHT as f32, 0.1, 100.0); shader.setMat4(c_str!("projection"), &projection); shader.setInt(c_str!("tex"), 0); glCheckError!(); (shader, cubeVAO, texture) }; // render loop // ----------- while !window.should_close() { // events // ----- process_events(&mut window, &events); // render // ------ unsafe { gl::ClearColor(0.2, 0.3, 0.3, 1.0); gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); shader.useProgram(); let rotationSpeed = 10.0; let angle = glfw.get_time() as f32 * rotationSpeed; let mut model: Matrix4<f32> = Matrix4::from_translation(vec3(0., 0., -2.5)); model = model * Matrix4::from_axis_angle(vec3(1.0, 1.0, 1.0).normalize(), Rad(angle)); shader.setMat4(c_str!("model"), &model); gl::BindTexture(gl::TEXTURE_2D, texture); gl::BindVertexArray(cubeVAO); gl::DrawArrays(gl::TRIANGLES, 0, 36); gl::BindVertexArray(0); } // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) // ------------------------------------------------------------------------------- window.swap_buffers(); glfw.poll_events(); } } // NOTE: not the same version as in common.rs! fn process_events(window: &mut glfw::Window, events: &Receiver<(f64, glfw::WindowEvent)>) { 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::Key(Key::Escape, _, Action::Press, _) => window.set_should_close(true), _ => {} } } }