Files
IfcOpenShell/src
Dion Moult 126d2d4c06 wgpu: spatial instance bucketing for streaming (env-gated prototype)
WGPU_SPATIAL_BUCKETS=1 swaps the applyCachedModelStreaming planner from
mesh-keyed Morton+greedy to octree-style instance bucketing. Default
behaviour unchanged (env var unset → mesh-keyed planner runs).

Phase 1 of #55 / #56. The mesh-keyed planner produces chunks whose
AABBs are the union of all instances of the chunk's meshes — for
heavily-deduplicated IFC meshes (a "standard floor tile" used 800
times across a federation) the mesh's "centroid" is a mean of scattered
instance positions and the chunk's AABB ends up spanning the entire
model. Symptom: chunk-level frustum cull rarely fires (AABB always
intersects view), and the screen-area priority metric under-rates
big-AABB chunks because their corners straddle the near plane. Visible
objects pop in/out as the camera tilts, even though they're fully on
screen.

The spatial planner bucketises INSTANCES directly. Each leaf bucket
contains its instance list + the unique mesh data those instances
reference. A mesh whose instances scatter into multiple buckets gets
its vertex/index data uploaded into multiple pool slices — duplication
is the cost for tight bucket AABBs. For IFC this is acceptable:
heavily-shared meshes tend to be small (fittings, fasteners), so
per-bucket duplication adds tens-of-MB not GB.

Octree implementation (planSpatialChunks):
  - work-stack subdivision: for each (instance subset, AABB), split into
    8 octants around centre and recurse
  - stop conditions: bucket fits WGPU_CHUNK_VERTEX_BYTES_LIMIT for
    union vertex bytes AND ≤ spatial_max_instances_ instances; OR single
    instance left; OR every instance falls into the same octant
    (pathological — emit as leaf rather than infinite recurse)
  - spatial_max_instances_ default 5000, overridable via
    WGPU_SPATIAL_BUCKET_MAX_INSTS env var so the prototype can be
    swept without rebuilding

Data-model adjustment beyond what dc2927997 prepared:
  - Per-chunk per-mesh chunk-local offset table (chunk_mesh_offsets)
    built during the chunk-construction loop. The mesh-keyed per-mesh
    global arrays (mesh_chunk_idx etc.) still get populated for
    legacy reads, but under spatial bucketing they're overwritten when
    the same mesh appears in multiple chunks — harmless because cull
    reads the per-instance arrays exclusively (per dc2927997).
  - Post-construction, per-instance arrays are populated from
    chunk_mesh_offsets via (instance_to_chunk[i], inst.mesh_id) lookup.
    Mesh-keyed planner derives identical values to before
    (pixel-identical); spatial planner now writes the correct
    per-bucket offsets even when the mesh appears in multiple chunks.

basic.ifc parity on all three paths confirmed (non-streaming
mesh-keyed, streaming mesh-keyed, streaming spatial all produce 0
pixel diff vs the reference). Spatial planner produced 1 bucket on
basic.ifc (3 instances, well under thresholds) as expected.

Non-streaming applyCachedModel left unchanged — the prototype targets
the streaming path which is where the federation-scale missing-objects
issue lives.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 16:19:56 +10:00
..
2026-04-07 09:46:45 +02:00
2026-05-09 21:03:55 +02:00
2026-05-09 21:03:55 +02:00
2026-05-08 10:56:41 +02:00
2026-05-10 17:01:11 +02:00
2026-03-31 20:51:46 +02:00