use glam::{Mat4, Vec2, Vec3};
use crate::types::Rect;
use crate::utils::math::round_to_even;
pub struct Camera {
pos: Vec2,
znear: f32,
zfar: f32,
logical_size: Vec2,
snap_to_px: bool,
zoom: f32,
}
impl Default for Camera {
fn default() -> Self {
Self {
pos: Vec2::ZERO,
zfar: 100.0,
znear: 0.1,
logical_size: Vec2::ZERO,
snap_to_px: true,
zoom: 1.0,
}
}
}
impl Camera {
pub fn build_view_projection_matrix(&self) -> Mat4 {
let size = self.viewport().size();
let pos = self.pos - size * 0.5;
let view = Mat4::look_at_rh(pos.extend(1.0), pos.extend(0.0), Vec3::Y);
let proj = Mat4::orthographic_rh(0.0, size.x, 0.0, size.y, self.znear, self.zfar);
proj * view
}
pub fn viewport(&self) -> Rect {
let scaled_size = round_to_even(self.logical_size / self.zoom);
let hf_size = scaled_size * 0.5;
let min = self.pos - hf_size;
let max = self.pos + hf_size;
Rect { min, max }
}
pub fn logical_size(&self) -> Vec2 {
round_to_even(self.logical_size / self.zoom)
}
pub fn viewport_to_world(&self, pos: Vec2) -> Vec2 {
round_to_even((pos / self.zoom) + self.pos)
}
pub fn world_to_viewport(&self, wpos: Vec2) -> Vec2 {
round_to_even((wpos - self.pos) * self.zoom)
}
pub fn snap_to_px(&self) -> bool {
self.snap_to_px
}
pub fn set_snap_to_px(&mut self, snap_to_px: bool) {
self.snap_to_px = snap_to_px;
self.set_pos(self.pos);
}
pub fn pos(&self) -> Vec2 {
self.pos
}
pub fn set_pos(&mut self, pos: Vec2) {
if self.snap_to_px {
self.pos = pos.round();
} else {
self.pos = pos;
}
}
pub(crate) fn resize(&mut self, logical_size: Vec2) {
self.logical_size = logical_size;
}
pub fn zoom(&self) -> f32 {
self.zoom
}
pub fn set_zoom(&mut self, zoom: f32) {
self.zoom = zoom.max(0.01);
}
}