Skip to main content

enigma_3d/
camera.rs

1use crate::object::{Transform, TransformSerializer};
2use serde::{Deserialize, Serialize};
3
4#[derive(Serialize, Deserialize)]
5pub struct CameraSerializer {
6    transform: TransformSerializer,
7    fov: f32,
8    width: f32,
9    height: f32,
10    near: f32,
11    far: f32,
12    view: [[f32; 4]; 4],
13    projection: [[f32; 4]; 4],
14}
15
16#[derive(Copy, Clone)]
17pub struct Camera {
18    pub transform: Transform,
19    pub fov: f32,
20    pub width: f32,
21    pub height: f32,
22    pub near: f32,
23    pub far: f32,
24    pub view: [[f32; 4]; 4],
25    pub projection: [[f32; 4]; 4],
26}
27
28
29impl Camera {
30    pub fn new(
31        position: Option<[f32; 3]>,
32        rotation: Option<[f32; 3]>, // Expected in degrees, will be converted to radians
33        fov: Option<f32>, // Expected in degrees, will be converted to radians
34        aspect: Option<f32>,
35        near: Option<f32>,
36        far: Option<f32>
37    ) -> Self {
38        let mut c = Self {
39            transform: {
40                let mut t = Transform::new();
41                t.set_position(position.unwrap_or_else(|| [0.0, 0.0, 0.0]));
42                t.set_rotation(rotation.unwrap_or_else(|| [0.0, 0.0, 0.0]));
43                t
44            },
45            fov: fov.unwrap_or_else(|| 90.0).to_radians(),
46            width: aspect.unwrap_or_else(|| 1920.0),
47            height: aspect.unwrap_or_else(|| 1080.0),
48            near: near.unwrap_or_else(|| 0.1),
49            far: far.unwrap_or_else(|| 1024.0),
50            view: [[0.0; 4]; 4],
51            projection: [[0.0; 4]; 4],
52        };
53        c.update_matrices();
54        c
55    }
56
57    pub fn default() -> Self {
58        Camera::new(None, None, None, None, None, None)
59    }
60
61    pub fn from_serializer(serializer: CameraSerializer) -> Self {
62        Self {
63            transform: Transform::from_serializer(serializer.transform),
64            fov: serializer.fov,
65            width: serializer.width,
66            height: serializer.height,
67            near: serializer.near,
68            far: serializer.far,
69            view: serializer.view,
70            projection: serializer.projection,
71        }
72    }
73
74    pub fn to_serializer(&self) -> CameraSerializer {
75        CameraSerializer {
76            transform: self.transform.to_serializer(),
77            fov: self.fov,
78            width: self.width,
79            height: self.height,
80            near: self.near,
81            far: self.far,
82            view: self.view,
83            projection: self.projection,
84        }
85    }
86
87    pub fn update_matrices(&mut self) {
88        self.view = Camera::view_matrix(
89            &self.transform.get_position().into(),
90            &self.calculate_direction_vector(),
91            &[0.0, 1.0, 0.0],
92        );
93        self.projection = Camera::projection_matrix(
94            self.fov,
95            self.width / self.height,
96            self.near,
97            self.far,
98        );
99    }
100
101    pub fn calculate_direction_vector(&self) -> [f32; 3] {
102        let pitch = self.transform.rotation[0]; // Rotation around X-axis
103        let yaw = self.transform.rotation[1];   // Rotation around Y-axis
104
105        let x = yaw.sin() * pitch.cos();
106        let y = pitch.sin();
107        let z = yaw.cos() * pitch.cos();
108
109        [-x, y, -z] // Pointing down negative Z-axis
110    }
111
112
113    fn projection_matrix(fov: f32, aspect: f32, near: f32, far: f32) -> [[f32; 4]; 4] {
114        let f = 1.0 / (fov / 2.0).tan();
115        [
116            [f / aspect, 0.0, 0.0, 0.0],
117            [0.0, f, 0.0, 0.0],
118            [0.0, 0.0, (far + near) / (near - far), -1.0],
119            [0.0, 0.0, (2.0 * far * near) / (near - far), 0.0],
120        ]
121    }
122
123    fn view_matrix(position: &[f32; 3], direction: &[f32; 3], up: &[f32; 3]) -> [[f32; 4]; 4] {
124        let f = {
125            let len = (direction[0] * direction[0] + direction[1] * direction[1] + direction[2] * direction[2]).sqrt();
126            [-direction[0] / len, -direction[1] / len, -direction[2] / len] // Negate direction for a right-handed system
127        };
128
129        let s = [
130            up[1] * f[2] - up[2] * f[1],
131            up[2] * f[0] - up[0] * f[2],
132            up[0] * f[1] - up[1] * f[0],
133        ];
134        let s_norm = {
135            let len = (s[0] * s[0] + s[1] * s[1] + s[2] * s[2]).sqrt();
136            [s[0] / len, s[1] / len, s[2] / len]
137        };
138
139        let u = [
140            f[1] * s_norm[2] - f[2] * s_norm[1],
141            f[2] * s_norm[0] - f[0] * s_norm[2],
142            f[0] * s_norm[1] - f[1] * s_norm[0],
143        ];
144
145        let p = [
146            -position[0] * s_norm[0] - position[1] * s_norm[1] - position[2] * s_norm[2],
147            -position[0] * u[0] - position[1] * u[1] - position[2] * u[2],
148            -position[0] * f[0] - position[1] * f[1] - position[2] * f[2],
149        ];
150
151        [
152            [s_norm[0], u[0], f[0], 0.0],
153            [s_norm[1], u[1], f[1], 0.0],
154            [s_norm[2], u[2], f[2], 0.0],
155            [p[0], p[1], p[2], 1.0],
156        ]
157    }
158
159    pub fn get_view_matrix(&self) -> [[f32; 4]; 4] {
160        Camera::view_matrix(
161            &self.transform.get_position().into(),
162            &self.calculate_direction_vector(),
163            &[0.0, 1.0, 0.0],
164        )
165    }
166
167    pub fn get_projection_matrix(&self) -> [[f32; 4]; 4] {
168        Camera::projection_matrix(
169            self.fov,
170            self.width / self.height,
171            self.near,
172            self.far,
173        )
174    }
175
176    pub fn get_position(&self) -> [f32; 3] {
177        self.transform.get_position().into()
178    }
179
180    pub fn get_rotation(&self) -> [f32; 3] {
181        self.transform.get_rotation().into()
182    }
183
184    pub fn get_fov(&self) -> f32 {
185        self.fov.clone()
186    }
187
188    pub fn get_aspect(&self) -> (f32, f32) {
189        (self.width.clone(), self.height.clone())
190    }
191
192    pub fn get_near(&self) -> f32 {
193        self.near.clone()
194    }
195
196    pub fn get_far(&self) -> f32 {
197        self.far.clone()
198    }
199
200    pub fn get_view(&self) -> [[f32; 4]; 4] {
201        self.view.clone()
202    }
203
204    pub fn get_projection(&self) -> [[f32; 4]; 4] {
205        self.projection.clone()
206    }
207
208    pub fn set_position(&mut self, position: [f32; 3]) {
209        self.transform.set_position(position);
210        self.update_matrices();
211    }
212
213    pub fn set_rotation(&mut self, rotation: [f32; 3]) {
214        self.transform.set_rotation(rotation);
215        self.update_matrices();
216    }
217
218    pub fn set_fov(&mut self, fov: f32) {
219        self.fov = fov;
220        self.update_matrices();
221    }
222
223    pub fn set_aspect(&mut self, width: f32, heigth: f32) {
224        self.width = width;
225        self.height = heigth;
226        self.update_matrices();
227    }
228
229    pub fn set_near(&mut self, near: f32) {
230        self.near = near;
231        self.update_matrices();
232    }
233
234    pub fn set_far(&mut self, far: f32) {
235        self.far = far;
236        self.update_matrices();
237    }
238}