chaos_framework/graphics/
camera.rs

1use glam::{vec3, Mat4, Vec2, Vec3, Vec4};
2use glfw::{self, Key};
3use crate::{cstr, graphics::shader::Shader, EventLoop};
4use std::ffi::CString;
5
6const UP: Vec3 = Vec3::Y;
7const SENSITIVITY: f32 = 0.1; // todo: make this editable
8
9#[derive(Clone, Copy, Debug)]
10pub enum ProjectionType {
11    Perspective,
12    Orthographic,
13    Isometric,
14    Oblique,
15}
16
17#[derive(Debug, Clone, Copy)]
18pub struct Camera {
19    pub proj: Mat4,
20    pub view: Mat4,
21
22    projection_type: ProjectionType,
23
24    pub pos: Vec3,
25    _target: Vec3,
26    direction: Vec3,
27    pub right: Vec3,
28    pub front: Vec3,
29    pub up: Vec3,
30
31    pub pitch: f32,
32    pub yaw: f32,
33
34    pub speed: f32,
35
36    pub dt: f32,
37    last_frame: f32,
38
39    first_mouse: bool,
40    last_x: f32,
41    last_y: f32,
42}
43
44impl Camera {
45    pub fn new() -> Self {
46        let (pitch, yaw): (f32, f32) = (0.0, -90.0);
47        let pos = vec3(0.0, 0.0, 3.0);
48        let target = vec3(0.0, 0.0, -1.0);
49        let mut direction = (pos - target).normalize();
50        direction.x = yaw.to_radians().cos() * pitch.to_radians().cos();
51        direction.y = pitch.to_radians().sin();
52        direction.z = yaw.to_radians().sin() * pitch.to_radians().cos();
53        
54        let right = UP.cross(direction).normalize();
55        let up = direction.cross(right);
56        let front = direction.normalize();
57
58        let view = Mat4::look_at_rh(pos, pos + front, up);
59
60        Self {
61            proj: Mat4::perspective_rh_gl(70.0f32.to_radians(), 1.0, 0.1, 100000.0),
62            view,
63
64            pos,
65            _target: target,
66            direction,
67            right,
68            front,
69            up,
70
71            speed: 1.0,
72
73            pitch,
74            yaw,
75
76            dt: 0.0,
77            last_frame: 0.0,
78
79            projection_type: ProjectionType::Perspective,
80
81            first_mouse: true,
82            last_x: 400.0,
83            last_y: 400.0,
84        }
85    }
86
87    pub fn update(&mut self, y: Vec3, el: &EventLoop) {
88        self.pos = y;
89        
90        self.view = Mat4::look_at_rh(
91            self.pos,
92            self.pos + self.front,
93            self.up,
94        );
95        
96        let (w, h) = el.window.get_framebuffer_size();
97
98        match self.projection_type {
99            ProjectionType::Orthographic => {
100                let ar = w as f32 / h as f32;
101        
102                self.proj = Mat4::orthographic_rh(-ar, ar, -1.0, 1.0, -100.0, 100.0);
103            }
104
105            ProjectionType::Perspective => {
106                self.proj = Mat4::perspective_rh_gl(70.0f32.to_radians(), w as f32 / h as f32, 0.0001, 1000.0);
107            }
108
109            ProjectionType::Isometric => {
110                let rotate_y = Mat4::from_rotation_y(45.0_f32.to_radians());
111                let rotate_x = Mat4::from_rotation_x(35.064_f32.to_radians());
112                self.proj = rotate_x * rotate_y;
113            }
114
115            ProjectionType::Oblique => {
116                let scale = 0.5;
117                let angle = 45.0;
118
119                let angle_rad = f32::to_radians(angle);
120                let mut mat = Mat4::IDENTITY;
121                *mat.col_mut(2) = Vec4::new(scale * angle_rad.cos(), scale * angle_rad.sin(), 1.0, 0.0);
122
123                self.proj = mat;
124            }
125        }
126    }
127
128    pub fn input(
129        &mut self,
130        el: &EventLoop, 
131    ) {
132        let mut speed = self.speed;
133        let curr_frame = el.window.glfw.get_time() as f32;
134        self.dt = curr_frame - self.last_frame;
135        self.last_frame = curr_frame;
136
137        if el.is_key_down(Key::LeftShift) {
138            speed *= 20.0;
139        }
140        
141        if el.is_key_down(Key::RightShift) {
142            speed *= 20.0;
143        }
144
145        if el.is_key_down(Key::W) {
146            self.pos += speed * self.dt * self.front; 
147        }
148        if el.is_key_down(Key::S) {
149            self.pos -= speed * self.dt * self.front; 
150        }
151        if el.is_key_down(Key::Space) {
152            self.pos += speed * self.dt * self.up;
153        }
154        if el.is_key_down(Key::LeftControl) {
155            self.pos -= speed * self.dt * self.up;
156        }
157        if el.is_key_down(Key::A) {
158            self.pos -= speed * self.dt * self.front.cross(self.up).normalize(); 
159        }
160        if el.is_key_down(Key::D) {
161            self.pos += speed * self.dt * self.front.cross(self.up).normalize(); 
162        }
163    }
164
165    pub fn mouse_callback(
166        &mut self, 
167        pos: Vec2,
168        window: &glfw::Window,
169    ) {
170        let xpos = pos.x;
171        let ypos = pos.y;
172        
173        if window.get_cursor_mode() != glfw::CursorMode::Disabled {
174            self.first_mouse = true;
175            // return 
176        };
177        if self.first_mouse { 
178            self.last_x = xpos;
179            self.last_y = ypos;
180            self.first_mouse = false;
181        }
182
183        let mut xoffs = xpos - self.last_x;
184        let mut yoffs = self.last_y - ypos;
185
186        self.last_x = xpos;
187        self.last_y = ypos;
188
189        xoffs *= SENSITIVITY;
190        yoffs *= -SENSITIVITY;
191
192        self.yaw += xoffs;
193        self.pitch += yoffs;
194
195        self.direction.x = self.yaw.to_radians().cos() * self.pitch.to_radians().cos();
196        self.direction.y = self.pitch.to_radians().sin();
197        self.direction.z = self.yaw.to_radians().sin() * self.pitch.to_radians().cos();
198
199        self.front = self.direction.normalize();
200    }
201
202    // RENDERING //
203    pub unsafe fn send_uniforms(&self, shader: &Shader) {
204        shader.uniform_mat4fv(
205            cstr!("view"),
206            &self.view.to_cols_array(),
207        );
208
209        shader.uniform_mat4fv(
210            cstr!("proj"),
211            &self.proj.to_cols_array(),
212        );
213    }
214
215    pub fn set_projection(
216        &mut self, 
217        projection_type: ProjectionType,
218    ) {
219        match projection_type {
220            ProjectionType::Perspective => {
221                self.projection_type = ProjectionType::Perspective;
222            },
223            ProjectionType::Orthographic => {
224                self.projection_type = ProjectionType::Orthographic;
225            },
226            ProjectionType::Isometric => {
227                self.projection_type = ProjectionType::Isometric;
228            },
229            ProjectionType::Oblique => {
230                self.projection_type = ProjectionType::Oblique;
231            }
232        }
233    }
234 
235 }