summaryrefslogtreecommitdiff
path: root/engine/src/camera/fly.rs
blob: b6ba7aac35ca0da39bc24b8e0cb65f4ff762b300 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use ecs::component::local::Local;
use ecs::sole::Single;
use ecs::system::{Into, System};
use ecs::{Component, Query};
use glfw::window::{Key, KeyState};

use crate::camera::{Active as ActiveCamera, Camera};
use crate::delta_time::DeltaTime;
use crate::event::Update as UpdateEvent;
use crate::input::{Cursor, CursorFlags, Keys};
use crate::transform::Position;
use crate::util::builder;
use crate::vector::{Vec2, Vec3};

builder! {
/// A fly camera.
#[builder(name = Builder, derives = (Debug))]
#[derive(Debug, Component)]
#[non_exhaustive]
pub struct Fly {
    pub current_pitch: f64,
    pub current_yaw: f64,
    pub speed: f32,
}
}

impl Fly
{
    #[must_use]
    pub fn builder() -> Builder
    {
        Builder::default()
    }
}

impl Default for Fly
{
    fn default() -> Self
    {
        Self::builder().build()
    }
}

impl Default for Builder
{
    fn default() -> Self
    {
        Self {
            current_yaw: -90.0,
            current_pitch: 0.0,
            speed: 3.0,
        }
    }
}

/// Fly camera extension.
pub struct Extension(pub Options);

impl ecs::extension::Extension for Extension
{
    fn collect(self, mut collector: ecs::extension::Collector<'_>)
    {
        collector.add_system(
            UpdateEvent,
            update
                .into_system()
                .initialize((CursorState::default(), self.0)),
        );
    }
}

#[derive(Debug, Component)]
pub struct Options
{
    pub mouse_sensitivity: f32,
}

fn update(
    camera_query: Query<(Camera, Position, Fly, ActiveCamera)>,
    keys: Single<Keys>,
    cursor: Single<Cursor>,
    cursor_flags: Single<CursorFlags>,
    delta_time: Single<DeltaTime>,
    mut cursor_state: Local<CursorState>,
    options: Local<Options>,
)
{
    for (mut camera, mut camera_pos, mut fly_camera, _) in &camera_query {
        if cursor.has_moved && cursor_flags.is_first_move.flag {
            #[cfg(feature = "debug")]
            tracing::debug!("First cursor move");

            cursor_state.last_pos = cursor.position;
        }

        let delta_time = delta_time.duration;

        let mut x_offset = cursor.position.x - cursor_state.last_pos.x;
        let mut y_offset = cursor_state.last_pos.y - cursor.position.y;

        cursor_state.last_pos = cursor.position;

        x_offset *= f64::from(options.mouse_sensitivity);
        y_offset *= f64::from(options.mouse_sensitivity);

        fly_camera.current_yaw += x_offset;
        fly_camera.current_pitch += y_offset;

        fly_camera.current_pitch = fly_camera.current_pitch.clamp(-89.0, 89.0);

        // TODO: This casting to a f32 from a f64 is horrible. fix it
        #[allow(clippy::cast_possible_truncation)]
        let direction = Vec3 {
            x: (fly_camera.current_yaw.to_radians().cos()
                * fly_camera.current_pitch.to_radians().cos()) as f32,
            y: fly_camera.current_pitch.to_radians().sin() as f32,
            z: (fly_camera.current_yaw.to_radians().sin()
                * fly_camera.current_pitch.to_radians().cos()) as f32,
        }
        .normalize();

        let cam_right = direction.cross(&Vec3::UP).normalize();

        camera.global_up = cam_right.cross(&direction).normalize();

        if matches!(keys.get_key_state(Key::W), KeyState::Pressed) {
            camera_pos.position +=
                direction * fly_camera.speed * delta_time.as_secs_f32();
        }

        if matches!(keys.get_key_state(Key::S), KeyState::Pressed) {
            camera_pos.position -=
                direction * fly_camera.speed * delta_time.as_secs_f32();
        }

        if matches!(keys.get_key_state(Key::A), KeyState::Pressed) {
            let cam_left = -direction.cross(&Vec3::UP).normalize();

            camera_pos.position += cam_left * fly_camera.speed * delta_time.as_secs_f32();
        }

        if matches!(keys.get_key_state(Key::D), KeyState::Pressed) {
            let cam_right = direction.cross(&Vec3::UP).normalize();

            camera_pos.position +=
                cam_right * fly_camera.speed * delta_time.as_secs_f32();
        }

        camera.target = camera_pos.position + direction;
    }
}

#[derive(Debug, Default, Component)]
struct CursorState
{
    last_pos: Vec2<f64>,
}