use std::{ convert::TryInto, ffi::{ CString, c_void, }, time::Duration, }; use anyhow::{ anyhow, Context, }; use glam::{ Mat4, Vec3, }; use sdl2::{ event::Event, keyboard::{Keycode, Scancode}, }; use tracing::instrument; use opengl_rust::{ glezz, gpu_buffers, shader, timestep::TimeStep, }; struct GraphicsContext { window: sdl2::video::Window, gl_ctx: sdl2::video::GLContext, vertex_buffer: gpu_buffers::VertexBuffer, index_buffer: gpu_buffers::IndexBuffer, shader_program: shader::ShaderProgram, shader_locations: ShaderLocations, } struct ShaderLocations { attr_pos: u32, attr_color: u32, uni_mvp: i32, } impl ShaderLocations { pub fn new (shader_program: &shader::ShaderProgram) -> anyhow::Result { let attr = |name: &str| shader_program.get_attribute_location (&CString::new (name.as_bytes ())?).try_into ().context ("Attribute location negative"); let uni = |name: &str| shader_program.get_uniform_location (&CString::new (name.as_bytes ())?).try_into ().context ("Uniform location bad"); Ok (Self { attr_pos: attr ("attr_pos")?, attr_color: attr ("attr_color")?, uni_mvp: uni ("uni_mvp")?, }) } } #[instrument (level = "trace", skip (ctx))] fn draw_graphics (ctx: &GraphicsContext) { let shader_locs = &ctx.shader_locations; let attr_pos = shader_locs.attr_pos; let attr_color = shader_locs.attr_color; let uni_mvp = shader_locs.uni_mvp; ctx.window.gl_make_current (&ctx.gl_ctx).unwrap (); glezz::clear_color (0.392f32, 0.710f32, 0.965f32, 1.0f32); glezz::clear (gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); let screen_size = (960.0, 540.0); let proj_mat = Mat4::perspective_rh_gl (30.0f32.to_radians (), screen_size.0 / screen_size.1, 0.125, 200.0); let view_mat = proj_mat * Mat4::from_translation (Vec3::from ((0.0, 0.0, -20.0))); let mvp = view_mat; glezz::uniform_matrix_4fv (uni_mvp, &mvp); ctx.shader_program.use_program (); glezz::enable_vertex_attrib_array (Some (attr_pos)); glezz::enable_vertex_attrib_array (Some (attr_color)); ctx.vertex_buffer.bind (); ctx.index_buffer.bind (); unsafe { let num_quads = 64 * 64; let stride = 4 * 6; gl::VertexAttribPointer (attr_pos, 3, gl::FLOAT, 0, stride, 0 as *const u8 as *const c_void); gl::VertexAttribPointer (attr_color, 3, gl::FLOAT, 0, stride, (4 * 3) as *const u8 as *const c_void); gl::DrawRangeElements (gl::TRIANGLES, 0, num_quads * 4, num_quads as i32 * 6, gl::UNSIGNED_INT, 0 as *const u8 as *const c_void); } ctx.window.gl_swap_window (); } fn main () -> anyhow::Result <()> { tracing_subscriber::fmt::fmt () .with_env_filter (tracing_subscriber::EnvFilter::from_default_env()) .with_span_events (tracing_subscriber::fmt::format::FmtSpan::CLOSE) .init (); let sdl_context = sdl2::init ().map_err (|e| anyhow! ("Can't init SDL: {}", e))?; let video_subsystem = sdl_context.video ().map_err (|e| anyhow! ("Can't get SDL video subsystem: {}", e))?; let window = video_subsystem.window ("Heightmap terrain demo", 960, 540) .position_centered () .opengl () .build () ?; gl::load_with (|s| { video_subsystem.gl_get_proc_address (s) as *const _ }); assert! (gl::ClearColor::is_loaded ()); let gl_ctx = window.gl_create_context ().map_err (|e| anyhow! ("Can't create OpenGL context: {}", e))?; window.gl_make_current (&gl_ctx).map_err (|e| anyhow! ("Can't make OpenGL context current: {}", e))?; let mut time_step = TimeStep::new (60, 1000); let mut graphics_frames = 0; let controller_subsystem = sdl_context.game_controller ().unwrap (); let controller = controller_subsystem.open (0).ok (); let mut event_pump = sdl_context.event_pump ().unwrap (); let shader_program = shader::shader_from_files ("shaders/terrain-vert.glsl", "shaders/terrain-frag.glsl"); let shader_locations = ShaderLocations::new (&shader_program)?; let mut vertexes = vec![]; let mut indexes = vec![]; let mut start_index = 0; for y in 0..64 { for x in 0..64 { let (r, g, b) = if (x + y) % 2 == 0 { (0.4, 0.4, 0.4) } else { (0.6, 0.6, 0.6) }; let x = x as f32; let y = y as f32; let i = start_index; vertexes.extend (&[ x + 0.0, y + 0.0, 0.0, r, g, b, x + 1.0, y + 0.0, 0.0, r, g, b, x + 1.0, y + 1.0, 0.0, r, g, b, x + 0.0, y + 1.0, 0.0, r, g, b, ]); indexes.extend (&[ i + 0, i + 1, i + 2, i + 0, i + 2, i + 3, ]); start_index += 4; } } let vertex_buffer = gpu_buffers::VertexBuffer::from_slice (&vertexes); let index_buffer = gpu_buffers::IndexBuffer::from_slice_u32 (&indexes); let graphics_ctx = GraphicsContext { window, gl_ctx, vertex_buffer, index_buffer, shader_program, shader_locations, }; 'running: loop { let frames_to_do = time_step.step (); 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 }, _ => (), } } for _ in 0..frames_to_do { } draw_graphics (&graphics_ctx); graphics_frames += 1; std::thread::sleep (Duration::from_millis (15)); } Ok (()) }