Turn High-Energy Physics detector geometry into a scene you can actually fly through.
Detector geometries (ROOT/TGeo, DD4hep, GDML, GeoModel, …) are huge, deeply nested, and built for simulation — not for looking at. nodehammer converts them into a clean, semantically-tagged scene graph and renders it in real time with a physically-based pipeline, natively or straight in the browser.
TGeo / DD4hep ── (GDML/Geant4, GeoModel: planned)
│
▼
Semantic scene graph (.nhb / .nhb.zst)
nodes · shapes · materials · deduplicated transforms
│
┌──────┴──────┐
▼ ▼
glTF / OBJ Render IR (NHR8, FlatBuffers)
│
▼
Interactive viewer
native (Metal / GLCORE / D3D11) · web (WebGPU / GLES3, wasm)
Every stage is driven by declarative TOML config — keep_if/drop_if
selection rules with a small predicate-expression language
(tag.sensitive == "true" && any(path ~= "**/Pixels/**")), material
remapping, and analytical boolean cuts — so you can go from "the whole
detector" to "just the Pixel barrel" without touching code.
nodehammer is under active, pre-1.0 development. Here's what's real today versus what's on the roadmap.
-
Importers: ROOT/TGeo and DD4hep, each independently switchable at
build time (
NODEHAMMER_WITH_TGEO,NODEHAMMER_WITH_DD4HEP). -
Exporters: glTF/GLB and OBJ (render IR), plus the native
.nhb/.nhb.zstFlatBuffers semantic-scene container — a documented, zstd-compressible on-disk format. - Analytical geometry surgery: semantic-level azimuthal wedge cuts and boolean operations via manifold, correct on non-manifold swept primitives, cooperative with UI progress.
-
CLI:
convert,inspect,config,project,viewer. -
Viewer rendering pipeline, live today:
- Full offscreen HDR pipeline with ACES / Reinhard / AgX tonemapping and exposure control
- GTAO screen-space ambient occlusion, with selectable quality presets
- Nishita single-scattering atmospheric sky (real Rayleigh + Mie, a real sun disc) baked into the IBL pipeline, plus a visible background dome
- FXAA 3.11 console-quality antialiasing
- Dynamic, GPU-load-adaptive render scale with a power-saving idle profile — renders on demand and caps the idle frame rate
- Live ImPlot performance graphs in the debug panel
- High-resolution, supersampled PNG screenshot export
(
nodehammer viewer shot -o out.png ...) — verified on Metal; the WebGL2/GLES3 and WebGPU readback paths compile and link but are not yet verified in-browser
-
Builds on macOS, Linux, and Windows natively (Metal, GLCORE, and D3D11
respectively), plus a wasm build targeting both GLES3 and WebGPU from one
configure, with the browser shell auto-picking the right bundle via
navigator.gpu. All five configurations build and pass the (headless) test suite in CI. A headless compute-worker wasm module (nodehammer-compute) runs tessellation and boolean cuts off the main thread. macOS is the primary hand-verified target for the interactive viewer itself — see below for what's untested elsewhere. -
Modern C++: C++23 throughout (
<print>, the works); requires GCC ≥ 14, Clang ≥ 18/libc++, or MSVC ≥ 19.37.
-
GDML/Geant4 importer — CMake option and library wiring exist
(
NODEHAMMER_WITH_GEANT4); the actual importer isn't written yet. - GeoModel importer — CMake option declared only; no implementation.
-
Windows viewer verification — the D3D11 backend builds and passes CI's
headless tests, but the maintainer has no Windows machine to manually
verify interactive rendering; the PNG-export readback for D3D11 is a
build-time
#errorstub for now. -
Bloom and IBL-quality levels — UI controls exist and are wired
through
RenderQualitySettings, but both are no-ops for now. - Project save/export system — the archive-as-project model below is landing now (working set, IndexedDB persistence, sidecar viewer mode, layered steer, Publish package); in-viewer editor windows and a named multi-document switcher are still to come. See docs/viewer-project-strategy.md.
-
Single-instance enforcement + a real macOS
.appbundle — designed, not implemented; see docs/viewer-single-instance.md.
One
ZipWorkingSetis both the thing you author (edit / curate / drop files into) and the thing you publish (serialize → host → share). Authoring and publishing are the same object flowing in two directions.
A nodehammer project is a .nhproj archive: a ZIP container holding the
config, geometry, and materials for a scene, plus an optional root
nodehammer.toml that makes it self-describing. The same archive opens
natively, in the browser, and behind a published link.
| Term | Meaning |
|---|---|
| Working set | the live, editable in-memory project (ZipWorkingSet) — what the viewer edits |
| Archive | a serialized .nhproj of a working set — the portable, publishable unit |
| Project manifest | root nodehammer.toml inside the archive: [project] entry keys + [view] initial steer |
| Sidecar |
nh_manifest.json next to index.html — points at archive(s), carries deployment presentation (lock, steer overrides) |
| Steer | view-state (camera, angle cut, rotation, toggles) — the ephemeral per-link layer, committed to the URL query |
| Provenance |
Empty | Local(name) | Remote(url) — where the working set came from; drives persistence and posture |
| Package | the self-contained static folder emitted by Publish — drop it on any static host, zero server code |
The web build has two postures from one wasm binary, branched on whether a sidecar is present:
- Application mode (no sidecar) — empty start, editable, native-like; the working set auto-persists to IndexedDB and restores on reload. Your document.
- Viewer mode (sidecar present) — fetches the archive, content locked, re-fetched from source on reload. A publication.
Content-lock is a deployment property of the sidecar; steer is never frozen,
so a shared link keeps camera and cuts live. The only viewer→app bridge is
explicit: save the .nhproj and open it in application mode.
Full design of record, including backend mapping and mode transitions: docs/viewer-project-strategy.md.
nodehammer convert # geometry in, anything out — the pipeline
nodehammer inspect # summary | tree | tags — ask about a scene
nodehammer config # validate | flatten — TOML and Lua configs
nodehammer project # pack | publish | info — .nhproj archives
nodehammer viewer # open | serve | shot | bench — the app
Four nouns and one verb. The design of record, including the vocabulary and why each old name went, is docs/cli-design.md.
convert spans the whole pipeline, and the output format decides how far it
runs: -o scene.nhb stops at the semantic scene, -o scene.glb tessellates
first, and asking for both in one command is one pass rather than two.
Tessellation is lossy by construction — solids become triangles — so .nhb is
the faithful stop and .glb is a one-way door.
nodehammer project pack -c scene.toml -i odd.xml -o odd.nhproj
nodehammer viewer odd.nhproj serve # open it in a browser
nodehammer project publish odd.nhproj -o site/Dependencies are managed with Conan; builds with CMake + Ninja.
just recipes # export vendored recipes (sokol-shdc)
just deps # conan install (viewer=True)
just configure # cmake --preset conan-relwithdebinfo
just build
just testOr drive conan/cmake directly — see the Justfile for the
exact flags, including the Emscripten/wasm targets (just wasm-deps,
requires emsdk) and the
spack-based dev configuration (configure-full) that turns on the TGeo and
DD4hep importers together.
Key CMake options:
| Option | Purpose | Status |
|---|---|---|
NODEHAMMER_WITH_VIEWER |
Build the sokol/Dear ImGui interactive viewer | ✅ |
NODEHAMMER_WITH_TGEO |
ROOT/TGeo importer | ✅ |
NODEHAMMER_WITH_DD4HEP |
DD4hep importer | ✅ |
NODEHAMMER_WITH_GEANT4 |
GDML/Geant4 importer | 🚧 links Geant4 only, no importer yet |
NODEHAMMER_WITH_GEOMODEL |
GeoModel importer | 🚧 declared only |
NODEHAMMER_BUILD_TESTS |
Build the Catch2 unit-test binary | ✅ |
NODEHAMMER_BUILD_SHARED |
Also build and install libnodehammer + the public headers + a CMake package config |
🚧 packaging works; the API it exposes is still being written |
With NODEHAMMER_BUILD_SHARED=ON, cmake --install adds the shared library,
include/nodehammer/, and a package config, so a consumer needs only:
find_package(nodehammer REQUIRED)
target_link_libraries(app PRIVATE nodehammer::nodehammer)No find_dependency for zstd, flatbuffers, manifold or the rest — the shared
library absorbs them. ci/shared-build.sh runs the whole thing end to end and,
on ELF, checks that nothing third-party or internal escaped into the export
table. CI runs it on every native platform.
A consumer needs only C++20. nodehammer itself is built as C++23, but the
installed headers do not require it, and the exported target asks for no more
than cxx_std_20. ci/shared_consumer compiles at exactly that floor, so a
public header reaching past it fails there rather than in your build.
On Windows the library and its consumer must agree on the C runtime (/MD vs
/MT) and on _ITERATOR_DEBUG_LEVEL — so a Debug consumer needs a Debug
nodehammer. This is not specific to nodehammer; it is what passing std::
types across a DLL boundary requires, and mismatches usually surface as heap
corruption rather than as a link error.
The repo ships a real detector fixture — the Open Data Detector — ready to convert:
just odd # full ODD → glb (see the Justfile for single-stave recipes too)
build/RelWithDebInfo/nodehammer viewer openEvery push to main publishes the wasm viewer to
https://paulgessinger.github.io/nodehammer/, in both of the postures the
web build supports:
/app/ |
the open application — empty start; drop a .nhproj or loose config/geometry files onto it |
/odd/ |
the full Open Data Detector, preconfigured and content-locked |
/odd-simple/ |
the simplified ODD — same, lighter to build |
Each directory is a self-contained static package of the kind the viewer's
Publish command emits. Reproduce the whole site locally with
just pages-site after just wasm-release.
src/ core pipeline: cli, config, ir (intermediate representation),
scene building, tessellation, selection, the viewer, web glue
shaders/ sokol-shdc GLSL sources for the full render pipeline
schemas/ FlatBuffers schemas (render.fbs, semantic.fbs)
profiles/ Conan profiles (Emscripten cross-compilation)
recipes/ vendored/patched Conan recipes (sokol, sokol-shdc, imgui, implot, nfd)
fixtures/ sample detector geometries and configs
docs/ format specs and design docs (nhb format, predicate expressions,
orbit navigation, rendering-fidelity strategy, PNG export)
web/ browser viewer entry point
MIT © Paul Gessinger — see LICENSE. Third-party license texts for vendored/bundled assets live under LICENSES/.
