Skip to main content

oxide_engine/camera/
controller.rs

1//! FPS-style camera controller
2
3use glam::Vec3;
4use oxide_ecs::Component;
5
6use crate::ecs::World;
7use crate::input::{KeyboardInput, MouseInput};
8use oxide_math::prelude::Camera;
9
10#[derive(Component, Clone, Copy, Debug)]
11pub struct CameraComponent(pub Camera);
12
13impl Default for CameraComponent {
14    fn default() -> Self {
15        Self(Camera::new())
16    }
17}
18
19impl CameraComponent {
20    pub fn new() -> Self {
21        Self::default()
22    }
23}
24
25#[derive(Component)]
26pub struct CameraController {
27    pub speed: f32,
28    pub sensitivity: f32,
29    pub yaw: f32,
30    pub pitch: f32,
31}
32
33impl Default for CameraController {
34    fn default() -> Self {
35        Self {
36            speed: 5.0,
37            sensitivity: 0.002,
38            yaw: 0.0,
39            pitch: 0.0,
40        }
41    }
42}
43
44impl CameraController {
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    pub fn with_speed(mut self, speed: f32) -> Self {
50        self.speed = speed;
51        self
52    }
53
54    pub fn with_sensitivity(mut self, sensitivity: f32) -> Self {
55        self.sensitivity = sensitivity;
56        self
57    }
58
59    pub fn update_camera(&self, camera: &mut Camera) {
60        let rotation =
61            glam::Quat::from_rotation_y(self.yaw) * glam::Quat::from_rotation_x(self.pitch);
62        let forward = rotation * Vec3::NEG_Z;
63
64        camera.target = camera.position + forward;
65        camera.up = rotation * Vec3::Y;
66    }
67}
68
69pub fn camera_controller_system(world: &mut World) {
70    use winit::keyboard::KeyCode;
71
72    let (w, s, a, d, space, shift, dx, dy) = {
73        let keyboard = world.resource::<KeyboardInput>();
74        let mouse = world.resource::<MouseInput>();
75
76        let (dx, dy) = mouse.delta();
77
78        (
79            keyboard.pressed(KeyCode::KeyW),
80            keyboard.pressed(KeyCode::KeyS),
81            keyboard.pressed(KeyCode::KeyA),
82            keyboard.pressed(KeyCode::KeyD),
83            keyboard.pressed(KeyCode::Space),
84            keyboard.pressed(KeyCode::ShiftLeft),
85            dx,
86            dy,
87        )
88    };
89
90    let mut query = world.query::<(&mut CameraController, &mut CameraComponent)>();
91    for (controller, camera_comp) in query.iter_mut(world) {
92        let camera = &mut camera_comp.0;
93
94        controller.yaw -= dx * controller.sensitivity;
95        controller.pitch -= dy * controller.sensitivity;
96        controller.pitch = controller.pitch.clamp(
97            -std::f32::consts::FRAC_PI_2 + 0.01,
98            std::f32::consts::FRAC_PI_2 - 0.01,
99        );
100
101        let rotation = glam::Quat::from_rotation_y(controller.yaw)
102            * glam::Quat::from_rotation_x(controller.pitch);
103        let forward = rotation * Vec3::NEG_Z;
104        let right = rotation * Vec3::X;
105
106        let mut velocity = Vec3::ZERO;
107        if w {
108            velocity += forward;
109        }
110        if s {
111            velocity -= forward;
112        }
113        if a {
114            velocity -= right;
115        }
116        if d {
117            velocity += right;
118        }
119        if space {
120            velocity += Vec3::Y;
121        }
122        if shift {
123            velocity -= Vec3::Y;
124        }
125
126        if velocity != Vec3::ZERO {
127            camera.position += velocity.normalize() * controller.speed * 0.016;
128        }
129
130        controller.update_camera(camera);
131    }
132}