Skip to main content

rusterix/map/
softrig.rs

1use crate::{Map, ValueContainer};
2use serde::{Deserialize, Serialize};
3use theframework::prelude::{FxHashMap, FxHashSet};
4use uuid::Uuid;
5use vek::Vec2;
6
7#[derive(Serialize, Deserialize, Clone, Debug)]
8pub struct Keyform {
9    pub vertex_positions: Vec<(u32, Vec2<f32>)>,
10}
11
12#[derive(Serialize, Deserialize, Clone, Debug)]
13pub struct SoftRig {
14    pub id: Uuid,
15    pub name: String,
16    pub keyforms: Vec<Keyform>,
17    pub in_editor_playlist: bool,
18
19    pub values: ValueContainer,
20}
21
22impl SoftRig {
23    pub fn new(name: String) -> Self {
24        Self {
25            id: Uuid::new_v4(),
26            name,
27            keyforms: vec![],
28            in_editor_playlist: true,
29            values: ValueContainer::default(),
30        }
31    }
32}
33
34#[derive(Clone, Debug)]
35pub struct SoftRigAnimator {
36    pub keyframes: Vec<Uuid>,
37    pub total_duration: f32, // Duration in seconds
38    pub progress: f32,       // 0.0..1.0 normalized
39    pub playing: bool,
40    pub loop_playback: bool,
41}
42
43impl Default for SoftRigAnimator {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49impl SoftRigAnimator {
50    pub fn new() -> Self {
51        Self {
52            keyframes: Vec::new(),
53            total_duration: 1.0,
54            progress: 0.0,
55            playing: true,
56            loop_playback: true,
57        }
58    }
59
60    pub fn set_progress(&mut self, value: f32) {
61        self.progress = value.clamp(0.0, 1.0);
62    }
63
64    pub fn tick(&mut self, delta_time: f32) {
65        if !self.playing || self.keyframes.len() < 2 || self.total_duration <= 0.0 {
66            return;
67        }
68
69        self.progress += delta_time / self.total_duration;
70
71        if self.progress >= 1.0 {
72            if self.loop_playback {
73                self.progress %= 1.0;
74            } else {
75                self.progress = 1.0;
76                self.playing = false;
77            }
78        }
79    }
80
81    pub fn get_blended_rig(&self, map: &Map) -> Option<SoftRig> {
82        let len = self.keyframes.len();
83        if len == 0 {
84            return None;
85        } else if len == 1 {
86            return map.softrigs.get(&self.keyframes[0]).cloned();
87        }
88
89        let t = self.progress * (len as f32 - 1.0);
90        let i = t.floor() as usize;
91        let frac = t - i as f32;
92
93        let id_a = self.keyframes.get(i)?;
94        let id_b = self.keyframes.get(i + 1).unwrap_or(id_a);
95
96        let rig_a = map.softrigs.get(id_a)?;
97        let rig_b = map.softrigs.get(id_b)?;
98
99        Some(Self::blend_softrigs(rig_a, rig_b, frac, map))
100    }
101
102    pub fn blend_softrigs(a: &SoftRig, b: &SoftRig, t: f32, map: &Map) -> SoftRig {
103        let positions_a: FxHashMap<u32, Vec2<f32>> = a
104            .keyforms
105            .iter()
106            .flat_map(|k| k.vertex_positions.iter().copied())
107            .collect();
108
109        let positions_b: FxHashMap<u32, Vec2<f32>> = b
110            .keyforms
111            .iter()
112            .flat_map(|k| k.vertex_positions.iter().copied())
113            .collect();
114
115        let all_ids: FxHashSet<u32> = positions_a
116            .keys()
117            .chain(positions_b.keys())
118            .copied()
119            .collect();
120
121        let mut blended_keyform = Keyform {
122            vertex_positions: Vec::new(),
123        };
124
125        for id in all_ids {
126            let pa = positions_a
127                .get(&id)
128                .copied()
129                .or_else(|| map.find_vertex(id).map(|v| Vec2::new(v.x, v.y)));
130            let pb = positions_b
131                .get(&id)
132                .copied()
133                .or_else(|| map.find_vertex(id).map(|v| Vec2::new(v.x, v.y)));
134
135            let blended = match (pa, pb) {
136                (Some(a), Some(b)) => Vec2::lerp(a, b, t),
137                (Some(a), None) => a,
138                (None, Some(b)) => b,
139                _ => continue,
140            };
141
142            blended_keyform.vertex_positions.push((id, blended));
143        }
144
145        SoftRig {
146            id: Uuid::new_v4(),
147            name: "Blended".into(),
148            keyforms: vec![blended_keyform],
149            in_editor_playlist: false,
150            values: ValueContainer::default(),
151        }
152    }
153}