mirror of
https://codeberg.org/icewind/vbspview.git
synced 2026-06-03 18:24:09 +02:00
per prop interp
This commit is contained in:
parent
d93e824818
commit
4084fefd1a
4 changed files with 128 additions and 186 deletions
149
src/control.rs
149
src/control.rs
|
|
@ -1,5 +1,5 @@
|
|||
use crate::DemoInfo;
|
||||
use splines::{Interpolate, Spline};
|
||||
use splines::Spline;
|
||||
use std::ops::RangeInclusive;
|
||||
use three_d::egui::{Slider, Ui};
|
||||
use three_d::*;
|
||||
|
|
@ -137,7 +137,9 @@ impl DebugToggle {
|
|||
|
||||
pub struct DemoCamera {
|
||||
demo: DemoInfo,
|
||||
spline: Spline<f32, TickData>,
|
||||
positions: Spline<f32, Vec3>,
|
||||
pitch: Spline<f32, f32>,
|
||||
yaw: Spline<f32, f32>,
|
||||
playing: bool,
|
||||
start_tick: f64,
|
||||
playback_start_time: f64,
|
||||
|
|
@ -177,15 +179,11 @@ impl Control for DemoCamera {
|
|||
if self.playing | self.force_update {
|
||||
let tick = self.demo_tick(accumulated_time);
|
||||
self.ui_tick = tick as u32;
|
||||
if self.demo.positions.len() as f64 <= tick {
|
||||
if self.demo.ticks as f64 <= tick {
|
||||
self.playing = false;
|
||||
self.start_tick = self.demo_tick(accumulated_time);
|
||||
change = true;
|
||||
info!(
|
||||
tick = tick,
|
||||
length = self.demo.positions.len(),
|
||||
"end of demo"
|
||||
);
|
||||
info!(tick = tick, length = self.demo.ticks, "end of demo");
|
||||
} else {
|
||||
debug!(
|
||||
tick = tick,
|
||||
|
|
@ -194,7 +192,7 @@ impl Control for DemoCamera {
|
|||
"playing tick"
|
||||
);
|
||||
let data = self.get_tick(tick);
|
||||
self.apply_view(camera, data.position, data.angles[1], data.angles[0]);
|
||||
self.apply_view(camera, data.position, data.angles[0], data.angles[1]);
|
||||
}
|
||||
self.force_update = false;
|
||||
}
|
||||
|
|
@ -221,18 +219,14 @@ impl Control for DemoCamera {
|
|||
|
||||
impl DemoCamera {
|
||||
pub fn new(demo: DemoInfo) -> Self {
|
||||
let spline = Spline::from_iter(demo.positions.iter().cloned().map(
|
||||
|(tick, position, angles)| {
|
||||
splines::Key::new(
|
||||
(tick - demo.start_tick) as f32,
|
||||
TickData { position, angles },
|
||||
splines::Interpolation::Cosine,
|
||||
)
|
||||
},
|
||||
));
|
||||
let positions = Spline::from_vec(demo.positions.positions.clone());
|
||||
let pitch = Spline::from_vec(demo.positions.pitch.clone());
|
||||
let yaw = Spline::from_vec(demo.positions.yaw.clone());
|
||||
DemoCamera {
|
||||
demo,
|
||||
spline,
|
||||
positions,
|
||||
pitch,
|
||||
yaw,
|
||||
playing: false,
|
||||
start_tick: 0.0,
|
||||
playback_start_time: 0.0,
|
||||
|
|
@ -259,7 +253,7 @@ impl DemoCamera {
|
|||
}
|
||||
|
||||
fn tick_range(&self) -> RangeInclusive<u32> {
|
||||
self.demo.start_tick..=self.demo.positions.len() as u32 + self.demo.start_tick
|
||||
self.demo.start_tick..=self.demo.ticks + self.demo.start_tick
|
||||
}
|
||||
|
||||
fn set_tick(&mut self, tick: u32, time: f64) {
|
||||
|
|
@ -269,7 +263,16 @@ impl DemoCamera {
|
|||
}
|
||||
|
||||
fn get_tick(&self, tick: f64) -> TickData {
|
||||
self.spline.clamped_sample(tick as f32).unwrap()
|
||||
TickData {
|
||||
position: self
|
||||
.positions
|
||||
.clamped_sample(tick as f32)
|
||||
.unwrap_or(vec3(0.0, 0.0, 0.0)),
|
||||
angles: [
|
||||
self.pitch.clamped_sample(tick as f32).unwrap_or_default(),
|
||||
self.yaw.clamped_sample(tick as f32).unwrap_or_default(),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -322,105 +325,7 @@ fn apply_camera_action(
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct TickData {
|
||||
position: Vec3,
|
||||
angles: [f32; 2],
|
||||
}
|
||||
|
||||
impl Interpolate<f32> for TickData {
|
||||
fn step(t: f32, threshold: f32, a: Self, b: Self) -> Self {
|
||||
TickData {
|
||||
position: Vec3::step(t, threshold, a.position, b.position),
|
||||
angles: [
|
||||
f32::step(t, threshold, a.angles[0], b.angles[0]),
|
||||
f32::step(t, threshold, a.angles[1], b.angles[1]),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn lerp(t: f32, a: Self, b: Self) -> Self {
|
||||
TickData {
|
||||
position: <Vec3 as Interpolate<f32>>::lerp(t, a.position, b.position),
|
||||
angles: [
|
||||
f32::lerp(t, a.angles[0], b.angles[0]),
|
||||
f32::lerp(t, a.angles[1], b.angles[1]),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn cosine(t: f32, a: Self, b: Self) -> Self {
|
||||
TickData {
|
||||
position: Vec3::cosine(t, a.position, b.position),
|
||||
angles: [
|
||||
f32::cosine(t, a.angles[0], b.angles[0]),
|
||||
f32::cosine(t, a.angles[1], b.angles[1]),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn cubic_hermite(
|
||||
t: f32,
|
||||
x: (f32, Self),
|
||||
a: (f32, Self),
|
||||
b: (f32, Self),
|
||||
y: (f32, Self),
|
||||
) -> Self {
|
||||
TickData {
|
||||
position: Vec3::cubic_hermite(
|
||||
t,
|
||||
(x.0, x.1.position),
|
||||
(a.0, a.1.position),
|
||||
(b.0, b.1.position),
|
||||
(y.0, y.1.position),
|
||||
),
|
||||
angles: [
|
||||
f32::cubic_hermite(
|
||||
t,
|
||||
(x.0, x.1.angles[0]),
|
||||
(a.0, a.1.angles[0]),
|
||||
(b.0, b.1.angles[0]),
|
||||
(y.0, y.1.angles[0]),
|
||||
),
|
||||
f32::cubic_hermite(
|
||||
t,
|
||||
(x.0, x.1.angles[1]),
|
||||
(a.0, a.1.angles[1]),
|
||||
(b.0, b.1.angles[1]),
|
||||
(y.0, y.1.angles[1]),
|
||||
),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn quadratic_bezier(t: f32, a: Self, u: Self, b: Self) -> Self {
|
||||
TickData {
|
||||
position: Vec3::quadratic_bezier(t, a.position, u.position, b.position),
|
||||
angles: [
|
||||
f32::quadratic_bezier(t, a.angles[0], u.angles[0], b.angles[0]),
|
||||
f32::quadratic_bezier(t, a.angles[1], u.angles[1], b.angles[1]),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn cubic_bezier(t: f32, a: Self, u: Self, v: Self, b: Self) -> Self {
|
||||
TickData {
|
||||
position: Vec3::cubic_bezier(t, a.position, u.position, v.position, b.position),
|
||||
angles: [
|
||||
f32::cubic_bezier(t, a.angles[0], u.angles[0], v.angles[0], b.angles[0]),
|
||||
f32::cubic_bezier(t, a.angles[1], u.angles[1], v.angles[1], b.angles[1]),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn cubic_bezier_mirrored(t: f32, a: Self, u: Self, v: Self, b: Self) -> Self {
|
||||
TickData {
|
||||
position: Vec3::cubic_bezier_mirrored(
|
||||
t, a.position, u.position, v.position, b.position,
|
||||
),
|
||||
angles: [
|
||||
f32::cubic_bezier_mirrored(t, a.angles[0], u.angles[0], v.angles[0], b.angles[0]),
|
||||
f32::cubic_bezier_mirrored(t, a.angles[1], u.angles[1], v.angles[1], b.angles[1]),
|
||||
],
|
||||
}
|
||||
}
|
||||
pub struct TickData {
|
||||
pub position: Vec3,
|
||||
pub angles: [f32; 2],
|
||||
}
|
||||
|
|
|
|||
158
src/demo.rs
158
src/demo.rs
|
|
@ -1,5 +1,6 @@
|
|||
use crate::bsp::{map_coords, UNIT_SCALE};
|
||||
use crate::Error;
|
||||
use splines::{Interpolation, Key};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use tf_demo_parser::demo::data::UserInfo;
|
||||
|
|
@ -15,8 +16,9 @@ use tf_demo_parser::{Demo, DemoParser, MessageType, ParserState, ReadResult, Str
|
|||
use three_d::{vec3, Vec3};
|
||||
|
||||
pub struct DemoInfo {
|
||||
pub ticks: u32,
|
||||
pub map: String,
|
||||
pub positions: Vec<(u32, Vec3, [f32; 2])>,
|
||||
pub positions: Positions,
|
||||
pub start_tick: u32,
|
||||
pub time_per_tick: f64,
|
||||
}
|
||||
|
|
@ -30,6 +32,7 @@ impl DemoInfo {
|
|||
let (header, (positions, start_tick, interval_per_tick)) = parser.parse()?;
|
||||
|
||||
Ok(DemoInfo {
|
||||
ticks: header.ticks,
|
||||
map: header.map,
|
||||
positions,
|
||||
start_tick,
|
||||
|
|
@ -38,21 +41,28 @@ impl DemoInfo {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Positions {
|
||||
pub positions: Vec<Key<f32, Vec3>>,
|
||||
pub pitch: Vec<Key<f32, f32>>,
|
||||
pub yaw: Vec<Key<f32, f32>>,
|
||||
}
|
||||
|
||||
struct PovAnalyzer {
|
||||
last_position: Vector,
|
||||
last_angles: [f32; 2],
|
||||
view_offset: f32,
|
||||
positions: Vec<(u32, Vec3, [f32; 2])>,
|
||||
positions: Positions,
|
||||
name: String,
|
||||
player: Option<EntityId>,
|
||||
start_tick: u32,
|
||||
last_tick: u32,
|
||||
pov_name: String,
|
||||
is_pov: bool,
|
||||
last_tick: u32,
|
||||
last_pov_tick: u32,
|
||||
}
|
||||
|
||||
impl MessageHandler for PovAnalyzer {
|
||||
type Output = (Vec<(u32, Vec3, [f32; 2])>, u32, f32);
|
||||
type Output = (Positions, u32, f32);
|
||||
|
||||
fn does_handle(message_type: MessageType) -> bool {
|
||||
matches!(message_type, MessageType::PacketEntities)
|
||||
|
|
@ -66,59 +76,73 @@ impl MessageHandler for PovAnalyzer {
|
|||
}
|
||||
|
||||
fn handle_message(&mut self, message: &Message, tick: u32) {
|
||||
if tick != self.last_tick {
|
||||
if tick > self.last_tick {
|
||||
self.last_tick = tick;
|
||||
let pos = map_coords(self.last_position);
|
||||
self.positions.push((
|
||||
tick,
|
||||
vec3(pos[0], pos[1] + self.view_offset, pos[2]),
|
||||
self.last_angles,
|
||||
));
|
||||
}
|
||||
const NON_LOCAL_ORIGIN: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_vecOrigin");
|
||||
const NON_LOCAL_ORIGIN_Z: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_vecOrigin[2]");
|
||||
const NON_LOCAL_PITCH_ANGLES: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[0]");
|
||||
const NON_LOCAL_YAW_ANGLES: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[1]");
|
||||
const VIEW_OFFSET: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_LocalPlayerExclusive", "m_vecViewOffset[2]");
|
||||
|
||||
const NON_LOCAL_ORIGIN: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_vecOrigin");
|
||||
const NON_LOCAL_ORIGIN_Z: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_vecOrigin[2]");
|
||||
const NON_LOCAL_YAW_ANGLES: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[1]");
|
||||
const NON_LOCAL_PITCH_ANGLES: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[0]");
|
||||
const VIEW_OFFSET: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_LocalPlayerExclusive", "m_vecViewOffset[2]");
|
||||
let old_pos = self.last_position;
|
||||
let old_offset = self.view_offset;
|
||||
|
||||
if let (Message::PacketEntities(message), Some(player_id)) = (message, self.player) {
|
||||
if self.start_tick == 0 {
|
||||
self.start_tick = tick;
|
||||
}
|
||||
for entity in &message.entities {
|
||||
if entity.entity_index == player_id {
|
||||
for prop in &entity.props {
|
||||
match prop.identifier {
|
||||
NON_LOCAL_ORIGIN => {
|
||||
let pos_xy = VectorXY::try_from(&prop.value).unwrap_or_default();
|
||||
self.last_position.x = pos_xy.x;
|
||||
self.last_position.y = pos_xy.y;
|
||||
if let (Message::PacketEntities(message), Some(player_id)) = (message, self.player) {
|
||||
if self.start_tick == 0 {
|
||||
self.start_tick = tick;
|
||||
}
|
||||
for entity in &message.entities {
|
||||
if entity.entity_index == player_id {
|
||||
for prop in &entity.props {
|
||||
match prop.identifier {
|
||||
NON_LOCAL_ORIGIN => {
|
||||
let pos_xy =
|
||||
VectorXY::try_from(&prop.value).unwrap_or_default();
|
||||
self.last_position.x = pos_xy.x;
|
||||
self.last_position.y = pos_xy.y;
|
||||
}
|
||||
NON_LOCAL_ORIGIN_Z => {
|
||||
self.last_position.z =
|
||||
f32::try_from(&prop.value).unwrap_or_default()
|
||||
}
|
||||
NON_LOCAL_PITCH_ANGLES => {
|
||||
self.positions.pitch.push(Key::new(
|
||||
tick as f32,
|
||||
f32::try_from(&prop.value).unwrap_or_default(),
|
||||
Interpolation::Linear,
|
||||
));
|
||||
}
|
||||
NON_LOCAL_YAW_ANGLES => {
|
||||
self.positions.yaw.push(Key::new(
|
||||
tick as f32,
|
||||
f32::try_from(&prop.value).unwrap_or_default(),
|
||||
Interpolation::Linear,
|
||||
));
|
||||
}
|
||||
VIEW_OFFSET => {
|
||||
self.view_offset =
|
||||
f32::try_from(&prop.value).unwrap_or_default() * UNIT_SCALE;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
NON_LOCAL_ORIGIN_Z => {
|
||||
self.last_position.z =
|
||||
f32::try_from(&prop.value).unwrap_or_default()
|
||||
}
|
||||
NON_LOCAL_YAW_ANGLES => {
|
||||
self.last_angles[1] = f32::try_from(&prop.value).unwrap_or_default()
|
||||
}
|
||||
NON_LOCAL_PITCH_ANGLES => {
|
||||
self.last_angles[0] = f32::try_from(&prop.value).unwrap_or_default()
|
||||
}
|
||||
VIEW_OFFSET => {
|
||||
self.view_offset =
|
||||
f32::try_from(&prop.value).unwrap_or_default() * UNIT_SCALE;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self.last_position != old_pos || old_offset != self.view_offset) && !self.is_pov {
|
||||
let pos = map_coords(self.last_position);
|
||||
self.positions.positions.push(Key::new(
|
||||
tick as f32,
|
||||
vec3(pos[0], pos[1] + self.view_offset, pos[2]),
|
||||
Interpolation::CatmullRom,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,13 +155,27 @@ impl MessageHandler for PovAnalyzer {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_packet_meta(&mut self, meta: &MessagePacketMeta) {
|
||||
if self.is_pov {
|
||||
self.last_angles = [
|
||||
meta.view_angles.local_angles.1.x,
|
||||
meta.view_angles.local_angles.1.y,
|
||||
];
|
||||
self.last_position = meta.view_angles.origin.1
|
||||
fn handle_packet_meta(&mut self, tick: u32, meta: &MessagePacketMeta) {
|
||||
if tick != self.last_pov_tick {
|
||||
self.last_pov_tick = tick;
|
||||
if self.is_pov {
|
||||
self.positions.pitch.push(Key::new(
|
||||
tick as f32,
|
||||
meta.view_angles.local_angles.1.y,
|
||||
Interpolation::CatmullRom,
|
||||
));
|
||||
self.positions.yaw.push(Key::new(
|
||||
tick as f32,
|
||||
meta.view_angles.local_angles.1.x,
|
||||
Interpolation::CatmullRom,
|
||||
));
|
||||
let pos = map_coords(meta.view_angles.origin.1);
|
||||
self.positions.positions.push(Key::new(
|
||||
tick as f32,
|
||||
vec3(pos[0], pos[1] + self.view_offset, pos[2]),
|
||||
Interpolation::CatmullRom,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -154,15 +192,15 @@ impl PovAnalyzer {
|
|||
pub fn new(name: String) -> Self {
|
||||
PovAnalyzer {
|
||||
last_position: Vector::default(),
|
||||
last_angles: [0.0, 0.0],
|
||||
view_offset: 0.0,
|
||||
positions: vec![],
|
||||
positions: Positions::default(),
|
||||
name,
|
||||
player: None,
|
||||
start_tick: 0,
|
||||
last_tick: 0,
|
||||
pov_name: String::new(),
|
||||
is_pov: false,
|
||||
last_tick: 0,
|
||||
last_pov_tick: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue