1use core::f32::consts;
2
3use nalgebra::{Isometry3, Perspective3, Point3, Vector3};
4
5pub struct Camera {
6 pub position: Point3<f32>,
7 fov: f32,
8 pub near: f32,
9 pub far: f32,
10 view_matrix: nalgebra::Matrix4<f32>,
11 projection_matrix: nalgebra::Matrix4<f32>,
12 pub vp_matrix: nalgebra::Matrix4<f32>,
13 target: Point3<f32>,
14 aspect_ratio: f32,
15}
16
17impl Camera {
18 pub fn new(aspect_ratio: f32) -> Camera {
19 let mut ret = Camera {
20 position: Point3::new(0.0, 0.0, 0.0),
21 fov: consts::PI / 2.0,
22 view_matrix: nalgebra::Matrix4::identity(),
23 projection_matrix: nalgebra::Matrix4::identity(),
24 vp_matrix: nalgebra::Matrix4::identity(),
25 target: Point3::new(0.0, 0.0, 0.0),
26 aspect_ratio,
27 near: 0.4,
28 far: 20.0,
29 };
30
31 ret.update_projection();
32
33 ret
34 }
35
36 pub fn set_position(&mut self, pos: Point3<f32>) {
37 self.position = pos;
38
39 self.update_view();
40 }
41
42 pub fn set_fovy(&mut self, fovy: f32) {
43 self.fov = fovy;
44
45 self.update_projection();
46 }
47
48 pub fn set_near(&mut self, near: f32) {
49 self.near = near;
50
51 self.update_projection();
52 }
53
54 pub fn set_far(&mut self, far: f32) {
55 self.far = far;
56
57 self.update_projection();
58 }
59
60 pub fn set_near_far(&mut self, near: f32, far: f32) {
70 self.near = near;
71 self.far = far;
72
73 self.update_projection();
74 }
75
76 pub fn get_near_far_ratio(&self) -> f32 {
78 self.far / self.near
79 }
80
81 pub fn set_target(&mut self, target: Point3<f32>) {
82 self.target = target;
83 self.update_view();
84 }
85
86 pub fn get_direction(&self) -> Vector3<f32> {
87 let transpose = self.view_matrix; Vector3::new(transpose[(2, 0)], transpose[(2, 1)], transpose[(2, 2)])
90 }
91
92 pub fn get_aspect_ratio(&self) -> f32 {
93 self.aspect_ratio
94 }
95
96 fn update_view(&mut self) {
97 let view = Isometry3::look_at_rh(&self.position, &self.target, &Vector3::y());
98
99 self.view_matrix = view.to_homogeneous();
100 self.vp_matrix = self.projection_matrix * self.view_matrix;
101 }
102
103 fn update_projection(&mut self) {
104 let projection = Perspective3::new(self.aspect_ratio, self.fov, self.near, self.far);
105 self.projection_matrix = projection.to_homogeneous();
106 self.vp_matrix = self.projection_matrix * self.view_matrix;
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_camera_creation() {
116 let camera = Camera::new(16.0 / 9.0);
117 assert!((camera.get_aspect_ratio() - 16.0 / 9.0).abs() < 0.001);
118 assert_eq!(camera.near, 0.4);
119 assert_eq!(camera.far, 20.0);
120 assert_eq!(camera.position, Point3::new(0.0, 0.0, 0.0));
121 }
122
123 #[test]
124 fn test_camera_set_position() {
125 let mut camera = Camera::new(1.0);
126 let new_pos = Point3::new(5.0, 10.0, 15.0);
127 camera.set_position(new_pos);
128 assert_eq!(camera.position, new_pos);
129 }
130
131 #[test]
132 fn test_camera_set_target() {
133 let mut camera = Camera::new(1.0);
134 let target = Point3::new(1.0, 2.0, 3.0);
135 camera.set_target(target);
136 assert_eq!(camera.target, target);
137 }
138
139 #[test]
140 fn test_camera_set_fovy() {
141 let mut camera = Camera::new(1.0);
142 let new_fov = core::f32::consts::PI / 4.0; camera.set_fovy(new_fov);
144 assert!((camera.fov - new_fov).abs() < 0.001);
145 }
146
147 #[test]
148 fn test_camera_get_direction() {
149 let mut camera = Camera::new(1.0);
150 camera.set_position(Point3::new(0.0, 0.0, 5.0));
151 camera.set_target(Point3::new(0.0, 0.0, 0.0));
152
153 let direction = camera.get_direction();
154 assert!(direction.magnitude() > 0.0);
156 }
157
158 #[test]
159 fn test_camera_vp_matrix_updates() {
160 let mut camera = Camera::new(1.0);
161 let initial_vp = camera.vp_matrix;
162
163 camera.set_position(Point3::new(5.0, 5.0, 5.0));
165 assert_ne!(camera.vp_matrix, initial_vp);
166
167 let after_pos = camera.vp_matrix;
168
169 camera.set_fovy(core::f32::consts::PI / 4.0);
171 assert_ne!(camera.vp_matrix, after_pos);
172 }
173}