Triangles in world space
32-bit, display onlyEvery mesh triangle moved by its placement, cut by the section planes and stored for the kernels.
Vertices are transformed and clipped in Float64Array scratch and then pushed into Float32Array buffers. Normals use the inverse transpose of the placement, whose determinant is replaced by 1 when it is zero.
/** Every mesh item in world space (matrix applied, drawRange and index honoured), cut by the planes (keep n·p ≤ d). */
export function bakeTriangles(items, planes = [], ids = null) {
planes = planes || [];
const P = new Grow(), N = new Grow(), U = new Grow(), look = [], obj = [];
// a clipped polygon has at most 3 + planes vertices: fixed scratch, reused
const cap = 3 + planes.length * 2, pp = new Float64Array(cap * 3), pn = new Float64Array(cap * 3), pu = new Float64Array(cap * 2);
const qp = new Float64Array(cap * 3), qn = new Float64Array(cap * 3), qu = new Float64Array(cap * 2);
for (let ii = 0; ii < items.length; ii++) {
const it = items[ii];
if (!it || it.lines || !it.geometry) continue;
const oid = ids ? ids[ii] : ii;
const g = it.geometry, pa = g.getAttribute('position'), na = g.getAttribute('normal'), ua = g.getAttribute('uv');
if (!pa) continue;
const m = it.matrix || null;
// normal matrix = inverse transpose of the upper 3 × 3 (column-major m)
let nm = null;
if (m) {
const a = m[0], b = m[4], c = m[8], d = m[1], e = m[5], f = m[9], gg = m[2], h = m[6], i = m[10];
const A = e * i - f * h, B = -(d * i - f * gg), C = d * h - e * gg, det = a * A + b * B + c * C || 1;
// inverse transpose, row-major rows
nm = [A / det, B / det, C / det, -(b * i - c * h) / det, (a * i - c * gg) / det, -(a * h - b * gg) / det, (b * f - c * e) / det, -(a * f - c * d) / det, (a * e - b * d) / det];
}
const idx = g.index ? g.index.array : null, start = (g.drawRange && g.drawRange.start) || 0;
const total = idx ? idx.length : pa.count;
const rc = g.drawRange && Number.isFinite(g.drawRange.count) ? g.drawRange.count : Infinity;
const end = Math.min(total, start + rc);
const key = it.look || 'default';
for (let t = start; t + 2 < end; t += 3) {
let nv = 3;
for (let k = 0; k < 3; k++) {
const vi = idx ? idx[t + k] : t + k;
const x = pa.getX(vi), y = pa.getY(vi), z = pa.getZ(vi);
if (m) { pp[k * 3] = m[0] * x + m[4] * y + m[8] * z + m[12]; pp[k * 3 + 1] = m[1] * x + m[5] * y + m[9] * z + m[13]; pp[k * 3 + 2] = m[2] * x + m[6] * y + m[10] * z + m[14]; }
else { pp[k * 3] = x; pp[k * 3 + 1] = y; pp[k * 3 + 2] = z; }
let nx = na ? na.getX(vi) : 0, ny = na ? na.getY(vi) : 0, nz = na ? na.getZ(vi) : 0;
if (nm) { const X = nm[0] * nx + nm[1] * ny + nm[2] * nz, Y = nm[3] * nx + nm[4] * ny + nm[5] * nz, Z = nm[6] * nx + nm[7] * ny + nm[8] * nz; nx = X; ny = Y; nz = Z; }
const l = Math.hypot(nx, ny, nz);
pn[k * 3] = l ? nx / l : 0; pn[k * 3 + 1] = l ? ny / l : 0; pn[k * 3 + 2] = l ? nz / l : 0;
pu[k * 2] = ua ? ua.getX(vi) : 0; pu[k * 2 + 1] = ua ? ua.getY(vi) : 0;
}
// section cuts (Sutherland–Hodgman against each plane)
let src = [pp, pn, pu], dst = [qp, qn, qu];
for (const pl of planes) {
if (nv < 3) break;
const [sp, sn, su] = src, [dp, dn, du] = dst;
let out = 0;
for (let i = 0; i < nv; i++) {
const j = (i + 1) % nv;
const di = pl.n[0] * sp[i * 3] + pl.n[1] * sp[i * 3 + 1] + pl.n[2] * sp[i * 3 + 2] - pl.d;
const dj = pl.n[0] * sp[j * 3] + pl.n[1] * sp[j * 3 + 1] + pl.n[2] * sp[j * 3 + 2] - pl.d;
if (di <= 0) { for (let k = 0; k < 3; k++) { dp[out * 3 + k] = sp[i * 3 + k]; dn[out * 3 + k] = sn[i * 3 + k]; } du[out * 2] = su[i * 2]; du[out * 2 + 1] = su[i * 2 + 1]; out++; }
if ((di <= 0) !== (dj <= 0)) {
const f = di / (di - dj);
for (let k = 0; k < 3; k++) { dp[out * 3 + k] = sp[i * 3 + k] + (sp[j * 3 + k] - sp[i * 3 + k]) * f; dn[out * 3 + k] = sn[i * 3 + k] + (sn[j * 3 + k] - sn[i * 3 + k]) * f; }
du[out * 2] = su[i * 2] + (su[j * 2] - su[i * 2]) * f; du[out * 2 + 1] = su[i * 2 + 1] + (su[j * 2 + 1] - su[i * 2 + 1]) * f;
out++;
}
}
nv = out;
const tmp = src; src = dst; dst = tmp;
}
if (nv < 3) continue;
const [sp, sn, su] = src;
for (let i = 1; i + 1 < nv; i++) {
for (const k of [0, i, i + 1]) { P.push3(sp[k * 3], sp[k * 3 + 1], sp[k * 3 + 2]); N.push3(sn[k * 3], sn[k * 3 + 1], sn[k * 3 + 2]); U.push2(su[k * 2], su[k * 2 + 1]); }
look.push(key); obj.push(oid);
}
}
}
return { n: look.length, pos: P.done(), nor: N.done(), uv: U.done(), look, obj };
}