use crate::render3d::math::{Mat4, Vec3};
#[derive(Debug, Clone, Copy)]
pub enum Projection {
Perspective { fov_y: f32, near: f32, far: f32 },
}
impl Default for Projection {
fn default() -> Self {
Self::Perspective {
fov_y: std::f32::consts::FRAC_PI_4,
near: 0.1,
far: 100.0,
}
}
}
#[derive(Debug, Clone)]
pub struct Camera {
pub position: Vec3,
pub target: Vec3,
pub up: Vec3,
pub projection: Projection,
}
impl Default for Camera {
fn default() -> Self {
Self {
position: Vec3::new(0.0, 2.0, 5.0),
target: Vec3::ZERO,
up: Vec3::Y,
projection: Projection::default(),
}
}
}
impl Camera {
pub fn view_matrix(&self) -> Mat4 {
glam::camera::rh::view::look_at_mat4(self.position, self.target, self.up)
}
pub fn projection_matrix(&self, aspect: f32) -> Mat4 {
match self.projection {
Projection::Perspective { fov_y, near, far } => {
glam::camera::rh::proj::directx::perspective(fov_y, aspect, near, far)
}
}
}
pub fn orbit(&mut self, yaw: f32, pitch: f32) {
let offset = self.position - self.target;
let radius = offset.length();
let theta = offset.z.atan2(offset.x) + yaw;
let phi = (offset.y / radius).acos() + pitch;
let phi = phi.clamp(0.05, std::f32::consts::PI - 0.05);
self.position = self.target
+ Vec3::new(
radius * phi.sin() * theta.cos(),
radius * phi.cos(),
radius * phi.sin() * theta.sin(),
);
}
pub fn zoom(&mut self, delta: f32) {
let offset = self.position - self.target;
let radius = (offset.length() + delta).max(0.5);
self.position = self.target + offset.normalize() * radius;
}
}