1use std::{
2 collections::{HashMap, HashSet},
3 time::Duration,
4};
5
6use effect_events::input::EffectEvent;
7use wgpu::util::DeviceExt;
8use winit::keyboard::KeyCode;
9
10use crate::primitives::vector::Vector3;
11
12pub struct Camera2D {
13 look_at: glam::Mat4,
14 proj: glam::Mat4,
15 position: Vector3<f32>,
16 _near: f32,
17 _far: f32,
18 _fov_deg: f32,
19 bind_group: wgpu::BindGroup,
20 bind_group_layout: wgpu::BindGroupLayout,
21 buffer: wgpu::Buffer,
22 key_codes: HashMap<CameraAction, KeyCode>,
23 current_actions: HashSet<CameraAction>,
24 speed: f32,
25}
26
27#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
28pub enum CameraAction {
29 Up,
30 Right,
31 Left,
32 Down,
33 ZoomIn,
34 ZoomOut,
35}
36
37impl Camera2D {
38 pub fn new(device: &wgpu::Device, fov_deg: f32, aspect_ratio: f32, speed: f32) -> Self {
39 let near = 0.01;
40 let far = 100.0;
41 let fov_rad = fov_deg.to_radians();
42 let proj = glam::Mat4::perspective_rh(fov_rad, aspect_ratio, near, far);
43 let look_at = glam::Mat4::look_at_rh(
44 glam::Vec3::new(0.0f32, 0.0, 1.0),
45 glam::Vec3::new(0.0, 0.0, 0.0),
46 glam::Vec3::Y,
47 );
48 let comp = proj * look_at;
49
50 let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
51 label: Some("Camera"),
52 contents: bytemuck::cast_slice(&[comp.to_cols_array()]),
53 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
54 });
55 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
56 label: None,
57 entries: &[wgpu::BindGroupLayoutEntry {
58 binding: 0,
59 visibility: wgpu::ShaderStages::VERTEX,
60 ty: wgpu::BindingType::Buffer {
61 ty: wgpu::BufferBindingType::Uniform,
62 has_dynamic_offset: false,
63 min_binding_size: None,
64 },
65 count: None,
66 }],
67 });
68
69 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
70 label: None,
71 layout: &bind_group_layout,
72 entries: &[wgpu::BindGroupEntry {
73 binding: 0,
74 resource: buffer.as_entire_binding(),
75 }],
76 });
77 let key_codes = HashMap::new();
78 let current_actions = HashSet::new();
79 let position = Vector3::new(0.0, 0.0, 1.0);
80 Self {
81 proj,
82 look_at,
83 _near: near,
84 _far: far,
85 _fov_deg: fov_deg,
86 buffer,
87 bind_group,
88 bind_group_layout,
89 key_codes,
90 speed,
91 current_actions,
92 position,
93 }
94 }
95
96 pub fn to_raw(&self) -> [[f32; 4]; 4] {
97 (self.proj * self.look_at).to_cols_array_2d()
98 }
99
100 pub fn buffer(&self) -> wgpu::BufferSlice {
101 self.buffer.slice(..)
102 }
103
104 pub fn bind_group(&self) -> &wgpu::BindGroup {
105 &self.bind_group
106 }
107
108 pub fn bind_group_layout(&self) -> &wgpu::BindGroupLayout {
109 &self.bind_group_layout
110 }
111
112 pub fn position(&self) -> Vector3<f32> {
113 self.position
114 }
115}
116
117pub struct Camera2DSystem;
118
119impl Camera2DSystem {
120 pub fn transform(camera: &mut Camera2D, position: Vector3<f32>) {
121 camera.position = position;
122 camera.look_at = glam::Mat4::look_at_rh(
123 glam::Vec3::new(position.x, position.y, position.z),
124 glam::Vec3::new(position.x, position.y, 0.0),
125 glam::Vec3::Y,
126 );
127 }
128
129 pub fn set_speed(camera: &mut Camera2D, speed: f32) {
130 camera.speed = speed;
131 }
132
133 pub fn update(camera: &mut Camera2D, queue: &wgpu::Queue) {
134 let comp = camera.proj * camera.look_at;
135 queue.write_buffer(
136 &camera.buffer,
137 0,
138 bytemuck::cast_slice(&comp.to_cols_array()),
139 );
140 }
141
142 pub fn process_inputs(camera: &mut Camera2D, ctx: &EffectEvent, delta_time: Duration) {
143 for (camera_action, key_code) in camera.key_codes.iter() {
144 if ctx.is_key_pressed(*key_code) {
145 camera.current_actions.insert(*camera_action);
146 }
147 if ctx.is_key_released(*key_code) {
148 camera.current_actions.remove(camera_action);
149 }
150 }
151 let dt = delta_time.as_micros() as f32 / 1000.0;
152 for action in camera.current_actions.iter() {
153 match action {
154 CameraAction::Up => {
155 camera.position.y += camera.speed * dt;
156 }
157 CameraAction::Down => {
158 camera.position.y -= camera.speed * dt;
159 }
160 CameraAction::Right => {
161 camera.position.x += camera.speed * dt;
162 }
163 CameraAction::Left => {
164 camera.position.x -= camera.speed * dt;
165 }
166 CameraAction::ZoomIn => {
167 camera.position.z -= camera.speed * dt;
168 }
169 CameraAction::ZoomOut => {
170 camera.position.z += camera.speed * dt;
171 }
172 }
173 }
174
175 camera.look_at = glam::Mat4::look_at_rh(
176 glam::Vec3::new(camera.position.x, camera.position.y, camera.position.z),
177 glam::Vec3::new(camera.position.x, camera.position.y, 0.0),
178 glam::Vec3::Y,
179 );
180 }
181
182 pub fn set_inputs(camera: &mut Camera2D, inputs: &[(CameraAction, KeyCode)]) {
183 for (action, code) in inputs {
184 camera.key_codes.insert(*action, *code);
185 }
186 }
187
188 pub fn remove_inputs(camera: &mut Camera2D, inputs: &[(CameraAction, KeyCode)]) {
189 for (action, _) in inputs {
190 let _ = camera.key_codes.remove(&action);
191 }
192 }
193
194 pub fn reset_inputs(camera: &mut Camera2D) {
195 camera.key_codes.clear();
196 }
197}