Draws the real-time 3D view, bakes its bounce light, times the path tracers and writes the standalone viewer's scene.
How the precision is kept
These files produce pictures. Radiance is accumulated in 64-bit by the reference tracer and handed on as Float32Array; probe light is stored as half floats on the GPU; the viewer file carries 32-bit geometry buffers. None of these values is a dimension or a quantity of the model.
Calculations under this policy
4 calculations in 4 source files. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
Positions, normals and texture coordinates are converted with Float32Array.from before they are written as base64. Placement matrices, views and bounds are written as plain numbers. The file feeds the viewer's renderer.
rt/rt-export.js
lines 31 to 68
BimCraft 0.8.1
export async function exportScene({ rt, plain, settings = {}, views = [], title = '', words = {}, maxScatter = 250000 }) {
const geos = new Map(), geometries = [];
const items = [];
for (const it of plain.items) {
if (!it.geometry) continue;
let gi = geos.get(it.geometry);
if (gi === undefined) {
const g = it.geometry, P = g.getAttribute('position'), N = g.getAttribute('normal'), U = g.getAttribute('uv');
geometries.push({ position: toB64(Float32Array.from(P.array)), normal: N ? toB64(Float32Array.from(N.array)) : null, uv: U ? toB64(Float32Array.from(U.array)) : null, index: g.index ? toB64(Uint32Array.from(g.index.array)) : null });
gi = geometries.length - 1;
geos.set(it.geometry, gi);
}
items.push({ g: gi, m: it.matrix ? Array.from(it.matrix) : null, look: it.look || 'default', lines: !!it.lines });
}
const resolved = {};
for (const k of new Set(['__ground', 'default', ...items.filter((x) => !x.lines).map((x) => x.look)])) resolved[k] = await resolveMaterial(k, settings, plain);
let left = maxScatter;
const scatter = [], used = new Set();
for (const s of plain.scatter || []) {
const n = Math.min(s.result.count, Math.max(0, left));
if (!n) continue;
left -= n;
for (let i = 0; i < n; i++) used.add(s.slugs[s.result.item[i]]);
scatter.push({ count: n, matrices: toB64(s.result.matrices.subarray(0, n * 16)), item: toB64(s.result.item.subarray(0, n)), slugs: s.slugs });
}
const scatterLibrary = (plain.scatterLibrary || []).filter((e) => used.has(e.slug)).map((e) => ({ slug: e.slug, parts: e.parts.map((p) => ({ color: p.color, alpha: p.alpha ?? 1, P: toB64(Float32Array.from(p.P)), I: toB64(Uint32Array.from(p.I)) })) }));
const g = rt.giProbes && rt.giProbes.data;
const cam = rt.camera;
return {
format: 'cadcraft-live-scene', version: VERSION, title, words,
up: plain.up, bounds: plain.bounds, groundAt: plain.groundAt ?? 0, sun: plain.sun || null, clip: plain.clip || [], defaultColor: plain.defaultColor,
settings: { sky: settings.sky, ground: settings.ground, edges: settings.edges, exposure: settings.exposure, exposureMode: settings.exposureMode, fstop: settings.fstop, shutter: settings.shutter, iso: settings.iso, whiteBalance: settings.whiteBalance, post: settings.post },
geometries, items, resolved, lights: plain.lights || [], scatter, scatterLibrary,
gi: g ? { min: g.min, max: g.max, res: g.res, sh: toB64(g.sh) } : null,
views: views.map((v) => ({ name: v.name, world: Array.from(v.world), proj: Array.from(v.proj) })),
start: { world: cam.matrixWorld.elements.slice(), proj: cam.projectionMatrix.elements.slice() },
};
}
In detail
Method
Tone curve after John Hable (the file's comment cites GDC 2010) with fixed constants; probe light as twelve coefficients per probe with the sky's part subtracted; the mean of path-traced samples per pixel.
Number types
Float64Array accumulators in the reference tracer; Float32Array and half-float textures for everything sent to the GPU or written to the viewer file.
Approach: Display only
Computed to draw the screen, often in 32-bit on the graphics processor. No number a person reads, saves or exports comes from it.