1use crate::geo::vec3::{random_in_unit_disc, Vec3, ZERO_VECTOR};
4use crate::geo::{Ray, Uv};
5use crate::util::degrees_to_radians;
6
7pub struct CameraConfig {
9    pub vertical_fov_degrees: f64,
11    pub aperture_size: f64,
13    pub look_from: Vec3,
15    pub look_at: Vec3,
17    pub up: Vec3,
19}
20
21impl Default for CameraConfig {
22    fn default() -> Self {
23        CameraConfig {
24            vertical_fov_degrees: 50.0,
25            aperture_size: 0.0,
26            look_from: ZERO_VECTOR,
27            look_at: ZERO_VECTOR,
28            up: Vec3::new(0., 1., 0.),
29        }
30    }
31}
32
33pub struct Camera {
36    origin: Vec3,
37    lower_left_corner: Vec3,
38    horizontal: Vec3,
39    vertical: Vec3,
40    u: Vec3,
41    v: Vec3,
42    lens_radius: f64,
43}
44
45impl Camera {
46    pub fn new(image_width: usize, image_height: usize, c: &CameraConfig) -> Camera {
48        let aspect_ratio = image_width as f64 / image_height as f64;
49        let theta = degrees_to_radians(c.vertical_fov_degrees);
50        let h = (theta / 2.).tan();
51        let view_port_height = 2. * h;
52        let view_port_width = aspect_ratio * view_port_height;
53
54        let look_v = c.look_from - c.look_at;
55        let focus_distance = look_v.length();
56        let w = look_v.unit();
57        let u = c.up.unit().cross(w).unit();
58        let v = w.cross(u);
59
60        let horizontal = (u * view_port_width) * focus_distance;
61        let vertical = (v * view_port_height) * focus_distance;
62        let lower_left_corner =
63            c.look_from - (horizontal / 2.) - (vertical / 2.) - (w * focus_distance);
64
65        Camera {
66            origin: c.look_from,
67            lower_left_corner,
68            horizontal,
69            vertical,
70            u,
71            v,
72            lens_radius: c.aperture_size / 2.,
73        }
74    }
75
76    pub fn get_ray(&self, uv: Uv) -> Ray {
78        let offset = if self.lens_radius > 0. {
79            let rd = random_in_unit_disc() * self.lens_radius;
80            self.u * rd.x + self.v * rd.y
81        } else {
82            ZERO_VECTOR
83        };
84
85        let r_dir = self.lower_left_corner + (self.horizontal * uv.u) + (self.vertical * uv.v)
86            - self.origin
87            - offset;
88        Ray::new(self.origin + offset, r_dir)
89    }
90}