Skip to main content

volumepassage/
volumepassage.rs

1use cuneus::compute::*;
2use cuneus::prelude::*;
3
4cuneus::uniform_params! {
5    struct VolumeParams {
6    speed: f32,
7    intensity: f32,
8    color1_r: f32,
9    color1_g: f32,
10    color1_b: f32,
11    color2_r: f32,
12    color2_g: f32,
13    color2_b: f32,
14    color3_r: f32,
15    color3_g: f32,
16    color3_b: f32,
17    gamma: f32,
18    zoom: f32,
19    _padding1: f32,
20    _padding2: f32,
21    _padding3: f32}
22}
23
24struct VolumeShader {
25    base: RenderKit,
26    compute_shader: ComputeShader,
27    current_params: VolumeParams}
28
29impl VolumeShader {
30    fn clear_buffers(&mut self, core: &Core) {
31        self.compute_shader.clear_all_buffers(core);
32    }
33}
34
35impl ShaderManager for VolumeShader {
36    fn init(core: &Core) -> Self {
37        let initial_params = VolumeParams {
38            speed: 1.0,
39            intensity: 0.001,
40            color1_r: 0.1,
41            color1_g: 0.3,
42            color1_b: 0.7,
43            color2_r: 0.8,
44            color2_g: 0.4,
45            color2_b: 0.2,
46            color3_r: 1.0,
47            color3_g: 1.0,
48            color3_b: 1.0,
49            gamma: 0.8,
50            zoom: 1.0,
51            _padding1: 0.0,
52            _padding2: 0.0,
53            _padding3: 0.0};
54
55        let base = RenderKit::new(core);
56
57        let config = ComputeShader::builder()
58            .with_entry_point("main")
59            .with_custom_uniforms::<VolumeParams>()
60            .with_workgroup_size([8, 8, 1])
61            .with_texture_format(COMPUTE_TEXTURE_FORMAT_RGBA16)
62            .with_label("Volume Passage Unified")
63            .build();
64
65        let compute_shader = cuneus::compute_shader!(core, "shaders/volumepassage.wgsl", config);
66
67        compute_shader.set_custom_params(initial_params, &core.queue);
68
69        Self {
70            base,
71            compute_shader,
72            current_params: initial_params}
73    }
74
75    fn update(&mut self, core: &Core) {
76        // Handle export
77        self.compute_shader.handle_export(core, &mut self.base);
78    }
79
80    fn resize(&mut self, core: &Core) {
81        self.base.default_resize(core, &mut self.compute_shader);
82    }
83
84    fn render(&mut self, core: &Core) -> Result<(), cuneus::SurfaceError> {
85        let mut frame = self.base.begin_frame(core)?;
86
87        let mut params = self.current_params;
88        let mut changed = false;
89        let mut should_start_export = false;
90        let mut export_request = self.base.export_manager.get_ui_request();
91        let mut controls_request = self
92            .base
93            .controls
94            .get_ui_request(&self.base.start_time, &core.size, self.base.fps_tracker.fps());
95
96        let full_output = if self.base.key_handler.show_ui {
97            self.base.render_ui(core, |ctx| {
98                RenderKit::apply_default_style(ctx);
99
100                egui::Window::new("Volume Passage")
101                    .collapsible(true)
102                    .resizable(true)
103                    .default_width(250.0)
104                    .show(ctx, |ui| {
105                        egui::CollapsingHeader::new("Animation Settings")
106                            .default_open(false)
107                            .show(ui, |ui| {
108                                changed |= ui
109                                    .add(
110                                        egui::Slider::new(&mut params.speed, 0.1..=3.0)
111                                            .text("Speed"),
112                                    )
113                                    .changed();
114                            });
115
116                        egui::CollapsingHeader::new("Visual Settings")
117                            .default_open(false)
118                            .show(ui, |ui| {
119                                changed |= ui
120                                    .add(
121                                        egui::Slider::new(&mut params.intensity, 0.0001..=0.01)
122                                            .logarithmic(true)
123                                            .text("Intensity"),
124                                    )
125                                    .changed();
126                                changed |= ui
127                                    .add(
128                                        egui::Slider::new(&mut params.gamma, 0.1..=3.0)
129                                            .text("Gamma"),
130                                    )
131                                    .changed();
132                                changed |= ui
133                                    .add(
134                                        egui::Slider::new(&mut params.zoom, 0.1..=6.0).text("Zoom"),
135                                    )
136                                    .changed();
137                            });
138
139                        egui::CollapsingHeader::new("Colors")
140                            .default_open(false)
141                            .show(ui, |ui| {
142                                ui.horizontal(|ui| {
143                                    ui.label("Color 1:");
144                                    let mut color =
145                                        [params.color1_r, params.color1_g, params.color1_b];
146                                    if ui.color_edit_button_rgb(&mut color).changed() {
147                                        params.color1_r = color[0];
148                                        params.color1_g = color[1];
149                                        params.color1_b = color[2];
150                                        changed = true;
151                                    }
152                                });
153
154                                ui.horizontal(|ui| {
155                                    ui.label("Color 2:");
156                                    let mut color =
157                                        [params.color2_r, params.color2_g, params.color2_b];
158                                    if ui.color_edit_button_rgb(&mut color).changed() {
159                                        params.color2_r = color[0];
160                                        params.color2_g = color[1];
161                                        params.color2_b = color[2];
162                                        changed = true;
163                                    }
164                                });
165
166                                ui.horizontal(|ui| {
167                                    ui.label("Color 3:");
168                                    let mut color =
169                                        [params.color3_r, params.color3_g, params.color3_b];
170                                    if ui.color_edit_button_rgb(&mut color).changed() {
171                                        params.color3_r = color[0];
172                                        params.color3_g = color[1];
173                                        params.color3_b = color[2];
174                                        changed = true;
175                                    }
176                                });
177                            });
178
179                        ui.separator();
180                        ShaderControls::render_controls_widget(ui, &mut controls_request);
181                        ui.separator();
182                        should_start_export =
183                            ExportManager::render_export_ui_widget(ui, &mut export_request);
184                    });
185            })
186        } else {
187            self.base.render_ui(core, |_ctx| {})
188        };
189
190        self.base.export_manager.apply_ui_request(export_request);
191        if controls_request.should_clear_buffers {
192            self.clear_buffers(core);
193        }
194        self.base.apply_control_request(controls_request);
195
196        let current_time = self.base.controls.get_time(&self.base.start_time);
197
198        let delta = 1.0 / 60.0;
199        self.compute_shader
200            .set_time(current_time, delta, &core.queue);
201
202        if changed {
203            self.current_params = params;
204            self.compute_shader.set_custom_params(params, &core.queue);
205        }
206
207        if should_start_export {
208            self.base.export_manager.start_export();
209        }
210
211        self.compute_shader.dispatch(&mut frame.encoder, core);
212
213        self.base.renderer.render_to_view(&mut frame.encoder, &frame.view, &self.compute_shader.get_output_texture().bind_group);
214
215        self.base.end_frame(core, frame, full_output);
216
217        Ok(())
218    }
219
220    fn handle_input(&mut self, core: &Core, event: &WindowEvent) -> bool {
221        self.base.default_handle_input(core, event)
222    }
223}
224
225fn main() -> Result<(), Box<dyn std::error::Error>> {
226    env_logger::init();
227    let (app, event_loop) = cuneus::ShaderApp::new("Volume Passage", 600, 300);
228
229    app.run(event_loop, VolumeShader::init)
230}