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}
22
23impl Default for Motion {
24 fn default() -> Self {
25 Self::ALIVE
26 }
27}
28
29impl Motion {
30 pub const STILL: Self = Self {
31 wobble: 0.0,
32 breathe: 0.0,
33 drift: 0.0,
34 blink: false,
35 };
36 pub const ALIVE: Self = Self {
37 wobble: 0.34,
38 breathe: 0.014,
39 drift: 0.03,
40 blink: true,
41 };
42
43 pub fn shape(&self, mut shape: Shape, t: f32) -> Shape {
46 if self.wobble == 0.0 {
47 return shape;
48 }
49 for l in &mut shape.lobes {
50 l.phase += t * self.wobble * (0.7 + 0.13 * l.k as f32);
51 }
52 shape
53 }
54
55 pub fn beat(&self, t: f32) -> Beat {
57 let lid = if self.blink {
58 let p = (t / BLINK_EVERY).fract() * BLINK_EVERY;
59 if p < BLINK_FOR {
60 (PI * p / BLINK_FOR).sin()
61 } else {
62 0.0
63 }
64 } else {
65 0.0
66 };
67 Beat {
68 scale: 1.0 + self.breathe * (0.9 * t).sin(),
69 gaze: (
70 self.drift * (0.6 * (0.31 * t).sin() + 0.4 * (0.17 * t + 1.3).sin()),
71 self.drift * (0.5 * (0.23 * t + 0.7).sin() + 0.5 * (0.13 * t).sin()),
72 ),
73 lid,
74 }
75 }
76}
77
78#[derive(Clone, Copy, Debug, PartialEq)]
80pub struct Beat {
81 pub scale: f32,
82 pub gaze: (f32, f32),
83 pub lid: f32,
85}