clay_viewer/
motion.rs

1use std::{
2    time::Duration,
3    f64::consts::PI,
4
5};
6use sdl2::{
7    event::Event,
8    mouse::{MouseState, RelativeMouseState},
9    keyboard::Keycode,
10};
11use nalgebra::{Vector3, Rotation3};
12use crate::{WindowState, EventHandler};
13
14
15fn clamp(x: f64, a: f64, b: f64) -> f64 {
16    if x < a {
17        a
18    } else if x > b {
19        b
20    } else {
21        x
22    }
23}
24
25const KEYS: [Keycode; 12] = [
26    Keycode::W,
27    Keycode::Up,
28    Keycode::A,
29    Keycode::Left,
30    Keycode::S,
31    Keycode::Down,
32    Keycode::D,
33    Keycode::Right,
34    Keycode::Space,
35    Keycode::C,
36    Keycode::Q,
37    Keycode::E,
38];
39
40fn key_idx(key: Keycode) -> Option<usize> {
41    KEYS.iter().position(|k| *k == key)
42}
43
44fn key_dir(key: Keycode) -> (Vector3<f64>, Vector3<f64>) {
45    match key {
46        Keycode::W | Keycode::Up => (Vector3::new(0.0, 0.0, -1.0), Vector3::zeros()),
47        Keycode::A | Keycode::Left => (Vector3::new(-1.0, 0.0, 0.0), Vector3::zeros()),
48        Keycode::S | Keycode::Down => (Vector3::new(0.0, 0.0, 1.0), Vector3::zeros()),
49        Keycode::D | Keycode::Right => (Vector3::new(1.0, 0.0, 0.0), Vector3::zeros()),
50        Keycode::Space => (Vector3::zeros(), Vector3::new(0.0, 0.0, 1.0)),
51        Keycode::C => (Vector3::zeros(), Vector3::new(0.0, 0.0, -1.0)),
52        _ => (Vector3::zeros(), Vector3::zeros()),
53    }
54}
55
56pub struct Motion {
57    pub updated: bool,
58    pub key_mask: usize,
59
60    pub pos: Vector3<f64>,
61
62    // Euler angles
63    pub phi: f64,
64    pub theta: f64,
65
66    pub speed: f64,
67    pub sens: f64,
68
69    pub fov: f64,
70    pub fov_sens: f64,
71}
72
73impl Motion {
74    pub fn new(pos: Vector3<f64>, ori: Rotation3<f64>) -> Self {
75        let (theta, _, phi) = ori.euler_angles();
76        Self {
77            updated: false, key_mask: 0,
78            pos, phi, theta,
79            speed: 1.0, sens: 4e-3,
80            fov: 1.0, fov_sens: 1e-1,
81        }
82    }
83
84    pub fn set_speed(&mut self, speed: f64) {
85        self.speed = speed;
86    }
87    pub fn set_sensitivity(&mut self, sens: f64) {
88        self.sens = sens;
89    }
90
91    fn handle_keys(&mut self, event: &Event) {
92        match event {
93            Event::KeyDown { keycode: Some(key), .. } => if let Some(i) = key_idx(*key) {
94                self.key_mask |= 1 << i;
95                self.updated = true;
96            },
97            Event::KeyUp { keycode: Some(key), .. } => if let Some(i) = key_idx(*key) {
98                self.key_mask &= !(1 << i);
99                self.updated = true;
100            },
101            Event::MouseWheel { y, .. } => {
102                if *y != 0 {
103                    self.fov *= (self.fov_sens*(-*y as f64)).exp();
104                    self.updated = true;
105                }
106            }
107            _ => (),
108        }
109    }
110
111    fn handle_mouse(&mut self, mouse: &RelativeMouseState) {
112        if mouse.x() != 0 || mouse.y() != 0 {
113            self.phi -= self.sens*(mouse.x() as f64);
114            let t = (self.phi/(2.0*PI)).floor() as i32;
115            if t != 0 {
116                self.phi -= 2.0*PI*(t as f64);
117            }
118
119            self.theta -= self.sens*(mouse.y() as f64);
120            if self.theta < 0.0 {
121                self.theta = 0.0;
122            } else if self.theta > PI {
123                self.theta = PI;
124            }
125            self.updated = true;
126        }
127    }
128
129    pub fn pos(&self) -> Vector3<f64> {
130        self.pos.clone()
131    }
132    pub fn ori(&self) -> Rotation3<f64> {
133        Rotation3::from_euler_angles(self.theta, 0.0, self.phi)
134    }
135
136    pub fn was_updated(&self) -> bool {
137        self.updated || self.key_mask != 0
138    }
139
140    pub fn step(&mut self, dt: Duration) {
141        let (mut dir, mut idir) = (Vector3::zeros(), Vector3::zeros());
142        for (i, k) in KEYS.iter().enumerate() {
143            if ((self.key_mask >> i) & 1) != 0 {
144                let (dv, di) = key_dir(*k);
145                dir += dv;
146                idir += di;
147            }
148        }
149        dir = dir.map(|x| clamp(x, -1.0, 1.0));
150        if dir.norm() > 1e-4 {
151            dir = dir.normalize();
152        }
153        dir = self.ori()*dir;
154        self.pos += 1e-6*(dt.as_micros() as f64)*self.speed*(dir + idir);
155
156        self.updated = false;
157    }
158}
159
160impl EventHandler for Motion {
161    fn handle_keys(&mut self, _state: &WindowState, event: &Event) -> clay_core::Result<()> {
162        self.handle_keys(event);
163        Ok(())
164    }
165    fn handle_mouse(
166        &mut self, state: &WindowState,
167        ms: &MouseState, rms: &RelativeMouseState,
168    ) -> clay_core::Result<()> {
169        if state.capture {
170            self.handle_mouse(&rms);
171        } else if ms.left() {
172            self.handle_mouse(&rms);
173        }
174        Ok(())
175    }
176}