use std::{ sync::Arc, time::Instant, }; use anyhow::Result; use tokio::{ io::AsyncWriteExt, net::TcpListener, sync::watch, task::JoinHandle, }; use opengl_rust::{ prelude::*, }; mod graphics; mod level_loader; mod logic; mod virtual_gamepad; use graphics::Graphics; use level_loader::LoadedLevel; use logic::LogicState; use virtual_gamepad::VirtualGamepad; pub struct GameState { logic: LogicState, } #[tokio::main] async fn main () -> 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 ("3D platformer", 320 * 2, 240 * 2) .position_centered () .opengl () .build () ?; gl::load_with (|s| { video_subsystem.gl_get_proc_address (s) as *const _ }); if ! gl::ClearColor::is_loaded () { bail! ("gl::ClearColor didn't load, something is wrong with OpenGL"); } 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 event_pump = sdl_context.event_pump ().unwrap (); let mut time_step = TimeStep::new (60, 1000); let level = LoadedLevel::from_path ("gltf/level-00.glb")?; let mut graphics = Graphics::new (); let mut gl_state = Default::default (); let mut game_state = GameState { logic: Default::default (), }; let phys_params = opengl_rust::physics::Params { dt: 1.0 / 60.0, gravity: (0.0, 0.0, -0.5).into (), margin: 0.00125, }; game_state.logic.reset_level (&level); let mut next_upf_print = 60; let mut last_upf_instant = Instant::now (); let depth_buffer = vec! [0u8; 320 * 2 * 240 * 2 * 4]; let depth_buffer = Arc::new (depth_buffer); let (depth_tx, depth_rx) = watch::channel (depth_buffer); let depth_rx_2 = depth_rx.clone (); let _: JoinHandle > = tokio::spawn (async move { let tcp_listener = TcpListener::bind ("127.0.0.1:0").await?; tracing::info! ("Listening for TCP on {:?}", tcp_listener.local_addr ()); loop { let (socket, _) = tcp_listener.accept ().await?; let mut depth_rx_3 = depth_rx_2.clone (); let _: JoinHandle > = tokio::spawn (async move { let (_rx, mut tx) = socket.into_split (); loop { depth_rx_3.changed ().await?; let depth_buffer = Arc::clone (&depth_rx_3.borrow_and_update ()); tx.write_all (&depth_buffer [..]).await?; } }); } }); 'running: loop { let frames_to_do = time_step.step (); if frames_to_do != 1 { // debug! ("frames_to_do = {}", frames_to_do); } let mut player_gamepad = virtual_gamepad::VirtualGamepad::default (); for event in event_pump.poll_iter () { match event { Event::Quit {..} | Event::KeyDown { keycode: Some (Keycode::Escape), .. } => { break 'running }, Event::KeyDown { keycode: Some (Keycode::R), .. } => { game_state.logic.reset_level (&level); }, Event::KeyDown { keycode: Some (Keycode::F12), .. } => { let depth_bytes: Vec = depth_rx.borrow () .chunks_exact (4) .map (|x| u32::from_le_bytes ([x [0], x [1], x [2], x [3]])) .map (|x| (x >> 16) as u16) .map (|x| x.to_le_bytes ()) .flatten () .collect (); let mut f = std::fs::File::create ("screenshot.data")?; f.write_all (&depth_bytes)?; }, Event::KeyDown { scancode: Some (Scancode::Space), repeat: false, .. } => { player_gamepad.jump.pressed = true; }, Event::KeyDown { scancode: Some (Scancode::Left), repeat: false, .. } => { player_gamepad.d_left.pressed = true; }, Event::KeyDown { scancode: Some (Scancode::Right), repeat: false, .. } => { player_gamepad.d_right.pressed = true; }, Event::KeyDown { scancode: Some (Scancode::Up), repeat: false, .. } => { player_gamepad.d_up.pressed = true; }, Event::KeyDown { scancode: Some (Scancode::Down), repeat: false, .. } => { player_gamepad.d_down.pressed = true; }, _ => (), } } { let kb_state = event_pump.keyboard_state (); if kb_state.is_scancode_pressed (Scancode::Space) { player_gamepad.jump.held = true; } if kb_state.is_scancode_pressed (Scancode::Left) { player_gamepad.d_left.held = true; } if kb_state.is_scancode_pressed (Scancode::Right) { player_gamepad.d_right.held = true; } if kb_state.is_scancode_pressed (Scancode::Up) { player_gamepad.d_up.held = true; } if kb_state.is_scancode_pressed (Scancode::Down) { player_gamepad.d_down.held = true; } } let p_gp = player_gamepad; for _ in 0..frames_to_do { let logic_step_output = game_state.logic.step (&level.phys_tris, &phys_params, p_gp); if logic_step_output.reset_level { game_state.logic.reset_level (&level); } } // dbg! (game_state.logic.player.pos); window.gl_make_current (&gl_ctx).unwrap (); let prediction_frames = 4; let mut predicted_logic = game_state.logic.clone (); for _ in 0..prediction_frames { predicted_logic.step (&level.phys_tris, &phys_params, p_gp); } graphics.draw (&level.phys_tris, &predicted_logic, &mut gl_state, &level, &level.camera); graphics.frames += 1; if graphics.frames == next_upf_print { let now = Instant::now (); let upf = (now - last_upf_instant).as_micros () / 60; dbg! (upf); next_upf_print += 60; last_upf_instant = now; } window.gl_swap_window (); { let mut depth_buffer = vec! [0u8; 320 * 2 * 240 * 2 * 4]; unsafe { gl::ReadPixels (0, 0, 320 * 2, 240 * 2, gl::DEPTH_COMPONENT, gl::UNSIGNED_INT, &mut depth_buffer [0] as *mut u8 as *mut std::ffi::c_void); } let depth_buffer = Arc::new (depth_buffer); // Shouldn't fail, because we always keep one receiver open ourselves depth_tx.send (depth_buffer)?; } tokio::time::sleep (Duration::from_millis (10)).await; } Ok (()) } /* struct TriangleStream { pub verts: gpu_buffers::VertexBuffer, pub indices: gpu_buffers::IndexBuffer, } */