vox_engine 0.3.1

voxel game engine
use crate::{common::Updateable, physics::Physics};
use dear_imgui_reflect::ImGuiReflect;
use getset::{CopyGetters, Setters};
use rapier3d::glamx::{Mat3, Mat4, Quat, Vec3, vec3, vec4};

#[derive(Clone, ImGuiReflect, CopyGetters, Setters, Debug)]
pub struct Camera {
    #[imgui(slider, min = 0.0, max = 200.0)]
    #[getset(get_copy = "pub with_prefix", set = "pub")]
    fov: f32,
    #[getset(get_copy = "pub with_prefix", set = "pub")]
    near: f32,
    #[getset(get_copy = "pub with_prefix", set = "pub")]
    far: f32,
    #[getset(set = "pub")]
    camera_position: Vec3,
    #[getset(get_copy = "pub with_prefix")]
    front: Vec3,
    #[getset(get_copy = "pub with_prefix")]
    up: Vec3,
    #[getset(get_copy = "pub with_prefix")]
    right: Vec3,
    camera_orientation: Quat,
    #[imgui(slider, min = 0.0, max = 360.0)]
    #[getset(get_copy = "pub with_prefix", set = "pub")]
    yaw: f32,
    #[imgui(slider, min = -89.9, max = 89.9)]
    #[getset(get_copy = "pub with_prefix", set = "pub")]
    pitch: f32,
    dimensions: (u16, u16),
}

impl Camera {
    pub fn new(fov: f32, near: f32, far: f32, w: u16, h: u16, camera_position: Vec3) -> Self {
        Camera {
            fov,
            near,
            far,
            camera_position,
            up: Vec3::Y,
            front: Vec3::NEG_Z,
            right: Vec3::ZERO,
            camera_orientation: Quat::IDENTITY,
            yaw: 0.0,
            pitch: 0.0,
            dimensions: (w, h),
        }
    }

    pub fn add_to_yaw(&mut self, value: f32) {
        self.yaw += value;
    }

    pub fn add_to_pitch(&mut self, value: f32) {
        self.pitch += value;

        if self.pitch > 89.9 {
            self.pitch = 89.9;
        } else if self.pitch < -89.9 {
            self.pitch = -89.9;
        }
    }

    pub fn get_pixel_to_ray_matrix(&self, render_scale: u16) -> Mat4 {
        let (w, h) = self.dimensions;
        let (w, h) = ((w / render_scale) as f32, (h / render_scale) as f32);

        let tan_fov = (self.fov.to_radians() * 0.5).tan();

        let f_y = (h * 0.5) / tan_fov;
        let f_x = f_y;

        let cx = w * 0.5;
        let cy = h * 0.5;

        let k_inv = Mat3::from_cols(
            vec3(1.0 / f_x, 0.0, 0.0),
            vec3(0.0, -1.0 / f_y, 0.0),
            vec3(-cx / f_x, cy / f_y, -1.0),
        );

        let r = Mat3::from_quat(self.camera_orientation);

        let m = r * k_inv;

        Mat4::from_cols(
            vec4(m.col(0).x, m.col(0).y, m.col(0).z, 0.0),
            vec4(m.col(1).x, m.col(1).y, m.col(1).z, 0.0),
            vec4(m.col(2).x, m.col(2).y, m.col(2).z, 0.0),
            vec4(
                self.camera_position.x,
                self.camera_position.y,
                self.camera_position.z,
                1.0,
            ),
        )
    }
}

impl Camera {
    fn update_orientation(&mut self) {
        let r_yaw = self.yaw.to_radians();
        let r_pitch = self.pitch.to_radians();

        let q_yaw = Quat::from_axis_angle(Vec3::Y, r_yaw);
        let q_pitch = Quat::from_axis_angle(Vec3::X, r_pitch);

        self.camera_orientation = (q_yaw * q_pitch).normalize();
    }
    fn calculate_right(&mut self) {
        self.right = self.front.cross(self.up).normalize();
    }
    fn calculate_front(&mut self) {
        self.front = self.camera_orientation.mul_vec3(Vec3::NEG_Z).normalize();
    }
}

impl Updateable for Camera {
    fn fixed_update(&mut self, _physics: &mut Physics) {}

    fn update(&mut self, _alpha: f32, _physics: &mut Physics) {
        self.update_orientation();
        self.calculate_right();
        self.calculate_front();
    }
}