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 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}