This commit is contained in:
Robin Appelman 2022-04-03 14:45:56 +02:00
commit bc82d29c4f
6 changed files with 309 additions and 173 deletions

View file

@ -1,135 +0,0 @@
use three_d::*;
pub struct FirstPerson {
control: CameraControl,
speed: f32,
keys: [bool; 4],
pub debug: bool,
}
impl FirstPerson {
pub fn new(speed: f32) -> Self {
Self {
control: CameraControl {
left_drag_horizontal: CameraAction::Yaw {
speed: std::f32::consts::PI / 1800.0,
},
left_drag_vertical: CameraAction::Pitch {
speed: std::f32::consts::PI / 1800.0,
},
..Default::default()
},
speed,
keys: [false; 4],
debug: true,
}
}
pub fn handle_events(
&mut self,
camera: &mut Camera,
events: &mut [Event],
) -> ThreeDResult<bool> {
let change = self.control.handle_events(camera, events)?;
for event in events.iter_mut() {
match event {
Event::Text(text) => {
if text == "`" {
self.debug = !self.debug;
}
}
Event::KeyPress { kind, .. } => {
self.key_press(kind);
}
Event::KeyRelease { kind, .. } => {
self.key_release(kind);
}
_ => {}
};
}
if self.keys[0] {
self.handle_action(camera, CameraAction::Forward { speed: self.speed }, 1.0)?;
}
if self.keys[1] {
self.handle_action(camera, CameraAction::Forward { speed: self.speed }, -1.0)?;
}
if self.keys[2] {
self.handle_action(camera, CameraAction::Left { speed: self.speed }, 1.0)?;
}
if self.keys[3] {
self.handle_action(camera, CameraAction::Left { speed: self.speed }, -1.0)?;
}
Ok(self.keys.iter().fold(change, |change, key| change && *key))
}
fn key_press(&mut self, key: &Key) {
match key {
Key::W => self.keys[0] = true,
Key::S => self.keys[1] = true,
Key::A => self.keys[2] = true,
Key::D => self.keys[3] = true,
_ => {}
}
}
fn key_release(&mut self, key: &Key) {
match key {
Key::W => self.keys[0] = false,
Key::S => self.keys[1] = false,
Key::A => self.keys[2] = false,
Key::D => self.keys[3] = false,
_ => {}
}
}
fn handle_action(
&mut self,
camera: &mut Camera,
control_type: CameraAction,
x: f64,
) -> ThreeDResult<bool> {
match control_type {
CameraAction::Pitch { speed } => {
camera.pitch(radians(speed * x as f32))?;
}
CameraAction::OrbitUp { speed, target } => {
camera.rotate_around_with_fixed_up(&target, 0.0, speed * x as f32)?;
}
CameraAction::Yaw { speed } => {
camera.yaw(radians(speed * x as f32))?;
}
CameraAction::OrbitLeft { speed, target } => {
camera.rotate_around_with_fixed_up(&target, speed * x as f32, 0.0)?;
}
CameraAction::Roll { speed } => {
camera.roll(radians(speed * x as f32))?;
}
CameraAction::Left { speed } => {
let change = -camera.right_direction() * x as f32 * speed;
camera.translate(&change)?;
}
CameraAction::Up { speed } => {
let right = camera.right_direction();
let up = right.cross(camera.view_direction());
let change = up * x as f32 * speed;
camera.translate(&change)?;
}
CameraAction::Forward { speed } => {
let change = camera.view_direction() * speed * x as f32;
camera.translate(&change)?;
}
CameraAction::Zoom {
target,
speed,
min,
max,
} => {
camera.zoom_towards(&target, speed * x as f32, min, max)?;
}
CameraAction::None => {}
}
Ok(control_type != CameraAction::None)
}
}

264
src/control.rs Normal file
View file

@ -0,0 +1,264 @@
use crate::DemoInfo;
use three_d::*;
use tracing::{debug, info};
pub trait Control {
fn handle(
&mut self,
camera: &mut Camera,
events: &mut [Event],
elapsed_time: f64,
accumulated_time: f64,
) -> ThreeDResult<bool>;
}
pub struct FirstPerson {
control: CameraControl,
speed: f32,
keys: [bool; 4],
}
impl Control for FirstPerson {
fn handle(
&mut self,
camera: &mut Camera,
events: &mut [Event],
_elapsed_time: f64,
_accumulated_time: f64,
) -> ThreeDResult<bool> {
let change = self.control.handle_events(camera, events)?;
for event in events.iter_mut() {
match event {
Event::KeyPress { kind, .. } => {
self.key_press(kind);
}
Event::KeyRelease { kind, .. } => {
self.key_release(kind);
}
_ => {}
};
}
if self.keys[0] {
apply_camera_action(camera, CameraAction::Forward { speed: self.speed }, 1.0)?;
}
if self.keys[1] {
apply_camera_action(camera, CameraAction::Forward { speed: self.speed }, -1.0)?;
}
if self.keys[2] {
apply_camera_action(camera, CameraAction::Left { speed: self.speed }, 1.0)?;
}
if self.keys[3] {
apply_camera_action(camera, CameraAction::Left { speed: self.speed }, -1.0)?;
}
Ok(self.keys.iter().fold(change, |change, key| change && *key))
}
}
impl FirstPerson {
pub fn new(speed: f32) -> Self {
Self {
control: CameraControl {
left_drag_horizontal: CameraAction::Yaw {
speed: std::f32::consts::PI / 1800.0,
},
left_drag_vertical: CameraAction::Pitch {
speed: std::f32::consts::PI / 1800.0,
},
..Default::default()
},
speed,
keys: [false; 4],
}
}
fn key_press(&mut self, key: &Key) {
match key {
Key::W => self.keys[0] = true,
Key::S => self.keys[1] = true,
Key::A => self.keys[2] = true,
Key::D => self.keys[3] = true,
_ => {}
}
}
fn key_release(&mut self, key: &Key) {
match key {
Key::W => self.keys[0] = false,
Key::S => self.keys[1] = false,
Key::A => self.keys[2] = false,
Key::D => self.keys[3] = false,
_ => {}
}
}
}
pub struct DebugToggle {
pub enabled: bool,
}
impl Control for DebugToggle {
fn handle(
&mut self,
_camera: &mut Camera,
events: &mut [Event],
_elapsed_time: f64,
_accumulated_time: f64,
) -> ThreeDResult<bool> {
for event in events.iter_mut() {
match event {
Event::Text(text) => {
if text == "`" {
self.enabled = !self.enabled;
return Ok(true);
}
}
_ => {}
};
}
Ok(false)
}
}
impl DebugToggle {
pub fn new() -> Self {
DebugToggle { enabled: true }
}
}
pub struct DemoCamera {
demo: DemoInfo,
playing: bool,
start_tick: f64,
playback_start_time: f64,
}
impl Control for DemoCamera {
fn handle(
&mut self,
_camera: &mut Camera,
events: &mut [Event],
_elapsed_time: f64,
accumulated_time: f64,
) -> ThreeDResult<bool> {
let mut change = false;
for event in events.iter_mut() {
match event {
Event::Text(text) => {
if text == "p" {
change = true;
self.playing = !self.playing;
dbg!(self.playing);
if self.playing {
self.playback_start_time = accumulated_time;
} else {
self.start_tick = self.tick(accumulated_time);
}
}
}
_ => {}
};
}
if self.playing {
let tick = self.tick(accumulated_time);
if self.demo.positions.len() as f64 <= tick {
self.playing = false;
self.start_tick = self.tick(accumulated_time);
change = true;
info!(
tick = tick,
length = self.demo.positions.len(),
"end of demo"
);
} else {
debug!(
tick = tick,
start_tick = self.start_tick,
play_time = accumulated_time - self.playback_start_time,
"playing tick"
);
// todo: interpolate
let (position, yaw, pitch) = self.demo.positions[tick as usize];
self.apply_view(_camera, position, yaw, pitch);
}
}
Ok(self.playing | change)
}
}
impl DemoCamera {
pub fn new(demo: DemoInfo) -> Self {
DemoCamera {
demo,
playing: false,
start_tick: 0.0,
playback_start_time: 0.0,
}
}
fn tick(&self, time: f64) -> f64 {
let playback_time = (time - self.playback_start_time) / 1000.0;
self.start_tick + playback_time / self.demo.time_per_tick
}
fn apply_view(&self, camera: &mut Camera, position: Vec3, yaw: f32, pitch: f32) {
let forward = vec4(0.0, 0.0, 1.0, 1.0);
let angle_transform = Mat4::from_angle_y(degrees(yaw)) * Mat4::from_angle_x(degrees(pitch));
let target = position + (angle_transform * forward).truncate();
camera
.set_view(position, target, vec3(0.0, 1.0, 0.0))
.unwrap();
}
}
fn apply_camera_action(
camera: &mut Camera,
control_type: CameraAction,
x: f64,
) -> ThreeDResult<bool> {
match control_type {
CameraAction::Pitch { speed } => {
camera.pitch(radians(speed * x as f32))?;
}
CameraAction::OrbitUp { speed, target } => {
camera.rotate_around_with_fixed_up(&target, 0.0, speed * x as f32)?;
}
CameraAction::Yaw { speed } => {
camera.yaw(radians(speed * x as f32))?;
}
CameraAction::OrbitLeft { speed, target } => {
camera.rotate_around_with_fixed_up(&target, speed * x as f32, 0.0)?;
}
CameraAction::Roll { speed } => {
camera.roll(radians(speed * x as f32))?;
}
CameraAction::Left { speed } => {
let change = -camera.right_direction() * x as f32 * speed;
camera.translate(&change)?;
}
CameraAction::Up { speed } => {
let right = camera.right_direction();
let up = right.cross(camera.view_direction());
let change = up * x as f32 * speed;
camera.translate(&change)?;
}
CameraAction::Forward { speed } => {
let change = camera.view_direction() * speed * x as f32;
camera.translate(&change)?;
}
CameraAction::Zoom {
target,
speed,
min,
max,
} => {
camera.zoom_towards(&target, speed * x as f32, min, max)?;
}
CameraAction::None => {}
}
Ok(control_type != CameraAction::None)
}

View file

@ -11,6 +11,7 @@ pub struct DemoInfo {
pub map: String,
pub positions: Vec<(Vec3, f32, f32)>,
pub start_tick: u32,
pub time_per_tick: f64,
}
impl DemoInfo {
@ -69,6 +70,7 @@ impl DemoInfo {
map: header.map,
positions,
start_tick,
time_per_tick: ticker.parser_state().demo_meta.interval_per_tick as f64,
})
}
}

View file

@ -64,15 +64,17 @@ impl Loader {
#[tracing::instrument]
pub fn load(&self, name: &str) -> Result<Vec<u8>, Error> {
debug!("loading {}", 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 name.ends_with("bsp") {
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)? {

View file

@ -1,5 +1,5 @@
mod bsp;
mod camera;
mod control;
mod demo;
mod loader;
mod renderer;
@ -7,10 +7,11 @@ mod ui;
use clap::Parser;
use crate::bsp::load_map;
use crate::control::DemoCamera;
use crate::demo::DemoInfo;
use crate::renderer::Renderer;
use crate::ui::DebugUI;
use camera::FirstPerson;
use control::FirstPerson;
use loader::Loader;
use thiserror::Error;
use three_d::*;
@ -21,8 +22,8 @@ use tracing_tree::HierarchicalLayer;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
/// Path of the demo file
demo: String,
/// Path of the demo or map file
path: String,
/// Name of the player to follow
player: String,
}
@ -70,16 +71,16 @@ fn main() -> Result<(), Error> {
let args = Args::parse();
let window = Window::new(WindowSettings {
title: args.demo.clone(),
title: args.path.clone(),
max_size: Some((1920, 1080)),
..Default::default()
})?;
let demo = DemoInfo::new(args.demo, &args.player)?;
let demo = DemoInfo::new(args.path, &args.player)?;
let mut loader = Loader::new()?;
let map = loader.load(&format!("maps/{}.bsp", demo.map))?;
let mut renderer = Renderer::new(&window)?;
let mut renderer = Renderer::new(&window, DemoCamera::new(demo))?;
let meshes = load_map(&map, &mut loader)?;
let material = PhysicalMaterial {
@ -97,21 +98,7 @@ fn main() -> Result<(), Error> {
.map(|mesh| Model::new_with_material(&renderer.context, &mesh, material.clone()))
.collect::<Result<_, _>>()?;
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()
})?;
window.render_loop(move |frame_input| renderer.render(frame_input).unwrap())?;
Ok(())
}

View file

@ -1,18 +1,20 @@
use crate::control::{Control, DebugToggle};
use crate::{DebugUI, FirstPerson};
use three_d::*;
pub struct Renderer {
pub struct Renderer<C: Control> {
gui: DebugUI,
pub models: Vec<Model<PhysicalMaterial>>,
lights: Lights,
pub context: Context,
pipeline: ForwardPipeline,
control: FirstPerson,
control: C,
debug_toggle: DebugToggle,
pub camera: Camera,
}
impl Renderer {
pub fn new(window: &Window) -> ThreeDResult<Self> {
impl<C: Control> Renderer<C> {
pub fn new(window: &Window, control: C) -> ThreeDResult<Self> {
let context = window.gl().unwrap();
let forward_pipeline = ForwardPipeline::new(&context).unwrap();
let camera = Camera::new_perspective(
@ -38,7 +40,7 @@ impl Renderer {
],
..Default::default()
};
let control = FirstPerson::new(0.1);
// let control = FirstPerson::new(0.1);
Ok(Self {
models: Vec::new(),
@ -47,6 +49,7 @@ impl Renderer {
lights,
context,
control,
debug_toggle: DebugToggle::new(),
camera,
})
}
@ -86,7 +89,20 @@ impl Renderer {
};
self.camera.set_viewport(viewport).unwrap();
self.control
.handle_events(&mut self.camera, &mut frame_input.events)
.handle(
&mut self.camera,
&mut frame_input.events,
frame_input.elapsed_time,
frame_input.accumulated_time,
)
.unwrap();
self.debug_toggle
.handle(
&mut self.camera,
&mut frame_input.events,
frame_input.elapsed_time,
frame_input.accumulated_time,
)
.unwrap();
// Light pass
@ -149,7 +165,7 @@ impl Renderer {
.render_pass(&self.camera, &self.models, &self.lights)?
}
};
if self.control.debug {
if self.debug_toggle.enabled {
self.gui.render()?;
}
Ok(())