use super::{GL::Frame, *};
use crate::math::{la::*, *};
#[derive(Debug, Clone)]
pub struct FocusCam {
pub target: V3,
proj: Memoized<M4, (uVec2, Vec2)>,
view: Memoized<M4, (Q, V3)>,
orient: Memoized<(Q, V3), (Vec3, V3)>,
view_proj: Cell<Option<M4>>,
inv_view: Cell<Option<M4>>,
}
impl FocusCam {
pub fn pos(&self) -> Vec3 {
Vec3(self.orient.get().1)
}
pub fn fov(&self) -> f32 {
self.proj.get_args().1 .0
}
pub fn new(target: V3, polar_zoom: Vec3) -> Self {
let proj = Memoized::zero(proj_f);
let view = Memoized::zero(view_f);
let orient = Memoized::zero(orient_f);
let (view_proj, inv_view) = Def();
let mut c = FocusCam { target, proj, orient, view, view_proj, inv_view };
c.set_polar(polar_zoom);
c
}
pub fn track(&mut self, tgt: V3) {
let Self { target, orient, view_proj, .. } = self;
if &tgt != target {
*target = tgt;
orient.reset();
view_proj.replace(None);
}
}
pub fn set_proj(&mut self, f: &impl Frame, (fov, far): Vec2) {
if self.proj.apply((f.size(), (fov, far))).0 {
self.view_proj.replace(None);
}
}
pub fn set_polar(&mut self, polar_zoom: Vec3) {
let Self { target, orient, view, view_proj, inv_view, .. } = self;
let (c, (o, p)) = orient.apply((&polar_zoom, &*target));
if c && view.apply((o, p)).0 {
view_proj.replace(None);
inv_view.replace(None);
}
}
pub fn V(&self) -> &M4 {
&self.view
}
pub fn iV(&self) -> &M4 {
let Self { inv_view, .. } = self;
if inv_view.inspect(|s| s.is_none()) {
inv_view.replace(Some(inverse4(*self.view)));
}
unsafe { &*inv_view.as_ptr() }.as_ref().valid()
}
pub fn VP(&self) -> &M4 {
let Self { view_proj, .. } = self;
if view_proj.inspect(|s| s.is_none()) {
view_proj.replace(Some(self.P() * self.V()));
}
unsafe { &*view_proj.as_ptr() }.as_ref().valid()
}
pub fn P(&self) -> &M4 {
&self.proj
}
pub fn MV(&self, model: &M4) -> M4 {
self.V() * model
}
pub fn MVP(&self, model: &M4) -> M4 {
self.VP() * model
}
pub fn iL(&self) -> Vec3 {
Vec3(-self.orient.1)
}
pub fn N(&self, model: &M4) -> M3 {
inverse3(crop_3x3(model)).transpose()
}
pub fn NV(&self, model: &M4) -> M3 {
let m = self.V() * model;
inverse3(crop_3x3(&m).transpose())
}
}
impl Default for FocusCam {
fn default() -> Self {
Self::new(V3::new(0., 0., 0.), Vec3((0, -90, 1)))
}
}
#[cfg(feature = "adv_fs")]
mod serde {
use {super::*, crate::ser::*};
impl Serialize for FocusCam {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let Self { target, proj, view, orient, .. } = self;
(target, proj, view, orient).serialize(s)
}
}
impl<'de> Deserialize<'de> for FocusCam {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let (target, proj, view, orient) = <(V3, Memoized<M4, (uVec2, Vec2)>, Memoized<M4, (Q, V3)>, Memoized<(Q, V3), (Vec3, V3)>)>::deserialize(d)?;
let proj = proj.finalize_deserialization(proj_f);
let view = view.finalize_deserialization(view_f);
let orient = orient.finalize_deserialization(orient_f);
Ok(Self { target, proj, view, orient, ..Def() })
}
}
}
fn proj_f(&(size, (fov, far)): &(uVec2, Vec2)) -> M4 {
let (w, h) = size;
let aspect = f32(w) / f32(h);
let fov = fov.to_radians();
let fov = if w < h { 2. * ((fov * 0.5).tan() / aspect).atan() } else { fov };
perspective(aspect, fov, 0.01, far)
}
fn view_f((orient, pos): &(Q, V3)) -> M4 {
let rot = orient.inverse().to_rotation_matrix().to_homogeneous();
let trans = M4::new_translation(&-pos);
rot * trans
}
fn orient_f(&((a, e, dist), target): &(Vec3, V3)) -> (Q, V3) {
let (a, e) = (a, e).map(|c| c.to_radians());
let mut orient = Q::identity();
let yaw = Q::from_axis_angle(&V3::y_axis(), a);
orient = yaw * orient;
let pitch = Q::from_axis_angle(&V3::x_axis(), e);
orient *= pitch;
let dir = orient * -V3::z_axis();
let pos = target - *dir * dist;
(orient, pos)
}