1use crate::resources::{DualPipeline, ViewportGpuResources};
7use wgpu::util::DeviceExt as _;
8
9#[repr(C)]
27#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
28pub struct ImplicitPrimitive {
29 pub kind: u32,
31 pub blend: f32,
34 #[doc(hidden)]
35 pub _pad: [f32; 2],
36 pub params: [f32; 8],
38 pub colour: [f32; 4],
41}
42
43#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
45pub enum ImplicitBlendMode {
46 #[default]
48 Union,
49 SmoothUnion,
51 Intersection,
53}
54
55#[derive(Clone, Copy, Debug)]
57pub struct GpuImplicitOptions {
58 pub max_steps: u32,
60 pub step_scale: f32,
63 pub hit_threshold: f32,
65 pub max_distance: f32,
67}
68
69impl Default for GpuImplicitOptions {
70 fn default() -> Self {
71 Self {
72 max_steps: 128,
73 step_scale: 0.85,
74 hit_threshold: 5e-4,
75 max_distance: 40.0,
76 }
77 }
78}
79
80#[non_exhaustive]
100pub struct GpuImplicitItem {
101 pub primitives: Vec<ImplicitPrimitive>,
103 pub blend_mode: ImplicitBlendMode,
105 pub march_options: GpuImplicitOptions,
107 pub id: u64,
109 pub appearance: crate::scene::material::AppearanceSettings,
111 pub selected: bool,
113}
114
115impl Default for GpuImplicitItem {
116 fn default() -> Self {
117 Self {
118 primitives: Vec::new(),
119 blend_mode: crate::resources::ImplicitBlendMode::Union,
120 march_options: GpuImplicitOptions::default(),
121 id: 0,
122 appearance: crate::scene::material::AppearanceSettings::default(),
123 selected: false,
124 }
125 }
126}
127
128impl ImplicitPrimitive {
129 pub fn zeroed() -> Self {
131 bytemuck::Zeroable::zeroed()
132 }
133}
134
135#[repr(C)]
143#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
144pub(crate) struct ImplicitUniformRaw {
145 pub num_primitives: u32,
146 pub blend_mode: u32,
147 pub max_steps: u32,
148 pub unlit: u32,
149 pub step_scale: f32,
150 pub hit_threshold: f32,
151 pub max_distance: f32,
152 pub opacity: f32,
153 pub primitives: [ImplicitPrimitive; 16],
154}
155
156pub(crate) struct ImplicitGpuItem {
158 pub _uniform_buf: wgpu::Buffer,
159 pub bind_group: wgpu::BindGroup,
160}
161
162impl ViewportGpuResources {
167 pub(crate) fn ensure_implicit_pipeline(&mut self, device: &wgpu::Device) {
172 if self.implicit_pipeline.is_some() {
173 return;
174 }
175
176 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
177 label: Some("implicit_shader"),
178 source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/implicit.wgsl").into()),
179 });
180
181 let implicit_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
183 label: Some("implicit_bgl"),
184 entries: &[wgpu::BindGroupLayoutEntry {
185 binding: 0,
186 visibility: wgpu::ShaderStages::FRAGMENT,
187 ty: wgpu::BindingType::Buffer {
188 ty: wgpu::BufferBindingType::Uniform,
189 has_dynamic_offset: false,
190 min_binding_size: None,
191 },
192 count: None,
193 }],
194 });
195
196 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
198 label: Some("implicit_pipeline_layout"),
199 bind_group_layouts: &[&self.camera_bind_group_layout, &implicit_bgl],
200 push_constant_ranges: &[],
201 });
202
203 let make = |fmt: wgpu::TextureFormat| {
204 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
205 label: Some("implicit_pipeline"),
206 layout: Some(&layout),
207 vertex: wgpu::VertexState {
208 module: &shader,
209 entry_point: Some("vs_main"),
210 buffers: &[],
211 compilation_options: wgpu::PipelineCompilationOptions::default(),
212 },
213 fragment: Some(wgpu::FragmentState {
214 module: &shader,
215 entry_point: Some("fs_main"),
216 targets: &[Some(wgpu::ColorTargetState {
217 format: fmt,
218 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
219 write_mask: wgpu::ColorWrites::ALL,
220 })],
221 compilation_options: wgpu::PipelineCompilationOptions::default(),
222 }),
223 primitive: wgpu::PrimitiveState {
224 topology: wgpu::PrimitiveTopology::TriangleList,
225 cull_mode: None,
226 ..Default::default()
227 },
228 depth_stencil: Some(wgpu::DepthStencilState {
230 format: wgpu::TextureFormat::Depth24PlusStencil8,
231 depth_write_enabled: true,
232 depth_compare: wgpu::CompareFunction::LessEqual,
233 stencil: wgpu::StencilState::default(),
234 bias: wgpu::DepthBiasState::default(),
235 }),
236 multisample: wgpu::MultisampleState {
237 count: 1,
238 mask: !0,
239 alpha_to_coverage_enabled: false,
240 },
241 multiview: None,
242 cache: None,
243 })
244 };
245
246 self.implicit_bgl = Some(implicit_bgl);
247 self.implicit_pipeline = Some(DualPipeline {
248 ldr: make(self.target_format),
249 hdr: make(wgpu::TextureFormat::Rgba16Float),
250 });
251 }
252
253 pub(crate) fn ensure_implicit_outline_mask_pipeline(&mut self, device: &wgpu::Device) {
259 if self.implicit_outline_mask_pipeline.is_some() {
260 return;
261 }
262
263 let implicit_bgl = self.implicit_bgl.as_ref().expect(
264 "ensure_implicit_pipeline must be called before ensure_implicit_outline_mask_pipeline",
265 );
266
267 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
268 label: Some("implicit_outline_mask_shader"),
269 source: wgpu::ShaderSource::Wgsl(
270 include_str!("../shaders/implicit_outline_mask.wgsl").into(),
271 ),
272 });
273
274 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
275 label: Some("implicit_outline_mask_pipeline_layout"),
276 bind_group_layouts: &[&self.camera_bind_group_layout, implicit_bgl],
277 push_constant_ranges: &[],
278 });
279
280 self.implicit_outline_mask_pipeline = Some(device.create_render_pipeline(
281 &wgpu::RenderPipelineDescriptor {
282 label: Some("implicit_outline_mask_pipeline"),
283 layout: Some(&layout),
284 vertex: wgpu::VertexState {
285 module: &shader,
286 entry_point: Some("vs_main"),
287 buffers: &[],
288 compilation_options: wgpu::PipelineCompilationOptions::default(),
289 },
290 fragment: Some(wgpu::FragmentState {
291 module: &shader,
292 entry_point: Some("fs_main"),
293 targets: &[Some(wgpu::ColorTargetState {
294 format: wgpu::TextureFormat::R8Unorm,
295 blend: None,
296 write_mask: wgpu::ColorWrites::ALL,
297 })],
298 compilation_options: wgpu::PipelineCompilationOptions::default(),
299 }),
300 primitive: wgpu::PrimitiveState {
301 topology: wgpu::PrimitiveTopology::TriangleList,
302 cull_mode: None,
303 ..Default::default()
304 },
305 depth_stencil: Some(wgpu::DepthStencilState {
306 format: wgpu::TextureFormat::Depth24PlusStencil8,
307 depth_write_enabled: true,
308 depth_compare: wgpu::CompareFunction::Less,
309 stencil: wgpu::StencilState::default(),
310 bias: wgpu::DepthBiasState::default(),
311 }),
312 multisample: wgpu::MultisampleState {
313 count: 1,
314 mask: !0,
315 alpha_to_coverage_enabled: false,
316 },
317 multiview: None,
318 cache: None,
319 },
320 ));
321 }
322
323 pub(crate) fn upload_implicit_item(
327 &self,
328 device: &wgpu::Device,
329 item: &GpuImplicitItem,
330 ) -> ImplicitGpuItem {
331 let blend_mode_u32 = match item.blend_mode {
333 ImplicitBlendMode::Union => 0u32,
334 ImplicitBlendMode::SmoothUnion => 1,
335 ImplicitBlendMode::Intersection => 2,
336 };
337
338 let mut raw = ImplicitUniformRaw {
339 num_primitives: item.primitives.len().min(16) as u32,
340 blend_mode: blend_mode_u32,
341 max_steps: item.march_options.max_steps,
342 unlit: item.appearance.unlit as u32,
343 step_scale: item.march_options.step_scale,
344 hit_threshold: item.march_options.hit_threshold,
345 max_distance: item.march_options.max_distance,
346 opacity: item.appearance.opacity,
347 primitives: [ImplicitPrimitive::zeroed(); 16],
348 };
349
350 for (i, prim) in item.primitives.iter().take(16).enumerate() {
351 raw.primitives[i] = *prim;
352 }
353
354 let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
355 label: Some("implicit_uniform_buf"),
356 contents: bytemuck::bytes_of(&raw),
357 usage: wgpu::BufferUsages::UNIFORM,
358 });
359
360 let bgl = self
361 .implicit_bgl
362 .as_ref()
363 .expect("ensure_implicit_pipeline not called");
364
365 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
366 label: Some("implicit_bind_group"),
367 layout: bgl,
368 entries: &[wgpu::BindGroupEntry {
369 binding: 0,
370 resource: uniform_buf.as_entire_binding(),
371 }],
372 });
373
374 ImplicitGpuItem {
375 _uniform_buf: uniform_buf,
376 bind_group,
377 }
378 }
379}