twgpu_tools/
tripod.rs

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    /// Local index of the frames rendered in this thread, starts at 0.
42    pub frame_index: i64,
43    /// Global index of the frames rendered, starts above 0 if we skip frames.
44    /// TODO: Also implement this for non-tripod rendering.
45    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    /// This methods chunks together FrameInfos with the same tick.
63    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}