per prop interp

This commit is contained in:
Robin Appelman 2022-04-07 23:03:20 +02:00
commit 4084fefd1a
4 changed files with 128 additions and 186 deletions

1
Cargo.lock generated
View file

@ -2705,7 +2705,6 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "tf-demo-parser"
version = "0.4.0"
source = "git+https://github.com/demostf/parser#3f693e96ae5754be7bf61f953774a4932aa585a4"
dependencies = [
"bitbuffer",
"enumflags2",

View file

@ -19,7 +19,7 @@ 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", git = "https://github.com/demostf/parser" }
tf-demo-parser = { version = "0.4.0", path = "../demostf/tf-demo-parser" }
steamid-ng = "1.0.0"
clap = { version = "3.1.8", features = ["derive"] }
splines = { version = "4.1.0", features = ["cgmath"] }

View file

@ -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],
}

View file

@ -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,
}
}