forked from 1b-agent/opencadstudio-upstream
first commit
This commit is contained in:
1 parent
d52c6440ff
commit
d2fdac7f1e
274 files changed
+89330
No files matched your search
@@ -0,0 +1,191 @@
|
||||
// Hatch shader — GPU-driven hatch fill over an arbitrary polygon boundary.
|
||||
//
|
||||
// Vertex shader : projects a bounding-box quad over the hatch region.
|
||||
// Fragment shader: three stages —
|
||||
// 1. Point-in-polygon (ray casting against boundary vertices in world XY).
|
||||
// 2. Pattern test:
|
||||
// mode 0 — N line families (PAT format), each with optional dash pattern.
|
||||
// mode 1 — solid fill.
|
||||
// mode 2 — linear gradient.
|
||||
//
|
||||
// GPU limits: MAX_FAMILIES = 16, MAX_DASHES = 128.
|
||||
|
||||
// ── Group 0: frame uniforms (shared) ──────────────────────────────────────
|
||||
|
||||
struct Uniforms {
|
||||
view_proj: mat4x4<f32>,
|
||||
camera_pos: vec4<f32>,
|
||||
viewport_size: vec2<f32>,
|
||||
_pad: vec2<f32>,
|
||||
}
|
||||
@group(0) @binding(0) var<uniform> u: Uniforms;
|
||||
|
||||
// ── Group 1: per-hatch data ────────────────────────────────────────────────
|
||||
//
|
||||
// mode encoding:
|
||||
// 0 → Pattern (evaluate FamilyBatch)
|
||||
// 1 → Solid (return h.color immediately)
|
||||
// 2 → Gradient (mix h.color → h.color2 along grad_cos/grad_sin)
|
||||
|
||||
struct HatchUniforms {
|
||||
color: vec4<f32>, // 0: primary RGBA
|
||||
color2: vec4<f32>, // 16: gradient end color
|
||||
mode: u32, // 32: 0=pattern, 1=solid, 2=gradient
|
||||
vcount: u32, // 36: boundary vertex count
|
||||
angle_offset: f32, // 40: pattern rotation (radians, added to each family)
|
||||
scale: f32, // 44: pattern scale multiplier
|
||||
grad_cos: f32, // 48: gradient direction cos
|
||||
grad_sin: f32, // 52: gradient direction sin
|
||||
grad_min: f32, // 56: gradient proj_min
|
||||
grad_range: f32, // 60: gradient proj_range
|
||||
}
|
||||
@group(1) @binding(0) var<uniform> h: HatchUniforms;
|
||||
|
||||
struct Boundary {
|
||||
verts: array<vec4<f32>, 64>,
|
||||
}
|
||||
@group(1) @binding(1) var<uniform> b: Boundary;
|
||||
|
||||
// One line family (48 bytes, matches LineFamilyGpu in hatch_gpu.rs).
|
||||
struct LineFamily {
|
||||
cos_a: f32, // base cos(angle)
|
||||
sin_a: f32, // base sin(angle)
|
||||
x0: f32, // family origin x
|
||||
y0: f32, // family origin y
|
||||
dx: f32, // step vector x
|
||||
dy: f32, // step vector y
|
||||
perp_step: f32, // -dx*sin_a + dy*cos_a (perpendicular spacing)
|
||||
along_step: f32, // dx*cos_a + dy*sin_a (phase shift per step)
|
||||
line_width: f32, // half-width of each line (|perp_step| × 0.08)
|
||||
period: f32, // sum(|dashes|) — 0 means solid
|
||||
n_dashes: u32, // number of dash entries
|
||||
dash_off: u32, // start index in dash_values
|
||||
}
|
||||
|
||||
struct FamilyBatch {
|
||||
families: array<LineFamily, 16>,
|
||||
dash_values: array<vec4<f32>, 32>, // 128 f32s packed as 32×vec4 (uniform stride=16)
|
||||
n_families: u32,
|
||||
_pad0: u32, _pad1: u32, _pad2: u32,
|
||||
}
|
||||
@group(1) @binding(2) var<uniform> f: FamilyBatch;
|
||||
|
||||
// ── Vertex shader ──────────────────────────────────────────────────────────
|
||||
|
||||
struct VIn { @location(0) pos: vec3<f32> }
|
||||
struct VOut {
|
||||
@builtin(position) clip: vec4<f32>,
|
||||
@location(0) xz: vec2<f32>,
|
||||
}
|
||||
|
||||
@vertex fn vs_main(v: VIn) -> VOut {
|
||||
var o: VOut;
|
||||
o.clip = u.view_proj * vec4<f32>(v.pos, 1.0);
|
||||
o.xz = vec2<f32>(v.pos.x, v.pos.y);
|
||||
return o;
|
||||
}
|
||||
|
||||
// ── Point-in-polygon (ray casting) ────────────────────────────────────────
|
||||
|
||||
fn in_polygon(p: vec2<f32>) -> bool {
|
||||
var inside = false;
|
||||
let n = h.vcount;
|
||||
var j = n - 1u;
|
||||
for (var i = 0u; i < n; i++) {
|
||||
let vi = b.verts[i].xy;
|
||||
let vj = b.verts[j].xy;
|
||||
if (vi.y > p.y) != (vj.y > p.y) {
|
||||
let x_int = (vj.x - vi.x) * (p.y - vi.y) / (vj.y - vi.y) + vi.x;
|
||||
if p.x < x_int { inside = !inside; }
|
||||
}
|
||||
j = i;
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
|
||||
// ── Per-family hatch test ─────────────────────────────────────────────────
|
||||
//
|
||||
// Returns true if world point `xz` falls on a hatch line of `fam`.
|
||||
// `cos_off` / `sin_off` are cos/sin of `h.angle_offset` (precomputed once).
|
||||
// `scale` is `h.scale`.
|
||||
|
||||
fn check_family(
|
||||
xz: vec2<f32>,
|
||||
fam: LineFamily,
|
||||
cos_off: f32,
|
||||
sin_off: f32,
|
||||
scale: f32,
|
||||
) -> bool {
|
||||
// Rotate family direction by angle_offset.
|
||||
let cos_a = fam.cos_a * cos_off - fam.sin_a * sin_off;
|
||||
let sin_a = fam.sin_a * cos_off + fam.cos_a * sin_off;
|
||||
|
||||
// Rotate and scale the family origin.
|
||||
let ox = (fam.x0 * cos_off - fam.y0 * sin_off) * scale;
|
||||
let oz = (fam.x0 * sin_off + fam.y0 * cos_off) * scale;
|
||||
|
||||
let px = xz.x - ox;
|
||||
let pz = xz.y - oz;
|
||||
|
||||
// Perpendicular distance from the nearest parallel line.
|
||||
let perp_step = fam.perp_step * scale;
|
||||
let line_w = abs(fam.line_width * scale);
|
||||
|
||||
let perp = -px * sin_a + pz * cos_a;
|
||||
let k = round(perp / perp_step);
|
||||
let d = abs(perp - k * perp_step);
|
||||
let half_px = length(vec2<f32>(dpdx(perp), dpdy(perp))) * 0.5;
|
||||
|
||||
if d > half_px { return false; }
|
||||
|
||||
// Solid line family — no dash check needed.
|
||||
if fam.n_dashes == 0u { return true; }
|
||||
|
||||
// Dash pattern test: position along line k.
|
||||
let along_step = fam.along_step * scale;
|
||||
let period = fam.period * scale;
|
||||
let along = px * cos_a + pz * sin_a;
|
||||
let t = along - k * along_step;
|
||||
let t_mod = ((t % period) + period) % period;
|
||||
|
||||
var pos = 0.0;
|
||||
for (var j = 0u; j < fam.n_dashes; j++) {
|
||||
let idx = fam.dash_off + j;
|
||||
let sv = f.dash_values[idx / 4u][idx % 4u] * scale; // scale dash lengths
|
||||
if sv > 0.0 {
|
||||
if t_mod >= pos && t_mod < pos + sv { return true; }
|
||||
pos = pos + sv;
|
||||
} else {
|
||||
pos = pos - sv;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── Fragment shader ────────────────────────────────────────────────────────
|
||||
|
||||
@fragment fn fs_main(v: VOut) -> @location(0) vec4<f32> {
|
||||
// 1. Boundary test.
|
||||
if !in_polygon(v.xz) { discard; }
|
||||
|
||||
// 2. Mode dispatch.
|
||||
if h.mode == 1u {
|
||||
// Solid fill.
|
||||
return h.color;
|
||||
} else if h.mode == 2u {
|
||||
// Linear gradient.
|
||||
let proj = v.xz.x * h.grad_cos + v.xz.y * h.grad_sin;
|
||||
let t = clamp((proj - h.grad_min) / h.grad_range, 0.0, 1.0);
|
||||
return mix(h.color, h.color2, t);
|
||||
}
|
||||
|
||||
// 3. Pattern: evaluate each line family.
|
||||
let cos_off = cos(h.angle_offset);
|
||||
let sin_off = sin(h.angle_offset);
|
||||
for (var i = 0u; i < f.n_families; i++) {
|
||||
if check_family(v.xz, f.families[i], cos_off, sin_off, h.scale) {
|
||||
return h.color;
|
||||
}
|
||||
}
|
||||
discard;
|
||||
}
|
||||
Reference in new issue
Block a user