demo playing

This commit is contained in:
Robin Appelman 2022-04-03 00:22:29 +02:00
commit 90a5966682
8 changed files with 502 additions and 36 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@
*.obj
perf.data*
flamegraph.svg
*.dem

350
Cargo.lock generated
View file

@ -93,7 +93,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278"
dependencies = [
"num-traits",
"num-traits 0.2.14",
]
[[package]]
@ -215,6 +215,31 @@ dependencies = [
"syn",
]
[[package]]
name = "bitbuffer"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650346fecc0be01be480d078b014cc0deb000d26790a53f6ef854dda35f87d62"
dependencies = [
"bitbuffer_derive",
"err-derive",
"memchr",
"num-traits 0.2.14",
"serde",
]
[[package]]
name = "bitbuffer_derive"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4090254bfbc71442ff4a426ddba663346e26fd14b55b259281f763e350d7f621"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn_util",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@ -357,7 +382,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
dependencies = [
"approx",
"num-traits",
"num-traits 0.2.14",
]
[[package]]
name = "clap"
version = "3.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim 0.10.0",
"termcolor",
"textwrap 0.15.0",
]
[[package]]
name = "clap_derive"
version = "3.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
@ -592,7 +647,7 @@ dependencies = [
"ident_case",
"proc-macro2",
"quote",
"strsim",
"strsim 0.9.3",
"syn",
]
@ -724,6 +779,36 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "enum_primitive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180"
dependencies = [
"num-traits 0.1.43",
]
[[package]]
name = "enumflags2"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b3ab37dc79652c9d85f1f7b6070d77d321d2467f5fe7b00d6b7a86c57b092ae"
dependencies = [
"enumflags2_derive",
"serde",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "epaint"
version = "0.13.0"
@ -737,6 +822,20 @@ dependencies = [
"ordered-float",
]
[[package]]
name = "err-derive"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34a887c8df3ed90498c1c437ce21f211c8e27672921a8ffa293cb8d6d4caa9e"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"rustversion",
"syn",
"synstructure",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
@ -1034,6 +1133,12 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -1144,7 +1249,7 @@ dependencies = [
"jpeg-decoder",
"num-iter",
"num-rational",
"num-traits",
"num-traits 0.2.14",
"png",
"scoped_threadpool",
"tiff",
@ -1332,6 +1437,12 @@ dependencies = [
"crc",
]
[[package]]
name = "main_error"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "155db5e86c6e45ee456bf32fad5a290ee1f7151c2faca27ea27097568da67d1a"
[[package]]
name = "malloc_buf"
version = "0.0.6"
@ -1401,7 +1512,7 @@ dependencies = [
"supports-hyperlinks",
"supports-unicode",
"terminal_size",
"textwrap",
"textwrap 0.14.2",
"thiserror",
"unicode-width",
]
@ -1608,6 +1719,40 @@ dependencies = [
"winapi",
]
[[package]]
name = "num"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits 0.2.14",
]
[[package]]
name = "num-bigint"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3"
dependencies = [
"autocfg",
"num-integer",
"num-traits 0.2.14",
]
[[package]]
name = "num-complex"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
dependencies = [
"num-traits 0.2.14",
]
[[package]]
name = "num-integer"
version = "0.1.44"
@ -1615,7 +1760,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
"num-traits 0.2.14",
]
[[package]]
@ -1626,7 +1771,7 @@ checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
"num-traits 0.2.14",
]
[[package]]
@ -1636,8 +1781,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
"num-traits 0.2.14",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
dependencies = [
"num-traits 0.2.14",
]
[[package]]
@ -1749,7 +1904,16 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
dependencies = [
"num-traits",
"num-traits 0.2.14",
]
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
@ -1810,6 +1974,32 @@ dependencies = [
"winapi",
]
[[package]]
name = "parse-display"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "813e91c6232dbeb2e9deba0eb0dc5c967bd6f380676fd34419f9ddd71411faa7"
dependencies = [
"once_cell",
"parse-display-derive",
"regex",
]
[[package]]
name = "parse-display-derive"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "007ed61a69cf7d9b95cc5dc18489dbb4f70d4adb0a0c100e2dd46f0be241711a"
dependencies = [
"once_cell",
"proc-macro2",
"quote",
"regex",
"regex-syntax",
"structmeta",
"syn",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
@ -1908,6 +2098,30 @@ dependencies = [
"toml",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.36"
@ -2057,6 +2271,12 @@ dependencies = [
"owned_ttf_parser 0.6.0",
]
[[package]]
name = "rustversion"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]]
name = "ryu"
version = "1.0.9"
@ -2154,6 +2374,17 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_repr"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@ -2234,6 +2465,12 @@ dependencies = [
"wayland-protocols",
]
[[package]]
name = "snap"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45456094d1983e2ee2a18fdfebce3189fa451699d0502cb8e3b49dba5ba41451"
[[package]]
name = "socket2"
version = "0.4.4"
@ -2250,6 +2487,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "steamid-ng"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb049f8faa2cba570c5366dbaf88ee5849725b16edb771848639fac92e33673"
dependencies = [
"enum_primitive",
"lazy_static",
"num",
"regex",
"serde",
"serde_derive",
"thiserror",
]
[[package]]
name = "steamlocate"
version = "1.0.1"
@ -2277,6 +2529,35 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "structmeta"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59915b528a896f2e3bfa1a6ace65f7bb0ff9f9863de6213b0c01cb6fd3c3ac71"
dependencies = [
"proc-macro2",
"quote",
"structmeta-derive",
"syn",
]
[[package]]
name = "structmeta-derive"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b73800bcca56045d5ab138a48cd28a96093335335deaa916f22b5749c4150c79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "supports-color"
version = "1.3.0"
@ -2327,6 +2608,18 @@ dependencies = [
"syn",
]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "tempfile"
version = "3.3.0"
@ -2341,6 +2634,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
@ -2362,6 +2664,33 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "tf-demo-parser"
version = "0.4.0"
dependencies = [
"bitbuffer",
"enumflags2",
"err-derive",
"fnv",
"main_error",
"num-traits 0.2.14",
"num_enum",
"parse-display",
"serde",
"serde_json",
"serde_repr",
"snap",
"steamid-ng",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "thiserror"
version = "1.0.30"
@ -2691,10 +3020,13 @@ name = "vbspview"
version = "0.1.0"
dependencies = [
"cgmath",
"clap",
"delaunator",
"itertools",
"miette",
"steamid-ng",
"steamlocate",
"tf-demo-parser",
"thiserror",
"three-d",
"tracing",

View file

@ -19,6 +19,9 @@ tracing = "0.1.29"
tracing-subscriber = { version = "0.3.3", features = ["env-filter"] }
tracing-tree = "0.2.0"
cgmath = "0.18.0"
tf-demo-parser = { version = "0.4.0", path = "../demostf/tf-demo-parser" }
steamid-ng = "1.0.0"
clap = { version = "3.1.8", features = ["derive"] }
[profile.dev.package."*"]
opt-level = 2

View file

@ -7,10 +7,10 @@ use vmdl::mdl::Mdl;
use vmdl::vtx::Vtx;
use vmdl::vvd::Vvd;
pub fn load_map(data: &[u8]) -> Result<Vec<CPUMesh>, Error> {
pub fn load_map(data: &[u8], loader: &mut Loader) -> Result<Vec<CPUMesh>, Error> {
let (cpu_mesh, bsp) = load_world(data)?;
let loader = Loader::new(bsp.pack.clone())?;
let merged_props = load_props(&loader, bsp.static_props())?;
loader.set_pack(bsp.pack.clone());
let merged_props = load_props(loader, bsp.static_props())?;
Ok(vec![cpu_mesh, merged_props])
}
@ -19,7 +19,7 @@ fn apply_transform(coord: [f32; 3], transform: Mat4) -> [f32; 3] {
[coord.x, coord.y, coord.z]
}
fn map_coords<C: Into<[f32; 3]>>(vec: C) -> [f32; 3] {
pub fn map_coords<C: Into<[f32; 3]>>(vec: C) -> [f32; 3] {
let vec = vec.into();
[
vec[1] * UNIT_SCALE,

74
src/demo.rs Normal file
View file

@ -0,0 +1,74 @@
use crate::bsp::map_coords;
use crate::Error;
use std::fs;
use std::path::Path;
use tf_demo_parser::demo::parser::gamestateanalyser::{GameState, GameStateAnalyser};
use tf_demo_parser::{Demo, DemoParser};
use three_d::{vec3, Vec3};
use tracing::{error, info};
pub struct DemoInfo {
pub map: String,
pub positions: Vec<(Vec3, f32, f32)>,
pub start_tick: u32,
}
impl DemoInfo {
pub fn new(demo_path: impl AsRef<Path>, name: &str) -> Result<Self, Error> {
let file = fs::read(demo_path)?;
let demo = Demo::new(&file);
let parser = DemoParser::new_with_analyser(demo.get_stream(), GameStateAnalyser::new());
let (header, mut ticker) = parser.ticker()?;
let mut positions = Vec::with_capacity(header.ticks as usize);
let mut user_id = None;
let mut start_tick = 0;
while let Some(tick) = ticker.next()? {
let state: &GameState = tick.state;
if user_id.is_none() {
if let Some(found) = state
.players
.iter()
.enumerate()
.filter_map(|(i, player)| Some((i, player.info.as_ref()?)))
.find_map(|(i, player)| {
player.name.to_ascii_lowercase().contains(name).then(|| i)
})
{
info!(user_id = found, "found user");
start_tick = tick.tick;
user_id = Some(found);
}
}
if let Some(user_id) = user_id {
let player = &state.players[user_id];
let coords = map_coords(player.position);
positions.push((
vec3(coords[0], coords[1], coords[2]),
player.view_angle,
player.pitch_angle,
))
}
}
if user_id.is_none() {
let found = ticker
.into_state()
.players
.into_iter()
.filter_map(|player| Some(player.info?.name))
.collect::<Vec<_>>();
error!(
"User {} not found in demo, found: {}",
name,
found.join(", ")
);
return Err("Failed to find user in demo".into());
}
Ok(DemoInfo {
map: header.map,
positions,
start_tick,
})
}
}

View file

@ -1,5 +1,6 @@
use crate::Error;
use std::fmt::{Debug, Formatter};
use std::fs;
use std::path::PathBuf;
use steamlocate::SteamDir;
use tracing::{debug, error};
@ -7,8 +8,9 @@ use vbsp::Packfile;
use vpk::VPK;
pub struct Loader {
pack: Packfile,
pack: Option<Packfile>,
tf_dir: PathBuf,
download: PathBuf,
vpks: Vec<VPK>,
}
@ -21,13 +23,23 @@ impl Debug for Loader {
}
impl Loader {
pub fn new(pack: Packfile) -> Result<Self, Error> {
pub fn new() -> Result<Self, Error> {
Self::with_opt_pack(None)
}
#[allow(dead_code)]
pub fn with_pak(pack: Packfile) -> Result<Self, Error> {
Self::with_opt_pack(Some(pack))
}
pub fn with_opt_pack(pack: Option<Packfile>) -> Result<Self, Error> {
let tf_dir = SteamDir::locate()
.ok_or("Can't find steam directory")?
.app(&440)
.ok_or("Can't find tf2 directory")?
.path
.join("tf");
let download = tf_dir.join("download");
let vpks = tf_dir
.read_dir()?
.filter_map(|item| item.ok())
@ -37,16 +49,37 @@ impl Loader {
.filter_map(|res| res.ok())
.collect();
Ok(Loader { pack, tf_dir, vpks })
Ok(Loader {
pack,
tf_dir,
download,
vpks,
})
}
pub fn set_pack(&mut self, pack: Packfile) {
self.pack = Some(pack);
}
#[tracing::instrument]
pub fn load(&self, name: &str) -> Result<Vec<u8>, Error> {
debug!("loading {}", name);
if let Some(data) = self.pack.get(name)? {
let path = self.tf_dir.join(name);
if path.exists() {
debug!("found in tf2 dir");
return Ok(fs::read(path)?);
}
let path = self.download.join(name);
if path.exists() {
debug!("found in download dir");
return Ok(fs::read(path)?);
}
if let Some(pack) = &self.pack {
if let Some(data) = pack.get(name)? {
debug!("got {} bytes from packfile", data.len());
return Ok(data);
}
}
for vpk in self.vpks.iter() {
if let Some(entry) = vpk.tree.get(name) {
let data = entry.get()?.into_owned();

View file

@ -1,20 +1,32 @@
mod bsp;
mod camera;
mod demo;
mod loader;
mod renderer;
mod ui;
use clap::Parser;
use crate::bsp::load_map;
use crate::demo::DemoInfo;
use crate::renderer::Renderer;
use crate::ui::DebugUI;
use camera::FirstPerson;
use loader::Loader;
use std::env::args;
use thiserror::Error;
use three_d::*;
use tracing_subscriber::{prelude::*, EnvFilter};
use tracing_tree::HierarchicalLayer;
/// View a demo file
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
/// Path of the demo file
demo: String,
/// Name of the player to follow
player: String,
}
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
@ -27,6 +39,8 @@ pub enum Error {
Vpk(#[from] vpk::Error),
#[error(transparent)]
Mdl(#[from] vmdl::ModelError),
#[error(transparent)]
Demo(#[from] tf_demo_parser::ParseError),
#[error("{0}")]
Other(&'static str),
}
@ -53,26 +67,21 @@ fn setup() {
fn main() -> Result<(), Error> {
setup();
let mut args = args();
let bin = args.next().unwrap();
let file = match args.next() {
Some(file) => file,
None => {
eprintln!("usage: {} <file.bsp>", bin);
return Ok(());
}
};
let args = Args::parse();
let window = Window::new(WindowSettings {
title: file.clone(),
title: args.demo.clone(),
max_size: Some((1920, 1080)),
..Default::default()
})?;
let demo = DemoInfo::new(args.demo, &args.player)?;
let mut loader = Loader::new()?;
let map = loader.load(&format!("maps/{}.bsp", demo.map))?;
let mut renderer = Renderer::new(&window)?;
let map = std::fs::read(&file)?;
let meshes = load_map(&map)?;
let meshes = load_map(&map, &mut loader)?;
let material = PhysicalMaterial {
albedo: Color {
r: 128,
@ -88,7 +97,21 @@ fn main() -> Result<(), Error> {
.map(|mesh| Model::new_with_material(&renderer.context, &mesh, material.clone()))
.collect::<Result<_, _>>()?;
window.render_loop(move |frame_input| renderer.render(frame_input).unwrap())?;
let mut positions = demo.positions.into_iter();
let forward = vec4(0.0, 0.0, 1.0, 1.0);
window.render_loop(move |frame_input| {
if let Some((position, angle, pitch)) = positions.next() {
let angle_transform =
Mat4::from_angle_y(degrees(angle)) * Mat4::from_angle_x(degrees(pitch));
let target = position + (angle_transform * forward).truncate();
renderer
.camera
.set_view(position, target, vec3(0.0, 1.0, 0.0))
.unwrap();
}
renderer.render(frame_input).unwrap()
})?;
Ok(())
}

View file

@ -8,7 +8,7 @@ pub struct Renderer {
pub context: Context,
pipeline: ForwardPipeline,
control: FirstPerson,
camera: Camera,
pub camera: Camera,
}
impl Renderer {
@ -21,7 +21,7 @@ impl Renderer {
vec3(9.0, 4.0, 5.0),
vec3(0.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
degrees(90.0),
degrees(60.0),
0.1,
30.0,
)?;