ngen 0.1.4

A very simple game engine using OpenGL
Documentation
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 {
    /// Creates a simple perspective 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()
    }
}

impl std::ops::AddAssign<Vec2> for Camera {
    fn add_assign(&mut self, offset: Vec2) {
        self.eye += Vec3::new(offset.x, offset.y, 0.);
        self.look_at += Vec3::new(offset.x, offset.y, 0.);
    }
}

impl std::ops::SubAssign<Vec2> for Camera {
    fn sub_assign(&mut self, offset: Vec2) {
        self.eye -= Vec3::new(offset.x, offset.y, 0.);
        self.look_at -= Vec3::new(offset.x, offset.y, 0.);
    }
}