bottomless_pit/
camera.rs

1//! This contains a simple 2D camera that can be used to transform
2//! the world.
3//! ```rust
4//!     fn render<'pass, 'others>(
5//!         &'others mut self,
6//!         mut render_handle: RenderInformation<'pass, 'others>,
7//!     ) where
8//!     'others: 'pass,
9//!     {
10//!         self.material.add_rectangle(Vec2 { x: 0.0, y: 0.0 }, Vec2{x: 300.0, y: 300.0}, Colour::WHITE, &render_handle);
11//!         // Draws objects with a cameras transform
12//!         self.camera.set_active(&mut render_handle);
13//!         self.material.draw(&mut render_handle);
14//!         // Resets back to the default camera great for static elements like a HUD or UI
15//!         render_handle.reset_camera();
16//!         self.text.add_instance(vec2!(0.0), Colour::WHITE, &render_handle);
17//!         self.text.draw(&mut render_handle);
18//! }
19//! ```
20
21use glam::Mat3;
22use wgpu::util::DeviceExt;
23
24use crate::context::WgpuClump;
25use crate::layouts;
26use crate::render::Renderer;
27use crate::vectors::Vec2;
28
29/// A simple 2D camera that can translate, rotate, and scale everything on the screen.
30pub struct Camera {
31    inner: Option<InternalCamera>,
32    /// The center of the camera becomes the center of the screen
33    pub center: Vec2<f32>,
34    /// needs to be in degrees
35    pub rotation: f32,
36    /// This controls the size of every object in view
37    pub scale: Vec2<f32>,
38}
39
40impl Camera {
41    /// creates a new camera with center point, rotation and scale.
42    pub fn new(center: Vec2<f32>, rotation: f32, scale: Vec2<f32>) -> Self {
43        Self {
44            inner: None,
45            center,
46            rotation,
47            scale,
48        }
49    }
50
51    /// This will transform a point in screen space to camera space.
52    /// You can get the screen size from [crate::Engine::get_window_size]
53    pub fn transform_point(&self, point: Vec2<f32>, screen_size: Vec2<u32>) -> Vec2<f32> {
54        let screen_size = Vec2 {
55            x: screen_size.x as f32,
56            y: screen_size.y as f32,
57        };
58
59        let scale_x = self.scale.x;
60        let scale_y = self.scale.y;
61
62        let rot_x = screen_size.x / 2.0;
63        let rot_y = screen_size.y / 2.0;
64
65        let x_trans = rot_x - self.center.x;
66        let y_trans = rot_y - self.center.y;
67
68        let sin = self.rotation.to_radians().sin();
69        let cos = self.rotation.to_radians().cos();
70
71        let mat: Mat3 = Mat3::from_cols_array(&[
72            scale_x * cos,
73            scale_y * sin,
74            0.0,
75            //c2
76            -scale_x * sin,
77            scale_y * cos,
78            0.0,
79            //c3
80            scale_x * x_trans * cos - scale_x * y_trans * sin - rot_x * scale_x * cos
81                + rot_y * scale_x * sin
82                + rot_x,
83            scale_y * x_trans * sin + scale_y * y_trans * cos
84                - rot_x * scale_y * sin
85                - rot_y * scale_y * cos
86                + rot_y,
87            1.0,
88        ])
89        .inverse();
90
91        mat.transform_point2(point.into()).into()
92    }
93
94    fn write_matrix(&mut self, wgpu: &WgpuClump, screen_size: Vec2<u32>) {
95        let screen_size = Vec2 {
96            x: screen_size.x as f32,
97            y: screen_size.y as f32,
98        };
99
100        let scale_x = self.scale.x;
101        let scale_y = self.scale.y;
102
103        let rot_x = screen_size.x / 2.0;
104        let rot_y = screen_size.y / 2.0;
105
106        let x_trans = rot_x - self.center.x;
107        let y_trans = rot_y - self.center.y;
108
109        let sin = self.rotation.to_radians().sin();
110        let cos = self.rotation.to_radians().cos();
111
112        //THIS IS T(rot)S(x_scale, y_scale)R(d)T(-rot)T(x_trans, y_trans)
113        let matrix: [f32; 16] = [
114            //c1
115            scale_x * cos,
116            scale_y * sin,
117            0.0,
118            0.0,
119            //c2
120            -scale_x * sin,
121            scale_y * cos,
122            0.0,
123            0.0,
124            //c3
125            scale_x * x_trans * cos - scale_x * y_trans * sin - rot_x * scale_x * cos
126                + rot_y * scale_x * sin
127                + rot_x,
128            scale_y * x_trans * sin + scale_y * y_trans * cos
129                - rot_x * scale_y * sin
130                - rot_y * scale_y * cos
131                + rot_y,
132            1.0,
133            0.0,
134            //screen size
135            screen_size.x,
136            screen_size.y,
137            0.0,
138            0.0,
139        ];
140
141        if self.inner.is_none() {
142            self.inner = Some(InternalCamera::new(wgpu, &matrix));
143        }
144
145        wgpu.queue.write_buffer(
146            &self.inner.as_ref().unwrap().buffer,
147            0,
148            bytemuck::cast_slice(&matrix),
149        );
150    }
151
152    /// Sets this camera to the active camera transforming all objects with this camera.
153    pub fn set_active<'others>(&'others mut self, renderer: &mut Renderer<'_, 'others>) {
154        self.write_matrix(renderer.wgpu, renderer.size);
155
156        renderer
157            .pass
158            .set_bind_group(1, &self.inner.as_ref().unwrap().bind_group, &[]);
159    }
160}
161
162impl Default for Camera {
163    /// creates a new camera with these values:
164    /// ```rust
165    /// Camera {
166    ///     center: Vec2{x : 0.0, y: 0.0},
167    ///     rotation: 0.0,
168    ///     scale: Vec2{x: 1.0, y: 1.0},
169    /// }
170    /// ```
171    fn default() -> Self {
172        Self {
173            inner: None,
174            center: Vec2 { x: 0.0, y: 0.0 },
175            rotation: 0.0,
176            scale: Vec2 { x: 1.0, y: 1.0 },
177        }
178    }
179}
180
181struct InternalCamera {
182    bind_group: wgpu::BindGroup,
183    buffer: wgpu::Buffer,
184}
185
186impl InternalCamera {
187    fn new(wgpu: &WgpuClump, matrix: &[f32; 16]) -> Self {
188        let buffer = wgpu
189            .device
190            .create_buffer_init(&wgpu::util::BufferInitDescriptor {
191                label: Some("Camera Buffer"),
192                contents: bytemuck::cast_slice(matrix),
193                usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
194            });
195
196        let camera_bind_group_layout = layouts::create_camera_layout(&wgpu.device);
197
198        let bind_group = wgpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
199            layout: &camera_bind_group_layout,
200            entries: &[wgpu::BindGroupEntry {
201                binding: 0,
202                resource: buffer.as_entire_binding(),
203            }],
204            label: Some("camera_bind_group"),
205        });
206
207        Self { bind_group, buffer }
208    }
209}