mirror of
https://codeberg.org/icewind/vbspview.git
synced 2026-06-03 18:24:09 +02:00
interp wip
This commit is contained in:
parent
50afa29952
commit
7850af5b11
4 changed files with 160 additions and 37 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -2481,6 +2481,15 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "splines"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85d3c90d443c10f66d69fc45269f3bf1d3b0171c239751fe6129b7d79a82c535"
|
||||||
|
dependencies = [
|
||||||
|
"cgmath",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
@ -3024,6 +3033,7 @@ dependencies = [
|
||||||
"delaunator",
|
"delaunator",
|
||||||
"itertools",
|
"itertools",
|
||||||
"miette",
|
"miette",
|
||||||
|
"splines",
|
||||||
"steamid-ng",
|
"steamid-ng",
|
||||||
"steamlocate",
|
"steamlocate",
|
||||||
"tf-demo-parser",
|
"tf-demo-parser",
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ cgmath = "0.18.0"
|
||||||
tf-demo-parser = { version = "0.4.0", path = "../demostf/tf-demo-parser" }
|
tf-demo-parser = { version = "0.4.0", path = "../demostf/tf-demo-parser" }
|
||||||
steamid-ng = "1.0.0"
|
steamid-ng = "1.0.0"
|
||||||
clap = { version = "3.1.8", features = ["derive"] }
|
clap = { version = "3.1.8", features = ["derive"] }
|
||||||
|
splines = { version = "4.1.0", features = ["cgmath"] }
|
||||||
|
|
||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
opt-level = 2
|
opt-level = 2
|
||||||
|
|
|
||||||
127
src/control.rs
127
src/control.rs
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::DemoInfo;
|
use crate::DemoInfo;
|
||||||
|
use splines::{Interpolate, Spline};
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use three_d::egui::{Slider, Ui};
|
use three_d::egui::{Slider, Ui};
|
||||||
use three_d::*;
|
use three_d::*;
|
||||||
|
|
@ -136,6 +137,7 @@ impl DebugToggle {
|
||||||
|
|
||||||
pub struct DemoCamera {
|
pub struct DemoCamera {
|
||||||
demo: DemoInfo,
|
demo: DemoInfo,
|
||||||
|
spline: Spline<f32, TickData>,
|
||||||
playing: bool,
|
playing: bool,
|
||||||
start_tick: f64,
|
start_tick: f64,
|
||||||
playback_start_time: f64,
|
playback_start_time: f64,
|
||||||
|
|
@ -149,7 +151,7 @@ pub struct DemoCamera {
|
||||||
impl Control for DemoCamera {
|
impl Control for DemoCamera {
|
||||||
fn handle(
|
fn handle(
|
||||||
&mut self,
|
&mut self,
|
||||||
_camera: &mut Camera,
|
camera: &mut Camera,
|
||||||
events: &mut [Event],
|
events: &mut [Event],
|
||||||
_elapsed_time: f64,
|
_elapsed_time: f64,
|
||||||
accumulated_time: f64,
|
accumulated_time: f64,
|
||||||
|
|
@ -191,9 +193,8 @@ impl Control for DemoCamera {
|
||||||
play_time = accumulated_time - self.playback_start_time,
|
play_time = accumulated_time - self.playback_start_time,
|
||||||
"playing tick"
|
"playing tick"
|
||||||
);
|
);
|
||||||
// todo: interpolate
|
let data = self.get_tick(tick);
|
||||||
let (position, [pitch, yaw]) = self.demo.positions[tick as usize];
|
self.apply_view(camera, data.position, data.angles[1], data.angles[0]);
|
||||||
self.apply_view(_camera, position, yaw, pitch);
|
|
||||||
}
|
}
|
||||||
self.force_update = false;
|
self.force_update = false;
|
||||||
}
|
}
|
||||||
|
|
@ -220,8 +221,18 @@ impl Control for DemoCamera {
|
||||||
|
|
||||||
impl DemoCamera {
|
impl DemoCamera {
|
||||||
pub fn new(demo: DemoInfo) -> Self {
|
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,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
));
|
||||||
DemoCamera {
|
DemoCamera {
|
||||||
demo,
|
demo,
|
||||||
|
spline,
|
||||||
playing: false,
|
playing: false,
|
||||||
start_tick: 0.0,
|
start_tick: 0.0,
|
||||||
playback_start_time: 0.0,
|
playback_start_time: 0.0,
|
||||||
|
|
@ -256,6 +267,10 @@ impl DemoCamera {
|
||||||
self.playback_start_time = time;
|
self.playback_start_time = time;
|
||||||
self.force_update = true;
|
self.force_update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_tick(&self, tick: f64) -> TickData {
|
||||||
|
self.spline.sample(tick as f32).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_camera_action(
|
fn apply_camera_action(
|
||||||
|
|
@ -305,3 +320,107 @@ fn apply_camera_action(
|
||||||
}
|
}
|
||||||
Ok(control_type != CameraAction::None)
|
Ok(control_type != CameraAction::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
59
src/demo.rs
59
src/demo.rs
|
|
@ -16,7 +16,7 @@ use three_d::{vec3, Vec3};
|
||||||
|
|
||||||
pub struct DemoInfo {
|
pub struct DemoInfo {
|
||||||
pub map: String,
|
pub map: String,
|
||||||
pub positions: Vec<(Vec3, [f32; 2])>,
|
pub positions: Vec<(u32, Vec3, [f32; 2])>,
|
||||||
pub start_tick: u32,
|
pub start_tick: u32,
|
||||||
pub time_per_tick: f64,
|
pub time_per_tick: f64,
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ struct PovAnalyzer {
|
||||||
last_position: Vector,
|
last_position: Vector,
|
||||||
last_angles: [f32; 2],
|
last_angles: [f32; 2],
|
||||||
view_offset: f32,
|
view_offset: f32,
|
||||||
positions: Vec<(Vec3, [f32; 2])>,
|
positions: Vec<(u32, Vec3, [f32; 2])>,
|
||||||
name: String,
|
name: String,
|
||||||
player: Option<EntityId>,
|
player: Option<EntityId>,
|
||||||
start_tick: u32,
|
start_tick: u32,
|
||||||
|
|
@ -52,7 +52,7 @@ struct PovAnalyzer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageHandler for PovAnalyzer {
|
impl MessageHandler for PovAnalyzer {
|
||||||
type Output = (Vec<(Vec3, [f32; 2])>, u32, f32);
|
type Output = (Vec<(u32, Vec3, [f32; 2])>, u32, f32);
|
||||||
|
|
||||||
fn does_handle(message_type: MessageType) -> bool {
|
fn does_handle(message_type: MessageType) -> bool {
|
||||||
matches!(message_type, MessageType::PacketEntities)
|
matches!(message_type, MessageType::PacketEntities)
|
||||||
|
|
@ -65,31 +65,23 @@ impl MessageHandler for PovAnalyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_packet_meta(&mut self, meta: &MessagePacketMeta) {
|
fn handle_message(&mut self, message: &Message, tick: u32) {
|
||||||
if self.is_pov {
|
if tick != self.last_tick {
|
||||||
self.last_angles = [
|
self.last_tick = tick;
|
||||||
meta.view_angles.local_angles.1.x,
|
let pos = map_coords(self.last_position);
|
||||||
meta.view_angles.local_angles.1.y,
|
self.positions.push((
|
||||||
];
|
tick,
|
||||||
self.last_position = meta.view_angles.origin.1
|
vec3(pos[0], pos[1] + self.view_offset, pos[2]),
|
||||||
}
|
self.last_angles,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_message(&mut self, message: &Message, tick: u32) {
|
|
||||||
const LOCAL_ORIGIN: SendPropIdentifier =
|
|
||||||
SendPropIdentifier::new("DT_TFLocalPlayerExclusive", "m_vecOrigin");
|
|
||||||
const NON_LOCAL_ORIGIN: SendPropIdentifier =
|
const NON_LOCAL_ORIGIN: SendPropIdentifier =
|
||||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_vecOrigin");
|
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_vecOrigin");
|
||||||
const LOCAL_ORIGIN_Z: SendPropIdentifier =
|
|
||||||
SendPropIdentifier::new("DT_TFLocalPlayerExclusive", "m_vecOrigin[2]");
|
|
||||||
const NON_LOCAL_ORIGIN_Z: SendPropIdentifier =
|
const NON_LOCAL_ORIGIN_Z: SendPropIdentifier =
|
||||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_vecOrigin[2]");
|
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_vecOrigin[2]");
|
||||||
const LOCAL_YAW_ANGLES: SendPropIdentifier =
|
|
||||||
SendPropIdentifier::new("DT_TFLocalPlayerExclusive", "m_angEyeAngles[1]");
|
|
||||||
const NON_LOCAL_YAW_ANGLES: SendPropIdentifier =
|
const NON_LOCAL_YAW_ANGLES: SendPropIdentifier =
|
||||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[1]");
|
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[1]");
|
||||||
const LOCAL_PITCH_ANGLES: SendPropIdentifier =
|
|
||||||
SendPropIdentifier::new("DT_TFLocalPlayerExclusive", "m_angEyeAngles[0]");
|
|
||||||
const NON_LOCAL_PITCH_ANGLES: SendPropIdentifier =
|
const NON_LOCAL_PITCH_ANGLES: SendPropIdentifier =
|
||||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[0]");
|
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[0]");
|
||||||
const VIEW_OFFSET: SendPropIdentifier =
|
const VIEW_OFFSET: SendPropIdentifier =
|
||||||
|
|
@ -103,19 +95,19 @@ impl MessageHandler for PovAnalyzer {
|
||||||
if entity.entity_index == player_id {
|
if entity.entity_index == player_id {
|
||||||
for prop in &entity.props {
|
for prop in &entity.props {
|
||||||
match prop.identifier {
|
match prop.identifier {
|
||||||
LOCAL_ORIGIN | NON_LOCAL_ORIGIN => {
|
NON_LOCAL_ORIGIN => {
|
||||||
let pos_xy = VectorXY::try_from(&prop.value).unwrap_or_default();
|
let pos_xy = VectorXY::try_from(&prop.value).unwrap_or_default();
|
||||||
self.last_position.x = pos_xy.x;
|
self.last_position.x = pos_xy.x;
|
||||||
self.last_position.y = pos_xy.y;
|
self.last_position.y = pos_xy.y;
|
||||||
}
|
}
|
||||||
LOCAL_ORIGIN_Z | NON_LOCAL_ORIGIN_Z => {
|
NON_LOCAL_ORIGIN_Z => {
|
||||||
self.last_position.z =
|
self.last_position.z =
|
||||||
f32::try_from(&prop.value).unwrap_or_default()
|
f32::try_from(&prop.value).unwrap_or_default()
|
||||||
}
|
}
|
||||||
LOCAL_YAW_ANGLES | NON_LOCAL_YAW_ANGLES => {
|
NON_LOCAL_YAW_ANGLES => {
|
||||||
self.last_angles[1] = f32::try_from(&prop.value).unwrap_or_default()
|
self.last_angles[1] = f32::try_from(&prop.value).unwrap_or_default()
|
||||||
}
|
}
|
||||||
LOCAL_PITCH_ANGLES | NON_LOCAL_PITCH_ANGLES => {
|
NON_LOCAL_PITCH_ANGLES => {
|
||||||
self.last_angles[0] = f32::try_from(&prop.value).unwrap_or_default()
|
self.last_angles[0] = f32::try_from(&prop.value).unwrap_or_default()
|
||||||
}
|
}
|
||||||
VIEW_OFFSET => {
|
VIEW_OFFSET => {
|
||||||
|
|
@ -128,15 +120,6 @@ impl MessageHandler for PovAnalyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if tick > self.last_tick {
|
|
||||||
self.last_tick = tick;
|
|
||||||
let pos = map_coords(self.last_position);
|
|
||||||
self.positions.push((
|
|
||||||
vec3(pos[0], pos[1] + self.view_offset, pos[2]),
|
|
||||||
self.last_angles,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_string_entry(&mut self, table: &str, _index: usize, entry: &StringTableEntry) {
|
fn handle_string_entry(&mut self, table: &str, _index: usize, entry: &StringTableEntry) {
|
||||||
|
|
@ -148,6 +131,16 @@ 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 into_output(self, state: &ParserState) -> Self::Output {
|
fn into_output(self, state: &ParserState) -> Self::Output {
|
||||||
(
|
(
|
||||||
self.positions,
|
self.positions,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue