mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-23 12:22:37 +00:00
web: MODULARIZE build + embedded JS-integration example + selection callback
Restructure the web viewer so the wasm is a reusable module and add a second example that drives it from ordinary page DOM. Build: - Emit IfcViewerWeb.js (a `createIfcViewer` factory, MODULARIZE) + .wasm instead of a single baked page (dropped --shell-file); copy the static example pages next to it at build time. - Unbreak the web build: CameraMath.h / ViewportCore.cpp used boost::math::constants::pi just for pi, pulling all of boost/math into a header shared with the Emscripten build (no Boost in its sysroot). Replace with a constexpr kPiF — identical value, no dependency, desktop unaffected. JS integration (web/ifcviewer.js): - A small helper wraps the factory: boots the viewer on a canvas, runs the RAF loop from onRuntimeInitialized (NOT a post-await .then, which stalls Dawn-web's device callback and leaves the device half-initialised), and exposes addFile/addUrl, clearScene, model list/progress, and onSelect(...). - ViewportCore/main_web emit each pick to JS via Module.__ifcvOnSelect (object id + IFC GlobalId + model index; empty on deselect); onSelect also dispatches an 'ifcviewer:select' DOM event. - Fix input coords for a non-fullscreen canvas: mousemove/mouseup are window-targeted, so convert their coords to canvas-relative via the canvas client-rect origin (marquee + box-pick were offset when embedded). Examples: - IfcViewerWeb.html: the fullscreen viewer (same DOM/behaviour as before, now loading the module) — the Playwright smoke suite still targets it. - embedded.html: a sized viewer with DOM outside it to add models (file or URL), list loaded models with streaming progress, and show the model + GlobalId of the clicked object. Starts empty (drops the wasm's embedded sample, which the fullscreen page/tests still use). - index.html links both. Federation note: the web viewer already streams multiple models into one scene (a byte-source per file/URL); it doesn't need the desktop Federation document for this. Verified: 11/11 web smoke tests pass; embedded example loads models, reports the picked model + GUID, and the marquee aligns. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -89,26 +89,32 @@ target_link_options(IfcViewerWeb PRIVATE
|
||||
# also instrument every function reachable from emscripten_sleep,
|
||||
# adding ~30% to wasm size for no win here.
|
||||
#
|
||||
# EXIT_RUNTIME=0 + Module.noExitRuntime=true (set in shell.html)
|
||||
# EXIT_RUNTIME=0 + Module.noExitRuntime=true (set in the host page (web/ifcviewer.js))
|
||||
# keeps wasm alive after main() returns so Dawn-web's
|
||||
# RequestAdapter/RequestDevice promise callbacks land. The
|
||||
# alternative — calling emscripten_set_main_loop_arg early in
|
||||
# main() to set noExitRuntime as a side effect — registers a RAF
|
||||
# that starves the device promise (observed: ~10s delay in Firefox).
|
||||
"-sEXIT_RUNTIME=0"
|
||||
# Emit a reusable module factory (IfcViewerWeb.js) instead of a baked page,
|
||||
# so multiple static example pages can load the same wasm. Each page does
|
||||
# createIfcViewer({ canvas, ... }).then(Module => …)
|
||||
# (see web/ifcviewer.js, which wraps this into a small integration API).
|
||||
"-sMODULARIZE=1"
|
||||
"-sEXPORT_NAME=createIfcViewer"
|
||||
# Expose the C entry points to JS. _raf_tick_c drives the RAF loop
|
||||
# (shell.html); _load_sidecar_from_blob_c loads a user-picked File via
|
||||
# (the host page (web/ifcviewer.js)); _load_sidecar_from_blob_c loads a user-picked File via
|
||||
# byte-range Blob.slice reads; _load_sidecar_from_url_c streams a remote
|
||||
# sidecar via HTTP Range; _ifcv_on_range_done / _ifcv_source_ready are the
|
||||
# JS→C completion callbacks for a landed range / a resolved URL size.
|
||||
# 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
|
||||
# add them to Module. ccall lets the host page (web/ifcviewer.js) 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','_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','_toggle_fly_c','_fly_is_active_c','_hide_selected_c','_isolate_selected_c','_show_all_c','_toggle_xray_c','_xray_is_active_c','_toggle_section_c','_clear_section_c','_section_is_active_c']"
|
||||
# ccall: shell.html passes the ?model URL string to load_sidecar_from_url_c.
|
||||
# ccall: the host page (web/ifcviewer.js) 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.
|
||||
"-sEXPORTED_RUNTIME_METHODS=['ccall','HEAPU8']"
|
||||
"-sEXPORTED_RUNTIME_METHODS=['ccall','HEAPU8','UTF8ToString']"
|
||||
# Streaming + chunked geometry want a heap that can grow as buffers
|
||||
# arrive. 256 MB initial, 2 GB ceiling (matches the wasm32 pointer
|
||||
# cap; --shared64 / MEMORY64 would lift this later if we need it).
|
||||
@@ -124,7 +130,22 @@ target_link_options(IfcViewerWeb PRIVATE
|
||||
# mounts the file at the virtual path the wasm fopen()s. User-picked
|
||||
# files instead stream via Blob.slice byte ranges (load_sidecar_from_blob_c).
|
||||
"--embed-file=${CMAKE_CURRENT_SOURCE_DIR}/sample.ifcview@/sample.ifcview"
|
||||
# Shell template wraps the JS output in our canvas page.
|
||||
"--shell-file=${CMAKE_CURRENT_SOURCE_DIR}/shell.html"
|
||||
)
|
||||
set_target_properties(IfcViewerWeb PROPERTIES SUFFIX ".html")
|
||||
# MODULARIZE emits IfcViewerWeb.js (the createIfcViewer factory) + .wasm.
|
||||
set_target_properties(IfcViewerWeb PROPERTIES SUFFIX ".js")
|
||||
|
||||
# Copy the static example pages + the JS integration helper next to the wasm so
|
||||
# a plain `python3 -m http.server --directory build-web` serves the whole demo:
|
||||
# /IfcViewerWeb.html fullscreen example
|
||||
# /embedded.html embedded viewer + DOM model list / selection (JS API)
|
||||
# /index.html links to both
|
||||
set(IFCVIEWERWEB_STATIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/web/ifcviewer.js"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/web/IfcViewerWeb.html"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/web/embedded.html"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/web/index.html"
|
||||
)
|
||||
add_custom_command(TARGET IfcViewerWeb POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${IFCVIEWERWEB_STATIC} "$<TARGET_FILE_DIR:IfcViewerWeb>"
|
||||
COMMENT "Copying web example pages next to IfcViewerWeb.js")
|
||||
|
||||
Reference in New Issue
Block a user