Commit Graph

11 Commits

Author SHA1 Message Date
Dion Moult 8ab5c31e75 refactor: merge ifcviewer-wgpu into ifcviewer, drop Wgpu prefix
The GL backend is gone (task #53). The wgpu/non-wgpu folder split and
the Wgpu* class prefix were both disambiguation artefacts from the
overlap period — now pure dead weight.

## Folder + library merge

* `src/ifcviewer-wgpu/`  → folded into `src/ifcviewer/` (git mv tracks
  every file as a rename so blame/log history survives).
* `src/ifcviewer-wgpu-minimal/` → `src/ifcviewer-minimal/` (the exe was
  already named `IfcViewerMinimal`; this just brings the folder + CMake
  target name into line).
* `src/ifcviewer-wgpu/tests/test_wgpu_{selection,visibility}.cpp` →
  `src/ifcviewer/tests/test_{selection,visibility}.cpp`, folded into
  the existing `add_ifcviewer_unit_test(...)` helper.
* The `IfcViewerWgpu` static library is dissolved — its sources become
  part of the unified `IfcViewer` static library, which now bundles
  scene/loader + renderer in one target. The pre-merge circular
  dependency (IfcViewer linking IfcViewerWgpu just to get the
  ViewportWindow.h include path that SceneLoader.h needs) goes away.
* The wgpu-native FetchContent block, the Cocoa/QuartzCore link on
  Apple, the OBJCXX-enabled `.mm` source, and the wgpu-native runtime
  install all move into `src/ifcviewer/CMakeLists.txt` unchanged.

## Type renames (Wgpu prefix dropped from every Wgpu* identifier)

  WgpuAreaMeasurement   → AreaMeasurement
  WgpuBufferPool        → BufferPool
  WgpuLengthMeasurement → LengthMeasurement
  WgpuMetalSurface      → MetalSurface
  WgpuModelGpuData      → ModelGpuData
  WgpuOverlayFrame      → OverlayFrame
  WgpuOverlayRenderer   → OverlayRenderer
  WgpuSectionPlane      → SectionPlane
  WgpuSelectionState    → SelectionState
  WgpuStreamingLoader   → StreamingLoader
  WgpuStreamingThread   → StreamingThread
  WgpuViewportWindow    → ViewportWindow
  WgpuVisibilityState   → VisibilityState

  CMake target IfcViewerWgpuMinimal → IfcViewerMinimal (exe name was
  already this since wgpu shipped as default).

Deliberately kept: `onWgpuLog` (wgpu-native log callback — names a
binding to an external API, not one of *our* types), and the WGPU*
enum/struct prefixes from wgpu-native's own headers. `WgpuMemProbe`
lives in the separate `src/wgpu-mem-probe/` standalone diagnostic
project and isn't touched.

## Include-path updates

Every `#include "../ifcviewer-wgpu/Wgpu<X>.h"` → `"../ifcviewer/<X>.h"`,
every in-directory `#include "Wgpu<X>.h"` → `"<X>.h"`. Includes from
sibling subdirectories (modules/, etc.) are updated to point at
`../../../ifcviewer/` instead of `../../../ifcviewer-wgpu/`.

## cmake/CMakeLists.txt simplification

The redundant `add_subdirectory(ifcviewer-wgpu)` blocks (one inside
the BUILD_BONSAIVIEWER fan-in, one in the BONSAIVIEWER-less standalone
block) collapse into a single unconditional
`add_subdirectory(../src/ifcviewer ifcviewer)`. The standalone block
keeps only `wgpu-mem-probe` (the diagnostic tool, unrelated to the
viewer lib).

## Verification

* Full build green: `IfcViewer` static lib, `IfcViewerMinimal` exe,
  `BonsaiViewer` exe, all four pre-existing ifcviewer unit tests, and
  the two new-location tests (`test_selection`, `test_visibility`).
* No stray `Wgpu<X>` identifier remains across `src/ifcviewer/`,
  `src/bonsaiviewer/`, `src/ifcviewer-minimal/` (verified by grep).
* Renames tracked by git as `R` entries — `git log --follow` on
  ViewportWindow.cpp etc. continues to show history through the move.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 11:11:14 +10:00
Dion Moult 2b91e41fc4 bonsaiviewer: stage all IfcOpenShell dylibs (core + plug-ins) on macOS
The .app bundle's Frameworks/ staging rule was only globbing
ifcopenshell.*.dylib (the dlopen-only plug-ins) on the assumption
that macdeployqt would follow BonsaiViewer's link-time @rpath deps
for the lib-prefixed core shared libs. In practice it doesn't —
non-Qt @rpath deps whose source path is outside the standard system
/ Qt prefixes get skipped silently.

In a static build this didn't matter: libifcopenshell.geometry,
libIfcParse, libIfcViewer, etc. were statically embedded in
BonsaiViewer.exe, so there was no runtime dep. With --shared (added
in ddee88bed for the bundle-size win) they're separate dylibs that
must physically live in Frameworks/, or the binary won't even start:

    dyld: Library not loaded: @rpath/libifcopenshell.geometry.dylib
      Reason: tried '…/BonsaiViewer.app/Contents/Frameworks/
              libifcopenshell.geometry.dylib' (no such file)

Broaden the install(CODE) glob to *.dylib so both flavours land:

  * lib-prefixed linked core libs (libifcopenshell.geometry.dylib,
    libIfcParse.dylib, libIfcViewer.dylib, lib<mapping/kernel>.dylib)
  * non-prefixed plug-ins (ifcopenshell.parse.schema.ifcXxX.dylib,
    ifcopenshell.geometry.mapping.ifcXxX.dylib, etc.)

<prefix>/lib/ is IfcOpenShell-exclusive (Qt / boost / eigen live in
their own brew / build prefixes), so the broad glob doesn't risk
sweeping in unrelated dylibs. The geometry-writer EXCLUDE regex is
preserved so the size win from the writer-skip side of ddee88bed
stays in place.

In a static build the lib/ directory simply has no *.dylib files
that match, so the rule no-ops cleanly — same code path is safe for
both --shared and the (unused but possible) default static config.

Linux didn't need a counterpart: build_rocky.yml already does the
equivalent in workflow bash (`patchelf --set-rpath '$ORIGIN'` +
`stage_runtime_payload`), and local Linux dev uses CMake's
BUILD_RPATH which auto-resolves to the build subdirectories. macOS
.app bundles are treated as opaque by the packaging step (no
stage_runtime_payload against Contents/), so the staging has to
happen at CMake install time when the bundle is being assembled.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 15:29:59 +10:00
Dion Moult ddee88bed3 build_osx: --shared + skip geometry-writer plug-ins (~3x bundle shrink)
Mirrors the Rocky workflow's two-part size reduction (27249770e
"Reduce Rocky package size") on macOS:

1. Pass `--shared` to nix/build-all.py. The default builds
   IfcOpenShell as static libs, which means every plug-in dylib
   (schemas × 8, kernels × 3, mappings × 8, writers × 8, document
   serializers × ~4, linework processing) statically embeds a full
   copy of libIfcParse + libIfcGeom. With --shared the plug-ins
   reference @rpath/libIfcParse.dylib + @rpath/libIfcGeom.dylib and
   the per-plug-in dylib drops from ~30-50 MB to a few MB each.
   Dominant size win.

2. Filter `ifcopenshell.geometry.writer.*.dylib` out of BonsaiViewer's
   plug-in staging step in src/bonsaiviewer/CMakeLists.txt. These are
   the per-schema OBJ / glTF / DAE / STP / IGS / SVG / TTL export
   converters — heavy because each one inlines the full schema, and
   BonsaiViewer is a viewer, never an exporter, so they're pure
   deadweight inside the bundle. Additive on top of --shared.

Bundle went 300 MB → expected ~100 MB, in line with Linux (~100 MB)
and Windows (~80 MB).

The IFCOPENSHELL_BUILD_PYTHON_WRAPPER=off gate is unchanged for now
— once we confirm BonsaiViewer.app size + functionality look sane,
we can ungate the Python wrapper and see if shared-builds-on-macOS
shake out its install issues too.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 08:06:42 +10:00
Dion Moult 37aa68e66c ifcopenshell plug-in loader: stable anchor + bundle-aware fallbacks
# What

Two upstream-shaped fixes to ifcopenshell's runtime plug-in discovery
so the schema/serializer plug-ins are findable in deployment layouts
other than a flat \`<prefix>/lib/\` (specifically: macOS .app bundles).

## (1) Stable anchor variable instead of a function pointer

\`schema_plugin_directory()\` used \`&load_schema_plugins\` as the
anchor whose containing module \`dladdr\` is asked to resolve. Function
addresses are not reliably equal to a single canonical location across
toolchains — on macOS arm64 with BonsaiViewer.app, \`&load_schema_plugins\`
took the address of a PLT/stub inside the consumer binary rather than
the actual symbol inside \`libIfcParse.dylib\`. \`dladdr\` then dutifully
returned the consumer's path and the loader started searching
\`BonsaiViewer.app/Contents/MacOS/\` for plug-ins that were never
installed there.

A variable doesn't suffer from this — it has exactly one canonical
address inside its defining dylib. Add \`ifcopenshell_libifcparse_anchor\`
(exported via IFC_PARSE_API) and use \`&that\` instead. Standard pattern
used by Boost.DLL, GStreamer, \`_dyld_get_image_*\`, etc.

## (2) Bundle-aware fallback search paths

The primary search path is \`dirname(libIfcParse)\`. That works for
flat installs (Linux \`lib/\`, Windows \`bin/\`) where plug-ins are
siblings of libIfcParse. macOS app bundles split the layout:
\`macdeployqt\` puts non-Qt @rpath deps in \`Contents/Frameworks/\`,
Apple convention asks for \`Contents/PlugIns/\`, and some install rules
co-locate libIfcParse with the exe in \`Contents/MacOS/\`. Plug-ins
typically end up in a sibling directory, not the same one.

In \`add_search_paths_or_default\`, after registering the primary path,
also register \`<parent>/PlugIns\`, \`<parent>/Frameworks\`, and
\`<parent>/MacOS\` on Apple platforms. \`discover_exact\` short-circuits
on the first hit so duplicates and missing directories are harmless.

# What this does NOT do

The plug-in dylibs still need to actually be inside the app bundle
somewhere for these fallbacks to find them — the upstream install
rules (\`install(TARGETS …)\` in \`src/ifcparse/CMakeLists.txt\`,
\`src/serializers/CMakeLists.txt\`, etc.) put them in \`<prefix>/lib/\`
which lives outside \`BonsaiViewer.app\`. That side of the fix is a
follow-up — either an explicit bundle-aware install destination on
the plug-in targets, or an install(CODE) sweep that mirrors them
into the bundle.

# What this also un-does

Reverts the BonsaiViewer-only \`install(CODE)\` hack that was about to
copy \`ifcopenshell.*.dylib\` from \`lib/\` into
\`BonsaiViewer.app/Contents/MacOS/\` — superseded by the loader-side
fix above, which lets us put the plug-ins anywhere sane inside the
bundle without further consumer-side stitching.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 21:18:59 +10:00
Dion Moult f66ad22f1d macOS: ship libwgpu_native.dylib into the bundle and set the rpath
BonsaiViewer.app crashed at launch on a fresh macOS arm64 mac with:

    Library not loaded: @rpath/libwgpu_native.dylib
    Referenced from: /Applications/BonsaiViewer.app/Contents/MacOS/BonsaiViewer
    Reason: no LC_RPATH's found

The exe had \`LC_LOAD_DYLIB @rpath/libwgpu_native.dylib\` (CMake baked
that in from the upstream dylib's install_name), but zero \`LC_RPATH\`
entries, so dyld had nowhere to look — and macdeployqt hadn't pulled
the dylib in either, since it sat at \`<install_root>/lib/\` rather
than inside the .app.

Two changes:

- \`src/ifcviewer-wgpu/CMakeLists.txt\`: on macOS, when
  BUILD_BONSAIVIEWER is on, install libwgpu_native.dylib straight
  into \`BonsaiViewer.app/Contents/Frameworks/\` instead of
  \`<prefix>/lib/\`. That matches the standard macOS bundle layout.

- \`src/bonsaiviewer/CMakeLists.txt\`: set
  \`INSTALL_RPATH "@executable_path/../Frameworks"\` on the
  BonsaiViewer target on Apple. That's where dyld looks at launch,
  and where the dylib now lives.

Together the @rpath load resolves at launch without depending on
macdeployqt to follow non-Qt @rpath references (it usually only
chases Qt frameworks).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 17:05:41 +10:00
Dion Moult 938dda80d0 ci: GCC 11 portability + BUNDLE DESTINATION "." on macOS
Linux (Rocky manylinux, GCC 11):

- WgpuAreaMeasurement.h: include <cstddef> directly. GCC 11 does not
  transitively pull `size_t` through <vector>, so triangleCount()'s
  return type fails to parse.

- WgpuViewportWindow.cpp:meshLocalToGlobal: use static_cast<double>(...)
  instead of double(mesh_local[N]) when constructing the Eigen::Vector4d.
  The latter triggers GCC 11's most-vexing-parse: it reads
  `Vector4d local(double(mesh_local[0]), double(mesh_local[1]), ...)`
  as a function declaration of `local` taking parameters
  `double mesh_local[0]` etc., colliding with the outer `mesh_local`
  parameter and failing with "redefinition of double* mesh_local".

macOS arm64:

- IfcViewerWgpuMinimal + BonsaiViewer install rules: change
  `BUNDLE DESTINATION bin` → `BUNDLE DESTINATION .`. Qt's deploy
  generator emits `macdeployqt <Target>.app` with no path prefix, which
  only resolves when the bundle sits at the install-prefix root.
  `BUNDLE DESTINATION bin` put it at `<prefix>/bin/Target.app` and the
  install/strip step failed with "Could not find app bundle".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 07:53:57 +10:00
Dion Moult 45e9f763c8 ci: fix bonsai cross-platform build on Linux, macOS arm64, Windows x64
Bundles four portability fixes uncovered by manually firing the platform
workflows against this branch:

- WgpuOverlayRenderer.cpp: GCC 11 (Rocky manylinux runner) does not parse
  a multi-line raw string inside `#define`. Converted
  THICK_LINE_HELPERS_WGSL from a `#define` to a `static const char*` and
  switched AXIS_WGSL / SECTION_WGSL / MARQUEE_WGSL to `std::string` so
  they can concatenate at static-init time. Three call sites now pass
  `.c_str()` to svFromCStr.

- bonsaiviewer/CMakeLists.txt: added BUNDLE DESTINATION to the install
  rule (same fix already applied to IfcViewerWgpuMinimal). MACOSX_BUNDLE
  targets fail at configure on macOS without it even when nobody runs
  `make install`.

- build_osx.yml: dropped the x64 (Intel cross-compile) matrix row. The
  runner is arm64 so `brew --prefix qt` returns the arm64 prefix; we'd
  need a separate x86_64 Qt install under /usr/local to cross-build
  BonsaiViewer. Revisit if Intel-Mac demand resurfaces.

- build_win.yml: dropped the ARM64 matrix row. wgpu-native does not ship
  a Windows-ARM64 binary, so IfcViewerWgpu's link step fails with ~60
  unresolved wgpu* externs. Re-enable when upstream publishes that
  target.

Cherry-pick this commit to v0.8.0 so the workflow_dispatch buttons see
the dropped rows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 23:13:59 +10:00
Dion Moult 2981500b3b Route bonsai through wgpu; delete the GL backend
Bonsai now drives the wgpu viewport for both sidecar and direct-IFC
loads. The GL viewer and its supporting state classes are gone.

SceneLoader rewire:
- Takes WgpuViewportWindow* instead of ViewportWindow*.
- Sidecar path reads metadata only (readSidecarMetadataOnly) and hands
  the StreamingSidecar off to the new applyCachedModel. Field accesses
  inside applySidecarData go through .meta.
- Direct-IFC path uses the wgpu A-path (upload{Mesh,Instance}Chunk +
  finalizeModel). The applyLodExtension call is dropped — wgpu has no
  live LOD1 splice; LOD1 still lands in the on-disk sidecar for the
  next open.

Bonsai migration:
- ViewportWindow → WgpuViewportWindow across MainWindow, Measurement,
  SessionState, and every modules/*/{Commands,Panel,View}.{h,cpp} —
  116 sites total. Same s/OverlayRenderer::/WgpuOverlayRenderer::/
  rename, 12 sites.
- Includes flipped from ../ifcviewer/ViewportWindow.h to
  ../ifcviewer-wgpu/WgpuViewportWindow.h. OverlayRenderer.h include
  dropped (transitively reached via the viewport header).
- BonsaiViewer links IfcViewerWgpu in addition to IfcViewer for the
  duration of the migration; the GL-side IfcViewer also publicly links
  IfcViewerWgpu so SceneLoader can resolve WgpuViewportWindow.

GL backend deletion:
- src/ifcviewer/ViewportWindow.{cpp,h}, BvhAccel.*, OverlayRenderer.*,
  Selection.*, Visibility.* all gone.
- src/ifcviewer-minimal/ removed entirely (MinimalWindow drove the GL
  viewport).
- src/ifcviewer/tests: test_bvh_accel, test_selection, test_visibility
  removed. The first has no replacement (wgpu doesn't use a per-instance
  BVH); the latter two are ported separately. test_lod_builder,
  test_sidecar_cache, test_instanced_geometry, test_federation remain
  (backend-agnostic).
- IfcViewer's CMakeLists drops OpenGL, Qt::OpenGL, Qt::Widgets — none
  of the surviving translation units reach for them.

Build flag plumbing:
- BUILD_BONSAIVIEWER now auto-enables BUILD_BONSAIVIEWER_WGPU since
  SceneLoader requires the wgpu lib for its WgpuViewportWindow* arg.
- The wgpu subprojects add_subdirectory ahead of the GL one so
  IfcViewerWgpu exists when IfcViewer's link evaluates.
- src/ifcviewer-minimal subdir reference removed from cmake/CMakeLists.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 17:38:33 +10:00
Dion Moult de7520418b Build the Bonsai Viewer in CI with the Autodesk connector bundled
Compile the Bonsai Viewer as part of the Linux and Windows binary builds,
and ship the Autodesk connector alongside the viewer executable.

Qt6 dependencies:
- The viewer links Qt6::Svg for runtime icon tinting. Svg is a separate
  base-Qt archive, so aqt now installs "qtbase qtsvg" (plus icu on Linux)
  rather than qtbase alone, on both Linux and Windows.
- Qt6::CorePrivate is exposed differently across Qt versions: Qt 6.8 ships
  the target inside Qt6Core, while Qt 6.10 provides it only as a separate
  CorePrivate config package. The viewer CMakeLists requests it via
  OPTIONAL_COMPONENTS so it resolves on both.
- When cross-compiling Windows ARM64, windeployqt runs from the host x64
  Qt, so qtsvg is installed into the host Qt as well.

Windows build:
- build-all-win.py passed -DBUILD_IFCVIEWER, a flag since renamed to
  BUILD_BONSAIVIEWER, so the Windows build compiled no viewer at all. It
  now passes -DBUILD_BONSAIVIEWER.
- The Autodesk connector is bundled under connectors/ next to
  BonsaiViewer.exe in the packaged archive, mirroring the Linux builds.
- The Windows workflow builds the connector (PyInstaller) before the main
  build so it is available to bundle.

Connector bundling:
- The Linux rocky workflows build the connector and bundle it into the
  BonsaiViewer archive; the Windows build now does the same.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:34:19 +10:00
Dion Moult 4913b84af7 Add recent projects to Bonsai Viewer
Replace the "Open Recent coming soon" placeholder with a working
most-recently-used project list. RecentProjects persists .ifcfed paths
via QSettings, capped and pruned to existing files. The Open Recent
ribbon button now shows a popup menu of recent projects; every
successful open or save (local or cloud) records an entry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:34:19 +10:00
Dion Moult 59e5b2b1b8 Rename IfcViewerFull to Bonsai Viewer
Directory src/ifcviewer-full -> src/bonsaiviewer, CMake target
IfcViewerFull -> BonsaiViewer, namespace ifcviewerfull -> bonsaiviewer,
QApplication / window titles / connector path now use the new brand.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:34:19 +10:00