1use cuneus::compute::*;
2use cuneus::prelude::*;
3
4cuneus::uniform_params! {
5 struct TreeParams {
6 pixel_offset: f32,
7 pixel_offset2: f32,
8 lights: f32,
9 exp: f32,
10 frame: f32,
11 col1: f32,
12 col2: f32,
13 decay: f32}
14}
15
16struct TreeShader {
17 base: RenderKit,
18 compute_shader: ComputeShader,
19 current_params: TreeParams}
20
21impl ShaderManager for TreeShader {
22 fn init(core: &Core) -> Self {
23 let initial_params = TreeParams {
24 pixel_offset: 1.35,
25 pixel_offset2: 1.0,
26 lights: 2.2,
27 exp: 4.0,
28 frame: 0.5,
29 col1: 205.0,
30 col2: 5.5,
31 decay: 0.96};
32 let base = RenderKit::new(core);
33
34 let passes = vec![
36 PassDescription::new("fractal", &[]), PassDescription::new("gradient", &["fractal"]), PassDescription::new("trace", &["trace", "gradient"]), PassDescription::new("main_image", &["trace"]),
40 ];
41
42 let config = ComputeShader::builder()
43 .with_entry_point("fractal")
44 .with_multi_pass(&passes)
45 .with_custom_uniforms::<TreeParams>()
46 .with_workgroup_size([16, 16, 1])
47 .with_texture_format(COMPUTE_TEXTURE_FORMAT_RGBA16)
48 .with_label("Tree Unified")
49 .build();
50
51 let compute_shader = cuneus::compute_shader!(core, "shaders/tree.wgsl", config);
52
53 compute_shader.set_custom_params(initial_params, &core.queue);
54
55 Self {
56 base,
57 compute_shader,
58 current_params: initial_params}
59 }
60
61 fn update(&mut self, core: &Core) {
62 self.compute_shader.handle_export(core, &mut self.base);
64
65 let current_time = self.base.controls.get_time(&self.base.start_time);
67 let delta = 1.0 / 60.0; self.compute_shader
69 .set_time(current_time, delta, &core.queue);
70 }
71
72 fn resize(&mut self, core: &Core) {
73 self.base.default_resize(core, &mut self.compute_shader);
74 }
75
76 fn render(&mut self, core: &Core) -> Result<(), cuneus::SurfaceError> {
77 let mut frame = self.base.begin_frame(core)?;
78
79 let mut params = self.current_params;
80 let mut changed = false;
81 let mut should_start_export = false;
82 let mut export_request = self.base.export_manager.get_ui_request();
83 let mut controls_request = self
84 .base
85 .controls
86 .get_ui_request(&self.base.start_time, &core.size, self.base.fps_tracker.fps());
87
88 let full_output = if self.base.key_handler.show_ui {
89 self.base.render_ui(core, |ctx| {
90 RenderKit::apply_default_style(ctx);
91
92 egui::Window::new("Fractal Tree")
93 .collapsible(true)
94 .resizable(true)
95 .default_width(280.0)
96 .show(ctx, |ui| {
97 egui::CollapsingHeader::new("Fractal Parameters")
98 .default_open(true)
99 .show(ui, |ui| {
100 changed |= ui
101 .add(
102 egui::Slider::new(&mut params.pixel_offset, -3.14..=3.14)
103 .text("Pixel Offset Y"),
104 )
105 .changed();
106 changed |= ui
107 .add(
108 egui::Slider::new(&mut params.pixel_offset2, -3.14..=3.14)
109 .text("Pixel Offset X"),
110 )
111 .changed();
112 changed |= ui
113 .add(
114 egui::Slider::new(&mut params.lights, 0.0..=12.2)
115 .text("Lights"),
116 )
117 .changed();
118 changed |= ui
119 .add(
120 egui::Slider::new(&mut params.exp, 1.0..=120.0).text("Exp"),
121 )
122 .changed();
123 });
124
125 egui::CollapsingHeader::new("Visual Settings")
126 .default_open(false)
127 .show(ui, |ui| {
128 changed |= ui
129 .add(
130 egui::Slider::new(&mut params.frame, 0.0..=2.2)
131 .text("Frame"),
132 )
133 .changed();
134 changed |= ui
135 .add(
136 egui::Slider::new(&mut params.col1, 0.0..=300.0)
137 .text("Iterations"),
138 )
139 .changed();
140 changed |= ui
141 .add(
142 egui::Slider::new(&mut params.col2, 0.0..=10.0)
143 .text("Color 2"),
144 )
145 .changed();
146 changed |= ui
147 .add(
148 egui::Slider::new(&mut params.decay, 0.0..=1.0)
149 .text("Feedback"),
150 )
151 .changed();
152 });
153
154 ui.separator();
155 ShaderControls::render_controls_widget(ui, &mut controls_request);
156
157 ui.separator();
158 should_start_export =
159 ExportManager::render_export_ui_widget(ui, &mut export_request);
160
161 ui.separator();
162 ui.label(format!("Frame: {}", self.compute_shader.current_frame));
163 ui.label("Multi-buffer fractal tree with particle tracing");
164 });
165 })
166 } else {
167 self.base.render_ui(core, |_ctx| {})
168 };
169
170 if controls_request.should_clear_buffers {
172 self.compute_shader.current_frame = 0;
174 }
175
176 self.compute_shader.dispatch(&mut frame.encoder, core);
178
179 self.base.renderer.render_to_view(&mut frame.encoder, &frame.view, &self.compute_shader.get_output_texture().bind_group);
180
181 self.base.apply_control_request(controls_request);
183 self.base.export_manager.apply_ui_request(export_request);
184
185 if changed {
186 self.current_params = params;
187 self.compute_shader.set_custom_params(params, &core.queue);
188 }
189
190 if should_start_export {
191 self.base.export_manager.start_export();
192 }
193
194 self.base.end_frame(core, frame, full_output);
195 Ok(())
196 }
197
198 fn handle_input(&mut self, core: &Core, event: &WindowEvent) -> bool {
199 self.base.default_handle_input(core, event)
200 }
201}
202
203fn main() -> Result<(), Box<dyn std::error::Error>> {
204 env_logger::init();
205 let (app, event_loop) = ShaderApp::new("Fractal Tree", 800, 600);
206 app.run(event_loop, TreeShader::init)
207}