witchcraft_renderer/types/
camera.rs

1use cgmath::Matrix4;
2
3/// A matrix used to convert an OpenGL matrix to a WGPU matrix by multiplication.
4#[rustfmt::skip]
5pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
6    1.0, 0.0, 0.0, 0.0,
7    0.0, 1.0, 0.0, 0.0,
8    0.0, 0.0, 0.5, 0.0,
9    0.0, 0.0, 0.5, 1.0,
10);
11
12/// A basic thread-safe camera type.
13pub struct Camera {
14    pub eye: cgmath::Point3<f32>,
15    pub target: cgmath::Point3<f32>,
16    pub up: cgmath::Vector3<f32>,
17    pub aspect: f32,
18    pub fovy: f32,
19    pub zfar: f32,
20    pub znear: f32,
21}
22impl Camera {
23    pub fn build_view_proj_matrix(&self) -> Matrix4<f32> {
24        // moves the world to be at the position and rotation the camera is looking at.
25        let view = cgmath::Matrix4::look_at_rh(self.eye, self.target, self.up);
26        // wrap the scene to give the effect of depth
27        let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar);
28
29        OPENGL_TO_WGPU_MATRIX * proj * view // multiplication order is very important when dealing with matrices
30    }
31}
32
33#[repr(C)]
34#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
35pub struct CameraUniform {
36    // cgmath can't be used with bytemuck, so usage of "raw" matrices is required
37    view_proj: [[f32; 4]; 4],
38}
39impl CameraUniform {
40    pub fn new() -> Self {
41        use cgmath::SquareMatrix;
42        Self {
43            view_proj: cgmath::Matrix4::identity().into(),
44        }
45    }
46
47    pub fn update_view_proj(&mut self, camera: &Camera) {
48        self.view_proj = camera.build_view_proj_matrix().into();
49    }
50}
51
52#[derive(Clone, Copy)]
53pub enum CameraControllerMovement {
54    /// Movement along the X axis.
55    X(f32),
56    /// Movement along the Y axis.
57    Y(f32),
58    /// Movement along the Z axis.
59    Z(f32),
60    /// Rotation around the X axis.
61    RotX(f32),
62    /// Rotation around the Y axis.
63    RotY(f32),
64    /// Rotation around the Z axis.
65    RotZ(f32),
66}
67
68/// Used to move and/or rotate the camera.
69pub struct CameraController<'a> {
70    camera: &'a mut Camera,
71}
72impl<'a> CameraController<'a> {
73    pub fn new(camera: &'a mut Camera) -> Self {
74        Self { camera: camera }
75    }
76
77    /// Moves the camera in a single direction at a time.
78    pub fn update_camera(&mut self, movement: CameraControllerMovement) {
79        use cgmath::InnerSpace;
80        // defining directions, used for movement calculations
81        let forward = self.camera.target - self.camera.eye;
82        let right = forward.normalize().cross(self.camera.up);
83
84        use crate::CameraControllerMovement::*;
85        match movement {
86            X(x) => self.camera.eye += right.normalize() * x,
87            Y(y) => self.camera.eye += self.camera.up * y,
88            Z(z) => self.camera.eye += forward.normalize() * z,
89            RotX(rot) => {
90                self.camera.eye = self.camera.target
91                    - (forward + self.camera.up * rot).normalize() * forward.magnitude()
92            }
93            RotY(rot) => {
94                self.camera.eye =
95                    self.camera.target - (forward + forward * rot).normalize() * forward.magnitude()
96            }
97            RotZ(rot) => {
98                self.camera.eye = self.camera.target
99                    - (forward + self.camera.up * rot).normalize() * forward.magnitude()
100            }
101        }
102    }
103
104    /// Runs `update_camera` on every movement provided.
105    pub fn update_camera_batch(&mut self, movements: &[CameraControllerMovement]) {
106        for movement in movements {
107            self.update_camera(*movement);
108        }
109    }
110}