forked from 1b-agent/opencadstudio-upstream
fix(pipeline): batched-hatch pattern math + invisible-instance clip
Two issues in the step-3 batched path surfaced during testing: 1. WGSL rejects compile-time NaN literals (`0.0 / 0.0`), so the visibility-skip path that emitted a NaN clip position failed `Device::create_shader_module` validation. Replaced with an out-of-frustum clip position `(2, 2, 2, 1)` — the GPU rasterizer clips the primitive and the fragment shader never runs. 2. Pattern hatches rendered with the wrong spacing / direction. The batched builder was rotating `perp_step` / `along_step` into the global frame, but the per-hatch shader (`hatch.wgsl`) expects them in QCAD PAT local-frame convention: `perp_step = family.dy`, `along_step = family.dx`, with the cos_off/sin_off rotation applied inside the shader. Now matches `build_family_batch` from `hatch_gpu.rs` line-for-line. `line_width` also reset to 0 (the shader picks 1 px from a screen-space derivative; the stored field is unused). 3. Padding's `max_spacing` likewise dropped its bogus rotation and now uses `|dy|` per family, matching the new perp_step convention. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
3593e9dabc
commit
cfe53bd789
2 files changed
+21
-13
No files matched your search
@@ -192,11 +192,16 @@ impl HatchBatchedGpu {
|
||||
dashes.push(d);
|
||||
}
|
||||
let n_dashes = (dashes.len() as u32 - dash_offset).min(u32::MAX);
|
||||
let perp_step = -fam.dx * fam.angle_deg.to_radians().sin()
|
||||
+ fam.dy * fam.angle_deg.to_radians().cos();
|
||||
let along_step = fam.dx * fam.angle_deg.to_radians().cos()
|
||||
+ fam.dy * fam.angle_deg.to_radians().sin();
|
||||
let line_width = perp_step.abs() * 0.08;
|
||||
// QCAD PAT local-frame convention (mirrors
|
||||
// `build_family_batch` in hatch_gpu.rs): `dy` is
|
||||
// the perpendicular spacing, `dx` is the along-line
|
||||
// phase shift — both in family-local coords. The
|
||||
// shader applies cos_off/sin_off to rotate them.
|
||||
let perp_step = fam.dy;
|
||||
let along_step = fam.dx;
|
||||
// Screen-space derivative drives 1-px line width
|
||||
// in the shader; this stored field is unused.
|
||||
let line_width = 0.0_f32;
|
||||
let period: f32 = fam.dashes.iter().map(|d| d.abs()).sum();
|
||||
families.push(LineFamilyGpu {
|
||||
cos_a: fam.angle_deg.to_radians().cos(),
|
||||
@@ -208,7 +213,7 @@ impl HatchBatchedGpu {
|
||||
perp_step,
|
||||
along_step,
|
||||
line_width,
|
||||
period,
|
||||
period: if n_dashes > 0 { period } else { 0.0 },
|
||||
n_dashes,
|
||||
dash_offset,
|
||||
});
|
||||
@@ -263,13 +268,13 @@ impl HatchBatchedGpu {
|
||||
// family origin. Mirrors the per-hatch shader's quad sizing
|
||||
// logic — `diag * 0.8 + max_spacing * 2 * scale`.
|
||||
let diag = ((max_x - min_x).powi(2) + (max_y - min_y).powi(2)).sqrt();
|
||||
// `perp_step.abs()` per family — uses the same QCAD local-
|
||||
// frame convention as `LineFamilyGpu.perp_step` above so
|
||||
// the quad padding matches what the shader will sample.
|
||||
let max_spacing = match &h.pattern {
|
||||
HatchPattern::Pattern(fs) => fs
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let a = f.angle_deg.to_radians();
|
||||
(-f.dx * a.sin() + f.dy * a.cos()).abs()
|
||||
})
|
||||
.map(|f| f.dy.abs())
|
||||
.fold(0.0f32, f32::max),
|
||||
_ => 5.0,
|
||||
};
|
||||
|
||||
@@ -100,10 +100,13 @@ fn corner_xy(c: u32, aabb: vec4<f32>) -> vec2<f32> {
|
||||
var o: VOut;
|
||||
let inst = instances[v.instance_index];
|
||||
|
||||
// CPU-driven visibility flag (Phase 4-B frustum skip). NaN clip
|
||||
// position degenerates the triangle so no fragment runs for it.
|
||||
// CPU-driven visibility flag (Phase 4-B frustum skip). Emit a clip
|
||||
// position whose x/y exceed |w|, so the GPU frustum-culls the
|
||||
// primitive and no fragment runs for it. (WGSL forbids literal NaN
|
||||
// so we can't go that route; pushing the vertex out of the unit
|
||||
// cube is the equivalent degenerate-triangle trick.)
|
||||
if inst.visible == 0u {
|
||||
o.clip = vec4<f32>(0.0 / 0.0, 0.0 / 0.0, 0.0 / 0.0, 1.0);
|
||||
o.clip = vec4<f32>(2.0, 2.0, 2.0, 1.0);
|
||||
o.xz = vec2<f32>(0.0, 0.0);
|
||||
o.instance_index = v.instance_index;
|
||||
return o;
|
||||
|
||||
Reference in new issue
Block a user