1use crate::transform::{Mat4, mul};
5
6pub trait Camera {
9 fn view_proj(&self) -> Mat4;
10}
11
12#[derive(Copy, Clone, Debug)]
14pub struct Camera3D {
15 pub eye: [f32; 3],
16 pub target: [f32; 3],
17 pub up: [f32; 3],
18 pub fov_y: f32,
20 pub aspect: f32,
21 pub near: f32,
22 pub far: f32,
23}
24
25impl Camera3D {
26 pub fn new(aspect: f32) -> Self {
27 Self {
28 eye: [0.0, 0.0, 5.0],
29 target: [0.0, 0.0, 0.0],
30 up: [0.0, 1.0, 0.0],
31 fov_y: 60.0_f32.to_radians(),
32 aspect,
33 near: 0.1,
34 far: 100.0,
35 }
36 }
37
38 pub fn resize(&mut self, w: f32, h: f32) {
39 if h > 0.0 {
40 self.aspect = w / h;
41 }
42 }
43}
44
45impl Camera for Camera3D {
46 fn view_proj(&self) -> Mat4 {
47 mul(
48 &perspective(self.fov_y, self.aspect, self.near, self.far),
49 &look_at(self.eye, self.target, self.up),
50 )
51 }
52}
53
54impl Camera for crate::Camera2D {
55 fn view_proj(&self) -> Mat4 {
56 crate::Camera2D::view_proj(self)
57 }
58}
59
60fn perspective(fov_y: f32, aspect: f32, near: f32, far: f32) -> Mat4 {
62 let f = 1.0 / (fov_y * 0.5).tan();
63 [
64 [f / aspect, 0.0, 0.0, 0.0],
65 [0.0, f, 0.0, 0.0],
66 [0.0, 0.0, far / (near - far), -1.0],
67 [0.0, 0.0, (near * far) / (near - far), 0.0],
68 ]
69}
70
71fn look_at(eye: [f32; 3], target: [f32; 3], up: [f32; 3]) -> Mat4 {
73 let z = normalize(sub(eye, target)); let x = normalize(cross(up, z));
75 let y = cross(z, x);
76 [
77 [x[0], y[0], z[0], 0.0],
78 [x[1], y[1], z[1], 0.0],
79 [x[2], y[2], z[2], 0.0],
80 [-dot(x, eye), -dot(y, eye), -dot(z, eye), 1.0],
81 ]
82}
83
84fn sub(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
85 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
86}
87fn dot(a: [f32; 3], b: [f32; 3]) -> f32 {
88 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
89}
90fn cross(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
91 [
92 a[1] * b[2] - a[2] * b[1],
93 a[2] * b[0] - a[0] * b[2],
94 a[0] * b[1] - a[1] * b[0],
95 ]
96}
97fn normalize(v: [f32; 3]) -> [f32; 3] {
98 let len = dot(v, v).sqrt().max(1e-6);
99 [v[0] / len, v[1] / len, v[2] / len]
100}