mirror of
https://codeberg.org/icewind/vbspview.git
synced 2026-06-03 10:14:10 +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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "splines"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85d3c90d443c10f66d69fc45269f3bf1d3b0171c239751fe6129b7d79a82c535"
|
||||
dependencies = [
|
||||
"cgmath",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
|
|
@ -3024,6 +3033,7 @@ dependencies = [
|
|||
"delaunator",
|
||||
"itertools",
|
||||
"miette",
|
||||
"splines",
|
||||
"steamid-ng",
|
||||
"steamlocate",
|
||||
"tf-demo-parser",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ cgmath = "0.18.0"
|
|||
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"] }
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 2
|
||||
|
|
|
|||
127
src/control.rs
127
src/control.rs
|
|
@ -1,4 +1,5 @@
|
|||
use crate::DemoInfo;
|
||||
use splines::{Interpolate, Spline};
|
||||
use std::ops::RangeInclusive;
|
||||
use three_d::egui::{Slider, Ui};
|
||||
use three_d::*;
|
||||
|
|
@ -136,6 +137,7 @@ impl DebugToggle {
|
|||
|
||||
pub struct DemoCamera {
|
||||
demo: DemoInfo,
|
||||
spline: Spline<f32, TickData>,
|
||||
playing: bool,
|
||||
start_tick: f64,
|
||||
playback_start_time: f64,
|
||||
|
|
@ -149,7 +151,7 @@ pub struct DemoCamera {
|
|||
impl Control for DemoCamera {
|
||||
fn handle(
|
||||
&mut self,
|
||||
_camera: &mut Camera,
|
||||
camera: &mut Camera,
|
||||
events: &mut [Event],
|
||||
_elapsed_time: f64,
|
||||
accumulated_time: f64,
|
||||
|
|
@ -191,9 +193,8 @@ impl Control for DemoCamera {
|
|||
play_time = accumulated_time - self.playback_start_time,
|
||||
"playing tick"
|
||||
);
|
||||
// todo: interpolate
|
||||
let (position, [pitch, yaw]) = self.demo.positions[tick as usize];
|
||||
self.apply_view(_camera, position, yaw, pitch);
|
||||
let data = self.get_tick(tick);
|
||||
self.apply_view(camera, data.position, data.angles[1], data.angles[0]);
|
||||
}
|
||||
self.force_update = false;
|
||||
}
|
||||
|
|
@ -220,8 +221,18 @@ 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,
|
||||
)
|
||||
},
|
||||
));
|
||||
DemoCamera {
|
||||
demo,
|
||||
spline,
|
||||
playing: false,
|
||||
start_tick: 0.0,
|
||||
playback_start_time: 0.0,
|
||||
|
|
@ -256,6 +267,10 @@ impl DemoCamera {
|
|||
self.playback_start_time = time;
|
||||
self.force_update = true;
|
||||
}
|
||||
|
||||
fn get_tick(&self, tick: f64) -> TickData {
|
||||
self.spline.sample(tick as f32).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_camera_action(
|
||||
|
|
@ -305,3 +320,107 @@ fn apply_camera_action(
|
|||
}
|
||||
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]),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
61
src/demo.rs
61
src/demo.rs
|
|
@ -16,7 +16,7 @@ use three_d::{vec3, Vec3};
|
|||
|
||||
pub struct DemoInfo {
|
||||
pub map: String,
|
||||
pub positions: Vec<(Vec3, [f32; 2])>,
|
||||
pub positions: Vec<(u32, Vec3, [f32; 2])>,
|
||||
pub start_tick: u32,
|
||||
pub time_per_tick: f64,
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ struct PovAnalyzer {
|
|||
last_position: Vector,
|
||||
last_angles: [f32; 2],
|
||||
view_offset: f32,
|
||||
positions: Vec<(Vec3, [f32; 2])>,
|
||||
positions: Vec<(u32, Vec3, [f32; 2])>,
|
||||
name: String,
|
||||
player: Option<EntityId>,
|
||||
start_tick: u32,
|
||||
|
|
@ -52,7 +52,7 @@ struct 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 {
|
||||
matches!(message_type, MessageType::PacketEntities)
|
||||
|
|
@ -65,31 +65,23 @@ 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_message(&mut self, message: &Message, tick: u32) {
|
||||
const LOCAL_ORIGIN: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFLocalPlayerExclusive", "m_vecOrigin");
|
||||
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 LOCAL_ORIGIN_Z: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFLocalPlayerExclusive", "m_vecOrigin[2]");
|
||||
const NON_LOCAL_ORIGIN_Z: SendPropIdentifier =
|
||||
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 =
|
||||
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 =
|
||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[0]");
|
||||
const VIEW_OFFSET: SendPropIdentifier =
|
||||
|
|
@ -103,19 +95,19 @@ impl MessageHandler for PovAnalyzer {
|
|||
if entity.entity_index == player_id {
|
||||
for prop in &entity.props {
|
||||
match prop.identifier {
|
||||
LOCAL_ORIGIN | NON_LOCAL_ORIGIN => {
|
||||
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;
|
||||
}
|
||||
LOCAL_ORIGIN_Z | NON_LOCAL_ORIGIN_Z => {
|
||||
NON_LOCAL_ORIGIN_Z => {
|
||||
self.last_position.z =
|
||||
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()
|
||||
}
|
||||
LOCAL_PITCH_ANGLES | NON_LOCAL_PITCH_ANGLES => {
|
||||
NON_LOCAL_PITCH_ANGLES => {
|
||||
self.last_angles[0] = f32::try_from(&prop.value).unwrap_or_default()
|
||||
}
|
||||
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) {
|
||||
|
|
@ -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 {
|
||||
(
|
||||
self.positions,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue