use sdl2::event::Event; use sdl2::keyboard::Keycode; use std::convert::TryInto; use std::ffi::{CStr, CString}; use std::time::Duration; const VERT_SHADER_SRC: &str = " #define lowp #define mediump #define highp #line 0 uniform highp mat4 uni_model; uniform highp mat4 uni_viewproj; attribute highp vec4 attr_pos; attribute mediump vec2 attr_uv; attribute lowp vec3 attr_normal; varying lowp vec4 vary_color; varying mediump vec2 vary_uv; void main (void) { vary_uv = attr_uv; lowp vec4 light_color = vec4 (1.0); vary_color = light_color; vec4 world_pos = uni_model * attr_pos; gl_Position = uni_viewproj * world_pos; }"; const FRAG_SHADER_SRC: &str = " #define lowp #define mediump #define highp #line 0 uniform lowp sampler2D uni_texture; varying lowp vec4 vary_color; varying mediump vec2 vary_uv; void main (void) { gl_FragColor = texture2D (uni_texture, vary_uv) * vary_color; //gl_FragColor = vec4 (1.0, 0.0, 1.0, 1.0); } "; pub struct ShaderObject { id: u32, } impl ShaderObject { pub fn id (&self) -> u32 { self.id } pub fn new (shader_type: u32, source: &str) -> Result { let id = unsafe { gl::CreateShader (shader_type) }; let sources = [ source.as_ptr () as *const i8, ]; let lengths = [ source.len ().try_into ().unwrap (), ]; let success = unsafe { gl::ShaderSource (id, sources.len ().try_into ().unwrap (), sources.as_ptr (), lengths.as_ptr ()); gl::CompileShader (id); let mut success = 0; gl::GetShaderiv (id, gl::COMPILE_STATUS, &mut success); success == 1 }; if success { Ok (ShaderObject { id, }) } else { let mut info_log = vec! [0u8; 4096]; let mut log_length = 0; unsafe { gl::GetShaderInfoLog (id, (info_log.len () - 1).try_into ().unwrap (), &mut log_length, info_log.as_mut_ptr () as *mut i8); } info_log.truncate (log_length.try_into ().unwrap ()); let info = String::from_utf8 (info_log).unwrap (); Err (info) } } } impl Drop for ShaderObject { fn drop (&mut self) { unsafe { gl::DeleteShader (self.id); } } } pub struct ShaderProgram { id: u32, } impl ShaderProgram { pub fn new (vert: &ShaderObject, frag: &ShaderObject) -> Result { let id = unsafe { gl::CreateProgram () }; unsafe { gl::AttachShader (id, vert.id ()); gl::AttachShader (id, frag.id ()); gl::LinkProgram (id); } let success = unsafe { let mut success = 0; gl::GetProgramiv (id, gl::LINK_STATUS, &mut success); success == 1 }; if success { Ok (ShaderProgram { id, }) } else { let mut info_log = vec! [0u8; 4096]; let mut log_length = 0; unsafe { gl::GetProgramInfoLog (id, (info_log.len () - 1).try_into ().unwrap (), &mut log_length, info_log.as_mut_ptr () as *mut i8); } info_log.truncate (log_length.try_into ().unwrap ()); let info = String::from_utf8 (info_log).unwrap (); Err (info) } } pub fn get_uniform_location (&self, name: &CStr) -> i32 { unsafe { gl::UseProgram (self.id); gl::GetUniformLocation (self.id, name.as_ptr ()) } } } impl Drop for ShaderProgram { fn drop (&mut self) { unsafe { gl::DeleteProgram (self.id); } } } fn main () { let sdl_context = sdl2::init ().unwrap (); let video_subsystem = sdl_context.video ().unwrap (); let window = video_subsystem.window ("OpenGL? In my Rust?", 1280, 720) .position_centered () .opengl () .build () .unwrap (); gl::load_with (|s| { let result = video_subsystem.gl_get_proc_address (s) as *const _; //println! ("{:?}", result); result }); assert! (gl::ClearColor::is_loaded ()); let gl_ctx = window.gl_create_context ().unwrap (); window.gl_make_current (&gl_ctx).unwrap (); // I love how with Rust I can throw unwrap ()s everywhere // It's safer than C / C++'s default behavior of unchecked errors // It's more programmer-friendly and explicit than C#'s unchecked // exceptions that can appear almost anywhere at runtime with no // compile-time warning // And I'm still not actually checking errors - Just checkmarking // that I know where they are. let vert_shader = ShaderObject::new (gl::VERTEX_SHADER, VERT_SHADER_SRC).unwrap (); let frag_shader = ShaderObject::new (gl::FRAGMENT_SHADER, FRAG_SHADER_SRC).unwrap (); let shader_program = ShaderProgram::new (&vert_shader, &frag_shader).unwrap (); let uni_model = shader_program.get_uniform_location (&CString::new ("uni_model").unwrap ()); let uni_viewproj = shader_program.get_uniform_location (CStr::from_bytes_with_nul (b"uni_viewproj\0").unwrap ()); println! ("uni_model: {}", uni_model); println! ("uni_viewproj: {}", uni_viewproj); let mut event_pump = sdl_context.event_pump ().unwrap (); 'running: loop { let _mouse = event_pump.mouse_state (); for event in event_pump.poll_iter() { match event { Event::Quit {..} | Event::KeyDown { keycode: Some (Keycode::Escape), .. } => { break 'running }, _ => (), } } window.gl_make_current (&gl_ctx).unwrap (); unsafe { gl::ClearColor (1.0f32, 0.0f32, 1.0f32, 1.0f32); gl::Clear (gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); } window.gl_swap_window (); ::std::thread::sleep (Duration::from_millis (15)); } }