heavens/
camera.rs

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, // Width / height
11    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}