fix(wipeout): frame draws above the mask; mask joins the draw order
The wipeout pass ran after the wire pass with no draw-order bias, so the mask erased its own boundary frame (and everything else at its plane regardless of draw order). The mask uniform now carries the entity's draw-order depth (uniform grows 80 → 96 bytes) and biases its clip-z like every other pipeline, and the frame wire rides a +half-rank override — the later mask pass fails the depth test at the frame's pixels, so the boundary stays visible, while entities drawn above the wipeout are no longer erased. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f72852e94b
commit
fd2607452d
4 changed files with 27 additions and 3 deletions
|
|
@ -1341,6 +1341,13 @@ pub(crate) fn tessellate_entity(
|
||||||
w.name = host_name.clone();
|
w.name = host_name.clone();
|
||||||
w.aci = aci;
|
w.aci = aci;
|
||||||
set_wire_aabb(w, aabb);
|
set_wire_aabb(w, aabb);
|
||||||
|
// A wipeout's frame sits ABOVE its own mask: the mask
|
||||||
|
// draws at the entity's rank, the frame half a rank
|
||||||
|
// closer, so the later mask pass fails the depth test at
|
||||||
|
// the frame's pixels instead of erasing it.
|
||||||
|
if matches!(e, EntityType::Wipeout(_)) {
|
||||||
|
w.depth_override = Some(0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bases.extend(wires);
|
bases.extend(wires);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1032,6 +1032,7 @@ impl Scene {
|
||||||
// epoch boundary and never re-evaluate as the user pans. The
|
// epoch boundary and never re-evaluate as the user pans. The
|
||||||
// pipeline's `wipeout_skip_flags` (compute_wipeout_lod) does
|
// pipeline's `wipeout_skip_flags` (compute_wipeout_lod) does
|
||||||
// the per-frame skip at draw time instead.
|
// the per-frame skip at draw time instead.
|
||||||
|
let depth_map = self.draw_depth_map();
|
||||||
let mut models = Vec::new();
|
let mut models = Vec::new();
|
||||||
for entity in self.document.entities() {
|
for entity in self.document.entities() {
|
||||||
let EntityType::Wipeout(wo) = entity else {
|
let EntityType::Wipeout(wo) = entity else {
|
||||||
|
|
@ -1072,7 +1073,12 @@ impl Scene {
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
world_origin: fill_origin,
|
world_origin: fill_origin,
|
||||||
draw_depth: 0.0,
|
// The mask erases what draws below the wipeout and loses
|
||||||
|
// to what draws above — including its own frame wire,
|
||||||
|
// which rides a +half-rank override on top of this.
|
||||||
|
draw_depth: depth_map
|
||||||
|
.get(&wo.common.handle.value())
|
||||||
|
.map_or(0.0, |d| d[0]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ impl HatchVertex {
|
||||||
|
|
||||||
// ── Uniform structs ───────────────────────────────────────────────────────
|
// ── Uniform structs ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Per-hatch parameters (binding 0). Must be 80 bytes (16-byte aligned).
|
/// Per-hatch parameters (binding 0). Must be 96 bytes (16-byte aligned).
|
||||||
///
|
///
|
||||||
/// `mode` encoding:
|
/// `mode` encoding:
|
||||||
/// 0 → Pattern (families in FamilyBatchData)
|
/// 0 → Pattern (families in FamilyBatchData)
|
||||||
|
|
@ -75,7 +75,9 @@ pub struct HatchUniformData {
|
||||||
// // is small (e.g. UTM drawing + sub-mm hatch).
|
// // is small (e.g. UTM drawing + sub-mm hatch).
|
||||||
pub origin_low: [f32; 2], // 72: anchor low residual (double-single) so the
|
pub origin_low: [f32; 2], // 72: anchor low residual (double-single) so the
|
||||||
// // vertex clip position stays precise at UTM scale.
|
// // vertex clip position stays precise at UTM scale.
|
||||||
} // total 80 bytes
|
pub draw_depth: f32, // 80: signed (-1,1) draw-order bias
|
||||||
|
pub _pad: [f32; 3], // 84..96
|
||||||
|
} // total 96 bytes
|
||||||
|
|
||||||
/// Boundary polygon (binding 1). Matches WGSL `Boundary`.
|
/// Boundary polygon (binding 1). Matches WGSL `Boundary`.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -298,6 +300,8 @@ impl WipeoutGpu {
|
||||||
(origin[0] - origin[0] as f32 as f64) as f32,
|
(origin[0] - origin[0] as f32 as f64) as f32,
|
||||||
(origin[1] - origin[1] as f32 as f64) as f32,
|
(origin[1] - origin[1] as f32 as f64) as f32,
|
||||||
],
|
],
|
||||||
|
draw_depth: model.draw_depth,
|
||||||
|
_pad: [0.0; 3],
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── BoundaryData (in snapped-local space) ────────────────────────
|
// ── BoundaryData (in snapped-local space) ────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,9 @@ struct HatchUniforms {
|
||||||
// // catastrophically cancel when world coords
|
// // catastrophically cancel when world coords
|
||||||
// // are large and pattern spacing is small.
|
// // are large and pattern spacing is small.
|
||||||
origin_low: vec2<f32>, // 72: anchor low residual (double-single)
|
origin_low: vec2<f32>, // 72: anchor low residual (double-single)
|
||||||
|
draw_depth: f32, // 80: signed (-1,1) draw-order bias; 0 = neutral
|
||||||
|
_pad0: f32, // 84
|
||||||
|
_pad1: vec2<f32>, // 88
|
||||||
}
|
}
|
||||||
@group(1) @binding(0) var<uniform> h: HatchUniforms;
|
@group(1) @binding(0) var<uniform> h: HatchUniforms;
|
||||||
|
|
||||||
|
|
@ -111,6 +114,10 @@ struct VOut {
|
||||||
v.pos.y + h.origin_low.y - u.eye_low.y,
|
v.pos.y + h.origin_low.y - u.eye_low.y,
|
||||||
v.pos.z - u.eye_low.z);
|
v.pos.z - u.eye_low.z);
|
||||||
o.clip = u.view_rot * vec4<f32>(hi + lo, 1.0);
|
o.clip = u.view_rot * vec4<f32>(hi + lo, 1.0);
|
||||||
|
// Draw-order bias (see wire.wgsl): the mask erases what draws BELOW its
|
||||||
|
// entity and loses to what draws above — including its own frame wire,
|
||||||
|
// which rides a +half-rank override.
|
||||||
|
o.clip.z = o.clip.z - h.draw_depth * 0.001 * o.clip.w;
|
||||||
o.xz = vec2<f32>(v.pos.x, v.pos.y);
|
o.xz = vec2<f32>(v.pos.x, v.pos.y);
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue