1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use crate::math::*;

/// A very simple perspective camera.
#[derive(Clone, Copy)]
pub struct Camera {
    pub eye: P3,
    pub look_at: P3,
    pub up: Vec3,
    pub aspect_ratio: f32,
    pub fov: f32,
    pub near: f32,
    pub far: f32,
    pub perspective: Mat4,
}

impl Camera {
    pub fn new(eye: P3, look_at: P3, aspect_ratio: f32) -> Self {
        let mut camera = Self {
            eye,
            look_at,
            up: Vec3::new(0., 1., 0.),
            aspect_ratio,
            fov: std::f32::consts::FRAC_PI_2,
            near: 0.1,
            far: 100.,
            perspective: Mat4::identity(),
        };

        camera.perspective = camera.perspective_transform();
        camera
    }

    /// Creates a camera based on how many tiles to display in the screen, where tile width is
    /// calculated as (screen_width / tiles_per_x). To keep things simple, we assume as screen size
    /// of 1920x1080 for now.
    pub fn from_tiles(tile_width: u32) -> Self {
        let screen_width = 1920.;
        let screen_height = 1080.;
        let tiles_per_x = screen_width / tile_width as f32;

        // Using simple trig to place the camera
        //    n
        // \-----| <- look_at
        //  \    |
        // h \   | d
        //    \  |
        //     \ |
        //      \|
        //       ^ camera eye
        let r = screen_height / screen_width;
        let n = tiles_per_x as f32 * 0.5;
        let d = n * r / std::f32::consts::FRAC_PI_4.tan(); // Assuming a FRAC_PI_2 FOV
        let h = n * r;

        let camera_eye = P3::new(n, h, d);
        let camera_target = P3::new(n, h, 0.);
        Camera::new(camera_eye, camera_target, screen_width / screen_height)
    }

    pub fn perspective_transform(&self) -> Mat4 {
        cgmath::perspective(
            cgmath::Rad(self.fov),
            self.aspect_ratio,
            self.near,
            self.far,
        )
    }

    pub fn look_at_transform(&self) -> Mat4 {
        Mat4::look_at(self.eye, self.look_at, self.up)
    }

    pub fn build_transform(&self) -> Mat4 {
        self.perspective * self.look_at_transform()
    }
}