use std::convert::TryInto; use std::sync::mpsc::Receiver; use glfw; use glfw::{Context, Glfw, Window, WindowEvent}; use gl; use gl::types::{GLint, GLsizei, GLuint}; use glfw::Key::P; const WIDTH: u32 = 900; const HEIGHT: u32 = 700; const TITLE: &str = "Hello From OpenGL World!"; pub fn compile_program(vertex: &str, fragment: &str) -> GLuint { let vertex_shader = unsafe { gl::CreateShader(gl::VERTEX_SHADER) }; unsafe { gl::ShaderSource(vertex_shader, 1, &vertex.as_bytes().as_ptr().cast(), &vertex.len().try_into().unwrap()); gl::CompileShader(vertex_shader); let mut success = 0; gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success); if success == 0 { let mut log_len = 0_i32; // gl::GetShaderiv(vertex_shader, gl::INFO_LOG_LENGTH, &mut log_len); // let mut v: Vec = Vec::with_capacity(log_len as usize); // gl::GetShaderInfoLog(vertex_shader, log_len, &mut log_len, v.as_mut_ptr().cast()); let mut v: Vec = Vec::with_capacity(1024); gl::GetShaderInfoLog(vertex_shader, 1024, &mut log_len, v.as_mut_ptr().cast()); v.set_len(log_len.try_into().unwrap()); panic!("Vertex Shader Compile Error: {}", String::from_utf8_lossy(&v)); } } let fragment_shader = unsafe { gl::CreateShader(gl::FRAGMENT_SHADER) }; unsafe { gl::ShaderSource(fragment_shader, 1, &fragment.as_bytes().as_ptr().cast(), &fragment.len().try_into().unwrap()); gl::CompileShader(fragment_shader); let mut success = 0; gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success); if success == 0 { let mut v: Vec = Vec::with_capacity(1024); let mut log_len = 0_i32; gl::GetShaderInfoLog(fragment_shader, 1024, &mut log_len, v.as_mut_ptr().cast()); v.set_len(log_len.try_into().unwrap()); panic!("Fragment Shader Compile Error: {}", String::from_utf8_lossy(&v)); } } let shader_program = unsafe { gl::CreateProgram() }; unsafe { gl::AttachShader(shader_program, vertex_shader); gl::AttachShader(shader_program, fragment_shader); gl::LinkProgram(shader_program); let mut success = 0; gl::GetProgramiv(shader_program, gl::LINK_STATUS, &mut success); if success == 0 { let mut v: Vec = Vec::with_capacity(1024); let mut log_len = 0_i32; gl::GetProgramInfoLog(shader_program, 1024, &mut log_len, v.as_mut_ptr().cast()); v.set_len(log_len.try_into().unwrap()); panic!("Program Link Error: {}", String::from_utf8_lossy(&v)); } gl::DetachShader(shader_program, vertex_shader); gl::DetachShader(shader_program, fragment_shader); gl::DeleteShader(vertex_shader); gl::DeleteShader(fragment_shader); } shader_program } pub fn setup() -> (Glfw, Window, Receiver<(f64, WindowEvent)>, GLuint, GLuint) { 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::OpenGlForwardCompat(true)); glfw.window_hint(glfw::WindowHint::Resizable(false)); let (mut window, events) = glfw.create_window(WIDTH, HEIGHT, TITLE, glfw::WindowMode::Windowed).unwrap(); let (screen_width, screen_height) = window.get_framebuffer_size(); window.make_current(); window.set_key_polling(true); gl::load_with(|ptr| window.get_proc_address(ptr) as *const _); unsafe { gl::Viewport(0, 0, screen_width, screen_height); clear_color(Color(0.4, 0.4, 0.4, 1.0)); } // ------------------------------------------- const VERT_SHADER: &str = "#version 330 core layout (location = 0) in vec3 Position; layout (location = 1) in vec3 Color; out VS_OUTPUT { vec3 Color; } OUT; void main() { gl_Position = vec4(Position, 1.0); OUT.Color = Color; }"; const FRAG_SHADER: &str = "#version 330 core in VS_OUTPUT { vec3 Color; } IN; layout(location = 0) out vec4 Color; void main() { Color = vec4(IN.Color, 1.0f); }"; let shader_program = compile_program(VERT_SHADER, FRAG_SHADER); let vertices = &[ // positions // colors 0.5f32, -0.5, 0.0, 1.0, 0.0, 0.0, // bottom right -0.5, -0.5, 0.0, 0.0, 1.0, 0.0, // bottom left 0.0, 0.5, 0.0, 0.0, 0.0, 1.0 // top ]; let mut vbo: gl::types::GLuint = 0; unsafe { gl::GenBuffers(1, &mut vbo); } unsafe { gl::BindBuffer(gl::ARRAY_BUFFER, vbo); gl::BufferData( gl::ARRAY_BUFFER, // target (vertices.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, // size of data in bytes vertices.as_ptr() as *const gl::types::GLvoid, // pointer to data gl::STATIC_DRAW, // usage ); gl::BindBuffer(gl::ARRAY_BUFFER, 0); } // set up vertex array object let mut vao: gl::types::GLuint = 0; unsafe { gl::GenVertexArrays(1, &mut vao); } unsafe { gl::BindVertexArray(vao); gl::BindBuffer(gl::ARRAY_BUFFER, vbo); gl::EnableVertexAttribArray(0); // this is "layout (location = 0)" in vertex shader gl::VertexAttribPointer( 0, // index of the generic vertex attribute ("layout (location = 0)") 3, // the number of components per generic vertex attribute gl::FLOAT, // data type gl::FALSE, // normalized (int-to-float conversion) (6 * std::mem::size_of::()) as gl::types::GLint, // stride (byte offset between consecutive attributes) std::ptr::null(), // offset of the first component ); gl::EnableVertexAttribArray(1); // this is "layout (location = 0)" in vertex shader gl::VertexAttribPointer( 1, // index of the generic vertex attribute ("layout (location = 0)") 3, // the number of components per generic vertex attribute gl::FLOAT, // data type gl::FALSE, // normalized (int-to-float conversion) (6 * std::mem::size_of::()) as gl::types::GLint, // stride (byte offset between consecutive attributes) (3 * std::mem::size_of::()) as *const gl::types::GLvoid, // offset of the first component ); gl::BindBuffer(gl::ARRAY_BUFFER, 0); gl::BindVertexArray(0); } // set up shared state for window unsafe { gl::Viewport(0, 0, 900, 700); gl::ClearColor(0.3, 0.3, 0.5, 1.0); } // ------------------------------------------- println!("OpenGL version: {}", gl_get_string(gl::VERSION)); println!("GLSL version: {}", gl_get_string(gl::SHADING_LANGUAGE_VERSION)); (glfw, window, events, shader_program, vao) } pub fn do_loop(mut glfw: Glfw, mut window: Window, events: Receiver<(f64, WindowEvent)>, shader_program: GLuint, vao: GLuint) { let mut framebuffer_handle = 0; let mut rendered_texture = 0; let mut quad_vbuf = 0; unsafe { // do frmaebuffer gl::GenFramebuffers(1, &mut framebuffer_handle); gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer_handle); // make tetxure gl::GenTextures(1, &mut rendered_texture); gl::BindTexture(gl::TEXTURE_2D, rendered_texture); // empty image gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB as GLint, WIDTH as GLsizei, HEIGHT as GLsizei, 0, gl::RGB, gl::UNSIGNED_BYTE, std::ptr::null_mut()); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint); // set color attachment gl::FramebufferTexture(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, rendered_texture, 0); let buffers = [gl::COLOR_ATTACHMENT0]; gl::DrawBuffers(1, buffers.as_ptr()); if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE { panic!("failed to create fbo") } let fullscreen_fbo = [ -1.0f32, -1.0, 0.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, ]; gl::GenBuffers(1, &mut quad_vbuf); gl::BindBuffer(gl::ARRAY_BUFFER, quad_vbuf); gl::BufferData( gl::ARRAY_BUFFER, // target (fullscreen_fbo.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, // size of data in bytes fullscreen_fbo.as_ptr() as *const gl::types::GLvoid, // pointer to data gl::STATIC_DRAW, // usage ); } const VERT_SHADER: &str = r"#version 150 core out vec2 v_tex; const vec2 pos[4]=vec2[4](vec2(-1.0, 1.0), vec2(-1.0,-1.0), vec2( 1.0, 1.0), vec2( 1.0,-1.0)); void main() { v_tex=0.5*pos[gl_VertexID] + vec2(0.5); gl_Position=vec4(pos[gl_VertexID], 0.0, 1.0); } "; const FRAG_SHADER: &str = r" #version 150 core in vec2 v_tex; uniform sampler2D texSampler; out vec4 color; void main() { color=texture(texSampler, v_tex); }"; let quad_programid = compile_program(VERT_SHADER, FRAG_SHADER); let mut quad_vao = 0; unsafe { gl::GenVertexArrays(1, &mut quad_vao); } while !window.should_close() { glfw.poll_events(); for (_, event) in glfw::flush_messages(&events) { glfw_handle_event(&mut window, event); } unsafe { // render to fb gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer_handle); // gl::BindFramebuffer(gl::FRAMEBUFFER, 0); gl::Viewport(0, 0, WIDTH as GLsizei, HEIGHT as GLsizei); // clear color // clear_color(Color(0.3, 0.4, 0.6, 1.0)); gl::Clear(gl::COLOR_BUFFER_BIT); // do the drawing gl::UseProgram(shader_program); // select vertices gl::BindVertexArray(vao); // draw to bound target gl::DrawArrays(gl::TRIANGLES, 0, 3); // unselect vertices gl::BindVertexArray(0); // texture is done now. // todo: insert postprocessing stuff to rendered_texture // map quad to screen gl::BindFramebuffer(gl::FRAMEBUFFER, 0); gl::ActiveTexture(gl::TEXTURE0); gl::BindTexture(gl::TEXTURE_2D, rendered_texture); gl::UseProgram(quad_programid); gl::BindVertexArray(quad_vao); gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4) } window.swap_buffers(); } } pub struct Color(f32, f32, f32, f32); pub fn clear_color(c: Color) { unsafe { gl::ClearColor(c.0, c.1, c.2, c.3) } } pub fn gl_get_string<'a>(name: gl::types::GLenum) -> &'a str { let v = unsafe { gl::GetString(name) }; let v: &std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(v as *const i8) }; v.to_str().unwrap() } fn glfw_handle_event(window: &mut glfw::Window, event: glfw::WindowEvent) { use glfw::WindowEvent as Event; use glfw::Key; use glfw::Action; match event { Event::Key(Key::Escape, _, Action::Press, _) => { window.set_should_close(true); }, _ => {}, } }