Render buffers of a definition
32-bit, display onlyThe triangles of a definition's shown faces as position, normal and texture-coordinate buffers.
Points are copied from the model's 64-bit vertices into Float32BufferAttribute arrays. The buffers are built for the render and disposed after it.
/** One definition, one placement paint → [{look, geometry}] per colour, plus its edges. */
function buildDef(def, paint) {
if (def.mesh) return buildMeshDef(def, paint);
const by = new Map();
const smooth = smoothNormals(def);
for (const [fid, f] of def.faces) {
if (f.hidden) continue;
const n = faceNormal(def, f), k = keyOf(f, paint);
let b = by.get(k);
if (!b) { b = { pos: [], nor: [], uv: k.startsWith('mat:') || k.startsWith('img:') ? [] : null }; by.set(k, b); }
const own = k.startsWith('img:') ? (f.color || paint) : null; // the image's placement rides in the paint
for (const tri of faceTris(def, f)) for (const v of tri) {
const p = def.verts.get(v), q = smooth.get(fid + '|' + v) || n;
b.pos.push(p[0], p[1], p[2]); b.nor.push(q[0], q[1], q[2]);
if (b.uv) { const w = own ? imageUV(own, p) : planarUV(p, n); b.uv.push(w[0], w[1]); } // image: its own placement; material: metres along the face
}
}
const meshes = [];
for (const [look, b] of by) {
if (!b.pos.length) continue;
const g = new THREE.BufferGeometry();
g.setAttribute('position', new THREE.Float32BufferAttribute(b.pos, 3));
g.setAttribute('normal', new THREE.Float32BufferAttribute(b.nor, 3));
if (b.uv) g.setAttribute('uv', new THREE.Float32BufferAttribute(b.uv, 2));
meshes.push({ look, geometry: g });
}
const ep = [];
for (const e of def.edges.values()) {
if (e.soft || e.hidden) continue;
const a = def.verts.get(e.a), c = def.verts.get(e.b);
ep.push(a[0], a[1], a[2], c[0], c[1], c[2]);
}
let edges = null;
if (ep.length) { edges = new THREE.BufferGeometry(); edges.setAttribute('position', new THREE.Float32BufferAttribute(ep, 3)); }
return { meshes, edges };
}