use nom::{ IResult, bytes::complete::{tag, take_while_m_n}, combinator::map_res, sequence::tuple }; use sdl2::event::Event; use sdl2::keyboard::Keycode; use std::collections::HashMap; use std::convert::TryInto; use std::ffi::{c_void, CStr, CString}; use std::fs::File; use std::io::Read; use std::path::Path; use std::time::Duration; pub fn parse_iqm (input: &[u8]) -> IResult<&[u8], ()> { let (input, _) = tag (b"INTERQUAKEMODEL\0")(input)?; Ok ((input, ())) } pub struct IqmBuffer { data: Vec , } pub fn load_small_file

(name: P) -> Vec where P: AsRef { let mut f = File::open (name).unwrap (); let len = f.metadata ().unwrap ().len (); if len > 1024 * 1024 { panic! ("File is too big"); } let mut data = vec! [0u8; len.try_into ().unwrap ()]; f.read (&mut data [..]).unwrap (); data } impl IqmBuffer { pub fn from_buffer (data: Vec ) -> IqmBuffer { assert_eq! (parse_iqm (&data [..]), Ok ((&data [16..], ()))); IqmBuffer { data, } } } 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 ()) } } pub fn get_attribute_location (&self, name: &CStr) -> i32 { unsafe { gl::UseProgram (self.id); gl::GetAttribLocation (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 unis: HashMap <_, _> = vec! [ "model", "viewproj", ].iter () .map (|name| { let mut s = String::from ("uni_"); s.push_str (name); let c_str = CString::new (s.as_bytes ()).unwrap (); (String::from (*name), shader_program.get_uniform_location (&c_str)) }) .collect (); println! ("uni_model: {}", unis ["model"]); println! ("uni_viewproj: {}", unis ["viewproj"]); let attrs: HashMap <_, u32> = vec! [ "pos", "uv", //"normal", ].iter () .map (|name| { let mut s = String::from ("attr_"); s.push_str (name); let c_str = CString::new (s.as_bytes ()).unwrap (); let loc = shader_program.get_attribute_location (&c_str); (String::from (*name), loc.try_into ().unwrap ()) }) .collect (); println! ("attr_pos: {}", attrs ["pos"]); let tri_mesh: Vec = vec! [ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, ]; let id_mat: Vec = vec! [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ]; let indexes: Vec = vec! [0, 1, 2]; const FALSE_U8: u8 = 0; unsafe { gl::EnableVertexAttribArray (attrs ["pos"]); let stride = 4 * 2; gl::VertexAttribPointer (attrs ["pos"], 2, gl::FLOAT, FALSE_U8, stride, &tri_mesh [0] as *const f32 as *const c_void); gl::UniformMatrix4fv (unis ["viewproj"], 1, FALSE_U8, &id_mat [0]); gl::UniformMatrix4fv (unis ["model"], 1, FALSE_U8, &id_mat [0]); } 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); gl::DrawElements (gl::TRIANGLES, 3, gl::UNSIGNED_SHORT, &indexes [0] as *const u16 as *const c_void); } window.gl_swap_window (); ::std::thread::sleep (Duration::from_millis (15)); } } #[cfg (test)] mod tests { use super::*; #[test] pub fn iqm () { assert_eq! (parse_iqm (b"INTERQUAKEMODEL\0"), Ok ((&b"" [..], ()))); IqmBuffer::from_buffer (load_small_file ("pumpking.iqm")); } }