Compare commits

...

15 Commits

Author SHA1 Message Date
_ fc69662060 add depth streaming feature 2022-01-09 18:02:07 +00:00
_ 77ed8db317 actually use the fixed timestep 2022-01-09 17:35:19 +00:00
_ 8872a45d50 ♻️ refactor: move stuff out of main.rs 2022-01-09 17:32:07 +00:00
_ 74251fcf37 ♻️ refactor: extract 2022-01-09 17:19:55 +00:00
_ 5509aece8f 🚨 fix cargo check lints 2022-01-09 17:16:58 +00:00
_ e96474b73a ignore more files 2022-01-09 17:05:03 +00:00
_ 0dcf70649d ♻️ refactor 2022-01-09 17:03:58 +00:00
_ 252c299c2c update 2022-01-09 17:01:18 +00:00
_ 8b799e817e add depth screenshot. I have an idea 2022-01-09 16:19:31 +00:00
_ 176ff3de42 add prediction 2022-01-09 14:26:09 +00:00
_ e742bcad5c ♻️ refactor: split up static level param in graphics 2022-01-09 14:16:38 +00:00
_ 6ccae593d3 ♻️ refactor: extract logic step function 2022-01-09 14:13:28 +00:00
_ 2c41ea1e6b 🐛 bug: sleep less, to hit 60 FPS 2022-01-09 13:49:57 +00:00
_ d570ee4cc8 ♻️ refactor: extract VirtualGamepad 2022-01-09 13:49:44 +00:00
_ e7854f9021 🐛 bug: fix the multiple jump bug 2022-01-09 13:33:38 +00:00
11 changed files with 482 additions and 365 deletions

6
.gitignore vendored
View File

@ -1,7 +1,13 @@
/checkerboard.png
/config.json
/gltf
/quic_server.crt /quic_server.crt
/target /target
**/*.rs.bk **/*.rs.bk
/videos /videos
*.blend* *.blend*
*.data
*.obj
*.trace *.trace
*.xcf

9
Cargo.lock generated
View File

@ -1327,11 +1327,10 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.5.0" version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
dependencies = [ dependencies = [
"autocfg 1.0.1",
"bytes", "bytes",
"libc", "libc",
"memchr", "memchr",
@ -1347,9 +1346,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-macros" name = "tokio-macros"
version = "1.1.0" version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -29,7 +29,7 @@ rmp-serde = "0.15.4"
serde = { version = "1.0.125", features = ["derive"] } serde = { version = "1.0.125", features = ["derive"] }
serde_json = "1.0.73" serde_json = "1.0.73"
sdl2 = "0.32.2" sdl2 = "0.32.2"
tokio = { version = "1.5.0", features = ["full"] } tokio = { version = "1.15.0", features = ["full"] }
tracing = "0.1.22" tracing = "0.1.22"
tracing-subscriber = "0.2.15" tracing-subscriber = "0.2.15"

BIN
crate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -4,7 +4,7 @@ use std::{
use glam::{ use glam::{
Quat, Quat,
Vec4, // Vec4,
}; };
use opengl_rust::{ use opengl_rust::{
@ -15,18 +15,13 @@ use opengl_rust::{
Pass, Pass,
}, },
renderable_model::{ renderable_model::{
attributes,
RenderableModel, RenderableModel,
attributes,
}, },
shader_closure::ShaderLookup, shader_closure::ShaderLookup,
texture::Texture, texture::Texture,
}; };
use crate::{
GameState,
TriangleStream,
};
mod uniforms { mod uniforms {
use iota::iota; use iota::iota;
iota! { iota! {
@ -47,12 +42,12 @@ pub struct Graphics {
shaders: Vec <ShaderClosure>, shaders: Vec <ShaderClosure>,
shader_lookup: HashMap <u32, usize>, shader_lookup: HashMap <u32, usize>,
mesh_cube: RenderableModel, // mesh_cube: RenderableModel,
mesh_protag_kun: RenderableModel, mesh_protag_kun: RenderableModel,
mesh_sky: RenderableModel, mesh_sky: RenderableModel,
mesh_sphere: RenderableModel, mesh_sphere: RenderableModel,
text_stream: TriangleStream, // text_stream: TriangleStream,
texture_crate: Texture, texture_crate: Texture,
texture_earth: Texture, texture_earth: Texture,
@ -100,7 +95,7 @@ impl Graphics {
}) })
.collect (); .collect ();
let mesh_cube = renderable_from_iqm_file ("cube.iqm"); // let mesh_cube = renderable_from_iqm_file ("cube.iqm");
let mesh_protag_kun = renderable_from_iqm_file ("protag-kun.iqm"); let mesh_protag_kun = renderable_from_iqm_file ("protag-kun.iqm");
let mesh_sky = renderable_from_iqm_file ("sky-sphere.iqm"); let mesh_sky = renderable_from_iqm_file ("sky-sphere.iqm");
let mesh_sphere = renderable_from_iqm_file ("sphere.iqm"); let mesh_sphere = renderable_from_iqm_file ("sphere.iqm");
@ -125,7 +120,7 @@ impl Graphics {
.depth_mask (1) .depth_mask (1)
.clone (), .clone (),
]; ];
/*
let text_stream = TriangleStream { let text_stream = TriangleStream {
verts: gpu_buffers::VertexBuffer::streaming (4 * 5 * 6 * 1024), verts: gpu_buffers::VertexBuffer::streaming (4 * 5 * 6 * 1024),
indices: { indices: {
@ -140,7 +135,7 @@ impl Graphics {
gpu_buffers::IndexBuffer::from_slice_u32 (&v) gpu_buffers::IndexBuffer::from_slice_u32 (&v)
} }
}; };
*/
let shader_lookup = HashMap::from_iter (shaders.iter ().enumerate () let shader_lookup = HashMap::from_iter (shaders.iter ().enumerate ()
.map (|(i, s)| { .map (|(i, s)| {
(s.get_id (), i) (s.get_id (), i)
@ -154,14 +149,14 @@ impl Graphics {
}); });
Graphics { Graphics {
mesh_cube, // mesh_cube,
mesh_protag_kun, mesh_protag_kun,
mesh_sky, mesh_sky,
mesh_sphere, mesh_sphere,
passes, passes,
shader_lookup, shader_lookup,
shaders, shaders,
text_stream, // text_stream,
texture_crate: Texture::from_file ("crate.png"), texture_crate: Texture::from_file ("crate.png"),
texture_earth: Texture::from_file ("earth.png"), texture_earth: Texture::from_file ("earth.png"),
texture_sky: Texture::from_file ("sky.png"), texture_sky: Texture::from_file ("sky.png"),
@ -172,14 +167,15 @@ impl Graphics {
pub fn draw ( pub fn draw (
&self, &self,
state: &GameState, phys_tris: &[opengl_rust::physics::Triangle],
logic: &crate::LogicState,
gl_state: &mut GlState, gl_state: &mut GlState,
level: &crate::LoadedLevel, level: &crate::LoadedLevel,
camera: &crate::Camera, camera: &crate::level_loader::Camera,
) { ) {
use serde::Deserialize; // use serde::Deserialize;
use uniforms as u; use uniforms as u;
/*
#[derive (Default, Deserialize)] #[derive (Default, Deserialize)]
struct Config { struct Config {
camera: CameraCfg, camera: CameraCfg,
@ -192,7 +188,7 @@ impl Graphics {
#[serde(default)] #[serde(default)]
translate_mix: f32, translate_mix: f32,
} }
/*
let cfg: Option <Config> = std::fs::read_to_string ("config.json").ok () let cfg: Option <Config> = std::fs::read_to_string ("config.json").ok ()
.map (|s| serde_json::from_str (s.as_str ()).ok ()) .map (|s| serde_json::from_str (s.as_str ()).ok ())
.flatten (); .flatten ();
@ -216,7 +212,7 @@ impl Graphics {
) = camera.transform.clone ().decomposed (); ) = camera.transform.clone ().decomposed ();
let cam_trans = Vec3::from (cam_trans); let cam_trans = Vec3::from (cam_trans);
let cam_trans = Vec3::new (cam_trans.x.max (state.player.pos.x + 4.0), cam_trans.y, cam_trans.z); let cam_trans = Vec3::new (cam_trans.x.max (logic.player.pos.x + 4.0), cam_trans.y, cam_trans.z);
let view_mat = let view_mat =
Mat4::from_quat (Quat::from_array (cam_rot).inverse ()) * Mat4::from_quat (Quat::from_array (cam_rot).inverse ()) *
@ -226,8 +222,6 @@ impl Graphics {
let view_mat = proj_mat * view_mat; let view_mat = proj_mat * view_mat;
let world_model_mat = Mat4::IDENTITY;
self.passes [0].with (gl_state, || { self.passes [0].with (gl_state, || {
glezz::clear_color (109f32 / 255.0, 194f32 / 255.0, 202f32 / 255.0, 1.0f32); glezz::clear_color (109f32 / 255.0, 194f32 / 255.0, 202f32 / 255.0, 1.0f32);
glezz::clear (gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); glezz::clear (gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
@ -244,7 +238,7 @@ impl Graphics {
self.texture_earth.bind (); self.texture_earth.bind ();
let mvp = view_mat * let mvp = view_mat *
Mat4::from_translation (state.player.pos) * Mat4::from_translation (logic.player.pos) *
Mat4::from_scale ((0.5, 0.5, 0.5).into ()); Mat4::from_scale ((0.5, 0.5, 0.5).into ());
glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp); glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp);
glezz::uniform_3fv (unis [&u::ALBEDO], &white); glezz::uniform_3fv (unis [&u::ALBEDO], &white);
@ -340,9 +334,9 @@ impl Graphics {
if true { if true {
// Raycast for player shadow // Raycast for player shadow
let coll = opengl_rust::physics::get_candidate ( let coll = opengl_rust::physics::get_candidate (
&state.phys_tris, &state.aabbs, &phys_tris, &[],
state.player.pos, logic.player.pos,
state.player.pos + Vec3::new (0.0, 0.0, -100.0), logic.player.pos + Vec3::new (0.0, 0.0, -100.0),
0.0 0.0
); );

View File

@ -0,0 +1,152 @@
use anyhow::Result;
use opengl_rust::{
prelude::*,
physics::Triangle,
};
pub struct LoadedLevel {
pub player_spawn: Vec3,
pub camera: Camera,
pub phys_tris: Vec <Triangle>,
pub buffer: Vec <u8>,
pub level: gltf::Document,
}
impl LoadedLevel {
pub fn from_path (path: &str) -> Result <Self> {
use gltf::{
Semantic,
};
let (level, buffers, _) = gltf::import (path)?;
let buffer = match buffers.get (0) {
None => bail! ("gltf didn't load any buffers"),
Some (x) => x.0.to_vec (),
};
let scene = match level.scenes ().next () {
None => bail! ("No scenes in glTF file"),
Some (x) => x,
};
let mut player_spawn = None;
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 (),
});
}
if node.name () == Some ("Player Spawn") {
let (translation, _, _) = node.transform ().decomposed ();
player_spawn = Some (Vec3::from (translation));
}
else if let Some (_camera) = node.camera () {
}
else if let Some (mesh) = node.mesh () {
let m = gltf_node_get_mat4 (&node);
for (_, prim) in mesh.primitives ().enumerate () {
let positions = match prim.get (&Semantic::Positions) {
None => continue,
Some (x) => x,
};
let normals = match prim.get (&Semantic::Normals) {
None => continue,
Some (x) => x,
};
let indices = match prim.indices () {
None => continue,
Some (x) => x,
};
let pos_view = match positions.view () {
None => continue,
Some (x) => x,
};
let _norm_view = match normals.view () {
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 = &buffer [idx_start..idx_start + indices.count () * 2];
let vert_start = positions.offset () + pos_view.offset ();
let vert_slice = &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 player_spawn = match player_spawn {
None => bail! ("glTF file must have `Player Spawn` node"),
Some (x) => x,
};
let camera = match camera {
None => bail! ("Couldn't find camera node in glTF file"),
Some (x) => x,
};
Ok (Self {
buffer,
camera,
level,
phys_tris,
player_spawn,
})
}
}
pub struct Camera {
pub transform: gltf::scene::Transform,
}
pub fn gltf_node_get_mat4 (node: &gltf::Node) -> Mat4 {
Mat4::from_cols_array_2d (&node.transform ().matrix ())
}

138
src/bin/platformer/logic.rs Normal file
View File

@ -0,0 +1,138 @@
use opengl_rust::{
prelude::*,
physics::{
PhysicsBody,
Triangle,
},
};
use crate::{
LoadedLevel,
VirtualGamepad,
};
#[derive (Clone, Default)]
pub struct LogicState {
pub player: PhysicsBody,
player_jump_vec: Option <Vec3>,
}
#[derive (Clone, Copy, Default)]
pub struct LogicStepOutput {
pub reset_level: bool,
}
impl LogicState {
pub fn reset_level (&mut self, level: &LoadedLevel) {
self.player = Default::default ();
self.player.pos = level.player_spawn;
}
pub fn step (&mut self, phys_tris: &[Triangle], phys_params: &opengl_rust::physics::Params, p_gp: VirtualGamepad) -> LogicStepOutput
{
let player_acc = 1.0;
let player_acc_air = 0.125;
let player_max_speed = 4.0;
let player_max_speed_air = 8.0;
let player_jump_speed = 12.0;
let kill_z = -3.0;
let mut output = LogicStepOutput::default ();
let mut wanted_dir = Vec3::default ();
if p_gp.d_left.any_press () {
wanted_dir.x -= 1.0;
}
if p_gp.d_right.any_press () {
wanted_dir.x += 1.0;
}
if p_gp.d_up.any_press () {
wanted_dir.y += 1.0;
}
if p_gp.d_down.any_press () {
wanted_dir.y -= 1.0;
}
let wanted_dir = if wanted_dir.length_squared () >= 1.0 {
wanted_dir.normalize ()
}
else {
wanted_dir
};
let wanted_dir = wanted_dir;
let old_vel = self.player.vel;
let old_vel_2 = old_vel * Vec3::new (1.0, 1.0, 0.0);
let new_vel_2 = match self.player_jump_vec.as_ref () {
Some (v) => {
// Ground
let acc = player_acc * v.z * v.z;
let wanted_vel = wanted_dir * player_max_speed;
let diff = wanted_vel - old_vel_2;
if diff.length_squared () < acc * acc {
// We're near the wanted velocity, so snap to it
wanted_vel
}
else {
// We're not near the wanted velocity, so accelerate
old_vel_2 + diff.normalize_or_zero () * acc
}
},
// Air
None => {
let proposed_vel = old_vel_2 + wanted_dir * player_acc_air;
if old_vel_2.length_squared () < player_max_speed_air * player_max_speed_air {
// Air control is normal below player_max_speed
proposed_vel
}
else {
// If the player's input would push them beyond player_max_speed,
// apply drag to preserve overall energy
proposed_vel * old_vel_2.length () / proposed_vel.length ()
}
},
};
self.player.vel = new_vel_2;
self.player.vel.z = old_vel.z;
// dbg! (self.player.vel);
if p_gp.jump.pressed {
if let Some (normal) = self.player_jump_vec.clone () {
self.player.vel.z = 0.0;
self.player.vel += normal * player_jump_speed;
}
}
let phys_result = opengl_rust::physics::step (&phys_params, phys_tris, &[], 0.5, &self.player);
self.player = phys_result.body;
if self.player.pos.z < kill_z {
output.reset_level = true;
}
// tracing::debug! ("player pos: {}", self.player.pos);
self.player_jump_vec = None;
for normal in &phys_result.normals_hit {
self.player_jump_vec = Some (match self.player_jump_vec {
None => *normal,
Some (old) => {
if normal.z > old.z {
*normal
}
else {
old
}
},
});
}
output
}
}

View File

@ -1,39 +1,32 @@
use std::{ use std::{
collections::HashMap, sync::Arc,
time::Instant, time::Instant,
}; };
use anyhow::Result; use anyhow::Result;
use maplit::hashmap; use tokio::{
io::AsyncWriteExt,
net::TcpListener,
sync::watch,
task::JoinHandle,
};
use opengl_rust::{ use opengl_rust::{
prelude::*, prelude::*,
gl_state::*,
physics::PhysicsBody,
renderable_model::{
attributes,
RenderableModel,
},
shader_closure::ShaderLookup,
texture::Texture,
}; };
mod graphics; mod graphics;
mod level_loader;
mod logic;
mod virtual_gamepad;
use graphics::Graphics; use graphics::Graphics;
use level_loader::LoadedLevel;
use logic::LogicState;
use virtual_gamepad::VirtualGamepad;
#[derive (Default)]
pub struct GameState { pub struct GameState {
player: PhysicsBody, logic: LogicState,
aabbs: Vec <opengl_rust::physics::Aabb>,
phys_tris: Vec <opengl_rust::physics::Triangle>,
}
impl GameState {
fn reset_level (&mut self, level: &LoadedLevel) {
self.player = Default::default ();
self.player.pos = level.player_spawn;
}
} }
#[tokio::main] #[tokio::main]
@ -68,122 +61,57 @@ async fn main () -> Result <()> {
let mut time_step = TimeStep::new (60, 1000); let mut time_step = TimeStep::new (60, 1000);
let level = LoadedLevel::from_path ("gltf/level-00.glb")?; 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 let Some (c) = node.camera () {
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 graphics = Graphics::new ();
let mut gl_state = Default::default (); let mut gl_state = Default::default ();
let mut game_state = GameState::default (); let mut game_state = GameState {
logic: Default::default (),
};
let phys_params = opengl_rust::physics::Params { let phys_params = opengl_rust::physics::Params {
dt: 1.0 / 60.0, dt: 1.0 / 60.0,
gravity: (0.0, 0.0, -0.5).into (), gravity: (0.0, 0.0, -0.5).into (),
margin: 0.00125, margin: 0.00125,
}; };
let player_acc = 1.0; game_state.logic.reset_level (&level);
let player_acc_air = 0.125;
let player_max_speed = 4.0;
let player_max_speed_air = 8.0;
let player_jump_speed = 12.0;
let kill_z = -3.0;
let mut player_jump_vec: Option <Vec3> = None;
game_state.reset_level (&level); let mut next_upf_print = 60;
game_state.phys_tris = phys_tris; let mut last_upf_instant = Instant::now ();
let mut next_mpf_print = 60; let depth_buffer = vec! [0u8; 320 * 2 * 240 * 2 * 4];
let mut last_mpf_instant = Instant::now (); 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 <anyhow::Result <()>> = 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 <anyhow::Result <()>> = 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 { 'running: loop {
let _frames_to_do = time_step.step (); let frames_to_do = time_step.step ();
let mut player_wants_to_jump = false; 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 () { for event in event_pump.poll_iter () {
match event { match event {
@ -192,243 +120,115 @@ async fn main () -> Result <()> {
break 'running break 'running
}, },
Event::KeyDown { keycode: Some (Keycode::R), .. } => { Event::KeyDown { keycode: Some (Keycode::R), .. } => {
game_state.reset_level (&level); game_state.logic.reset_level (&level);
}, },
Event::KeyDown { keycode: Some (Keycode::Space), .. } => { Event::KeyDown { keycode: Some (Keycode::F12), .. } => {
player_wants_to_jump = true; let depth_bytes: Vec <u8> = 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 (); {
let kb_state = event_pump.keyboard_state ();
let mut wanted_dir = Vec3::default (); if kb_state.is_scancode_pressed (Scancode::Space) {
player_gamepad.jump.held = true;
if kb_state.is_scancode_pressed (Scancode::Left) { }
wanted_dir.x -= 1.0; if kb_state.is_scancode_pressed (Scancode::Left) {
} player_gamepad.d_left.held = true;
if kb_state.is_scancode_pressed (Scancode::Right) { }
wanted_dir.x += 1.0; if kb_state.is_scancode_pressed (Scancode::Right) {
} player_gamepad.d_right.held = true;
if kb_state.is_scancode_pressed (Scancode::Up) { }
wanted_dir.y += 1.0; if kb_state.is_scancode_pressed (Scancode::Up) {
} player_gamepad.d_up.held = true;
if kb_state.is_scancode_pressed (Scancode::Down) { }
wanted_dir.y -= 1.0; if kb_state.is_scancode_pressed (Scancode::Down) {
} player_gamepad.d_down.held = true;
let wanted_dir = if wanted_dir.length_squared () >= 1.0 {
wanted_dir.normalize ()
}
else {
wanted_dir
};
let old_vel = game_state.player.vel;
let old_vel_2 = old_vel * Vec3::new (1.0, 1.0, 0.0);
let new_vel_2 = match player_jump_vec.as_ref () {
Some (v) => {
// Ground
let acc = player_acc * v.z * v.z;
let wanted_vel = wanted_dir * player_max_speed;
let diff = wanted_vel - old_vel_2;
if diff.length_squared () < acc * acc {
// We're near the wanted velocity, so snap to it
wanted_vel
}
else {
// We're not near the wanted velocity, so accelerate
old_vel_2 + diff.normalize_or_zero () * acc
}
},
// Air
None => {
let proposed_vel = old_vel_2 + wanted_dir * player_acc_air;
if old_vel_2.length_squared () < player_max_speed_air * player_max_speed_air {
// Air control is normal below player_max_speed
proposed_vel
}
else {
// If the player's input would push them beyond player_max_speed,
// apply drag to preserve overall energy
proposed_vel * old_vel_2.length () / proposed_vel.length ()
}
},
};
game_state.player.vel = new_vel_2;
game_state.player.vel.z = old_vel.z;
// dbg! (game_state.player.vel);
if player_wants_to_jump {
if let Some (normal) = player_jump_vec.clone () {
game_state.player.vel.z = 0.0;
game_state.player.vel += normal * player_jump_speed;
} }
} }
let phys_result = opengl_rust::physics::step (&phys_params, &game_state.phys_tris, &[], 0.5, &game_state.player); let p_gp = player_gamepad;
game_state.player = phys_result.body; for _ in 0..frames_to_do {
if game_state.player.pos.z < kill_z { let logic_step_output = game_state.logic.step (&level.phys_tris, &phys_params, p_gp);
game_state.reset_level (&level); if logic_step_output.reset_level {
} game_state.logic.reset_level (&level);
// tracing::debug! ("player pos: {}", game_state.player.pos); }
player_jump_vec = None;
for normal in &phys_result.normals_hit {
player_jump_vec = Some (match player_jump_vec {
None => *normal,
Some (old) => {
if normal.z > old.z {
*normal
}
else {
old
}
},
});
} }
// dbg! (game_state.player.pos); // dbg! (game_state.logic.player.pos);
window.gl_make_current (&gl_ctx).unwrap (); window.gl_make_current (&gl_ctx).unwrap ();
graphics.draw (&game_state, &mut gl_state, &level, &camera); 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; graphics.frames += 1;
if graphics.frames == next_mpf_print { if graphics.frames == next_upf_print {
let now = Instant::now (); let now = Instant::now ();
let mpf = (now - last_mpf_instant).as_millis () / 60; let upf = (now - last_upf_instant).as_micros () / 60;
dbg! (mpf); dbg! (upf);
next_mpf_print += 60; next_upf_print += 60;
last_mpf_instant = now; last_upf_instant = now;
} }
window.gl_swap_window (); window.gl_swap_window ();
tokio::time::sleep (Duration::from_millis (15)).await; {
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 (()) Ok (())
} }
struct LoadedLevel { /*
player_spawn: Vec3,
buffer: Vec <u8>,
level: gltf::Document,
}
impl LoadedLevel {
fn from_path (path: &str) -> Result <Self> {
use gltf::{
Semantic,
scene::Transform,
};
let (level, buffers, _) = gltf::import (path)?;
let buffer = match buffers.get (0) {
None => bail! ("gltf didn't load any buffers"),
Some (x) => x.0.to_vec (),
};
let scene = match level.scenes ().next () {
None => bail! ("No scenes in glTF file"),
Some (x) => x,
};
let mut player_spawn = None;
for node in scene.nodes () {
if node.name () == Some ("Player Spawn") {
let (translation, _, _) = node.transform ().decomposed ();
player_spawn = Some (Vec3::from (translation));
}
else if let Some (camera) = node.camera () {
}
else if let Some (mesh) = node.mesh () {
for (i, prim) in mesh.primitives ().enumerate () {
let positions = match prim.get (&Semantic::Positions) {
None => continue,
Some (x) => x,
};
let normals = match prim.get (&Semantic::Normals) {
None => continue,
Some (x) => x,
};
let indices = match prim.indices () {
None => continue,
Some (x) => x,
};
let pos_view = match positions.view () {
None => continue,
Some (x) => x,
};
let norm_view = match normals.view () {
None => continue,
Some (x) => x,
};
let indices_view = match indices.view () {
None => continue,
Some (x) => x,
};
}
}
}
let player_spawn = match player_spawn {
None => bail! ("glTF file must have `Player Spawn` node"),
Some (x) => x,
};
Ok (Self {
buffer,
level,
player_spawn,
})
}
}
struct ShaderLocations {
attr_pos: u32,
attr_normal: u32,
attr_color: u32,
uni_mvp: i32,
}
impl ShaderLocations {
pub fn new (shader_program: &opengl_rust::shader::ShaderProgram) -> anyhow::Result <Self>
{
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_normal: attr ("attr_normal")?,
attr_color: attr ("attr_color")?,
uni_mvp: uni ("uni_mvp")?,
})
}
}
struct TriangleStream { struct TriangleStream {
pub verts: gpu_buffers::VertexBuffer, pub verts: gpu_buffers::VertexBuffer,
pub indices: gpu_buffers::IndexBuffer, pub indices: gpu_buffers::IndexBuffer,
} }
*/
struct Camera {
transform: gltf::scene::Transform,
}
fn gltf_node_get_mat4 (node: &gltf::Node) -> Mat4 {
Mat4::from_cols_array_2d (&node.transform ().matrix ())
}

View File

@ -0,0 +1,25 @@
#[derive (Clone, Copy, Default)]
pub struct VirtualGamepad {
pub jump: VirtualButton,
pub d_left: VirtualButton,
pub d_right: VirtualButton,
pub d_up: VirtualButton,
pub d_down: VirtualButton,
}
#[derive (Clone, Copy, Default)]
pub struct VirtualButton {
/// True iff the button is down now
pub held: bool,
/// True if the button was pressed at any point since the last frame,
/// even if it's up now.
/// This is needed to make really fast clicks air-tight.
pub pressed: bool,
}
impl VirtualButton {
pub fn any_press (self) -> bool {
self.held || self.pressed
}
}

View File

@ -1,7 +1,7 @@
use glam::{Vec2, Vec3}; use glam::{Vec2, Vec3};
use partial_min_max::{min, max}; use partial_min_max::{min, max};
#[derive (Debug, Default, PartialEq)] #[derive (Clone, Copy, Debug, Default, PartialEq)]
pub struct PhysicsBody { pub struct PhysicsBody {
pub pos: Vec3, pub pos: Vec3,
pub vel: Vec3, pub vel: Vec3,
@ -155,6 +155,8 @@ pub fn step (
} }
} }
let _ = t_remaining;
PhysicsResult { PhysicsResult {
body: PhysicsBody { body: PhysicsBody {
pos: old_pos, pos: old_pos,
@ -178,7 +180,7 @@ pub fn get_candidate (
radius, radius,
radius radius
)); ));
let v = p1 - p0; let _v = p1 - p0;
let mut candidate = Collision { let mut candidate = Collision {
t: 2.0, t: 2.0,
@ -358,12 +360,12 @@ pub fn get_candidate (
fn get_candidate_face (verts: &[Vec3], normal: Vec3, p0: Vec3, p1: Vec3, radius: f32) fn get_candidate_face (verts: &[Vec3], normal: Vec3, p0: Vec3, p1: Vec3, radius: f32)
-> Option <PrimCollision> -> Option <PrimCollision>
{ {
let radius3 = Vec3::from (( let _radius3 = Vec3::from ((
radius, radius,
radius, radius,
radius radius
)); ));
let v = p1 - p0; let _v = p1 - p0;
let distance_to_face0 = Vec3::dot (normal, p0 - verts [0]) - radius; let distance_to_face0 = Vec3::dot (normal, p0 - verts [0]) - radius;
let distance_to_face1 = Vec3::dot (normal, p1 - verts [0]) - radius; let distance_to_face1 = Vec3::dot (normal, p1 - verts [0]) - radius;

View File

@ -4,6 +4,7 @@ pub use std::{
CString, CString,
c_void, c_void,
}, },
io::Write,
time::Duration, time::Duration,
}; };