polyscope_structures/camera_view/
camera_parameters.rs1use glam::{Mat3, Mat4, Vec3};
4
5#[derive(Debug, Clone, Copy)]
7pub struct CameraIntrinsics {
8 pub fov_vertical_degrees: f32,
10 pub aspect_ratio: f32,
12}
13
14impl CameraIntrinsics {
15 #[must_use]
17 pub fn new(fov_vertical_degrees: f32, aspect_ratio: f32) -> Self {
18 Self {
19 fov_vertical_degrees,
20 aspect_ratio,
21 }
22 }
23
24 #[must_use]
26 pub fn from_horizontal_fov(fov_horizontal_degrees: f32, aspect_ratio: f32) -> Self {
27 let h_rad = fov_horizontal_degrees.to_radians();
29 let v_rad = 2.0 * ((h_rad / 2.0).tan() / aspect_ratio).atan();
30 Self {
31 fov_vertical_degrees: v_rad.to_degrees(),
32 aspect_ratio,
33 }
34 }
35
36 #[must_use]
38 pub fn default_intrinsics() -> Self {
39 Self {
40 fov_vertical_degrees: 60.0,
41 aspect_ratio: 16.0 / 9.0,
42 }
43 }
44}
45
46impl Default for CameraIntrinsics {
47 fn default() -> Self {
48 Self::default_intrinsics()
49 }
50}
51
52#[derive(Debug, Clone, Copy)]
54pub struct CameraExtrinsics {
55 pub position: Vec3,
57 pub look_dir: Vec3,
59 pub up_dir: Vec3,
61}
62
63impl CameraExtrinsics {
64 #[must_use]
66 pub fn new(position: Vec3, look_dir: Vec3, up_dir: Vec3) -> Self {
67 Self {
68 position,
69 look_dir: look_dir.normalize(),
70 up_dir: up_dir.normalize(),
71 }
72 }
73
74 #[must_use]
76 pub fn default_extrinsics() -> Self {
77 Self {
78 position: Vec3::ZERO,
79 look_dir: -Vec3::Z,
80 up_dir: Vec3::Y,
81 }
82 }
83
84 #[must_use]
86 pub fn look_at(position: Vec3, target: Vec3, up: Vec3) -> Self {
87 let look_dir = (target - position).normalize();
88 Self::new(position, look_dir, up)
89 }
90
91 #[must_use]
93 pub fn right_dir(&self) -> Vec3 {
94 self.look_dir.cross(self.up_dir).normalize()
95 }
96
97 #[must_use]
99 pub fn camera_frame(&self) -> (Vec3, Vec3, Vec3) {
100 let right = self.right_dir();
101 let up = right.cross(self.look_dir).normalize();
103 (self.look_dir, up, right)
104 }
105
106 #[must_use]
108 pub fn view_matrix(&self) -> Mat4 {
109 let (look, up, right) = self.camera_frame();
110 let rotation = Mat3::from_cols(right, up, -look);
112 let translation = -rotation * self.position;
113 Mat4::from_cols(
114 rotation.x_axis.extend(0.0),
115 rotation.y_axis.extend(0.0),
116 rotation.z_axis.extend(0.0),
117 translation.extend(1.0),
118 )
119 }
120}
121
122impl Default for CameraExtrinsics {
123 fn default() -> Self {
124 Self::default_extrinsics()
125 }
126}
127
128#[derive(Debug, Clone, Copy, Default)]
130pub struct CameraParameters {
131 pub intrinsics: CameraIntrinsics,
133 pub extrinsics: CameraExtrinsics,
135}
136
137impl CameraParameters {
138 #[must_use]
140 pub fn new(intrinsics: CameraIntrinsics, extrinsics: CameraExtrinsics) -> Self {
141 Self {
142 intrinsics,
143 extrinsics,
144 }
145 }
146
147 #[must_use]
149 pub fn from_vectors(
150 position: Vec3,
151 look_dir: Vec3,
152 up_dir: Vec3,
153 fov_vertical_degrees: f32,
154 aspect_ratio: f32,
155 ) -> Self {
156 Self {
157 intrinsics: CameraIntrinsics::new(fov_vertical_degrees, aspect_ratio),
158 extrinsics: CameraExtrinsics::new(position, look_dir, up_dir),
159 }
160 }
161
162 #[must_use]
164 pub fn look_at(
165 position: Vec3,
166 target: Vec3,
167 up: Vec3,
168 fov_vertical_degrees: f32,
169 aspect_ratio: f32,
170 ) -> Self {
171 Self {
172 intrinsics: CameraIntrinsics::new(fov_vertical_degrees, aspect_ratio),
173 extrinsics: CameraExtrinsics::look_at(position, target, up),
174 }
175 }
176
177 #[must_use]
179 pub fn position(&self) -> Vec3 {
180 self.extrinsics.position
181 }
182
183 #[must_use]
185 pub fn look_dir(&self) -> Vec3 {
186 self.extrinsics.look_dir
187 }
188
189 #[must_use]
191 pub fn up_dir(&self) -> Vec3 {
192 self.extrinsics.up_dir
193 }
194
195 #[must_use]
197 pub fn right_dir(&self) -> Vec3 {
198 self.extrinsics.right_dir()
199 }
200
201 #[must_use]
203 pub fn camera_frame(&self) -> (Vec3, Vec3, Vec3) {
204 self.extrinsics.camera_frame()
205 }
206
207 #[must_use]
209 pub fn fov_vertical_degrees(&self) -> f32 {
210 self.intrinsics.fov_vertical_degrees
211 }
212
213 #[must_use]
215 pub fn aspect_ratio(&self) -> f32 {
216 self.intrinsics.aspect_ratio
217 }
218
219 #[must_use]
221 pub fn view_matrix(&self) -> Mat4 {
222 self.extrinsics.view_matrix()
223 }
224
225 #[must_use]
227 pub fn projection_matrix(&self, near: f32, far: f32) -> Mat4 {
228 Mat4::perspective_rh(
229 self.intrinsics.fov_vertical_degrees.to_radians(),
230 self.intrinsics.aspect_ratio,
231 near,
232 far,
233 )
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_camera_frame() {
243 let extrinsics = CameraExtrinsics::new(
244 Vec3::new(0.0, 0.0, 5.0),
245 Vec3::new(0.0, 0.0, -1.0),
246 Vec3::new(0.0, 1.0, 0.0),
247 );
248 let (look, up, right) = extrinsics.camera_frame();
249 assert!((look - Vec3::new(0.0, 0.0, -1.0)).length() < 1e-6);
250 assert!((up - Vec3::new(0.0, 1.0, 0.0)).length() < 1e-6);
251 assert!((right - Vec3::new(1.0, 0.0, 0.0)).length() < 1e-6);
252 }
253
254 #[test]
255 fn test_look_at() {
256 let params =
257 CameraParameters::look_at(Vec3::new(0.0, 0.0, 5.0), Vec3::ZERO, Vec3::Y, 60.0, 1.5);
258 assert!((params.look_dir() - Vec3::new(0.0, 0.0, -1.0)).length() < 1e-6);
259 assert_eq!(params.fov_vertical_degrees(), 60.0);
260 assert_eq!(params.aspect_ratio(), 1.5);
261 }
262}