Skip to content

feat(dart): physics_basics example (reactphysics3d_dart + thermion) - #238

Closed
nmfisher wants to merge 71 commits into
developfrom
asb/dart-physics-sample
Closed

feat(dart): physics_basics example (reactphysics3d_dart + thermion)#238
nmfisher wants to merge 71 commits into
developfrom
asb/dart-physics-sample

Conversation

@nmfisher

Copy link
Copy Markdown
Owner

Closes the-tvf4 (scope a: native/headless path only).

What

  • New physics_basics example in examples/dart/examples_lib (lib/src/physics_basics.dart): four boxes and three spheres dropped into a walled pen. Physics runs on reactphysics3d_dart, rendering on thermion_dart. The world is stepped a fixed number of times with a fixed time step, then each body's transform is copied to its Thermion asset — so the headless runner captures one deterministic frame of the settled pile.
  • Registered as physics_basics in examples/dart/examples_lib/lib/src/registry.dart, so it runs via the headless CLI runner:
    cd examples/dart/headless_runner
    dart run bin/run_example.dart physics_basics 512 512   # -> output/physics_basics.png
  • Web gallery deliberately NOT wired (documented in the example's doc comment and examples/README.md). reactphysics3d_dart pins ffigen_js 0.0.5-pre, conflicting with thermion_dart's ^0.0.14-pre. The packages only resolve together through dependency_overrides: ffigen_js: ^0.0.14-pre, added to the roots where pub honours them (examples_lib, headless_runner, web_gallery). The override is safe: ffigen_js is only imported by reactphysics3d_dart's web (JS interop) bindings, unused on the native path. Its WASM runtime path is unverified, so the example stays out of galleryScenes until the pin is relaxed upstream.

Two issues found while verifying

  1. Upstream artifact broken on Linux arm64: native/linux/arm64/libreactphysics3d.a in reactphysics3d_dart@master is a macOS (Mach-O) archive, so the build hook cannot link on this machine class. Fixed upstream in fix: linux/arm64 static lib is a macOS archive reactphysics3d_dart#2 (rebuilt 0.10.2 with -fPIC). Until that merges, arm64 Linux hosts need the artifact from that PR in their pub cache; x86_64 Linux and macOS work as committed.
  2. Ubershader material instances deadlock teardown: if a MaterialInstance from createUbershaderMaterialInstance() is still alive, FilamentApp.destroy() never completes (reproduced with a single unused instance — separate from any scene resource). The example works around it by destroying its colour materials via viewer.onDispose(...); worth a dedicated ticket for a proper fix.

Test results

  • dart analyze clean in examples_lib, headless_runner, web_gallery.
  • dart run bin/run_example.dart physics_basics 512 512 produces output/physics_basics.png (settled, coloured pile) and exits cleanly.
  • geometry_primitives re-run as a regression check — unchanged output.
  • dart pub get succeeds in all three packages.
  • Web gallery WASM build not run (out of scope, documented as blocked).

🤖 Generated with Claude Code

nmfisher and others added 30 commits August 12, 2026 12:16
Version bump (v1.69.1 -> v1.74.0) so CI produces a fresh set of prebuilt
artifacts: filament.version, the build hook fallback, build-script usage
examples, the Makefile web artifact, BUILDING.md CI refs, zip_android.sh,
and the native test CMakeLists/Makefile version pins.

Add libassimp to the platform build scripts (macOS/iOS/Linux/Web/Windows):
each builds libassimp from the Filament checkout's third_party/libassimp/tnt
overlay and copies libassimp.a into the target dirs; copy_headers.sh copies
the Assimp headers.

Add scripts/patch_libassimp_tnt.py, run after the Filament tag checkout in
every platform script, to idempotently patch the tnt overlay to enable
STL/PLY import and glTF2/FBX export (the overlay ships OBJ/FBX import only
and is export-stripped).

The thermion-side assimp integration (model importer, viewer API, hook
linking) is intentionally not included here; it stacks on top in a follow-up.

Co-Authored-By: Claude <noreply@anthropic.com>
… only)

CI (macOS) showed the glTF exporters don't compile in the minimal tnt build:
glTFExporter.cpp / glTF2Exporter.cpp depend on the glTF asset model
(glTF::Asset, AssetWriter, Accessor, ...) which assimp compiles out under
ASSIMP_BUILD_NO_GLTF*_IMPORTER. Enabling glTF export would require also
enabling the full glTF importer, which is pointless here (Filament loads
glTF natively via gltfio).

So: stop adding the glTF exporter sources, and add -DASSIMP_BUILD_NO_GLTF_EXPORTER
so Exporter.cpp's table no longer references them. STL/PLY import and FBX
export all compiled cleanly in CI and remain enabled.

Co-Authored-By: Claude <noreply@anthropic.com>
The build-architecture build scripts placed the cmake binary dir directly
inside `out/.../third_party/libassimp/`, so the output `libassimp.a` lands at
`.../libassimp/libassimp.a`, not at `.../libassimp/tnt/libassimp.a` (the
`tnt` subdir is the cmake source, not where ninja writes artifacts).

Co-Authored-By: Claude <noreply@anthropic.com>
Builds libz + patches + builds libassimp from a Filament checkout without
compiling any Filament library. Useful for validating tnt overlay changes
or the patcher before running the full platform build.

Usage:  scripts/build_libassimp.sh <FILAMENT_BASE_DIR> [TARGET_DIR] [release|debug]

Co-Authored-By: Claude <noreply@anthropic.com>
Adds assimp header copy to the R2 upload zip staging sections of all
5 platform build scripts (macOS, iOS, Linux, Android, Windows) so the
prebuilt artifacts on Cloudflare R2 include the full header tree:
core Filament API, imageio, stb, and libassimp.

Also adds scripts/update_vendored_headers.sh -- a standalone workflow
script that refreshes vendored headers after a filament.version bump.
Two modes:

  scripts/update_vendored_headers.sh <FILAMENT_BASE_DIR>
    Copy all headers from a local Filament checkout (fast, no rebuild).

  scripts/update_vendored_headers.sh --from-r2
    Download the prebuilt macOS zips from R2 and extract all headers.
    Now works fully since the R2 artifacts include third-party headers.

Co-Authored-By: Claude <noreply@anthropic.com>
Filament v1.74.0 renamed the CMake option from -DWEBGL to -DWASM
(commit 96b8015fb, "web: rename WEBGL to WASM for cmake (#9941)").

Without this fix, CMake's `NOT WASM` evaluates TRUE, the LINUX
variable gets set, and the Emscripten build picks up linux-specific
source files (libs/utils/src/linux/Mutex.cpp, Condition.cpp) that
include `<linux/futex.h>` — a host-only header unavailable in the
Emscripten SDK.

Co-Authored-By: Claude <noreply@anthropic.com>
Filament v1.74.0 moved BlueVK.h out of the standard install tree
(out/release/filament/include/bluevk/) to the source tree
(libs/bluevk/include/bluevk/).  All header copy paths (copy_headers.sh,
update_vendored_headers.sh, and all platform build scripts' R2 zip
staging + inline thermion_dart copy sections) now explicitly copy
bluevk from the source tree.

Co-Authored-By: Claude <noreply@anthropic.com>
…rsions

Filament v1.74.0 strips 'const' qualifiers from Builder::build() methods
in installed headers (VertexBuffer.h, RenderableManager.h, FramePacer.h),
but the prebuilt library was compiled from source (which has const).
This causes linker errors for any code using these builders.

Also copies source-tree utils/compiler.h which has UTILS_SHARED_LINKING
that BlueVK.h depends on (also stripped from the install tree).
…ne workflow

libassimp's tnt overlay hardcodes ASSIMP_BUILD_NO_OWN_ZLIB, so its sources
(passes ZLIB_*), but Windows and Linux CI have no zlib.h on the include path,
so the libassimp build fails (e.g. FBXParser.cpp C1083). thermion links -lz
itself, so libassimp only needs the header -- put Filament's vendored
third_party/libz on the include path via CMAKE_C_FLAGS/CMAKE_CXX_FLAGS.

Also add a temporary 'Build libassimp (standalone)' workflow_dispatch that builds
ONLY libassimp (clone Filament at the tag, patch tnt, cmake+build) on
windows/linux/macos, so assimp build errors surface in ~2min instead of after
the full ~40min Filament build.

Co-Authored-By: Claude <noreply@anthropic.com>
…e patch script)

Co-Authored-By: Claude <noreply@anthropic.com>
… all platforms

build_android.sh previously copied libassimp headers but never built the
library, so the Android R2 artifact had no libassimp.a (thermion_android could
not link it). Add the per-arch libassimp build (NDK toolchain + zlib include
fix, mirroring imageio/tinyexr) and copy libassimp.a per ABI. Also apply
patch_libassimp_tnt.py after checkout (was missing).

Extend the standalone 'Build libassimp' workflow_dispatch to all six platforms
(windows, linux, macos, ios, android, web) so any platform's libassimp build can
be checked in ~2min instead of the full ~40min Filament build.

Co-Authored-By: Claude <noreply@anthropic.com>
The post-build step downloaded filament-v1.74.0-windows-release-vulkan.zip from
R2 to extract vulkan-1.lib, but that zip does not exist for any version (404),
so every Windows build that reached this step failed -- a first-build
chicken-and-egg. vulkan-1.lib is the Vulkan loader import lib (version-
independent, not Filament-specific), and the Filament build above already
linked against the runner's Vulkan SDK, so copy it from there: VULKAN_SDK env,
then a C:\VulkanSDK\*\Lib glob, then the R2 download as a last resort.

Co-Authored-By: Claude <noreply@anthropic.com>
The runner has no Vulkan SDK (Filament builds with vendored headers + loads the
loader dynamically), and the version-specific filament-v1.74.0-...-vulkan.zip was
never uploaded (404 for every version), so the post-build vulkan-1.lib step always
failed -- a first-build chicken-and-egg. vulkan-1.lib is version-independent, so
extract the single 'vulkan-1.lib' entry from the existing v1.69.1 Windows release
zip on R2 via .NET ZipFile (no full 98MB unzip). Keep a runner-Vulkan-SDK fast
path in case a future image exposes it.

Co-Authored-By: Claude <noreply@anthropic.com>
libassimp.a linked into thermion's shared lib (.so) failed on Linux:
  ld: libassimp.a(FBXExporter.cpp.o): relocation R_X86_64_PC32 ... can not be
       used when making a shared object; recompile with -fPIC
macOS is PIC by default (arm64) and Windows MSVC /LD needs no PIC, but Linux
(and Android) ELF shared libs require it. imageio/tinyexr already set
CMAKE_POSITION_INDEPENDENT_CODE=ON; add the same to the libassimp cmake in
build_linux.sh and build_android.sh.

Co-Authored-By: Claude <noreply@anthropic.com>
…latforms

Filament v1.74.0 made -fno-rtti the global default (FILAMENT_ENABLE_RTTI now
defaults OFF; v1.69.1 only disabled RTTI on android-release/webgl). With RTTI
off, libbackend no longer emits 'typeinfo for Platform::ExternalImage' (the
vtable/dtor are still present). thermion's ExternalVulkanImage derives from
ExternalImage with RTTI on, so it references the missing base typeinfo and
libthermion_dart.so/.dll fails to load at runtime (dlopen: undefined symbol).
Restore v1.69.1 desktop behavior by passing -DFILAMENT_ENABLE_RTTI=ON to the
Filament build on linux/macos/ios/android (build.sh sed) and windows (cmake).
Web is intentionally left RTTI-off (WASM/emscripten::val).

Co-Authored-By: Claude <noreply@anthropic.com>
Updated from the v1.74.0 Filament build output (out/release/filament/include/)
and source tree (libs/imageio, libs/bluevk, third_party/stb,
third_party/libassimp).

Notable changes:
- BlueVK.h now lives in libs/bluevk/include/bluevk/ (moved out of
  the standard install tree) — copy_headers.sh and all build scripts
  updated to copy from the source location
- Several headers removed: vk_video/, vulkan/ platform headers,
  utils/android/, utils/linux/ (not present in v1.74.0 install tree)
- Core Filament API headers (Engine.h, Scene.h, etc.) updated
- tsl/robin_map hash/set headers updated
- uberarchive.h (release and debug) regenerated
- All 87 libassimp headers unchanged

Co-Authored-By: Claude <noreply@anthropic.com>
BlueVK.h in v1.74.0 uses UTILS_SHARED_LINKING, but the installed
utils/compiler.h (from out/release/filament/include/) strips this
macro. Since we mix source-tree headers (bluevk/vulkan/vk_video from
libs/bluevk/include/) with installed headers, also copy the
source-tree utils/compiler.h which includes the full macro set.
… missing FramePacer)

The Filament install step is unreliable in two ways:
(a) it strips 'const' from Builder::build() methods -> ABI/linker mismatch
    vs the prebuilt libs (compiled from source WITH const);
(b) it omits newly-added public headers not in the install manifest
    (FramePacer.h, FrameHistoryStream.h, FramePipelineEstimator.h in v1.74).

Overlay the authoritative source-tree filament/include/filament/*.h onto the
install-tree copy in both copy_headers.sh and update_vendored_headers.sh.

Co-Authored-By: Claude <noreply@anthropic.com>
Previous refresh (dd425ca) botched the target dir: a partial copy left
filament/ with only 4 files and dropped math/, utils/, gltffio/, backend/
entirely, so every 'fatal error: X file not found' cascaded. Regenerate the
full install tree from a local v1.74 checkout and overlay source-tree
filament/ public headers (restores const on Builder::build + the FramePacer
family the install manifest omits).

Co-Authored-By: Claude <noreply@anthropic.com>
…matched)

The local Filament checkout can sit ahead of the release tag (e.g. at
rc/1.74.1 while filament.version is v1.74.0). Headers from the working tree or
its install output then mismatch the prebuilt libs' ABI: after v1.74.0,
RenderableManager::Builder::build became const (lib keeps it non-const) and
backend::Platform gained FrameRateCompatibility; the install tree also strips
const and omits utils/tribool.h.

update_vendored_headers.sh now extracts every public header subdir straight
from the release tag via 'git archive' (non-invasive: never touches the working
tree), driven off the subdirs the install tree already established. It prefers
the canonical public location <name>/include/<name>, rejecting private bridges
that shadow the public tree (libs/filabridge/include/filament). copy_headers.sh
(which runs after build_*.sh has checked out the tag) overlays filament/ +
utils/ + math/ from the working tree.

Co-Authored-By: Claude <noreply@anthropic.com>
…0 tag

All public header subdirs now come from the v1.74.0 release tag (matching the
prebuilt libs), not the local rc checkout or its lossy install tree. Native
build compiles + links on macOS (libthermion_dart.dylib produced). FramePacer.h
and FramePipelineEstimator.h are removed: they postdate v1.74.0 and thermion
does not use them.

Co-Authored-By: Claude <noreply@anthropic.com>
…4.0 API

v1.74.0 changed the VulkanPlatform surface thermion overrides:
- createSync: dropped its VkFence parameter; VulkanSync lost its default ctor
  and its `fence` member (now: explicit VulkanSync(shared_ptr<VulkanCmdFence>)).
- createVkImageFromExternal: gained uint32_t logicalWidth, logicalHeight params.

Update LinuxVulkanPlatform.h (both methods) and WindowsVulkanPlatform .h/.cpp
(createVkImageFromExternal only; Windows doesn't override createSync). This
unblocked the Linux/Windows thermion native build on v1.74.0 (macOS was fine --
Metal). extractExternalImageMetadata was unchanged and needed no edit.

Co-Authored-By: Claude <noreply@anthropic.com>
Filament generates a platform-specific uberarchive.h (basis-compressed
ubershader material archive) -- UBERARCHIVE_DEFAULT_SIZE differs per
platform/backend. Thermion vendored a single copy and compiled it on every
platform while linking a platform-specific uberarchive lib, so the size
constant passed to gltfio's createUbershaderProvider disagreed with the
actual UBERARCHIVE_PACKAGE bytes -> at runtime the basis transcoder threw
"ArchiveCache: decompression failed: Src size is incorrect", breaking all
render tests (notably the entire Windows suite under the v1.74.0 bump).

Replace the single vendored uberarchive.h with a dispatch wrapper that
selects a per-platform variant via compiler platform macros (which align
with the lib build.dart links for the same target). Variants are generated
from the authoritative size in each platform's R2 artifact by
scripts/update_uberarchive_variants.sh (run after every Filament bump).

v1.74.0 sizes: linux 548108, windows 1073097, macos 677854, android 570288
(release); debug variants likewise. iOS placeholder (no artifact published
yet); web seeded from prior value pending web-build wiring.

Co-Authored-By: Claude <noreply@anthropic.com>
The dispatch wrappers under filament/{release,debug}/gltfio/materials/ were
shadowed by a stale generic copy at filament/gltfio/materials/uberarchive.h
(1,213,495). build.dart lists `native/include/filament` ahead of
`.../filament/<mode>`, so <gltfio/materials/uberarchive.h> resolved to the
generic copy for BOTH modes, ignoring the per-platform dispatch wrapper.
Runtime confirmed: size=1213495 (stale) with valid zstd data ->
srcSize_wrong.

Delete the generic copy so the include falls through to the mode-specific
dispatch wrapper (debug/release), which selects the correct per-platform
variant. Also harden update_uberarchive_variants.sh to remove any such
shadowing generic copy on re-runs.

Co-Authored-By: Claude <noreply@anthropic.com>
The v1.74.0 bump changed the material package format: MaterialDefinition
panics "Material version mismatch. Expected 74 but received 69" when
loading packages built by older matc. The example assets
(examples/assets/*.filamat) and embedded materials
(native/include/material/*) were all still version 69 because
generate-artifacts.yml only recompiles materials when materials/** or
*.mat sources change -- a version bump doesn't, so the step was skipped.

Regenerate everything with the official v1.74.0 release matc (all now
version 74). Also include filament.version in the workflow's
materials-changed path filter so future bumps recompile automatically.

Co-Authored-By: Claude <noreply@anthropic.com>
The thermion_flutter Windows plugin shipped pre-v1.74.0 duplicate copies of
utils/, bluevk/, vulkan/, and vk_video/ headers under its local include/ dir.
Because that dir is listed first in target_include_directories (ahead of the
canonical thermion_dart headers), #include <utils/compiler.h> resolved to the
stale copy, which defines UTILS_PUBLIC but lacks UTILS_SHARED_LINKING and
UTILS_GUARDED_BY — causing the thermion_flutter_plugin compile to fail:
github-actions Bot and others added 27 commits August 12, 2026 04:50
🤖 Generated with GitHub Actions
v1.74.0's Emscripten build emits zlib's SHARED target (named libz.a via
OUTPUT_NAME) as a shared module, so no libz.a lands in the web release
zip -- only libzstatic.a. The web CMakeLists links a `z` target expecting
libz.a (in v1.69.1 both archives were byte-identical), so the web build
fails at the final link with "No rule to make target .../libz.a".
Alias libzstatic.a to libz.a in both release and debug zips.

Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with GitHub Actions
Copy-paste bug: the debug header-bundle branch copied libs/bluevk/include
into $TARGET_RELEASE_DIR/include instead of $TARGET_DEBUG_DIR/include, so
the debug artifact zip shipped without bluevk/, vulkan/ and vk_video/
headers and Android debug builds failed with "bluevk/BlueVK.h not found".

Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with GitHub Actions
Filament v1.74.0 always compiles libs/utils/src/android/Systrace.cpp on
Android, and its debug object references perfetto::internal::
InProcessTracingBackend::GetInstance() (plus TracedValue, EventContext,
DataSourceBase, ...). build_android.sh bundles libperfetto.a in both the
release and debug zips, but the hook never linked it, so libthermion_dart.so
kept those symbols undefined and dlopen failed at runtime ("cannot locate
symbol ...InProcessTracingBackend..."). Link perfetto on Android in both
modes; it is a no-op when the archive is unused (static archives only yield
members needed to resolve references).

Verified: android-arm64 debug APK links with 0 undefined perfetto symbols
(remaining undefineds are standard bionic libc plus weak ZSTD_trace hooks).

Co-Authored-By: Claude <noreply@anthropic.com>
Filament debug builds enable the Material Debug Server and Frame Graph
viewer (build.sh -d/-t), so the debug zips for desktop (macOS/Linux) and
Android ship libmatdbg.a/libfgviewer.a and their filament archives
reference them (e.g. filament::matdbg::DebugServer). The hook only linked
them for macOS debug, so Android debug libthermion_dart.so kept those
symbols undefined and dlopen failed at runtime after the perfetto fix.

Verified: android-arm64 debug APK links with 0 undefined matdbg/fgviewer
symbols. iOS debug never enables them; Windows links via pragma libs.

Co-Authored-By: Claude <noreply@anthropic.com>
- tinyexr: Filament v1.75.0 bumped the vendored tinyexr, whose code trips
  its own -Weverything -Werror (-Wimplicit-int-conversion, -Wold-style-cast,
  -Wsign-conversion, -Wunused-parameter; confirmed by a local strict
  compile). CLANG_COMPILE_FLAGS are per-source COMPILE_FLAGS appended after
  the strict flags, so append the -Wno-* suppressions to that line in all
  scripts that rebuild tinyexr (macos, linux, ios, android). Idempotent and
  a no-op on versions without the anchor string.
- ios: v1.75.0's XCFramework slices overlap in architecture (simulator
  slices are fat arm64+x86_64, device slices arm64-only), so lipo -create of
  all slices fails. Deduplicate by extracting a thin slice per unique
  architecture before combining, in both release and debug.
- Remove stray editor backup file 'thermion_dart/CHANGELOG.md~49ea654e ...'
  whose path Windows checkout rejects, breaking the Windows job.

Co-Authored-By: Claude <noreply@anthropic.com>
- tinyexr: extend the CMakeLists suppression list with the warnings each
  platform's clang actually fires: -Wimplicit-int-float-conversion
  (linux/android, clang 15/18 keep it separate from
  -Wimplicit-int-conversion) and -Wunused-function (macOS). Enumerated by
  collecting every -Werror diagnostic from the failed jobs; also patch
  build_ios_simulator.sh, which rebuilds tinyexr with the same strict flags.
- ios: v1.75.0 renamed the device build dir to
  out/cmake-ios-<target>-arm64-iphoneos (platform suffix added), breaking
  the hardcoded third_party paths for libz/imageio/tinyexr/libassimp in
  build_ios.sh. Point them at the -iphoneos dirs (release + debug); the
  simulator script builds into its own dirs and is unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
Bumps vendored Filament to v1.75.0 and fixes the resulting
compile/link/runtime breakages:

- SingleInstanceComponentManager (1.75.0) requires EntityManager& and
  a name in its base constructor and no longer has a default ctor.
  Morph/Bone/GltfAnimationComponentManager now take EntityManager& and
  forward to the base; AnimationManager passes EntityManager::get().
- Engine::create() was replaced by Engine::Builder in 1.75.0; switch
  TEngine.cpp to the builder API.
- build.dart: _getFilamentVersion derived filament.version from
  Platform.script, but for a compiled build hook that URI points at the
  consuming package's hook.dill, not this source — so the file was never
  found and the hook threw. Resolve it relative to the package root.
- libfilamat.a in 1.75.0 references external ZSTD_* symbols (e.g.
  ZSTD_getFrameContentSize) that are no longer bundled in the Filament
  archives. zip_android.sh used to skip zstd; it now bundles libzstd.a
  per arch, and build.dart links -lzstd on Android. Without this the
  Android shared lib ships with ZSTD_getFrameContentSize unresolved and
  dlopen fails at runtime (Android has no system libzstd).

Co-Authored-By: Claude <noreply@anthropic.com>
build_ios_simulator.sh's build_sim_target passed -DIMPORT_EXECUTABLES_DIR=out
plus -DCMAKE_BUILD_TYPE=Debug, so Filament's CMake resolved to
out/ImportExecutables-Debug.cmake. On 1.75.0 that file is never produced:
build_ios.sh (Filament's build.sh) builds the host code-generators once as
prebuilt artifacts at out/prebuilt-tools-release/ImportExecutables-Prebuilt.cmake
and every iOS cross-build imports those via
-DFILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR. The arm64-simulator build must do
the same, otherwise CMake configure fails with
"include could not find requested file: out/ImportExecutables-Debug.cmake".

Also add a guard that fails fast with a clear message if the prebuilt import
file is missing (i.e. build_ios.sh wasn't run first) instead of failing deep
in the configure step.

Co-Authored-By: Claude <noreply@anthropic.com>
The 1.75.0 fix for the simulator configure step got past cmake, but the
build then failed in copy_sim_libs:

  cp: out/ios-debug-sim/filament/lib/arm64/*.a: No such file or directory

Filament 1.75.0's iOS install emits XCFramework bundles
(lib<name>.xcframework/) instead of the flat lib/<arch>/*.a layout older
versions produced — the device build already extracts slices from these
(build_ios.sh globs *.xcframework). The simulator script still assumed the
flat layout, so it found nothing.

copy_sim_libs now extracts the simulator slice (the .a under a *simulator*
xcframework subdirectory) from each bundle into a flat lib<name>.a, with
fallbacks for the legacy lib/arm64/*.a and lib/*.a layouts, and dumps the
actual install tree in the log if nothing is found. build_sim_target also
echoes the install layout right after ninja install so this is never a
blind guess again.

Co-Authored-By: Claude <noreply@anthropic.com>
The install-layout diagnostic from the previous run showed where Filament
1.75.0 actually puts the arm64 iphonesimulator archives:

  out/ios-debug-sim/filament/lib/arm64-iphonesimulator/*.a

The subdir is "${IOS_ARCH}-${PLATFORM_NAME}" (arm64-iphonesimulator), not
the lib/arm64 the old fallback checked, and not xcframeworks. So copy_sim_libs
found nothing and bailed.

copy_sim_libs now collects every .a the install produced at lib/*.a and
lib/*/*.a (covers arm64-iphonesimulator, plain arm64, and any other
single-level arch subdir), while still extracting a slice from any
*.xcframework bundle as a safety net.

Co-Authored-By: Claude <noreply@anthropic.com>
Two bugs broke Linux debug hook builds against v1.75.0 R2 artifacts:

1. scripts/build_linux.sh's debug block copied the bluevk headers to
   $TARGET_RELEASE_DIR instead of $TARGET_DEBUG_DIR, so debug zips ship
   libbluevk.a without bluevk/, vulkan/ or vk_video/ headers and any
   compile of <bluevk/BlueVK.h> fails. Same typo fixed in
   build_macos.sh and build_ios.sh (build_android.sh was correct).

2. hook/build.dart linked libmatdbg.a and libfgviewer.a inside
   -Wl,--whole-archive on Linux; both bundle civetweb, so every member
   of both archives was pulled in and the link died with "multiple
   definition of mg_*". They now link after --no-whole-archive.

Also added a self-heal in getLibDir: when the extracted artifact's
include/ lacks bluevk/BlueVK.h, fetch the release zip of the same
version/platform and merge the bluevk headers in (they are
mode-independent). This fixes the already-published v1.75.0 debug
artifacts on user machines without an R2 re-upload; it is a no-op once
correct zips are uploaded.

Verified with examples/dart/cli_headless (mode: debug, linux-arm64):
hook compiles and links the 347MB debug .so; ldd -r shows the same
undefined-symbol set as release (pre-existing). Release build with the
same hook still completes. See .tickets/the-c8d3.md.

Co-Authored-By: Claude <noreply@anthropic.com>
Code fix complete and verified. PR not opened from sandbox (push
blocked); main agent to push asb/linux-debug-artifact and open the PR.
R2 debug zips still need a re-upload (see resolution notes).

Co-Authored-By: Claude <noreply@anthropic.com>
DelegateInputHandler:
- queue events in dispatch order and drain serially, so overlapping
  handle() calls from ThermionListenerWidget can no longer interleave
  inside delegates or complete out of order
- reject new events once dispose begins, drain the queue before
  disposing the delegate
- capture/log handler errors instead of leaking uncaught async errors
- coalesce backlogged move/hover events (deltas accumulate, latest
  position wins)
- ScaleUpdateEvent.localFocalPointDelta is now non-nullable (its only
  producer always supplied a value; delegates force-unwrapped it)

ThermionViewerFFI:
- loadGltfFromBuffer/createGeometry/destroyAsset/destroyAssets now run
  through _serializeSceneResourceOperation and throw
  ViewerDisposedException after dispose (dispose uses the internal
  variants so it still drains and tears down)

Material/asset lifetimes:
- FFIFilamentApp tracks material-instance bindings (createGeometry,
  createInstance, setMaterialInstanceAt swaps); destroying an instance
  still referenced by a live asset throws MaterialInstanceInUseException
  instead of leaving a native use-after-free
- FFIMaterial/FFIMaterialInstance/FFIAsset destruction is idempotent,
  and operations after destruction throw StateError

Co-Authored-By: Claude <noreply@anthropic.com>
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>
Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with GitHub Actions
@nmfisher

Copy link
Copy Markdown
Owner Author

Superseded by #239 (clean PR with only the physics example changes; #238 carried unrelated harden-branch commits).

@nmfisher nmfisher closed this Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant