| // Copyright 2015 Brendan Zabarauskas and the gl-rs developers |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| extern crate gl; |
| extern crate glutin; |
| |
| use gl::types::*; |
| use std::ffi::CString; |
| use std::mem; |
| use std::ptr; |
| use std::str; |
| |
| // Vertex data |
| static VERTEX_DATA: [GLfloat; 6] = [0.0, 0.5, 0.5, -0.5, -0.5, -0.5]; |
| |
| // Shader sources |
| static VS_SRC: &'static str = " |
| #version 150 |
| in vec2 position; |
| |
| void main() { |
| gl_Position = vec4(position, 0.0, 1.0); |
| }"; |
| |
| static FS_SRC: &'static str = " |
| #version 150 |
| out vec4 out_color; |
| |
| void main() { |
| out_color = vec4(1.0, 1.0, 1.0, 1.0); |
| }"; |
| |
| fn compile_shader(src: &str, ty: GLenum) -> GLuint { |
| let shader; |
| unsafe { |
| shader = gl::CreateShader(ty); |
| // Attempt to compile the shader |
| let c_str = CString::new(src.as_bytes()).unwrap(); |
| gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null()); |
| gl::CompileShader(shader); |
| |
| // Get the compile status |
| let mut status = gl::FALSE as GLint; |
| gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); |
| |
| // Fail on error |
| if status != (gl::TRUE as GLint) { |
| let mut len = 0; |
| gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); |
| let mut buf = Vec::with_capacity(len as usize); |
| buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character |
| gl::GetShaderInfoLog( |
| shader, |
| len, |
| ptr::null_mut(), |
| buf.as_mut_ptr() as *mut GLchar, |
| ); |
| panic!( |
| "{}", |
| str::from_utf8(&buf) |
| .ok() |
| .expect("ShaderInfoLog not valid utf8") |
| ); |
| } |
| } |
| shader |
| } |
| |
| fn link_program(vs: GLuint, fs: GLuint) -> GLuint { |
| unsafe { |
| let program = gl::CreateProgram(); |
| gl::AttachShader(program, vs); |
| gl::AttachShader(program, fs); |
| gl::LinkProgram(program); |
| // Get the link status |
| let mut status = gl::FALSE as GLint; |
| gl::GetProgramiv(program, gl::LINK_STATUS, &mut status); |
| |
| // Fail on error |
| if status != (gl::TRUE as GLint) { |
| let mut len: GLint = 0; |
| gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len); |
| let mut buf = Vec::with_capacity(len as usize); |
| buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character |
| gl::GetProgramInfoLog( |
| program, |
| len, |
| ptr::null_mut(), |
| buf.as_mut_ptr() as *mut GLchar, |
| ); |
| panic!( |
| "{}", |
| str::from_utf8(&buf) |
| .ok() |
| .expect("ProgramInfoLog not valid utf8") |
| ); |
| } |
| program |
| } |
| } |
| |
| fn main() { |
| let mut events_loop = glutin::EventsLoop::new(); |
| let window = glutin::WindowBuilder::new(); |
| let gl_window = glutin::ContextBuilder::new() |
| .build_windowed(window, &events_loop) |
| .unwrap(); |
| |
| // It is essential to make the context current before calling `gl::load_with`. |
| let gl_window = unsafe { gl_window.make_current() }.unwrap(); |
| |
| // Load the OpenGL function pointers |
| // TODO: `as *const _` will not be needed once glutin is updated to the latest gl version |
| gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _); |
| |
| // Create GLSL shaders |
| let vs = compile_shader(VS_SRC, gl::VERTEX_SHADER); |
| let fs = compile_shader(FS_SRC, gl::FRAGMENT_SHADER); |
| let program = link_program(vs, fs); |
| |
| let mut vao = 0; |
| let mut vbo = 0; |
| |
| unsafe { |
| // Create Vertex Array Object |
| gl::GenVertexArrays(1, &mut vao); |
| gl::BindVertexArray(vao); |
| |
| // Create a Vertex Buffer Object and copy the vertex data to it |
| gl::GenBuffers(1, &mut vbo); |
| gl::BindBuffer(gl::ARRAY_BUFFER, vbo); |
| gl::BufferData( |
| gl::ARRAY_BUFFER, |
| (VERTEX_DATA.len() * mem::size_of::<GLfloat>()) as GLsizeiptr, |
| mem::transmute(&VERTEX_DATA[0]), |
| gl::STATIC_DRAW, |
| ); |
| |
| // Use shader program |
| gl::UseProgram(program); |
| gl::BindFragDataLocation(program, 0, CString::new("out_color").unwrap().as_ptr()); |
| |
| // Specify the layout of the vertex data |
| let pos_attr = gl::GetAttribLocation(program, CString::new("position").unwrap().as_ptr()); |
| gl::EnableVertexAttribArray(pos_attr as GLuint); |
| gl::VertexAttribPointer( |
| pos_attr as GLuint, |
| 2, |
| gl::FLOAT, |
| gl::FALSE as GLboolean, |
| 0, |
| ptr::null(), |
| ); |
| } |
| |
| events_loop.run_forever(|event| { |
| use glutin::{ControlFlow, Event, WindowEvent}; |
| |
| if let Event::WindowEvent { event, .. } = event { |
| if let WindowEvent::CloseRequested = event { |
| return ControlFlow::Break; |
| } |
| } |
| |
| unsafe { |
| // Clear the screen to black |
| gl::ClearColor(0.3, 0.3, 0.3, 1.0); |
| gl::Clear(gl::COLOR_BUFFER_BIT); |
| |
| // Draw a triangle from the 3 vertices |
| gl::DrawArrays(gl::TRIANGLES, 0, 3); |
| } |
| |
| gl_window.swap_buffers().unwrap(); |
| |
| ControlFlow::Continue |
| }); |
| |
| // Cleanup |
| unsafe { |
| gl::DeleteProgram(program); |
| gl::DeleteShader(fs); |
| gl::DeleteShader(vs); |
| gl::DeleteBuffers(1, &vbo); |
| gl::DeleteVertexArrays(1, &vao); |
| } |
| } |