Skip to content

feat(ar): ship the Ember Fox companion — package, placement and animation - #246

Merged
scrimshawlife-ctrl merged 2 commits into
mainfrom
fix/fox-mesh-integration
Jul 29, 2026
Merged

scrimshawlife-ctrl merged 2 commits into
mainfrom
fix/fox-mesh-integration

Conversation

@prabu-openclaw

Copy link
Copy Markdown
Collaborator

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_present and never re-planted. The real asset loaded successfully and sat unused for the entire session, which is why the HUD honestly read animated_usdz … anim=PLAYING while 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 with scene.addAnchor, which the scene's anchor collection owns — removeFromParent() does not detach them. Every replacement therefore rendered behind its predecessor. Covered by a test asserting scene.anchors goes 2 → 1 on replace.

Asset

41MB source → 19.4MB packaged. Texture 4096 PNG → 2048 JPEG. The baked DomeLight is stripped so ARKit environment lighting applies — also required for validation, since .hdr is not a permitted USDZ extension. Repacked with usdzip for 64-byte alignment.

  • usdchecker --arkit: PASS
  • SkelRoot / Skeleton / SkelAnimation intact; 24-joint humanoid rig
  • evidence class MESHY_EMBER_FOX_WALK_V1 (not reusing the artist-blend label), EXPORT_OK rewritten to describe what ships
  • triple copy in sync; integrity script PASS

Animation

Clip selection. RealityKit surfaces one authored SkelAnimation as six entries, and availableAnimations.first is the scene animation on the root — it replays the export's baked root transform every loop and never touches the skeleton. Measured, it was picking LiraRoot / global scene animation; it now picks Armature / 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>.usdz sidecars 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

  • 415 unit tests, 0 failures
  • check_lira_usdz_integrity.sh: PASS
  • device: installed and confirmed by the operator — the fox renders

New 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_COMPUTABLE pending the device protocol.

🤖 Generated with Claude Code

prabu-openclaw and others added 2 commits July 28, 2026 18:45
…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>
@prabu-openclaw

Copy link
Copy Markdown
Collaborator Author

Blocks the TestFlight cut — filed as #247.

TESTFLIGHT_RC_CHECKLIST.md currently lists 0.9.0 (2) on main as Ready to archive, but that build renders the procedural placeholder for the whole session rather than the packaged companion. Both bugs this PR fixes are present there, and the diagnostics on that tip read healthy (animated_usdz … anim=PLAYING … ok_present) while the wrong thing is on screen — which is why it went unnoticed across several device sessions.

Suggested order: land this, re-run make validate (code has moved past the Phase A receipt SHA), then re-cut from the new tip or bump to build 3.

On AR_MVP_FREEZE.md (FROZEN_FOR_ENGINEERING): this stays inside the frozen envelope — still packaged Lira_AR_Base.usdz through LiraARAssetLoader with procedural fallback, no new AR surfaces or commands. It reads as maintenance rather than feature creep, but flagging so you can confirm rather than have me assume.

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.

2 participants