1use glam::Mat3;
22use wgpu::util::DeviceExt;
23
24use crate::context::WgpuClump;
25use crate::layouts;
26use crate::render::Renderer;
27use crate::vectors::Vec2;
28
29pub struct Camera {
31 inner: Option<InternalCamera>,
32 pub center: Vec2<f32>,
34 pub rotation: f32,
36 pub scale: Vec2<f32>,
38}
39
40impl Camera {
41 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 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 -scale_x * sin,
77 scale_y * cos,
78 0.0,
79 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 let matrix: [f32; 16] = [
114 scale_x * cos,
116 scale_y * sin,
117 0.0,
118 0.0,
119 -scale_x * sin,
121 scale_y * cos,
122 0.0,
123 0.0,
124 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.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 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 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}