ferravex_graphics/
camera.rs1use crate::{Bounds, Mat4, Vec3, Vec4};
2#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3pub enum ClipDepth {
4 NegativeOneToOne,
5 ZeroToOne,
6}
7#[derive(Clone, Copy, Debug)]
8pub struct Camera {
9 pub view_projection: Mat4,
10 pub position: Vec3,
11 pub clip_depth: ClipDepth,
12}
13impl Camera {
14 pub fn perspective(
15 eye: Vec3,
16 target: Vec3,
17 up: Vec3,
18 fov_y: f32,
19 aspect: f32,
20 near: f32,
21 far: f32,
22 ) -> Self {
23 Self {
24 view_projection: Mat4::perspective_rh(fov_y, aspect, near, far)
25 * Mat4::look_at_rh(eye, target, up),
26 position: eye,
27 clip_depth: ClipDepth::ZeroToOne,
28 }
29 }
30 pub fn frustum(self) -> Option<Frustum> {
31 Frustum::from_matrix(self.view_projection, self.clip_depth)
32 }
33}
34#[derive(Clone, Copy, Debug)]
35pub struct Frustum {
36 planes: [Vec4; 6],
37}
38impl Frustum {
39 pub fn from_matrix(matrix: Mat4, depth: ClipDepth) -> Option<Self> {
40 if !matrix.is_finite() {
41 return None;
42 }
43 let r0 = matrix.row(0);
44 let r1 = matrix.row(1);
45 let r2 = matrix.row(2);
46 let r3 = matrix.row(3);
47 let mut planes = [
48 r3 + r0,
49 r3 - r0,
50 r3 + r1,
51 r3 - r1,
52 if depth == ClipDepth::ZeroToOne {
53 r2
54 } else {
55 r3 + r2
56 },
57 r3 - r2,
58 ];
59 for plane in &mut planes {
60 let length = plane.truncate().length();
61 if !length.is_finite() {
62 return None;
63 }
64 if length > 0.0 {
65 *plane /= length;
66 }
67 }
68 Some(Self { planes })
69 }
70 pub fn intersects_bounds(self, bounds: Bounds, transform: Mat4) -> bool {
72 let center = transform.transform_point3(bounds.center());
73 let half = bounds.half_extents();
74 self.planes.into_iter().all(|plane| {
75 let normal = plane.truncate();
76 let radius = normal.dot(transform.x_axis.truncate()).abs() * half.x
77 + normal.dot(transform.y_axis.truncate()).abs() * half.y
78 + normal.dot(transform.z_axis.truncate()).abs() * half.z;
79 normal.dot(center) + plane.w + radius >= 0.0
80 })
81 }
82 pub fn contains_point(self, point: Vec3) -> bool {
83 self.planes
84 .into_iter()
85 .all(|plane| plane.truncate().dot(point) + plane.w >= 0.0)
86 }
87}