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 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-13 10:04:25 +03:00
1 parent 088dec4ab6
commit d1a2e9db64
1 file changed
+27 -3
+27 -3
View File
@@ -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<HatchModel>, Vec<HatchModel>) =
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();