feat(dart): physics_basics example (reactphysics3d_dart + thermion) - #239
Open
nmfisher wants to merge 6 commits into
Open
feat(dart): physics_basics example (reactphysics3d_dart + thermion)#239nmfisher wants to merge 6 commits into
nmfisher wants to merge 6 commits into
Conversation
Scope (a) of the-tvf4: native/headless path only. - New physics_basics example in examples_lib: boxes and spheres dropped into a walled pen, simulated with reactphysics3d_dart, rendered with thermion_dart. Fixed-step settle loop (no wall-clock), transforms copied to Thermion assets so the headless runner captures one deterministic frame. - Registered in the example registry (headless CLI runner). Not wired into the web gallery: reactphysics3d_dart pins ffigen_js 0.0.5-pre, which conflicts with thermion_dart's ^0.0.14-pre, so the packages only resolve together via dependency_overrides (added to examples_lib, headless_runner and web_gallery roots, where pub honours them). - Ubershader material instances created for body colours are destroyed on viewer dispose; leaving them alive deadlocks FilamentApp.destroy(). - Ignore headless_runner output PNGs. Co-Authored-By: Claude <noreply@anthropic.com>
nmfisher
force-pushed
the
asb/dart-physics-sample-clean
branch
from
August 19, 2026 04:59
866eab8 to
470e44c
Compare
Research notes for PR #239's blocked web path: exact ffigen_js version conflict (0.0.5-pre exact pin vs ^0.0.14-pre, with the 0.0.5-generated bindings being runtime-incompatible with the 0.0.14 runtime), the single NativeLibrary.instance global in ffigen_js that prevents two WASM modules per page, the unfinished reactphysics3d_dart web build, and thermion's existing EXTERNAL_PROJECTS whole-archive mechanism as the recommended path to one combined WASM. Co-Authored-By: Claude <noreply@anthropic.com>
Wire the existing EXTERNAL_PROJECTS hook in thermion_dart/native/web/
CMakeLists.txt into `make wasm`, so reactphysics3d is linked into the single
thermion_dart WASM instead of being a second module.
- scripts/build_reactphysics3d_web.sh stages everything the hook needs: a
reactphysics3d_dart checkout at a pinned ref (52eec72e, the same ref
examples_lib depends on) and an Emscripten build of libreactphysics3d.a
dropped next to reactphysics3d_dart.cmake, which imports it as a prebuilt
library. The engine sources are not vendored by reactphysics3d_dart (only
its headers are), so they are fetched from upstream v0.10.2 -- verified
byte-identical to that tag's include/ tree, which is what the C API wrapper
compiles against. Cached under native/web/lib/external/, which survives
`make wasm-clean` and is gitignored/pubignored like the Filament web libs.
- scripts/verify_web_wasm_exports.sh gates the build: the link runs with
-sERROR_ON_UNDEFINED_SYMBOLS=0 and pulls external projects in through
-Wl,--whole-archive, so a misconfigured build still links and only fails in
the browser. It checks the JS glue's Module["_name"]=wasmExports["..."]
assignments (Emscripten minifies the wasm export names themselves).
- `make wasm` now stages rp3d, configures with -DEXTERNAL_PROJECTS=..., and
verifies the exports afterwards. Set EXTERNAL_PROJECTS_CMAKE= to build
without physics.
- analysis_options: the staged third-party sources under
native/web/lib/external/ do not resolve as a package; exclude them.
One module means NativeLibrary.initBindings("thermion_dart") serves both
packages on the web, and the _rp3d_* C API is exported from the same Module
object as the _Thermion_* one -- no Dart-side changes and no ffigen_js work.
Design: docs/research/web-physics-scope.md (Option C).
Verified locally with emsdk 6.0.4: 142 _rp3d_* exports on the Module object
(rp3d_c_api.cpp has 142 EMSCRIPTEN_KEEPALIVE functions), and a
create-world/step/destroy cycle runs against the built wasm in node.
thermion_dart.wasm grows 3.43MB -> 3.99MB.
Co-Authored-By: Claude <noreply@anthropic.com>
The web job already runs `make wasm`, which now links ReactPhysics3D in and verifies the _rp3d_* exports. Two adjustments so that actually happens on this change set: - paths-filter: the Makefile and the two scripts it calls decide what goes into the shipped thermion_dart.wasm, so they must trigger the web build the same way thermion_dart/native/** does. - Cache the staged ReactPhysics3D sources and the Emscripten build of libreactphysics3d.a (native/web/lib/external/), keyed on the staging script so a ref bump in it rebuilds. The zip this job uploads to R2 (and the web.version bump that points the download tool at it) therefore carries rp3d from the next merge to develop onwards. Co-Authored-By: Claude <noreply@anthropic.com>
Register the scene as 'physics' in galleryScenes. With ReactPhysics3D linked
into the single thermion_dart WASM, the module that the gallery's existing
NativeLibrary.initBindings("thermion_dart") call points at exports the
_rp3d_* C API alongside the _Thermion_* one, so no loader or init changes are
needed -- the gallery's scene list is generated from galleryScenes.keys.
The ffigen_js dependency_overrides in the three example pubspecs stay for now:
reactphysics3d_dart at the pinned ref (52eec72e) still pins ffigen_js
0.0.5-pre, and removing them makes `dart pub get` fail with
Because every version of reactphysics3d_dart from git depends on
ffigen_js 0.0.5-pre and every version of thermion_dart from path depends on
ffigen_js ^0.0.14-pre, reactphysics3d_dart from git is incompatible ...
The comments on them are updated to say what actually removes them: bump the
git ref once reactphysics3d_dart regenerates its web bindings under 0.0.14,
then delete the three overrides.
Known limitation while that ref is pinned: `dart compile wasm` of the gallery
fails inside reactphysics3d_dart itself (0.0.5-era Pointer operator[] /
missing bindings / a direct dart:ffi import), so the physics scene is not
servable until the ref bump. This is not caused by the gallery registration --
examples_lib already exports physics_basics.dart, so the import chain from
web_gallery pulls those files in regardless.
Co-Authored-By: Claude <noreply@anthropic.com>
reactphysics3d_dart now publishes prebuilt libraries from its own "Build artifacts" workflow (v* tag pushes), so thermion no longer needs to compile ReactPhysics3D itself. Replace scripts/build_reactphysics3d_web.sh with scripts/download_reactphysics3d.sh, which stages what the EXTERNAL_PROJECTS hook needs from the pinned release (v0.1.0): - a reactphysics3d_dart checkout at that tag, providing native/web/reactphysics3d_dart.cmake, the C API wrapper it compiles from source, and the headers, and - libreactphysics3d.a (engine, wasm32) from the release zip, which the cmake hook imports as a prebuilt library. The prebuilt wrapper in the zip is not extracted -- compiling the wrapper from source keeps the C API matched to the checked-out tag's headers. Both fetch and download come from the same tag, so the bindings and the engine library cannot drift apart. Also remove scripts/verify_web_wasm_exports.sh and its Makefile wiring: that repository's build-artifacts workflow verifies the exports of what it publishes, so thermion's link step only needs to consume it. The Makefile keeps the EXTERNAL_PROJECTS_CMAKE escape hatch for a physics-less WASM. The CI cache and paths-filter now key on the download script, so a tag bump in it re-fetches instead of rebuilding. reactphysics3d_dart publishes on v* tag pushes only; until that repository pushes v0.1.0, `make wasm` fails with instructions to cut the release there. Verified locally against a mirror of the genuine workflow artifact: clean `make wasm` produced thermion_dart.wasm with 142 _rp3d_* exports, and a node smoke test ran the same 300-step simulation as the physics example. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes ticket the-tvf4.
Adds a physics_basics example: boxes and spheres dropped into a walled pen, simulated with reactphysics3d_dart and rendered with thermion_dart. Registered in the example registry for the headless CLI runner, and as the
physicsscene in the web gallery.Supersedes #238.
Web path (this update)
Implements Option C of
docs/research/web-physics-scope.md: ReactPhysics3D is linked into the singlethermion_dart.wasm, so there is only one Emscripten module and theNativeLibrary.instancesingleton conflict dissolves — the module that the gallery's existinginitBindings("thermion_dart")call points at exports the_rp3d_*C API alongside the_Thermion_*one. No Dart-side changes, no ffigen_js work, no second loader.This PR no longer builds ReactPhysics3D itself. reactphysics3d_dart now publishes prebuilt libraries from its own "Build artifacts" workflow (
v*tag pushes), so thermion downloads the published artifact instead:scripts/download_reactphysics3d.shreplacesscripts/build_reactphysics3d_web.sh. Pinned toRP3D_DART_TAG=v0.1.0, it stages exactly what theEXTERNAL_PROJECTShook needs from that release:reactphysics3d_dartcheckout at the tag — this providesnative/web/reactphysics3d_dart.cmake(the fileEXTERNAL_PROJECTSpoints at), the C API wrapper that cmake compiles from source, and the headers;libreactphysics3d.a(engine, wasm32) from thereactphysics3d_dart-0.1.0-web-emscripten.ziprelease asset, dropped next to the cmake file, which imports it as a prebuilt library.libreactphysics3d_dart.a) is deliberately not extracted: compiling the wrapper from source keeps the C API matched to the checked-out tag's vendored headers. Checkout and zip come from the same tag, so the bindings and the engine library cannot drift apart.make wasmstill configures with-DEXTERNAL_PROJECTS=<that .cmake>; the existing-Wl,--whole-archivegroup keeps the otherwise-unreferenced rp3d objects alive.EXTERNAL_PROJECTS_CMAKE=(empty) still builds a physics-less WASM.scripts/verify_web_wasm_exports.shis also removed: that repository's build-artifacts workflow verifies the exports of what it publishes, so thermion's link step only consumes it. The CI cache and paths-filter now key on the download script, so a tag bump in it re-fetches instead of rebuilding.Verified locally (emsdk 6.0.4, same as CI): against a local mirror serving the genuine
web-emscriptenartifact from that workflow, the download script staged the tagged checkout +libreactphysics3d.a, was idempotent on re-run, and a cleanmake wasmproducedthermion_dart.wasm(3.99 MB) with 142_rp3d_*exports; a node smoke test ran the same create → 300 steps → destroy cycle assetupPhysicsBasicsagainst the built WASM.dart analyzeclean onthermion_dartandexamples_lib.One prerequisite:
v0.1.0must exist as a tag + release on reactphysics3d_dart (git tag v0.1.0 && git push origin v0.1.0there). Until it does,make wasmfails with that exact instruction printed by the download script — it cannot fall back to a silent rp3d-less build.Remaining blocker:
reactphysics3d_dartref bumpThe three
ffigen_jsdependency_overridesstay — they cannot be deleted yet. At the pinned ref (52eec72e)reactphysics3d_dartstill pinsffigen_js 0.0.5-pre, and removing the overrides makesdart pub getfail:Additionally,
dart compile wasmof the web gallery currently fails insidereactphysics3d_dart(0.0.5-eraPointeroperator[], missing generated bindings, a directdart:ffiimport) — note this is independent of the gallery registration here, becauseexamples_lib.dartalready exportsphysics_basics.dart, so the import chain fromweb_gallerypulls those files in either way.Once
reactphysics3d_dartlands the ffigen_js bump + regenerated web bindings, the follow-up on this repo is: bump the git ref inexamples_lib/pubspec.yaml, bumpRP3D_DART_TAGinscripts/download_reactphysics3d.sh, and delete the three overrides.What CI must confirm
Generate Artifacts → Compile webrunsmake wasmwith rp3d linked — afterv0.1.0is pushed on reactphysics3d_dart.web.versionbump steps produce an artifact that actually contains rp3d —thermion_dart/native/web/web.versionstill points at67bfb19until then, so anything downloading before that still gets the rp3d-less wasm.Deploy site(release tag) builds the gallery — blocked on thereactphysics3d_dartref bump above.Verified
output/physics_basics.png,dart analyzeclean (from the original PR)_rp3d_*; rp3d runs inside it (see above)🤖 Generated with Claude Code