1use nalgebra::{Matrix4, Rotation3, Unit, Vector3};
2
3#[derive(Copy, Clone, Debug)]
4pub struct Camera {
5 eye_position: Vector3<f32>,
6 target_position: Vector3<f32>,
7 upward_direction: Unit<Vector3<f32>>,
8
9 fov_y: f32,
10 aspect_ratio: f32, near_clip: f32,
12 far_clip: f32,
13
14 zoom: f32,
15}
16
17impl Camera {
18 pub fn new(eye_position: [f32; 3], target_position: [f32; 3], fov_x: f32, zoom: f32) -> Self {
19 debug_assert!(fov_x > 0.0);
20 debug_assert!(zoom > 0.0);
21
22 let aspect_ratio = 16.0 / 16.0;
23 let fov_y = fov_x * aspect_ratio;
24 let near_clip = 0.1;
25 let far_clip = 1.0e27;
26
27 debug_assert!(near_clip < far_clip);
28
29 Self {
30 eye_position: Vector3::from(eye_position),
31 target_position: Vector3::from(target_position),
32 upward_direction: Vector3::z_axis(),
33 fov_y,
34 aspect_ratio,
35 near_clip,
36 far_clip,
37 zoom,
38 }
39 }
40
41 pub fn as_slice(&self) -> Vec<f32> {
42 let mut slice = ((self.look_at() * self.perspective()).transpose().as_slice()).to_vec();
43 slice.push(self.zoom);
44 slice.push(self.zoom);
45 slice.push(self.zoom);
46 slice.push(self.zoom);
47
48 slice
49 }
50
51 pub fn rotate_azimuthal(&mut self, delta: f32) {
52 let forward = (self.target_position - self.eye_position).normalize();
53 let right = self.upward_direction.cross(&forward).normalize();
54 let actual_up: Vector3<f32> = forward.cross(&right);
55 let actual_up_dir = Unit::new_normalize(actual_up);
56
57 let rotation = Rotation3::from_axis_angle(&actual_up_dir, delta);
58
59 let new_forward = rotation * forward;
60 self.target_position = self.eye_position + new_forward;
61 }
62
63 pub fn rotate_polar(&mut self, delta: f32) {
64 let forward = (self.target_position - self.eye_position).normalize();
65 let right = Unit::new_normalize(self.upward_direction.cross(&forward));
66
67 let rotation = Rotation3::from_axis_angle(&right, -delta);
68
69 let new_forward = rotation * forward;
70 self.target_position = self.eye_position + new_forward;
71 }
72
73 pub fn magnify(&mut self, delta: f32) {
74 self.zoom *= delta;
75 }
76
77 fn look_at(&self) -> Matrix4<f32> {
78 let forward = (self.target_position - self.eye_position).normalize();
79 let right = self.upward_direction.cross(&forward).normalize();
80 let actual_up = forward.cross(&right).normalize();
81
82 let rotation = Matrix4::new(
83 right.x,
84 actual_up.x,
85 forward.x,
86 0.0,
87 right.y,
88 actual_up.y,
89 forward.y,
90 0.0,
91 right.z,
92 actual_up.z,
93 forward.z,
94 0.0,
95 0.0,
96 0.0,
97 0.0,
98 1.0,
99 );
100
101 let translation = Matrix4::new(
102 1.0,
103 0.0,
104 0.0,
105 -self.eye_position.x,
106 0.0,
107 1.0,
108 0.0,
109 -self.eye_position.y,
110 0.0,
111 0.0,
112 1.0,
113 -self.eye_position.z,
114 0.0,
115 0.0,
116 0.0,
117 1.0,
118 );
119
120 rotation * translation
121 }
122
123 fn perspective(&self) -> Matrix4<f32> {
124 let f = 1.0 / (self.fov_y / 2.0).tan();
125 let nf = 1.0 / (self.near_clip - self.far_clip);
126
127 Matrix4::new(
128 f / self.aspect_ratio,
129 0.0,
130 0.0,
131 0.0,
132 0.0,
133 f,
134 0.0,
135 0.0,
136 0.0,
137 0.0,
138 (self.far_clip + self.near_clip) * nf,
139 2.0 * self.far_clip * self.near_clip * nf,
140 0.0,
141 0.0,
142 -1.0,
143 0.0,
144 )
145 }
146}