Skip to main content

voronoi/
voronoi.rs

1use cuneus::prelude::ComputeShader;
2use cuneus::{Core, RenderKit, ShaderApp, ShaderManager};
3use cuneus::WindowEvent;
4cuneus::uniform_params! {
5    struct ShaderParams {
6    scale: f32,
7    offset_value: f32,
8    cell_index: f32,
9    edge_width: f32,
10    highlight: f32,
11    _pad_m1: f32,
12    _pad_m2: f32,
13    _pad_m3: f32,
14    }
15}
16fn main() -> Result<(), Box<dyn std::error::Error>> {
17    cuneus::gst::init()?;
18    env_logger::init();
19    let (app, event_loop) = ShaderApp::new("voronoi", 800, 600);
20    app.run(event_loop, Voronoi::init)
21}
22struct Voronoi {
23    base: RenderKit,
24    compute_shader: ComputeShader,
25    current_params: ShaderParams}
26impl ShaderManager for Voronoi {
27    fn init(core: &Core) -> Self {
28        let base = RenderKit::new(core);
29
30        let initial_params = ShaderParams {
31            scale: 24.0,
32            offset_value: -1.0,
33            cell_index: 0.0,
34            edge_width: 0.1,
35            highlight: 0.15,
36            _pad_m1: 0.0,
37            _pad_m2: 0.0,
38            _pad_m3: 0.0,
39        };
40
41        let config = ComputeShader::builder()
42            .with_entry_point("main")
43            .with_input_texture()
44            .with_custom_uniforms::<ShaderParams>()
45            .build();
46
47        let compute_shader = cuneus::compute_shader!(core, "shaders/voronoi.wgsl", config);
48
49        compute_shader.set_custom_params(initial_params, &core.queue);
50
51        Self {
52            base,
53            compute_shader,
54            current_params: initial_params}
55    }
56
57    fn update(&mut self, core: &Core) {
58        // Update time
59        let current_time = self.base.controls.get_time(&self.base.start_time);
60        let delta = 1.0 / 60.0;
61        self.compute_shader
62            .set_time(current_time, delta, &core.queue);
63
64        // Update input textures for media processing
65        self.base.update_current_texture(core, &core.queue);
66        if let Some(texture_manager) = self.base.get_current_texture_manager() {
67            self.compute_shader.update_input_texture(
68                &texture_manager.view,
69                &texture_manager.sampler,
70                &core.device,
71            );
72        }
73        // Handle export
74        self.compute_shader.handle_export(core, &mut self.base);
75    }
76    fn render(&mut self, core: &Core) -> Result<(), cuneus::SurfaceError> {
77        let mut frame = self.base.begin_frame(core)?;
78
79        let _video_updated = if self.base.using_video_texture {
80            self.base.update_video_texture(core, &core.queue)
81        } else {
82            false
83        };
84        let _webcam_updated = if self.base.using_webcam_texture {
85            self.base.update_webcam_texture(core, &core.queue)
86        } else {
87            false
88        };
89
90        let mut params = self.current_params;
91        let mut changed = false;
92
93        let mut controls_request = self
94            .base
95            .controls
96            .get_ui_request(&self.base.start_time, &core.size, self.base.fps_tracker.fps());
97
98        let using_video_texture = self.base.using_video_texture;
99        let using_hdri_texture = self.base.using_hdri_texture;
100        let using_webcam_texture = self.base.using_webcam_texture;
101        let video_info = self.base.get_video_info();
102        let hdri_info = self.base.get_hdri_info();
103        let webcam_info = self.base.get_webcam_info();
104
105        let full_output = if self.base.key_handler.show_ui {
106            self.base.render_ui(core, |ctx| {
107                ctx.global_style_mut(|style| {
108                    style.visuals.window_fill =
109                        egui::Color32::from_rgba_premultiplied(0, 0, 0, 180);
110                });
111                egui::Window::new("Voronoi Settings")
112                    .collapsible(true)
113                    .default_size([300.0, 100.0])
114                    .show(ctx, |ui| {
115                        ui.collapsing("Media", |ui: &mut egui::Ui| {
116                            cuneus::ShaderControls::render_media_panel(
117                                ui,
118                                &mut controls_request,
119                                using_video_texture,
120                                video_info,
121                                using_hdri_texture,
122                                hdri_info,
123                                using_webcam_texture,
124                                webcam_info,
125                            );
126                        });
127
128                        ui.separator();
129
130                        // Pattern Settings
131                        ui.collapsing("Pattern Settings", |ui| {
132                            changed |= ui
133                                .add(
134                                    egui::Slider::new(&mut params.scale, 1.0..=100.0)
135                                        .text("Cell Scale"),
136                                )
137                                .changed();
138                            changed |= ui
139                                .add(
140                                    egui::Slider::new(&mut params.offset_value, -1.0..=2.0)
141                                        .text("Pattern Offset"),
142                                )
143                                .changed();
144                        });
145
146                        // Cell Settings
147                        ui.collapsing("Cell Settings", |ui| {
148                            changed |= ui
149                                .add(
150                                    egui::Slider::new(&mut params.cell_index, 0.0..=3.0)
151                                        .text("Cell Index"),
152                                )
153                                .changed();
154                        });
155
156                        // Edge Settings
157                        ui.collapsing("Edge Settings", |ui| {
158                            changed |= ui
159                                .add(
160                                    egui::Slider::new(&mut params.edge_width, 0.0..=1.0)
161                                        .text("Edge Width"),
162                                )
163                                .changed();
164                            changed |= ui
165                                .add(
166                                    egui::Slider::new(&mut params.highlight, 0.0..=15.0)
167                                        .text("Edge Highlight"),
168                                )
169                                .changed();
170                        });
171
172                        ui.separator();
173                        cuneus::ShaderControls::render_controls_widget(ui, &mut controls_request);
174                    });
175            })
176        } else {
177            self.base.render_ui(core, |_ctx| {})
178        };
179
180        self.base.apply_media_requests(core, &controls_request);
181
182        if changed {
183            self.current_params = params;
184            self.compute_shader.set_custom_params(params, &core.queue);
185        }
186
187        // Create command encoder
188
189        // Update time uniform
190        let current_time = self.base.controls.get_time(&self.base.start_time);
191        let delta_time = 1.0 / 60.0; // Approximate delta time
192        self.compute_shader
193            .set_time(current_time, delta_time, &core.queue);
194
195        // Dispatch compute shader
196        self.compute_shader.dispatch(&mut frame.encoder, core);
197
198        self.base.renderer.render_to_view(&mut frame.encoder, &frame.view, &self.compute_shader.get_output_texture().bind_group);
199
200        self.base.end_frame(core, frame, full_output);
201
202        Ok(())
203    }
204    fn resize(&mut self, core: &Core) {
205        self.base.default_resize(core, &mut self.compute_shader);
206    }
207
208    fn handle_input(&mut self, core: &Core, event: &WindowEvent) -> bool {
209        self.base.default_handle_input(core, event)
210    }
211}