Models now resolve to global coordinates, which alone would make things worse:
composed per-instance transforms are float32, and around six million metres
that quantises at roughly half a metre. So the first model to load also sets a
false origin, derived from where its geometry actually sits, unless a host has
set one itself.
WebFederation owns the concepts an .ifcfed carries — a federation unit, a false
origin, a per-model transform and display name — without the file format. The
desktop Federation class is a document model whose sources are local filesystem
paths, which mean nothing in a browser; a host page that wants .ifcfed can parse
the JSON and drive these calls.
Models are keyed by the JS source id rather than the session model id. The
source id exists the moment a File or URL is registered, whereas the session id
is minted inside the async range-read chain, so keying on it lets a transform be
set before the model has streamed and applied when it arrives — the model never
visibly jumps. loadSidecarMetadataWeb gained a completion callback to carry that
id back out, and addFile/addUrl now return the source id and fire onModelLoaded,
where before they were fire-and-forget with no handle and no completion signal.
The embedded sample bypasses the source registry, so it is bound separately;
otherwise the guess never runs for a page that only ever shows the sample.
georef-a and georef-b are the regression fixture: two boxes whose different map
conversions resolve to the same real-world point, so a viewer that applies them
draws one box's worth of scene and one that ignores them spans 707 m. They carry
two meshes each because reorderSidecarByMorton bails out below two and then
writes no chunk table, and a sidecar without one cannot stream over byte ranges.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>