ifcviewer-web: contribution-cull streaming + combined loaded/needed/total bar

Streaming picked candidates on raw frustum visibility, so viewAll over a big
federation (all models in-frustum) fetched every chunk — even fine chunks that
project to sub-pixel and the renderer never draws. Gate streaming on the SAME
contribution decision the render path already makes.

- New per-chunk contribution_visible_count: instances that passed frustum AND
  the contribution cull (projected radius >= min_radius_px), counted BEFORE HiZ
  — so it's stable while the camera is still (unlike the HiZ-post counters,
  which flip frame-to-frame and would thrash the loader) and shifts only on
  navigation, when the working set should. The candidate gate skips chunks with
  count 0: the network pulls only what's resolvable now; the rest stream in as
  you approach. Verified: fit view needs the geometry, zoomed-out needs 0.

- Loading UI reworked from per-model segments to a combined bar over the whole
  federation: dark track = not needed for this view, dim = needed-but-unloaded,
  bright = loaded. "loaded / needed" = how done THIS view is; "needed / total" =
  how much of the model the view requires — "Loading 45 / 90 MB for this view ·
  12% of 718 MB total". Driven by ViewportCore::streamingByteProgress + ifcv_bytes_*.
  Two fixes from testing: (1) show a distinct overhead phase while total==0
  ("Loading model data — X MB · Y/N models ready") so the metadata download
  isn't a dead-looking bar; (2) re-assert display:block every active frame so the
  bar REAPPEARS when navigation reveals new chunks (was only set in
  beginLoadProgress → stayed hidden after the first catch-up). Per-model exports
  kept for a future detailed view.

111/111 unit + 6/6 web smoke pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-07-01 16:28:44 +10:00
parent 001476c5a9
commit 5299f6c13c
6 changed files with 141 additions and 51 deletions
+1 -1
View File
@@ -104,7 +104,7 @@ target_link_options(IfcViewerWeb PRIVATE
# EMSCRIPTEN_KEEPALIVE alone keeps the symbols in the binary but doesn't
# add them to Module. ccall lets shell.html pass a JS string (the ?model
# URL) to load_sidecar_from_url_c without manual heap marshalling.
"-sEXPORTED_FUNCTIONS=['_main','_raf_tick_c','_load_sidecar_from_source_c','_clear_scene_c','_ifcv_on_range_done','_ifcv_chunks_resident_c','_ifcv_chunks_total_c','_ifcv_model_count_c','_ifcv_model_resident_c','_ifcv_model_total_c','_view_all_c','_frame_selection_c','_toggle_projection_c','_projection_is_ortho_c','_standard_view_c']"
"-sEXPORTED_FUNCTIONS=['_main','_raf_tick_c','_load_sidecar_from_source_c','_clear_scene_c','_ifcv_on_range_done','_ifcv_chunks_resident_c','_ifcv_chunks_total_c','_ifcv_model_count_c','_ifcv_model_resident_c','_ifcv_model_total_c','_ifcv_bytes_total_c','_ifcv_bytes_needed_c','_ifcv_bytes_loaded_c','_view_all_c','_frame_selection_c','_toggle_projection_c','_projection_is_ortho_c','_standard_view_c']"
# ccall: shell.html passes the ?model URL string to load_sidecar_from_url_c.
# HEAPU8: lets tooling/tests read the wasm heap size (e.g. to verify a large
# sidecar streams by range instead of loading whole). Standard, zero-cost.