From d1a2e9db6441ce55929dbd6e9e03938026acb91a Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Tue, 13 Oct 2026 10:04:20 +0300 Subject: [PATCH] fix(render): draw transparent hatches over the lines behind them Hatches are drawn before the wires and write depth, so a transparent hatch above lines in draw order still hid them through the depth test. Transparent hatches now go to their own renderer drawn after the wires: the lines behind show through the blend and the lines in front still win the depth test. Fixes #1720 Co-Authored-By: Claude Opus 5.5 --- src/scene/pipeline/mod.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/scene/pipeline/mod.rs b/src/scene/pipeline/mod.rs index 66278bcfd..bbf779c40 100644 --- a/src/scene/pipeline/mod.rs +++ b/src/scene/pipeline/mod.rs @@ -136,6 +136,9 @@ pub struct Pipeline { /// Capability-selected hatch renderer. Storage and texture transports are /// private backends behind one upload/LOD/draw lifecycle. hatch_gpu: hatch_gpu::HatchGpu, + /// Hatches with transparency, drawn after the wires so the lines behind + /// them show through the blend instead of failing the depth test. + hatch_translucent_gpu: hatch_gpu::HatchGpu, image_pipeline: wgpu::RenderPipeline, point_cloud_pipeline: wgpu::RenderPipeline, /// SDF text-quad pipeline (Phase 2b): draws per-glyph quads sampling the @@ -1100,6 +1103,14 @@ impl Pipeline { device_caps, force_compat_renderer, ); + let hatch_translucent_gpu = hatch_gpu::HatchGpu::new( + device, + format, + &frame_bgl, + &content_stencil, + device_caps, + force_compat_renderer, + ); #[cfg(not(target_arch = "wasm32"))] if std::env::var_os("RUST_LOG").is_some() { eprintln!( @@ -2339,6 +2350,7 @@ impl Pipeline { gpu_error_epoch: gpu_errors_seen(), wipeout_pipeline, hatch_gpu, + hatch_translucent_gpu, image_pipeline, point_cloud_pipeline, text_pipeline, @@ -3429,10 +3441,12 @@ analytic={:.1} regular={:.1} blocks={:.1}", clip_h: u32, ) { let perf_started = crate::perf::enabled().then(iced::time::Instant::now); - let instance_count = self.hatch_gpu.update_visibility(queue, |aabb| { + let visible = |aabb: [f32; 4]| { !aabb_below_pixel(aabb, view_rot, eye, clip_w, clip_h, 2.0) && !aabb_offscreen(aabb, view_rot, eye, clip_w, clip_h) - }); + }; + let instance_count = self.hatch_gpu.update_visibility(queue, visible) + + self.hatch_translucent_gpu.update_visibility(queue, visible); if instance_count == 0 { return; } @@ -3786,7 +3800,10 @@ analytic={:.1} regular={:.1} blocks={:.1}", .iter() .filter(|hatch| hatch.boundary.len() >= 3) .count(); - self.hatch_gpu.upload(device, queue, hatches); + let (translucent, opaque): (Vec, Vec) = + hatches.iter().cloned().partition(|hatch| hatch.color[3] < 0.999); + self.hatch_gpu.upload(device, queue, &opaque); + self.hatch_translucent_gpu.upload(device, queue, &translucent); if let Some(started) = perf_started { let elapsed_ms = started.elapsed().as_secs_f64() * 1000.0; if elapsed_ms >= 1.0 { @@ -4819,6 +4836,12 @@ analytic={:.1} regular={:.1} blocks={:.1}", } } } + // Transparent hatches blend over the lines already drawn; the + // lines in front of them still win the depth test. + if !self.skip_hatch_frame { + self.hatch_translucent_gpu + .draw(&mut pass, &self.uniform_bind_group, stencil_ref); + } } // ── Pass 5c: SDF text quads (drawn over wires) ──────────────────── @@ -5311,6 +5334,7 @@ analytic={:.1} regular={:.1} blocks={:.1}", self.gpu_preview_circles.clear(); self.gpu_preview_ellipses.clear(); self.hatch_gpu.clear(); + self.hatch_translucent_gpu.clear(); self.gpu_wipeouts.clear(); self.wipeout_skip_flags.clear(); self.gpu_images.clear();