Child boxes of a node
32-bit, display onlyStores the boxes of a node's four children as 8-bit offsets from the node's corner.
scale = 2^e, the smallest with 255 × 2^e ≥ extent; lo = floor((box − origin) ÷ scale), hi = ceil(…)
Offsets are rounded outward and then checked with Math.fround, stepping one further out when the 32-bit result would cut into the child, so a decoded box always contains its geometry. The boxes are looser than the geometry by up to one step of the scale.
/**
* Quantize the child boxes of one node into its words. nodeBox: 6 numbers (f32-exact). kidBox(i, out6) fills child i's box
* and returns 1 inner / 2 leaf / 0 empty.
*/
function quantize(words, f, base, nodeBox, nb0, kidKind, kidBoxes) {
// origin = node min (already f32, rounded down); scale per axis: smallest power of two with 255 · s ≥ extent
const ox = nodeBox[nb0], oy = nodeBox[nb0 + 1], oz = nodeBox[nb0 + 2];
let ebits = 0, l0 = 0, l1 = 0, l2 = 0, h0 = 0, h1 = 0, h2 = 0, innerMask = 0;
const S = [0, 0, 0], O = [ox, oy, oz];
for (let ax = 0; ax < 3; ax++) {
const ext = nodeBox[nb0 + 3 + ax] - nodeBox[nb0 + ax];
let e = ext > 0 ? Math.ceil(Math.log2(ext / 255)) : -126;
if (e < -126) e = -126; if (e > 127) e = 127;
while (e < 127 && 255 * 2 ** e < ext) e++; // log2 can land one short through rounding
S[ax] = 2 ** e;
ebits |= ((e + 127) & 255) << (8 * ax);
}
f[base] = ox; f[base + 1] = oy; f[base + 2] = oz;
for (let i = 0; i < 4; i++) {
const kind = kidKind[i];
if (!kind) continue;
if (kind === 1) innerMask |= 1 << i;
const kb = i * 6;
for (let ax = 0; ax < 3; ax++) {
const o = O[ax], s = S[ax];
let ql = Math.floor((kidBoxes[kb + ax] - o) / s), qh = Math.ceil((kidBoxes[kb + 3 + ax] - o) / s);
ql = ql < 0 ? 0 : ql > 255 ? 255 : ql; qh = qh < 0 ? 0 : qh > 255 ? 255 : qh;
// conservative in f32 (o + q·s with one rounding, as the kernels compute it)
while (ql > 0 && f32(o + ql * s) > kidBoxes[kb + ax]) ql--;
while (qh < 255 && f32(o + qh * s) < kidBoxes[kb + 3 + ax]) qh++;
const sh = 8 * i;
if (ax === 0) { l0 |= ql << sh; h0 |= qh << sh; } else if (ax === 1) { l1 |= ql << sh; h1 |= qh << sh; } else { l2 |= ql << sh; h2 |= qh << sh; }
}
}
words[base + 3] = (ebits | (innerMask << 24)) >>> 0;
words[base + 8] = l0 >>> 0; words[base + 9] = l1 >>> 0; words[base + 10] = l2 >>> 0;
words[base + 11] = h0 >>> 0; words[base + 12] = h1 >>> 0; words[base + 13] = h2 >>> 0;
}