1use std::{error::Error, path::Path};
2
3use serde::{Deserialize, Serialize};
4use twgpu::vek;
5use vek::Vec2;
6
7#[derive(Clone, Copy, Serialize, Deserialize)]
8struct TripodState {
9 tick: f64,
10 x: f32,
11 y: f32,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 player_id: Option<u32>,
14 width: f64,
15}
16
17impl TripodState {
18 fn into_camera(self, aspect_ratio: f32) -> twgpu::Camera {
19 let mut camera = twgpu::Camera::new(aspect_ratio);
20 let zoom = self.width as f32 / camera.base_dimensions.x;
21 camera.zoom = Vec2::broadcast(zoom);
22 camera.position = Vec2::new(self.x, self.y);
23 camera
24 }
25}
26
27#[derive(Serialize, Deserialize)]
28pub struct Tripod {
29 pub name: String,
30 pub video: String,
31 states: Vec<TripodState>,
32}
33
34pub struct TripodPool {
35 video_paths: Vec<String>,
36 frames: Vec<FrameInfo>,
37}
38
39#[derive(Copy, Clone)]
40pub struct FrameInfo {
41 pub frame_index: i64,
43 pub image_index: usize,
46 pub camera_index: usize,
47 pub tick: f64,
48 pub camera: twgpu::Camera,
49 pub focused_player: Option<u32>,
50 pub last_frame: bool,
51}
52
53impl TripodPool {
54 pub fn camera_count(&self) -> usize {
55 self.video_paths.len()
56 }
57
58 pub fn video_paths(&self) -> impl Iterator<Item = &Path> {
59 self.video_paths.iter().map(|p| p.as_ref())
60 }
61
62 pub fn iter_frames(&self) -> impl Iterator<Item = &[FrameInfo]> {
64 self.frames.chunk_by(|f1, f2| f1.tick == f2.tick)
65 }
66
67 pub fn deserialize(
68 slice: &[u8],
69 start_tick: Option<i32>,
70 end_tick: Option<i32>,
71 aspect_ratio: f32,
72 ) -> Result<Self, Box<dyn Error>> {
73 let tripods: Vec<Tripod> = serde_json::from_slice(slice)?;
74 let mut video_paths = Vec::new();
75 let mut frames = Vec::new();
76
77 for (camera_index, tripod) in tripods.into_iter().enumerate() {
78 video_paths.push(tripod.video);
79 for (state_index, (image_index, state)) in tripod
80 .states
81 .into_iter()
82 .enumerate()
83 .filter(|(_, state)| {
84 start_tick.is_none_or(|st| st as f64 <= state.tick)
85 && end_tick.is_none_or(|et| et as f64 >= state.tick)
86 })
87 .enumerate()
88 {
89 frames.push(FrameInfo {
90 frame_index: state_index.try_into().unwrap(),
91 image_index,
92 camera_index,
93 tick: state.tick,
94 camera: state.into_camera(aspect_ratio),
95 focused_player: state.player_id,
96 last_frame: false,
97 })
98 }
99 if let Some(last_frame) = frames.last_mut() {
100 last_frame.last_frame = true;
101 }
102 }
103
104 frames.sort_unstable_by(|frame1, frame2| frame1.tick.partial_cmp(&frame2.tick).unwrap());
105
106 Ok(Self {
107 video_paths,
108 frames,
109 })
110 }
111}