1use bb_geometry::rotation3d::Rotation3D;
4use bb_geometry::vector3d::Vector3D;
5use std::f64::consts::PI;
6
7pub struct Camera3D {
8 distance_min: f64,
9 distance_max: f64,
10 pos: Vector3D,
11 psi: f64,
12 theta: f64,
13 phi: f64,
14 fov_x: f64,
15 fov_y: f64,
16}
17
18const DEFAULT_FOV_X: f64 = 60.0;
19const DEFAULT_FOV_Y: f64 = 60.0;
20const DEFAULT_POS: Vector3D = Vector3D {
21 x: 0.0,
22 y: 0.0,
23 z: 0.0,
24};
25const DEFAULT_PHI: f64 = 0.0;
26const DEFAULT_THETA: f64 = PI / 2.0;
27const DEFAULT_DISTANCE_MIN: f64 = 0.01;
28const DEFAULT_DISTANCE_MAX: f64 = 100.0;
29
30impl Camera3D {
31 pub fn new() -> Self {
32 Camera3D {
33 distance_min: DEFAULT_DISTANCE_MIN,
34 distance_max: DEFAULT_DISTANCE_MAX,
35 pos: DEFAULT_POS,
36 psi: DEFAULT_PHI,
37 theta: DEFAULT_THETA,
38 phi: DEFAULT_PHI,
39 fov_x: DEFAULT_FOV_X,
40 fov_y: DEFAULT_FOV_Y,
41 }
42 }
43
44 pub fn move_pos(&mut self, delta_v: &Vector3D) {
45 self.pos = self.pos.plus(delta_v);
46 }
47
48 pub fn move_psi(&mut self, delta_psi: f64) {
49 self.psi += delta_psi;
50 }
51
52 pub fn move_theta(&mut self, delta_theta: f64) {
53 self.theta += delta_theta;
54 }
55
56 pub fn move_phi(&mut self, delta_phi: f64) {
57 self.phi += delta_phi;
58 }
59
60 pub fn adjust_fov(&mut self, delta_fov_x: f64, delta_fov_y: f64) {
61 self.fov_x += delta_fov_x;
62 self.fov_y += delta_fov_y;
63 }
64
65 pub fn rotation_matrix(&self) -> Rotation3D {
66 Rotation3D::from_euler_angles(self.psi, self.theta, self.phi)
67 }
68
69 pub fn position(&self) -> &Vector3D {
70 &self.pos
71 }
72
73 pub fn theta(&self) -> f64 {
74 self.theta
75 }
76
77 pub fn phi(&self) -> f64 {
78 self.phi
79 }
80
81 pub fn psi(&self) -> f64 {
82 self.psi
83 }
84
85 pub fn fov_x(&self) -> f64 {
86 self.fov_x
87 }
88
89 pub fn fov_y(&self) -> f64 {
90 self.fov_y
91 }
92
93 pub fn distance_min(&self) -> f64 {
94 self.distance_min
95 }
96
97 pub fn distance_max(&self) -> f64 {
98 self.distance_max
99 }
100
101 pub fn projection_matrix(&self) -> [[f32; 4]; 4] {
102 let aspect_ratio = 800.0 / 600.0;
103 let fov_y = self.fov_y.to_radians() as f32;
104 let near = self.distance_min as f32;
105 let far = self.distance_max as f32;
106
107 let f = 1.0 / (fov_y / 2.0).tan();
108
109 [
110 [f / aspect_ratio, 0.0, 0.0, 0.0],
111 [0.0, f, 0.0, 0.0],
112 [0.0, 0.0, (far + near) / (near - far), -1.0],
113 [0.0, 0.0, (2.0 * far * near) / (near - far), 0.0],
114 ]
115 }
116
117 pub fn view_matrix(&self) -> [[f32; 4]; 4] {
118 let rot = self.rotation_matrix().components();
119 let pos = self.rotation_matrix().inverse().act_on(&self.pos).revert().components();
120 [
121 [rot[0][0] as f32, rot[1][0] as f32, rot[2][0] as f32, pos[0] as f32],
122 [rot[0][1] as f32, rot[1][1] as f32, rot[2][1] as f32, pos[1] as f32],
123 [rot[0][2] as f32, rot[1][2] as f32, rot[2][2] as f32, pos[2] as f32],
124 [0.0, 0.0, 0.0, 1.0]
125 ]
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 #[test]
133 fn test_references() {
134 let camera = Camera3D::new();
136
137 let mut x = camera.psi();
139 x += 0.5;
140
141 assert_eq!(x, 0.5);
143 assert_eq!(camera.psi(), 0.0);
144 }
145}