1use concinnity_core::math::sqrt;
8use concinnity_core::math::vec3::{cross, dot, sub};
9
10pub const IDENTITY4: [[f32; 4]; 4] = [
12 [1.0, 0.0, 0.0, 0.0],
13 [0.0, 1.0, 0.0, 0.0],
14 [0.0, 0.0, 1.0, 0.0],
15 [0.0, 0.0, 0.0, 1.0],
16];
17
18pub(crate) fn look_at(eye: [f32; 3], centre: [f32; 3], up: [f32; 3]) -> [[f32; 4]; 4] {
19 let f = normalize3(sub(centre, eye));
20 let r = normalize3(cross(f, up));
21 let u = cross(r, f);
22 [
23 [r[0], u[0], -f[0], 0.0],
24 [r[1], u[1], -f[1], 0.0],
25 [r[2], u[2], -f[2], 0.0],
26 [-dot(r, eye), -dot(u, eye), dot(f, eye), 1.0],
27 ]
28}
29
30pub(crate) fn ortho_rh(
32 left: f32,
33 right: f32,
34 bottom: f32,
35 top: f32,
36 near: f32,
37 far: f32,
38) -> [[f32; 4]; 4] {
39 let rml = right - left;
40 let tmb = top - bottom;
41 let fmn = far - near;
42 [
43 [2.0 / rml, 0.0, 0.0, 0.0],
44 [0.0, 2.0 / tmb, 0.0, 0.0],
45 [0.0, 0.0, -1.0 / fmn, 0.0],
46 [
47 -(right + left) / rml,
48 -(top + bottom) / tmb,
49 -near / fmn,
50 1.0,
51 ],
52 ]
53}
54
55pub(crate) fn normalize3(v: [f32; 3]) -> [f32; 3] {
58 let len = sqrt(dot(v, v)).max(1e-6);
59 [v[0] / len, v[1] / len, v[2] / len]
60}
61
62pub(crate) fn up_for(dir: [f32; 3]) -> [f32; 3] {
65 if dir[1].abs() > 0.99 {
66 [0.0, 0.0, 1.0]
67 } else {
68 [0.0, 1.0, 0.0]
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 fn transform(m: [[f32; 4]; 4], p: [f32; 3]) -> [f32; 4] {
77 let mut out = [0.0_f32; 4];
78 for row in 0..4 {
79 out[row] = m[0][row] * p[0] + m[1][row] * p[1] + m[2][row] * p[2] + m[3][row];
80 }
81 out
82 }
83
84 #[test]
85 fn look_at_puts_the_eye_at_the_origin_looking_down_negative_z() {
86 let v = look_at([0.0, 5.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 1.0]);
87 let eye = transform(v, [0.0, 5.0, 0.0]);
88 assert!(eye[0].abs() < 1e-5 && eye[1].abs() < 1e-5 && eye[2].abs() < 1e-5);
89 let target = transform(v, [0.0, 0.0, 0.0]);
91 assert!((target[2] + 5.0).abs() < 1e-5);
92 }
93
94 #[test]
97 fn up_for_avoids_a_degenerate_basis() {
98 assert_eq!(up_for([0.0, -1.0, 0.0]), [0.0, 0.0, 1.0]);
99 assert_eq!(up_for([0.0, 1.0, 0.0]), [0.0, 0.0, 1.0]);
100 assert_eq!(up_for([1.0, 0.0, 0.0]), [0.0, 1.0, 0.0]);
101 for dir in [[0.0, -1.0, 0.0], [0.3, -0.9, 0.2], [1.0, 0.0, 0.0]] {
103 let d = normalize3(dir);
104 assert!(dot(d, up_for(d)).abs() < 0.999);
105 }
106 }
107}