2021-32-bit-holiday-jam/src/bin/platformer/main.rs

291 lines
7.4 KiB
Rust

use std::{
time::Instant,
};
use anyhow::Result;
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,
static_level: StaticLevel,
}
pub struct StaticLevel {
tris: Vec <opengl_rust::physics::Triangle>,
}
#[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 scene = level.level.scenes ().next ().unwrap ();
let mut camera = None;
let mut phys_tris: Vec <opengl_rust::physics::Triangle> = Vec::new ();
for node in scene.nodes () {
if node.camera ().is_some () {
camera = Some (Camera {
transform: node.transform ().clone (),
});
}
let mesh = match node.mesh () {
None => continue,
Some (x) => x,
};
let m = gltf_node_get_mat4 (&node);
for prim in mesh.primitives () {
use gltf::Semantic;
let positions = match prim.get (&Semantic::Positions) {
None => continue,
Some (x) => x,
};
let pos_view = match positions.view () {
None => continue,
Some (x) => x,
};
let indices = match prim.indices () {
None => continue,
Some (x) => x,
};
let indices_view = match indices.view () {
None => continue,
Some (x) => x,
};
let idx_start = indices.offset () + indices_view.offset ();
let idx_slice = &level.buffer [idx_start..idx_start + indices.count () * 2];
let vert_start = positions.offset () + pos_view.offset ();
let vert_slice = &level.buffer [vert_start..vert_start + positions.count () * 4 * 3];
for chunk in idx_slice.chunks_exact (2 * 3) {
let read_idx = |i: usize| u16::from_le_bytes ([chunk [i * 2 + 0], chunk [i * 2 + 1]]);
let idxs = [
read_idx (0),
read_idx (1),
read_idx (2),
];
let read_pos_coord = |i| f32::from_le_bytes ([
vert_slice [i * 4 + 0],
vert_slice [i * 4 + 1],
vert_slice [i * 4 + 2],
vert_slice [i * 4 + 3],
]);
let read_pos = move |i| {
let i = usize::try_from (i).unwrap ();
m.transform_point3 (Vec3::new (
read_pos_coord (i * 3 + 0),
read_pos_coord (i * 3 + 1),
read_pos_coord (i * 3 + 2),
))
};
// glTF triangle winding is backwards from what I expected
// no biggie
let verts = [
read_pos (idxs [0]),
read_pos (idxs [2]),
read_pos (idxs [1]),
];
phys_tris.push (opengl_rust::physics::Triangle {
verts,
});
}
}
}
let camera = match camera {
None => bail! ("Couldn't find camera node in glTF file"),
Some (x) => x,
};
let mut graphics = Graphics::new ();
let mut gl_state = Default::default ();
let mut game_state = GameState {
logic: Default::default (),
static_level: StaticLevel {
tris: phys_tris,
},
};
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 mut depth_buffer = vec! [0u32; 320 * 2 * 240 * 2];
'running: loop {
let _frames_to_do = time_step.step ();
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 <u8> = depth_buffer.iter ()
.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;
let logic_step_output = game_state.logic.step (&game_state.static_level, &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 (&game_state.static_level, &phys_params, p_gp);
}
graphics.draw (&game_state.static_level, &predicted_logic, &mut gl_state, &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 ();
unsafe {
gl::ReadPixels (0, 0, 320 * 2, 240 * 2, gl::DEPTH_COMPONENT, gl::UNSIGNED_INT, &mut depth_buffer [0] as *mut u32 as *mut std::ffi::c_void);
}
tokio::time::sleep (Duration::from_millis (10)).await;
}
Ok (())
}
/*
struct TriangleStream {
pub verts: gpu_buffers::VertexBuffer,
pub indices: gpu_buffers::IndexBuffer,
}
*/
pub struct Camera {
transform: gltf::scene::Transform,
}
fn gltf_node_get_mat4 (node: &gltf::Node) -> Mat4 {
Mat4::from_cols_array_2d (&node.transform ().matrix ())
}