use nalgebra as na;
#[derive(Clone, Copy, Debug)]
pub struct StandardCamera {
aspect: f32,
fov_y: f32,
proj: na::Perspective3<f32>,
inv_proj: na::Matrix4<f32>,
}
impl StandardCamera {
pub fn new(aspect: f32, fov_y: f32) -> Self {
let proj = na::Perspective3::new(aspect, fov_y.to_radians(), 0.1, 10.0);
Self {
aspect,
fov_y,
inv_proj: proj.inverse(),
proj,
}
}
pub fn unproject(
&self,
coords: na::Point2<f32>,
inv_view: na::Matrix4<f32>,
) -> na::Point3<f32> {
let coords = coords * 2.0 - na::Vector2::new(1.0, 1.0);
(inv_view * self.inv_proj).transform_point(&na::Point3::new(coords.x, coords.y, 1.0))
}
pub fn as_matrix(&self) -> &na::Matrix4<f32> {
self.proj.as_matrix()
}
pub fn project(&self, world: na::Point3<f32>, view: na::Matrix4<f32>) -> na::Point2<f32> {
let screen = self.proj.project_point(&view.transform_point(&world));
let screen = na::Point2::new(screen.x / screen.z, screen.y / screen.z);
(screen + na::Vector2::new(1.0, 1.0)) * 0.5
}
pub fn rotate(&self, coords: na::Point2<f32>, rotation: na::Matrix4<f32>) -> na::Point2<f32> {
let view = na::matrix![
-1.0, 0.0, 0.0, 0.0;
0.0, 0.0, 1.0, 0.0;
0.0, 1.0, 0.0, 0.0;
0.0, 0.0, 0.0, 1.0;
];
let world = self.unproject(coords, view.transpose());
let world = rotation.transform_point(&world);
self.project(world, view)
}
pub fn delta(&self, coords: na::Point2<f32>, rotation: na::Matrix4<f32>) -> na::Vector2<f32> {
self.rotate(coords, rotation) - coords
}
pub fn intrinsics(&self) -> na::Matrix3<f32> {
let fy = 0.5 / (self.fov_y.to_radians() / 2.0).tan();
let fx = fy / self.aspect;
na::matrix![
fx, 0.0, 0.5;
0.0, fy, 0.5;
0.0, 0.0, 1.0
]
}
pub fn point_angle(&self, p: na::Point2<f32>) -> na::Vector2<f32> {
let intrinsics = self.intrinsics();
let p = p - intrinsics.fixed_slice::<2, 1>(0, 2);
let tan = p
.coords
.component_div(&na::matrix![intrinsics[(0, 0)]; intrinsics[(1, 1)]]);
na::matrix![tan.x.atan(); tan.y.atan()]
}
pub fn fov(&self) -> (f32, f32) {
let ty = (self.fov_y.to_radians() / 2.0).tan();
let tx = self.aspect * ty;
(tx.atan().to_degrees() * 2.0, self.fov_y)
}
pub fn aspect_ratio(&self) -> f32 {
self.aspect
}
pub fn essential(&self, f: na::Matrix3<f32>) -> na::Matrix3<f32> {
let k = self.intrinsics();
k.transpose() * f * k
}
}