1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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)
}