use crate::window::frame_io::Viewport;
use glam::{Mat4, Vec3};
#[derive(Debug, Clone, Copy)]
pub enum Projection {
Perspective {
fov: f32,
aspect: f32,
near: f32,
far: f32,
},
Orthographic {
width: f32,
height: f32,
near: f32,
far: f32,
},
}
impl Projection {
pub fn perspective(fov_degrees: f32, aspect: f32, near: f32, far: f32) -> Self {
Self::Perspective {
fov: fov_degrees.to_radians(),
aspect,
near,
far,
}
}
pub fn orthographic(width: f32, height: f32, near: f32, far: f32) -> Self {
Self::Orthographic {
width,
height,
near,
far,
}
}
pub fn matrix(&self) -> Mat4 {
match *self {
Projection::Perspective {
fov,
aspect,
near,
far,
} => Mat4::perspective_rh(fov, aspect, near, far),
Projection::Orthographic {
width,
height,
near,
far,
} => Mat4::orthographic_rh(
-width / 2.0,
width / 2.0,
-height / 2.0,
height / 2.0,
near,
far,
),
}
}
pub fn set_aspect(&mut self, aspect: f32) {
if let Projection::Perspective { aspect: a, .. } = self {
*a = aspect;
}
}
}
pub trait Viewer {
fn position(&self) -> Vec3;
fn view_matrix(&self) -> Mat4;
fn projection_matrix(&self) -> Mat4;
fn view_projection_matrix(&self) -> Mat4 {
self.projection_matrix() * self.view_matrix()
}
fn viewport(&self) -> Viewport;
}
#[derive(Debug, Clone)]
pub struct Camera {
pub position: Vec3,
pub target: Vec3,
pub up: Vec3,
pub projection: Projection,
viewport: Viewport,
}
impl Camera {
pub fn new_perspective(
position: Vec3,
target: Vec3,
up: Vec3,
fov_degrees: f32,
aspect: f32,
near: f32,
far: f32,
) -> Self {
Self {
position,
target,
up,
projection: Projection::perspective(fov_degrees, aspect, near, far),
viewport: Viewport {
x: 0,
y: 0,
width: 1,
height: 1,
},
}
}
pub fn new_orthographic(
position: Vec3,
target: Vec3,
up: Vec3,
width: f32,
height: f32,
near: f32,
far: f32,
) -> Self {
Self {
position,
target,
up,
projection: Projection::orthographic(width, height, near, far),
viewport: Viewport {
x: 0,
y: 0,
width: 1,
height: 1,
},
}
}
pub fn set_viewport(&mut self, viewport: Viewport) {
self.viewport = viewport;
self.projection.set_aspect(viewport.aspect());
}
pub fn forward(&self) -> Vec3 {
(self.target - self.position).normalize()
}
pub fn right(&self) -> Vec3 {
self.forward().cross(self.up).normalize()
}
}
impl Viewer for Camera {
fn position(&self) -> Vec3 {
self.position
}
fn view_matrix(&self) -> Mat4 {
Mat4::look_at_rh(self.position, self.target, self.up)
}
fn projection_matrix(&self) -> Mat4 {
self.projection.matrix()
}
fn viewport(&self) -> Viewport {
self.viewport
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct CameraUniform {
pub view_proj: [[f32; 4]; 4],
pub eye: [f32; 4],
}
impl CameraUniform {
pub fn from_viewer(viewer: &dyn Viewer) -> Self {
let vp = viewer.view_projection_matrix();
let pos = viewer.position();
Self {
view_proj: vp.to_cols_array_2d(),
eye: [pos.x, pos.y, pos.z, 1.0],
}
}
}