Skip to content

Commit 8ae586f

Browse files
feat(input): add touch controls
1 parent 62e93c6 commit 8ae586f

4 files changed

Lines changed: 158 additions & 0 deletions

File tree

crates/nightshade/src/ecs/camera/systems.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,93 @@ pub fn update_camera_aspect_ratios(world: &mut World) {
4242
}
4343

4444
pub fn fly_camera_system(world: &mut World) {
45+
touch_camera_system(world);
4546
look_camera_system(world);
4647
wasd_keyboard_controls_system(world);
4748
gamepad_fly_camera_system(world);
4849
}
4950

51+
fn touch_camera_system(world: &mut World) {
52+
let Some(camera_entity) = world.resources.active_camera else {
53+
return;
54+
};
55+
56+
let delta_time = world.resources.window.timing.delta_time;
57+
58+
let (right, up) = {
59+
let Some(local_transform) = world.get_local_transform(camera_entity) else {
60+
return;
61+
};
62+
(local_transform.right_vector(), local_transform.up_vector())
63+
};
64+
65+
let touch_count = world.resources.input.touch.touch_count;
66+
67+
if touch_count == 1 {
68+
let raw_delta = world.resources.input.touch.single_finger_delta;
69+
70+
if raw_delta.magnitude() > 0.0 {
71+
let Some(camera) = world.get_camera_mut(camera_entity) else {
72+
return;
73+
};
74+
let Some(smoothing) = camera.smoothing.as_mut() else {
75+
return;
76+
};
77+
78+
let smoothing_factor = 1.0 - (-delta_time * smoothing.mouse_responsiveness).exp();
79+
smoothing.smoothed_mouse_delta = smoothing.smoothed_mouse_delta * (1.0 - smoothing_factor)
80+
+ raw_delta * smoothing_factor;
81+
82+
let pixels_to_radians = (std::f32::consts::PI / 1000.0) * smoothing.mouse_dpi_scale;
83+
let mut delta =
84+
smoothing.smoothed_mouse_delta * smoothing.mouse_sensitivity * pixels_to_radians;
85+
delta.x *= -1.0;
86+
delta.y *= -1.0;
87+
88+
let Some(local_transform) = world.get_local_transform_mut(camera_entity) else {
89+
return;
90+
};
91+
92+
let yaw = nalgebra_glm::quat_angle_axis(delta.x, &Vec3::y());
93+
local_transform.rotation = yaw * local_transform.rotation;
94+
95+
let forward = local_transform.forward_vector();
96+
let current_pitch = forward.y.asin();
97+
98+
let new_pitch = current_pitch + delta.y;
99+
if new_pitch.abs() <= 89_f32.to_radians() {
100+
let pitch = nalgebra_glm::quat_angle_axis(delta.y, &Vec3::x());
101+
local_transform.rotation *= pitch;
102+
}
103+
104+
mark_local_transform_dirty(world, camera_entity);
105+
}
106+
} else if touch_count == 2 {
107+
let mut delta =
108+
world.resources.input.touch.two_finger_delta * world.resources.window.timing.delta_time;
109+
delta.x *= -1.0;
110+
delta.y *= -1.0;
111+
112+
if delta.magnitude() > 0.0 {
113+
let Some(local_transform) = world.get_local_transform_mut(camera_entity) else {
114+
return;
115+
};
116+
let translation_right = right * delta.x;
117+
let translation_up = up * delta.y;
118+
119+
local_transform.translation += translation_right;
120+
local_transform.translation += translation_up;
121+
122+
let changed = translation_right.magnitude() > 0.0 || translation_up.magnitude() > 0.0;
123+
if changed {
124+
mark_local_transform_dirty(world, camera_entity);
125+
}
126+
}
127+
}
128+
129+
world.resources.input.touch.reset();
130+
}
131+
50132
fn look_camera_system(world: &mut World) {
51133
let Some(camera_entity) = world.resources.active_camera else {
52134
return;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
pub mod gamepad;
22
pub mod keyboard;
33
pub mod mouse;
4+
pub mod touch;
45

56
pub use gamepad::*;
67
pub use keyboard::*;
78
pub use mouse::*;
9+
pub use touch::*;
810

911
#[derive(Default)]
1012
pub struct Input {
1113
pub keyboard: Keyboard,
1214
pub mouse: Mouse,
1315
pub gamepad: Gamepad,
16+
pub touch: Touch,
1417
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use crate::ecs::world::Vec2;
2+
3+
#[derive(Debug, Clone, Copy, PartialEq)]
4+
pub struct TouchPoint {
5+
pub id: u64,
6+
pub position: Vec2,
7+
}
8+
9+
#[derive(Default)]
10+
pub struct Touch {
11+
pub active_touches: Vec<TouchPoint>,
12+
pub previous_touches: Vec<TouchPoint>,
13+
pub single_finger_delta: Vec2,
14+
pub two_finger_delta: Vec2,
15+
pub touch_count: usize,
16+
}
17+
18+
impl Touch {
19+
pub fn update_delta(&mut self) {
20+
self.single_finger_delta = Vec2::zeros();
21+
self.two_finger_delta = Vec2::zeros();
22+
23+
if self.active_touches.len() == 1 && self.previous_touches.len() == 1 {
24+
if self.active_touches[0].id == self.previous_touches[0].id {
25+
self.single_finger_delta =
26+
self.active_touches[0].position - self.previous_touches[0].position;
27+
}
28+
} else if self.active_touches.len() == 2 && self.previous_touches.len() == 2 {
29+
let current_center = (self.active_touches[0].position + self.active_touches[1].position) / 2.0;
30+
let previous_center = (self.previous_touches[0].position + self.previous_touches[1].position) / 2.0;
31+
self.two_finger_delta = current_center - previous_center;
32+
}
33+
34+
self.touch_count = self.active_touches.len();
35+
}
36+
37+
pub fn reset(&mut self) {
38+
self.previous_touches = self.active_touches.clone();
39+
self.single_finger_delta = Vec2::zeros();
40+
self.two_finger_delta = Vec2::zeros();
41+
}
42+
43+
pub fn clear(&mut self) {
44+
self.active_touches.clear();
45+
self.previous_touches.clear();
46+
self.single_finger_delta = Vec2::zeros();
47+
self.two_finger_delta = Vec2::zeros();
48+
self.touch_count = 0;
49+
}
50+
}

crates/nightshade/src/run.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,29 @@ fn input_event_system(world: &mut World, window_event: &WindowEvent) {
180180
mouse.wheel_delta = Vec2::new(*h_lines, *v_lines);
181181
mouse.state.set(MouseState::SCROLLED, true);
182182
}
183+
winit::event::WindowEvent::Touch(touch_event) => {
184+
use crate::ecs::input::TouchPoint;
185+
let touch = &mut world.resources.input.touch;
186+
187+
match touch_event.phase {
188+
winit::event::TouchPhase::Started => {
189+
touch.active_touches.push(TouchPoint {
190+
id: touch_event.id,
191+
position: Vec2::new(touch_event.location.x as f32, touch_event.location.y as f32),
192+
});
193+
}
194+
winit::event::TouchPhase::Moved => {
195+
if let Some(existing) = touch.active_touches.iter_mut().find(|t| t.id == touch_event.id) {
196+
existing.position = Vec2::new(touch_event.location.x as f32, touch_event.location.y as f32);
197+
}
198+
}
199+
winit::event::TouchPhase::Ended | winit::event::TouchPhase::Cancelled => {
200+
touch.active_touches.retain(|t| t.id != touch_event.id);
201+
}
202+
}
203+
204+
touch.update_delta();
205+
}
183206
_ => {}
184207
}
185208
}

0 commit comments

Comments
 (0)