feat(ar): ship the Ember Fox companion — package, placement and animation - #246
Conversation
…ayback
Replaces the artist-blend primitive package with the Meshy Ember Fox walking
export supplied by the art side. The previous package was a Blender primitive
assembly (sphere/cone/circle parts) and never read as the brand fox.
Asset: 41MB source -> 19.4MB packaged. Texture 4096 PNG downscaled to 2048 JPEG;
the baked `DomeLight` is stripped so ARKit environment lighting applies, which
also clears validation (`.hdr` is not a permitted USDZ extension). Repacked with
`usdzip` for 64-byte alignment; `usdchecker --arkit` PASS. Triple copy in sync.
Evidence class is `MESHY_EMBER_FOX_WALK_V1` rather than reusing the artist-blend
label, and `EXPORT_OK` describes the package that actually ships.
Runtime fixes found while getting it on device:
- Clip selection. RealityKit surfaces one authored `SkelAnimation` as several
entries, and `availableAnimations.first` is the *scene* animation on the root:
it replays the export's baked root transform every loop and never drives the
skeleton, so the companion slid without stepping and fought follow motion for
the root. Now prefers a named clip on the deepest owning entity — measured as
`Armature` / `Anim[0]` instead of `LiraRoot` / `global scene animation`.
- Pose ownership. `authoredRigOwnsPose` was only set on the normal load path,
never on the authored path an animated export takes, so per-frame puppet
transforms kept overwriting the clip.
- The puppet player now stands down as soon as an authored clip exists. The
companion can spawn before the preload finishes, so both were driving the
same joints.
- No spectral FX geometry on authored textured rigs. That layer injects a 0.45m
ground disc and core/halo spheres sized for the primitive companion and paints
them in the skin palette, burying an authored mesh under pale blobs.
Adds measurement tests rather than assertions of intent: packaged height and
ground contact, animation inventory, clip selection, material preservation, and
bounds stability while the clip runs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Device walks kept showing the procedural placeholder — cone ears, a gold core sphere, a ground disc — while diagnostics reported the fox package loaded. Both were true at once, for two reasons. The placeholder is spawned as soon as the game asks for a companion, which is routinely before a ~19MB rig finishes decoding, so the first companion is always procedural. Continuity then reported `ok_present` and never re-planted, leaving the placeholder on screen for the whole session while the real asset sat loaded and unused. The loader now versions its template and the renderer upgrades a companion built from an older generation. Replacing a companion also never removed the previous one. Companions sit on `AnchorEntity`s added with `scene.addAnchor`, which are owned by the scene's anchor collection — `removeFromParent()` does not detach them, so every stale companion kept rendering in front of its replacement. Also, with one embedded walk cycle, looping unconditionally left her treading on the spot while standing still. The clip now runs only while she is covering ground, and the restart watchdog distinguishes a paused clip from a stalled one. Adds a state-aware path for `Fox_<State>.usdz` sidecars authored against the packaged rig: drop them in and they bind per state, with the embedded walk as fallback, so a partial set is safe. The retired `Lira_*` sidecars target the old 25-joint artist armature and share zero joints with the 24-joint humanoid fox — covered by a test so that is not rediscovered the hard way. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Blocks the TestFlight cut — filed as #247.
Suggested order: land this, re-run On |
Lands the Ember Fox as the packaged AR companion, on top of current
main, and fixes the reasons a device never actually showed it.Carries forward the work in #242 / #243 — same asset, and the simpler load-scale-ground approach from #243 — without the 124-commit rollback those branches carry (they branch from #45 and drop ~249 tests). Credit for the asset and the loading approach is @scrimshawlife-ctrl's.
Why the device never showed the fox
Two independent bugs, both of which made diagnostics look healthy while the wrong thing was on screen.
The placeholder was never swapped out. The companion is spawned as soon as the game asks, routinely before a ~19MB rig finishes decoding — so the first companion is the procedural placeholder. Continuity then reported
ok_presentand never re-planted. The real asset loaded successfully and sat unused for the entire session, which is why the HUD honestly readanimated_usdz … anim=PLAYINGwhile the screen showed cone ears and a gold core sphere. The loader now versions its template; the renderer upgrades a companion built from an older generation.Stale companions never left the scene. Companions sit on
AnchorEntitys added withscene.addAnchor, which the scene's anchor collection owns —removeFromParent()does not detach them. Every replacement therefore rendered behind its predecessor. Covered by a test assertingscene.anchorsgoes 2 → 1 on replace.Asset
41MB source → 19.4MB packaged. Texture 4096 PNG → 2048 JPEG. The baked
DomeLightis stripped so ARKit environment lighting applies — also required for validation, since.hdris not a permitted USDZ extension. Repacked withusdzipfor 64-byte alignment.usdchecker --arkit: PASSSkelRoot/Skeleton/SkelAnimationintact; 24-joint humanoid rigMESHY_EMBER_FOX_WALK_V1(not reusing the artist-blend label),EXPORT_OKrewritten to describe what shipsAnimation
Clip selection. RealityKit surfaces one authored
SkelAnimationas six entries, andavailableAnimations.firstis the scene animation on the root — it replays the export's baked root transform every loop and never touches the skeleton. Measured, it was pickingLiraRoot/global scene animation; it now picksArmature/Anim[0].Movement gating. With a single walk cycle, looping unconditionally left her treading on the spot at rest. It now runs only while she is covering ground, and the restart watchdog distinguishes a paused clip from a stalled one.
Per-state path.
Fox_<State>.usdzsidecars authored against the packaged rig bind per state, with the embedded walk as fallback, so a partial set is safe to ship. The fox is a standard humanoid — 19 of 24 joints use Mixamo names — so clips for that skeleton drop straight in.Retired clips. The six
Lira_*sidecars target the old 25-joint artist armature and share zero joints with the fox. They cannot bind and are covered by a test so it is not rediscovered the hard way. Worth deleting in a follow-up (~4.4MB of bundle).Validation
check_lira_usdz_integrity.sh: PASSNew tests measure rather than assert intent: packaged height (0.72m) and ground contact, animation inventory, clip selection, material preservation, bounds stability during playback, stale-anchor removal, and the rig-incompatibility record.
Not addressed
Per-state fox clips do not exist yet, so every state currently shows the walk cycle. Physical-device tracking, battery and thermal behaviour remain
NOT_COMPUTABLEpending the device protocol.🤖 Generated with Claude Code