ferravex_graphics/
camera.rs

1use 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    /// Exact transformed-box support test; conservative under nonuniform scale and shear.
71    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}