use std::{ ptr::{ null as null_ptr, null_mut as null_ptr_mut, }, ffi::{ c_void, CString, CStr, }, fs::read_to_string, mem::size_of, }; use sdl2::{ event::Event, keyboard::Keycode, video::GLProfile, }; use gl::types::*; use serde::Deserialize; use mod_engine::{ // make_key_type, // types::slot_map::{ // SlotMap, // KeyData, // Key // }, make_config_type, util::{ UnwrapPretty, load_json, }, }; /* make_key_type! { struct Key0; } */ static VERTEX_DATA: [GLfloat; 9] = [ 0.0, 0.5, 0.0, 0.5, -0.5, 0.0, -0.5, -0.5, 0.0 ]; fn compile_shader (src: &str, ty: GLenum) -> Result { unsafe { let shader = gl::CreateShader(ty); gl::ShaderSource(shader, 1, &CString::new(src).unwrap().as_ptr(), null_ptr()); gl::CompileShader(shader); let mut status: GLint = 0; gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); if status != 1 { let mut len: GLint = 0; gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); let mut buf: Vec = Vec::with_capacity(len as _); gl::GetShaderInfoLog(shader, len, null_ptr_mut(), buf.as_mut_ptr()); Err(CStr::from_ptr(buf.as_ptr()).to_str().unwrap().to_owned()) } else { Ok(shader) } } } fn link_program (vertex: GLuint, fragment: GLuint) -> Result { unsafe { let program = gl::CreateProgram(); gl::AttachShader(program, vertex); gl::AttachShader(program, fragment); gl::LinkProgram(program); let mut status: GLint = 0; gl::GetProgramiv(program, gl::LINK_STATUS, &mut status); if status != 1 { let mut len: GLint = 0; gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len); let mut buf: Vec = Vec::with_capacity(len as _); gl::GetProgramInfoLog(program, len, null_ptr_mut(), buf.as_mut_ptr()); Err(CStr::from_ptr(buf.as_ptr()).to_str().unwrap().to_owned()) } else { Ok(program) } } } extern "system" fn gl_error_handler ( _source: GLenum, kind: GLenum, id: GLuint, severity: GLenum, _length: GLsizei, message: *const GLchar, _user_param: *mut c_void ) { #[cfg(release)] { if id == 131169 || id == 131185 || id == 131218 || id == 131204 { return } } println!( "OpenGL Message kind: {} id: {} severity: {}, message: {}", match kind { gl::DEBUG_TYPE_ERROR => "General", gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated behavior", gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined behavior", gl::DEBUG_TYPE_PORTABILITY => "Portability", gl::DEBUG_TYPE_PERFORMANCE => "Performance", _ => "Other" }, id, match severity { gl::DEBUG_SEVERITY_LOW => "Low", gl::DEBUG_SEVERITY_MEDIUM => "Medium", gl::DEBUG_SEVERITY_HIGH => "High", gl::DEBUG_SEVERITY_NOTIFICATION => "Notification", _ => "Unknown" }, unsafe { CStr::from_ptr(message).to_str().unwrap() } ); } make_config_type! { struct OpenGLConfig { version: (u8, u8) = (3, 2), double_buffer: bool = true, depth_size: u8 = 24, stencil_size: u8 = 8, } } #[derive(Default, Deserialize, Debug)] struct EngineConfig { #[serde(default)] open_gl: OpenGLConfig } fn main() { let config: EngineConfig = if let Ok(c) = load_json("./engine_config.json") { println!("Loaded engine_config.json"); c } else { println!("Did not load engine_config.json"); Default::default() }; let sdl_context = sdl2::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); let gl_attr = video_subsystem.gl_attr(); gl_attr.set_context_profile(GLProfile::Core); gl_attr.set_context_version(config.open_gl.version.0, config.open_gl.version.1); gl_attr.set_double_buffer(config.open_gl.double_buffer); gl_attr.set_depth_size(config.open_gl.depth_size); gl_attr.set_stencil_size(config.open_gl.stencil_size); let window = video_subsystem.window(env!("CARGO_PKG_NAME"), 1280, 720) .opengl() .build() .unwrap(); let _ctx = window.gl_create_context().unwrap(); gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _); assert_eq!(gl_attr.context_profile(), GLProfile::Core); assert_eq!(gl_attr.context_version(), config.open_gl.version); println!("Created OpenGL context with version {:?}", config.open_gl.version); unsafe { gl::Enable(gl::DEBUG_OUTPUT); gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS); gl::DebugMessageCallback(gl_error_handler, null_ptr()); let mut unused_ids: GLuint = 0; gl::DebugMessageControl(gl::DONT_CARE, gl::DONT_CARE, gl::DONT_CARE, 0, &mut unused_ids, gl::TRUE); } let vs_src = read_to_string("resources/shaders/min3d.vert").expect_pretty("Failed to read vertex shader"); let fs_src = read_to_string("resources/shaders/min.frag").expect_pretty("Failed to read fragment shader"); let vs = compile_shader(&vs_src, gl::VERTEX_SHADER).expect_pretty("Failed to compile vertex shader"); let fs = compile_shader(&fs_src, gl::FRAGMENT_SHADER).expect_pretty("Failed to compile fragment shader"); let program = link_program(vs, fs).expect_pretty("Failed to link shader program"); let mut vao: GLuint = 0; let mut vbo: GLuint = 0; unsafe { gl::GenVertexArrays(1, &mut vao); gl::BindVertexArray(vao); gl::GenBuffers(1, &mut vbo); gl::BindBuffer(gl::ARRAY_BUFFER, vbo); gl::BufferData(gl::ARRAY_BUFFER, (VERTEX_DATA.len() * size_of::()) as GLsizeiptr, VERTEX_DATA.as_ptr() as _, gl::STATIC_DRAW); gl::UseProgram(program); // gl::GetAttribLocation(program, CStr::from_bytes_with_nul_unchecked(b"out_color\0").as_ptr()); let pos_attr = gl::GetAttribLocation(program, b"position\0".as_ptr() as _); gl::EnableVertexAttribArray(pos_attr as _); gl::VertexAttribPointer(pos_attr as _, 3, gl::FLOAT, gl::FALSE, 0, null_ptr()); } let mut event_pump = sdl_context.event_pump().unwrap(); 'running: loop { for event in event_pump.poll_iter() { match event { Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { break 'running }, _ => {} } } unsafe { gl::ClearColor(0.6, 0.0, 0.8, 1.0); gl::Clear(gl::COLOR_BUFFER_BIT); gl::DrawArrays(gl::TRIANGLES, 0, 3); } window.gl_swap_window(); } unsafe { gl::DeleteProgram(program); gl::DeleteShader(fs); gl::DeleteShader(vs); gl::DeleteBuffers(1, &vbo); gl::DeleteVertexArrays(1, &vao); } }