effect_core/camera/
camera2d.rs

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}