1use std::f32::consts::PI;
2
3use cgmath::{vec3, Deg};
4use cgmath::prelude::*;
5
6use log::{warn, trace};
7use num_traits::clamp;
8
9use crate::render::Camera;
14use crate::render::math::*;
15
16use glutin::dpi::PhysicalPosition;
17use glutin::dpi::PhysicalSize;
18
19#[derive(PartialEq, Clone, Copy)]
21pub enum CameraMovement {
22 FORWARD,
23 BACKWARD,
24 LEFT,
25 RIGHT,
26}
27use self::CameraMovement::*;
28
29#[derive(Debug)]
30pub struct CameraParams {
31 pub position: Vector3,
32 pub view_matrix: Matrix4,
33 pub projection_matrix: Matrix4,
34}
35
36const SPEED: f32 = 2.5;
38const ZOOM_SENSITIVITY: f32 = 0.1;
39pub const ZOOM: f32 = 45.0;
40const MIN_ZOOM: f32 = 1.0;
41const MAZ_ZOOM: f32 = 170.0;
42
43#[derive(Clone)]
44pub enum NavState {
45 None,
46 Rotating,
47 Panning,
48}
49
50pub struct OrbitControls {
52 pub camera: Camera,
53
54 pub position: Point3,
55
56 pub target: Point3,
58
59 pub state: NavState,
60
61 spherical: Spherical,
63 spherical_delta: Spherical,
64
65 scale: f32,
66 pan_offset: Vector3,
67
68 rotate_start: Option<Vector2>,
69 rotate_end: Vector2,
70
71 pan_start: Option<Vector2>,
72 pan_end: Vector2,
73
74 pub moving_left: bool,
77 pub moving_right: bool,
79 pub moving_forward: bool,
80 pub moving_backward: bool,
81
82 pub screen_size: PhysicalSize,
83}
84
85impl OrbitControls {
86 pub fn new(position: Point3, screen_size: PhysicalSize) -> Self {
87 OrbitControls {
88 camera: Camera::default(),
89
90 position,
91 target: Point3::new(0.0, 0.0, 0.0),
92
93 state: NavState::None,
94
95 spherical: Spherical::default(),
97 spherical_delta: Spherical::default(),
98
99 scale: 1.0, pan_offset: Vector3::zero(),
101
102 rotate_start: None,
103 rotate_end: Vector2::zero(),
104
105 pan_start: None,
106 pan_end: Vector2::zero(),
107
108 moving_left: false,
110 moving_right: false,
112 moving_forward: false,
113 moving_backward: false,
114
115 screen_size,
116 }
117 }
118
119 pub fn camera_params(&self) -> CameraParams {
121 CameraParams {
122 position: self.position.to_vec(),
123 view_matrix: self.view_matrix(),
124 projection_matrix: self.camera.projection_matrix,
125 }
126 }
127
128 fn view_matrix(&self) -> Matrix4 {
129 Matrix4::look_at(self.position, self.target, vec3(0.0, 1.0, 0.0))
130 }
131
132 pub fn handle_mouse_move(&mut self, pos: PhysicalPosition) {
133 match self.state {
134 NavState::Rotating => self.handle_mouse_move_rotate(pos),
135 NavState::Panning => self.handle_mouse_move_pan(pos),
136 NavState::None => ()
137 }
138 }
139
140 fn handle_mouse_move_rotate(&mut self, pos: PhysicalPosition) {
141 self.rotate_end.x = pos.x as f32;
142 self.rotate_end.y = pos.y as f32;
143 let rotate_delta = if let Some(rotate_start) = self.rotate_start {
144 self.rotate_end - rotate_start
145 } else {
146 Vector2::zero()
147 };
148
149 let rotate_speed = 1.0; let angle = 2.0 * PI * rotate_delta.x / self.screen_size.width as f32 * rotate_speed;
152 self.rotate_left(angle);
153
154 let angle = 2.0 * PI * rotate_delta.y / self.screen_size.height as f32 * rotate_speed;
156 self.rotate_up(angle);
157
158 self.rotate_start = Some(self.rotate_end);
159
160 self.update();
161 }
162
163 pub fn handle_mouse_up(&mut self) {
164 self.rotate_start = None;
165 self.pan_start = None;
166 }
167
168 fn rotate_left(&mut self, angle: f32) {
169 self.spherical_delta.theta -= angle;
170 }
171
172 pub fn rotate_object(&mut self, angle: f32) {
173 self.rotate_left(angle);
174 self.update();
175 }
176 fn rotate_up(&mut self, angle: f32) {
177 self.spherical_delta.phi -= angle;
178 }
179
180 fn handle_mouse_move_pan(&mut self, pos: PhysicalPosition) {
181 self.pan_end.x = pos.x as f32;
182 self.pan_end.y = pos.y as f32;
183
184 let pan_delta = if let Some(pan_start) = self.pan_start {
185 self.pan_end - pan_start
186 } else {
187 Vector2::zero()
188 };
189
190 self.pan(pan_delta);
191
192 self.pan_start = Some(self.pan_end);
193
194 self.update();
195 }
196
197 fn pan(&mut self, delta: Vector2) {
198 if self.camera.is_perspective() {
199 let offset = self.position - self.target;
200 let mut target_distance = offset.magnitude();
201
202 target_distance *= (self.camera.fovy / 2.0).tan() * PI / 180.0;
204
205 let distance = 50.0 * delta.x * target_distance / self.screen_size.height as f32;
207 self.pan_left(-distance);
208 let distance = 50.0 * delta.y * target_distance / self.screen_size.height as f32;
209 self.pan_up(-distance);
210 } else {
211 warn!("unimplemented: orthographic camera pan")
213 }
214 }
215
216 pub fn pan_left(&mut self, distance: f32) {
217 self.pan_offset.x -= distance
218 }
219
220 pub fn pan_up(&mut self, distance: f32) {
221 self.pan_offset.y -= distance
222 }
223
224 pub fn process_mouse_scroll(&mut self, mut yoffset: f32) {
226 yoffset *= ZOOM_SENSITIVITY;
227 if self.camera.fovy.0 >= MIN_ZOOM && self.camera.fovy.0 <= MAZ_ZOOM {
228 self.camera.fovy.0 -= yoffset;
229 }
230 if self.camera.fovy.0 <= MIN_ZOOM {
231 self.camera.fovy.0 = MIN_ZOOM;
232 }
233 if self.camera.fovy.0 >= MAZ_ZOOM {
234 self.camera.fovy.0 = MAZ_ZOOM;
235 }
236 self.camera.update_projection_matrix();
237 }
238
239 fn update(&mut self) {
241 let mut offset = self.position - self.target;
242
243 self.spherical = Spherical::from_vec3(offset);
247
248 self.spherical.theta += self.spherical_delta.theta;
249 self.spherical.phi += self.spherical_delta.phi;
250
251 let epsilon = 0.0001;
255 self.spherical.phi = clamp(self.spherical.phi, epsilon, PI - epsilon);
256
257 self.spherical.radius *= self.scale;
258
259 let pan_speed = 2.0; self.pan_offset *= pan_speed;
266 let right = offset.cross(Vector3::unit_y()).normalize();
267 let up = right.cross(offset).normalize();
268 self.position += right * self.pan_offset.x;
269 self.position += up * self.pan_offset.y;
270 self.target += right * self.pan_offset.x;
271 self.target += up * self.pan_offset.y;
272
273 offset = self.spherical.to_vec3();
275 self.position = self.target + offset;
276
277 self.spherical_delta = Spherical::from_vec3(Vector3::zero());
279
280 self.scale = 1.0;
281 self.pan_offset = Vector3::zero();
282
283 trace!("Position: {:?}\tTarget: {:?}\tfovy: {:?}", self.position, self.target, Deg(self.camera.fovy));
286 }
287
288 pub fn process_keyboard(&mut self, direction: CameraMovement, pressed: bool) {
289 match direction {
290 FORWARD => self.moving_forward = pressed,
291 BACKWARD => self.moving_backward= pressed,
292 LEFT => self.moving_left = pressed,
293 RIGHT => self.moving_right = pressed,
294 }
295 }
296
297 pub fn frame_update(&mut self, delta_time: f64) {
299 let velocity = SPEED * delta_time as f32;
300
301 let front = (self.target - self.position).normalize();
302 if self.moving_forward {
303 self.position += front * velocity;
304 self.target += front * velocity;
305 }
306 if self.moving_backward {
307 self.position += -(front * velocity);
308 self.target += -(front * velocity);
309 }
310
311 let right = front.cross(Vector3::unit_y()).normalize();
312 if self.moving_left {
313 self.position += -(right * velocity);
314 self.target += -(right * velocity);
315 }
316 if self.moving_right {
317 self.position += right * velocity;
318 self.target += right * velocity;
319 }
320 }
321
322 pub fn set_camera(&mut self, camera: &Camera, transform: &Matrix4) {
323 let pos = transform * vec4(0.0, 0.0, 0.0, 1.0);
325
326 let look_at = transform * vec4(0.0, 0.0, -1.0, 0.0);
328
329 self.position = Point3::new(pos.x, pos.y, pos.z);
330 self.target = Point3::new(look_at.x, look_at.y, look_at.z);
331
332 let mut camera = camera.clone();
334 camera.update_aspect_ratio(self.camera.aspect_ratio());
335 self.camera = camera;
336
337 self.camera.update_projection_matrix();
338 }
339}