1use crate::avatar::shape::Shape;
6use std::f32::consts::PI;
7
8const BLINK_EVERY: f32 = 4.4;
10const BLINK_FOR: f32 = 0.16;
11
12#[derive(Clone, Copy, Debug, PartialEq)]
13pub struct Motion {
14 pub wobble: f32,
16 pub breathe: f32,
18 pub drift: f32,
20 pub blink: bool,
21 pub lid: f32,
24}
25
26impl Default for Motion {
27 fn default() -> Self {
28 Self::ALIVE
29 }
30}
31
32impl Motion {
33 pub const STILL: Self = Self {
34 wobble: 0.0,
35 breathe: 0.0,
36 drift: 0.0,
37 blink: false,
38 lid: 0.0,
39 };
40 pub const ALIVE: Self = Self {
41 wobble: 0.34,
42 breathe: 0.014,
43 drift: 0.03,
44 blink: true,
45 lid: 0.0,
46 };
47 pub const ASLEEP: Self = Self {
49 wobble: 0.0,
50 breathe: 0.014,
51 drift: 0.0,
52 blink: false,
53 lid: 1.0,
54 };
55
56 pub fn shape(&self, mut shape: Shape, t: f32) -> Shape {
59 if self.wobble == 0.0 {
60 return shape;
61 }
62 for l in &mut shape.lobes {
63 l.phase += t * self.wobble * (0.7 + 0.13 * l.k as f32);
64 }
65 shape
66 }
67
68 pub fn beat(&self, t: f32) -> Beat {
70 let blink = if self.blink {
71 let p = (t / BLINK_EVERY).fract() * BLINK_EVERY;
72 if p < BLINK_FOR {
73 (PI * p / BLINK_FOR).sin()
74 } else {
75 0.0
76 }
77 } else {
78 0.0
79 };
80 let lid = blink.max(self.lid.clamp(0.0, 1.0));
81 Beat {
82 scale: 1.0 + self.breathe * (0.9 * t).sin(),
83 gaze: (
84 self.drift * (0.6 * (0.31 * t).sin() + 0.4 * (0.17 * t + 1.3).sin()),
85 self.drift * (0.5 * (0.23 * t + 0.7).sin() + 0.5 * (0.13 * t).sin()),
86 ),
87 lid,
88 }
89 }
90}
91
92#[derive(Clone, Copy, Debug, PartialEq)]
94pub struct Beat {
95 pub scale: f32,
96 pub gaze: (f32, f32),
97 pub lid: f32,
99}