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
extern crate cgmath;
use cgmath::prelude::*;
use cgmath::{BaseFloat, Matrix4, Quaternion, Vector2, Vector3};
use cgmath::num_traits::clamp;
pub struct ArcballCamera<F> {
look_at: Matrix4<F>,
translation: Matrix4<F>,
rotation: Quaternion<F>,
camera: Matrix4<F>,
inv_camera: Matrix4<F>,
motion_speed: F,
zoom_speed: F,
inv_screen: [F; 2],
}
impl<F: BaseFloat> ArcballCamera<F> {
pub fn new(look_at: &Matrix4<F>, motion_speed: F, zoom_speed: F, screen: [F; 2]) -> ArcballCamera<F> {
ArcballCamera {
look_at: *look_at,
translation: Transform::one(),
rotation: Quaternion::new(F::one(), F::zero(), F::zero(), F::zero()),
camera: *look_at,
inv_camera: look_at.invert().unwrap(),
motion_speed,
zoom_speed,
inv_screen: [F::one() / screen[0], F::one() / screen[1]],
}
}
pub fn get_mat4(&self) -> Matrix4<F> {
self.camera
}
pub fn rotate(&mut self, mouse_prev: Vector2<F>, mouse_cur: Vector2<F>) {
let one = F::one();
let two = F::from(2.0).unwrap();
let m_cur = Vector2::new(clamp(mouse_cur.x * two * self.inv_screen[0] - one, -one, one),
clamp(one - two * mouse_cur.y * self.inv_screen[1], -one, one));
let m_prev = Vector2::new(clamp(mouse_prev.x * two * self.inv_screen[0] - one, -one, one),
clamp(one - two * mouse_prev.y * self.inv_screen[1], -one, one));
let mouse_cur_ball = ArcballCamera::screen_to_arcball(m_cur);
let mouse_prev_ball = ArcballCamera::screen_to_arcball(m_prev);
self.rotation = mouse_cur_ball * mouse_prev_ball * self.rotation;
self.camera = self.translation * self.look_at * Matrix4::from(self.rotation);
self.inv_camera = self.camera.invert().unwrap();
}
pub fn zoom(&mut self, amount: F, elapsed: F) {
let motion = Vector3::new(F::zero(), F::zero(), amount);
self.translation = Matrix4::from_translation(motion * self.zoom_speed * elapsed) * self.translation;
self.camera = self.translation * self.look_at * Matrix4::from(self.rotation);
self.inv_camera = self.camera.invert().unwrap();
}
pub fn pan(&mut self, mouse_delta: Vector2<F>, elapsed: F) {
let motion = mouse_delta.extend(F::zero()) * self.motion_speed * elapsed;
self.translation = Matrix4::from_translation(motion) * self.translation;
self.camera = self.translation * self.look_at * Matrix4::from(self.rotation);
self.inv_camera = self.camera.invert().unwrap();
}
pub fn update_screen(&mut self, width: F, height: F) {
self.inv_screen[0] = F::one() / width;
self.inv_screen[1] = F::one() / height;
}
fn screen_to_arcball(p: Vector2<F>) -> Quaternion<F> {
let dist = cgmath::dot(p, p);
if dist <= F::one() {
Quaternion::new(F::zero(), p.x, p.y, F::sqrt(F::one() - dist))
} else {
let unit_p = p.normalize();
Quaternion::new(F::zero(), unit_p.x, unit_p.y, F::zero())
}
}
}