moon_engine/
camera.rs

1//! The [`Camera`] struct.
2
3use crate::transform::Transform;
4use crate::Mat4;
5use crate::Ortho;
6use crate::Vec3;
7
8/// The 'X' component at the left and right edges of the screen
9pub const FIXED_WIDTH: f32 = 20.0;
10/// Calculate the height from the `FIXED_WIDTH` to maintain 16:9 Aspect ratio
11pub const FIXED_HEIGHT: f32 = FIXED_WIDTH / 1.77;
12
13/// A [`Camera`] represents a Virtual Camera, that has a view and Orthographic projection matrices
14#[derive(Debug)]
15pub struct Camera {
16    /// [`Transform`] for the Camera
17    pub transform: Transform,
18    orthographic: Ortho,
19    width: f32,
20    height: f32,
21}
22
23impl Default for Camera {
24    fn default() -> Self {
25        Self {
26            transform: Transform::new(),
27            width: FIXED_WIDTH,
28            height: FIXED_HEIGHT,
29            orthographic: Ortho::new(
30                -FIXED_WIDTH / 2.0,
31                FIXED_WIDTH / 2.0,
32                FIXED_HEIGHT / 2.0,
33                -FIXED_HEIGHT / 2.0,
34                0f32,
35                1000.0f32,
36            ),
37        }
38    }
39}
40
41impl Camera {
42    /// Create a new `Camera` with default values.
43    pub fn new() -> Self {
44        Default::default()
45    }
46    /// Create a new `Camera` with an initial position.
47    pub fn with_position(position: Vec3) -> Self {
48        Self {
49            transform: Transform::new_with_position(position),
50            ..Default::default()
51        }
52    }
53    /// Create a new `Camera` with an initial transform.
54    pub fn with_transform(transform: Transform) -> Self {
55        Self {
56            transform,
57            ..Default::default()
58        }
59    }
60    /// Create a new `Camera` with an initial width and height.
61    pub fn with_width_and_height(width: f32, height: f32) -> Self {
62        Self {
63            width,
64            height,
65            orthographic: Ortho::new(
66                -width / 2.0,
67                width / 2.0,
68                height / 2.0,
69                -height / 2.0,
70                0f32,
71                1000.0f32,
72            ),
73            ..Default::default()
74        }
75    }
76
77    /// Set the width and height of the camera plane, and update the Projection Matrix to match.
78    pub fn set_width_and_height(&mut self, width: f32, height: f32) {
79        self.width = width;
80        self.height = height;
81    }
82
83    /// Return the Projection Matrix of the `Camera` as a slice of `f32` so it can be used by WebGL.
84    pub fn projection(&self) -> &[f32] {
85        self.orthographic.as_matrix().as_slice()
86    }
87
88    /// Return the calculated and combined view-projection matrix as a [`Mat4`].
89    pub fn view_projection_matrix(&self) -> Mat4 {
90        self.transform.matrix() * self.orthographic.as_matrix()
91    }
92
93    /// Get a position in screen co-ordinates to a range within the world.
94    ///
95    /// This works by first converting it into a `-1.0 to 1.0` range, and then multiplying its components by the [`FIXED_WIDTH`] and [`FIXED_HEIGHT`].
96    pub fn screen_to_world_coordinates(&self, screen_x: f32, screen_y: f32) -> (f32, f32) {
97        let clipped_x = screen_x / self.width - 0.5;
98        let clipped_y = screen_y / self.height - 0.5;
99
100        (clipped_x * FIXED_WIDTH, clipped_y * FIXED_HEIGHT)
101    }
102}