diff --git a/src/ifcviewer-web/CMakeLists.txt b/src/ifcviewer-web/CMakeLists.txt index b48348c387..f3eecfb032 100644 --- a/src/ifcviewer-web/CMakeLists.txt +++ b/src/ifcviewer-web/CMakeLists.txt @@ -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_blob_c','_load_sidecar_from_url_c','_ifcv_on_range_done','_ifcv_source_ready','_ifcv_chunks_resident_c','_ifcv_chunks_total_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']" # 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. diff --git a/src/ifcviewer-web/main_web.cpp b/src/ifcviewer-web/main_web.cpp index 031ceb6803..b1a1b7f029 100644 --- a/src/ifcviewer-web/main_web.cpp +++ b/src/ifcviewer-web/main_web.cpp @@ -191,32 +191,23 @@ extern "C" EMSCRIPTEN_KEEPALIVE void raf_tick_c(void* user) { } } -// Called from shell.html's file-browse handler after it has stashed the -// picked File on Module.__ifcvFile. Replaces whatever is currently loaded -// (the embedded sample on first use, or a prior pick) with the new sidecar. -// Byte-range (#88): the whole file is NOT copied into the wasm heap — the -// metadata is read via Blob.slice and chunk bytes stream per-chunk, so a -// 500 MB sidecar stays in the browser File object. Asynchronous: this -// returns immediately and the model frames itself from the JS completion -// callback. Exported to JS via EXPORTED_FUNCTIONS in CMakeLists.txt. -extern "C" EMSCRIPTEN_KEEPALIVE void load_sidecar_from_blob_c() { +// Stream a sidecar from a registered JS byte-source and APPEND it to the scene +// (federation). shell.html registers the source first — a picked File or a +// remote URL, sized up front — into Module.__ifcvSources[source_id], then calls +// this. Byte-range: the file is never copied whole into the wasm heap; metadata +// is read via ranges and chunks stream per-chunk, so a 500 MB sidecar stays in +// the File / on the server. Asynchronous; the model frames itself from the JS +// completion callback. Call clear_scene_c first to replace instead of append. +extern "C" EMSCRIPTEN_KEEPALIVE void load_sidecar_from_source_c(int source_id) { if (!g_app || !g_app->ready) return; - - // resetScene drops the previous model's GPU resources so a fresh load - // replaces rather than accumulates (loadSidecar* appends). - g_app->core.resetScene(); - g_app->core.loadSidecarFromBlobWeb(); + g_app->core.loadSidecarMetadataWeb(source_id, "source"); } -// Called from shell.html (e.g. a ?model=URL query param) to stream a sidecar -// hosted at `url` via HTTP Range requests — the same per-chunk byte-range path -// as the local File load, but the bytes come off the network instead of a -// Blob. Asynchronous; the model frames itself once metadata lands. Exported -// to JS via EXPORTED_FUNCTIONS in CMakeLists.txt. -extern "C" EMSCRIPTEN_KEEPALIVE void load_sidecar_from_url_c(const char* url) { - if (!g_app || !g_app->ready || !url) return; +// Drop all loaded models (used by shell.html to replace the embedded sample / +// a prior federation before loading a fresh set). +extern "C" EMSCRIPTEN_KEEPALIVE void clear_scene_c() { + if (!g_app || !g_app->ready) return; g_app->core.resetScene(); - g_app->core.loadSidecarFromUrlWeb(url); } // Streaming progress for the loading bar (shell.html polls these each frame). diff --git a/src/ifcviewer-web/shell.html b/src/ifcviewer-web/shell.html index 8254d22b15..fc1d14343a 100644 --- a/src/ifcviewer-web/shell.html +++ b/src/ifcviewer-web/shell.html @@ -21,10 +21,13 @@ #status.error { background: rgba(120,30,30,.85); color: #fff; } /* Errors re-expand and re-opaque even after the ready-collapse. */ #status.ready.error { max-height: 28vh; opacity: 1; } - #open-btn { position: fixed; top: 8px; right: 12px; z-index: 10; + #open-btn, #add-btn { position: fixed; top: 8px; z-index: 10; background: #2b6cb0; color: #fff; border: none; padding: 6px 12px; border-radius: 4px; font-size: 12px; cursor: pointer; } + #open-btn { right: 12px; } + #add-btn { right: 120px; background: #2d3748; } #open-btn:hover { background: #3182ce; } + #add-btn:hover { background: #3b465c; } #file-input { display: none; } /* Streaming loading bar: a thin top progress strip + a centred caption. Shown only while a network/file model streams; hidden once resident. */ @@ -42,8 +45,9 @@
+ - +
Starting…