mirror of
https://codeberg.org/icewind/vbspview.git
synced 2026-06-03 10:14:10 +02:00
democam
This commit is contained in:
parent
90a5966682
commit
bc82d29c4f
6 changed files with 309 additions and 173 deletions
135
src/camera.rs
135
src/camera.rs
|
|
@ -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
264
src/control.rs
Normal 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)
|
||||
}
|
||||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)? {
|
||||
|
|
|
|||
31
src/main.rs
31
src/main.rs
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue