feat(lwpolyline): render the wide-polyline band via the wire shader

A wide polyline's flat band was drawn by the hatch pipeline: a solid fill
with only draw-order depth. It ignored the entity linetype (the width
"crushed" the dash pattern) and showed through 3-D geometry in a tilted view.

Draw the band from the polyline's own centre-line wire instead. A new
`WireModel.world_width` (drawing units) makes the wire vertex shader expand
the quad by `world_half_width / world_per_pixel` (clamped to a hairline), so
the band is sized in world units and tracks zoom. Because it is now a wire:

- the GPU dash shader dashes the band by arc-length — the linetype applies;
- it is depth-tested at the polyline elevation — no draw-order see-through;
- no CPU band triangles or per-dash geometry are generated.

`world_half_width` rides in `WireConst` (native, replacing a pad) and the web
`WireInstance` (loc 14); both wire shaders branch on it. The tessellator sets
`world_width` from the LwPolyline width on the Contour / SegmentedLines arms,
and the model-space fill loop no longer emits a hatch band for any LwPolyline.
Pick still uses the band polygon (`pick_tris`).

Blocks keep the hatch band: `LocalWire` carries no `world_width` (finalize
zeroes it), so a scaled insert still scales its band correctly.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-20 14:50:27 +03:00
1 parent 3d37e1f8d8
commit bd1a5affd7
44 files changed
+158 -17

No files matched your search

+14 -2
View File
@@ -59,6 +59,8 @@ struct InstanceIn {
// "A"-type endpoint alignment: end-dash length + total wire length.
@location(12) align_end: f32,
@location(13) align_total: f32,
// World-space half-width for a wide-polyline band. 0 = normal wire.
@location(14) world_half_width: f32,
}
// Draw-order depth bias: shifts clip-space z so 2D entities of different
@@ -129,8 +131,18 @@ struct VertexOut {
// Select the clip-space position for this vertex's endpoint.
let clip_pos = mix(clip_a, clip_b, which_end);
// LWDISPLAY off → collapse to a 1-pixel-wide line (half_width = 0.5).
let hw = select(0.5, in.half_width, u.lwdisplay_enable > 0.5);
// A wide polyline carries its band width in world units: expand the quad by
// `world_half_width / world_per_pixel` (pixels) so the band tracks zoom. A
// normal wire (world_half_width == 0) uses the screen-pixel half-width,
// honouring the LWDISPLAY toggle (off → collapse to a 1-pixel line).
var hw: f32;
if in.world_half_width > 0.0 {
// Clamp to a half-pixel so a zoomed-out band never drops below a
// hairline (its centre-line) instead of vanishing.
hw = max(in.world_half_width / u.world_per_pixel, 0.5);
} else {
hw = select(0.5, in.half_width, u.lwdisplay_enable > 0.5);
}
// Offset in clip space (multiply by w to un-apply perspective division).
let ndc_offset = perp_ndc * hw * side;
+13 -2
View File
@@ -30,7 +30,7 @@ struct WireConst {
draw_depth: f32,
align_end: f32,
align_total: f32,
_pad0: f32,
world_half_width: f32,
_pad1: f32,
_pad2: f32,
}
@@ -93,7 +93,18 @@ struct VertexOut {
let clip_pos = mix(clip_a, clip_b, which_end);
let hw = select(0.5, c.half_width, u.lwdisplay_enable > 0.5);
// A wide polyline carries its band width in world units: expand the quad
// by `world_half_width / world_per_pixel` (screen pixels) so the band grows
// and shrinks with zoom. A normal wire (world_half_width == 0) uses the
// screen-pixel half-width, honouring the LWDISPLAY toggle.
var hw: f32;
if c.world_half_width > 0.0 {
// Clamp to a half-pixel so a zoomed-out band never drops below a
// hairline (its centre-line) instead of vanishing.
hw = max(c.world_half_width / u.world_per_pixel, 0.5);
} else {
hw = select(0.5, c.half_width, u.lwdisplay_enable > 0.5);
}
let ndc_offset = perp_ndc * hw * side;
let final_clip = clip_pos + vec4<f32>(ndc_offset * clip_pos.w, 0.0, 0.0);