1use cuneus::compute::*;
4use cuneus::prelude::*;
5use winit::event::ElementState;
6
7#[repr(C)]
8#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
9struct BlockGameParams {
10 game_state: i32,
12 score: u32,
13 current_block: u32,
14 total_blocks: u32,
15
16 block_x: f32,
17 block_y: f32,
18 block_z: f32,
19
20 block_width: f32,
21 block_height: f32,
22 block_depth: f32,
23
24 movement_speed: f32,
25 movement_range: f32,
26 drop_triggered: i32,
27
28 camera_height: f32,
29 camera_angle: f32,
30 camera_scale: f32,
31
32 perfect_placement: i32,
34 game_over: i32,
35
36 _padding: [f32; 2],
37}
38
39impl Default for BlockGameParams {
40 fn default() -> Self {
41 Self {
42 game_state: 0,
43 score: 0,
44 current_block: 0,
45 total_blocks: 1,
46
47 block_x: 0.0,
48 block_y: 1.0,
49 block_z: 0.0,
50
51 block_width: 3.0,
52 block_height: 0.6,
53 block_depth: 3.0,
54
55 movement_speed: 2.0,
56 movement_range: 2.5,
57 drop_triggered: 0,
58
59 camera_height: 0.0,
60 camera_angle: 0.0,
61 camera_scale: 65.0,
62
63 perfect_placement: 0,
64 game_over: 0,
65
66 _padding: [0.0; 2],
67 }
68 }
69}
70
71impl UniformProvider for BlockGameParams {
72 fn as_bytes(&self) -> &[u8] {
73 bytemuck::bytes_of(self)
74 }
75}
76
77struct BlockTowerGame {
78 base: RenderKit,
79 compute_shader: ComputeShader,
80 last_mouse_click: bool,
81 game_params: BlockGameParams,
82}
83
84impl ShaderManager for BlockTowerGame {
85 fn init(core: &Core) -> Self {
86 let base = RenderKit::new(core);
87
88 let config = ComputeShader::builder()
90 .with_entry_point("main")
91 .with_mouse()
92 .with_fonts()
93 .with_audio(1024) .with_workgroup_size([8, 8, 1])
95 .with_texture_format(COMPUTE_TEXTURE_FORMAT_RGBA16)
96 .with_label("Block Tower Game Unified")
97 .build();
98
99 let compute_shader = cuneus::compute_shader!(core, "shaders/blockgame.wgsl", config);
100
101
102 Self {
103 base,
104 compute_shader,
105 last_mouse_click: false,
106 game_params: BlockGameParams::default(),
107 }
108 }
109
110 fn update(&mut self, core: &Core) {
111 let current_time = self.base.controls.get_time(&self.base.start_time);
112 let delta = 1.0 / 60.0;
113 self.compute_shader
114 .set_time(current_time, delta, &core.queue);
115 self.compute_shader
116 .update_mouse_uniform(&self.base.mouse_tracker.uniform, &core.queue);
117
118 self.update_camera_in_shader(&core.queue);
119 let mouse_buttons = self.base.mouse_tracker.uniform.buttons[0];
120 let mouse_pressed = mouse_buttons & 1 != 0;
121 self.last_mouse_click = mouse_pressed;
122 }
123
124 fn resize(&mut self, core: &Core) {
125 self.base.default_resize(core, &mut self.compute_shader);
126 }
127
128 fn render(&mut self, core: &Core) -> Result<(), cuneus::SurfaceError> {
129 let mut frame = self.base.begin_frame(core)?;
130 let _controls_request = self
131 .base
132 .controls
133 .get_ui_request(&self.base.start_time, &core.size, self.base.fps_tracker.fps());
134
135 let full_output = if self.base.key_handler.show_ui {
136 self.base.render_ui(core, |ctx| {
137 RenderKit::apply_default_style(ctx);
138 egui::Window::new("Block Tower")
139 .collapsible(true)
140 .resizable(true)
141 .default_width(220.0)
142 .show(ctx, |ui| {
143 egui::CollapsingHeader::new("Camera")
144 .default_open(true)
145 .show(ui, |ui| {
146 ui.add(
147 egui::Slider::new(
148 &mut self.game_params.camera_height,
149 0.0..=20.0,
150 )
151 .text("Height"),
152 );
153 ui.add(
154 egui::Slider::new(
155 &mut self.game_params.camera_angle,
156 -3.14159..=3.14159,
157 )
158 .text("Angle"),
159 );
160 ui.add(
161 egui::Slider::new(
162 &mut self.game_params.camera_scale,
163 20.0..=200.0,
164 )
165 .text("Scale"),
166 );
167
168 ui.separator();
169 ui.label("Controls:");
170 ui.label("Q/E: Move up/down");
171 ui.label("W/S: Rotate left/right");
172
173 ui.separator();
174 ui.label("Scale presets:");
175 ui.horizontal(|ui| {
176 if ui.button("1080p").clicked() {
177 self.game_params.camera_scale = 50.0;
178 }
179 if ui.button("1440p").clicked() {
180 self.game_params.camera_scale = 65.0;
181 }
182 if ui.button("4K").clicked() {
183 self.game_params.camera_scale = 100.0;
184 }
185 });
186
187 if ui.button("Reset Camera").clicked() {
188 self.game_params.camera_height = 8.0;
189 self.game_params.camera_angle = 0.0;
190 self.game_params.camera_scale = 65.0;
191 }
192 });
193 });
194 })
195 } else {
196 self.base.render_ui(core, |_ctx| {})
197 };
198
199
200 self.compute_shader.dispatch(&mut frame.encoder, core);
201
202 self.base.renderer.render_to_view(&mut frame.encoder, &frame.view, &self.compute_shader.get_output_texture().bind_group);
203
204 self.base.end_frame(core, frame, full_output);
205 Ok(())
206 }
207
208 fn handle_input(&mut self, core: &Core, event: &WindowEvent) -> bool {
209 let ui_handled = self.base.forward_to_egui(core, event);
210
211 if self.base.handle_mouse_input(core, event, ui_handled) {
212 return true;
213 }
214
215 if let WindowEvent::KeyboardInput { event, .. } = event {
216 if let winit::keyboard::PhysicalKey::Code(key_code) = event.physical_key {
217 if event.state == ElementState::Pressed {
218 let camera_speed = 0.5;
219
220 match key_code {
221 winit::keyboard::KeyCode::KeyQ => {
222 self.game_params.camera_height += camera_speed;
223 return true;
224 }
225 winit::keyboard::KeyCode::KeyE => {
226 self.game_params.camera_height -= camera_speed;
227 return true;
228 }
229 winit::keyboard::KeyCode::KeyW => {
230 self.game_params.camera_angle += 0.1;
231 return true;
232 }
233 winit::keyboard::KeyCode::KeyS => {
234 self.game_params.camera_angle -= 0.1;
235 return true;
236 }
237 _ => {}
238 }
239 }
240 }
241 return self
242 .base
243 .key_handler
244 .handle_keyboard_input(core.window(), event);
245 }
246
247 false
248 }
249}
250
251impl BlockTowerGame {
252 fn update_camera_in_shader(&self, queue: &wgpu::Queue) {
253 if let Some(audio_buffer) = self.compute_shader.get_audio_buffer() {
254 let camera_data = [
255 self.game_params.camera_height,
256 self.game_params.camera_angle,
257 self.game_params.camera_scale,
258 ];
259
260 let camera_data_bytes = bytemuck::cast_slice(&camera_data);
261 let offset = 5 * std::mem::size_of::<f32>();
262
263 queue.write_buffer(audio_buffer, offset as u64, camera_data_bytes);
264 }
265 }
266}
267
268fn main() -> Result<(), Box<dyn std::error::Error>> {
269 env_logger::init();
270 cuneus::gst::init()?;
271
272 let (app, event_loop) = ShaderApp::new("Block Tower Game", 600, 800);
273
274 app.run(event_loop, BlockTowerGame::init)
275}