BLOBBO/lanes/LANE-I-asset-runtime.md
type-two cabc60def8 Workshop plan + lane docs I (asset runtime) and J (editor)
Custom-GLB drop-in system: manifest/registry with procedural fallbacks,
IndexedDB override layer for zero-deploy playtesting, and a three-panel
in-browser fitting editor. Physics/visual decoupling makes every mesh
swappable; blob.body is the one paintable special case (UV requirements).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:30:09 +10:00

67 lines
3.9 KiB
Markdown

# Lane I — Asset runtime (manifest, registry, slot hooks)
**Owns:** `src/assets/`, `assets/manifest.json`, `demos/lane-i.html`,
`src/demo/lane-i.ts`, PLUS surgical slot-hook edits inside `src/blob/createBlob.ts`,
`src/paint/cannon.ts` (barrel build only), `src/machine/parts.ts` (mesh builds
only), `src/course/greybox.ts` (scenery only). Those four are OTHER lanes' files:
your edits there must be strictly additive-visual — replace direct mesh
construction with a registry hook that DEFAULTS to the existing construction.
Never touch physics/colliders/logic in them, and never edit contracts/world/
game/main/package.json.
## Mission
Custom GLB assets drop into named slots without code changes; an empty manifest
produces today's game exactly. See docs/WORKSHOP-PLAN.md for the slot table —
implement every slot listed there except the UI rows.
## Deliverables
1. `src/assets/manifest.ts` — schema + loader:
```ts
interface SlotEntry { url: string; offset?: [n,n,n]; rotationDeg?: [n,n,n];
scale?: number | [n,n,n]; idleClip?: string }
type Manifest = Record<SlotId, SlotEntry>
```
Load order (later wins): `assets/manifest.json` (fetch, may 404 → {}) →
IndexedDB override (`blobbo-workshop` db: `manifest` + `blobs` stores; GLB
entries may be `idb:<key>` urls resolved to object URLs).
2. `src/assets/registry.ts``AssetRegistry`:
- `slotObject(slot, buildFallback: () => THREE.Object3D): THREE.Object3D`
synchronous placeholder pattern: returns a Group immediately containing the
fallback; if the manifest has the slot, async-load the GLB (GLTFLoader from
`three/examples/jsm` — already in the three package, no new deps), then
swap the group's child in place, applying offset/rotation/scale.
- Cache by url (clone via `SkeletonUtils.clone` for skinned, `.clone()` else).
- Load failure → keep fallback + one console.warn (never throw).
- `paintableInfo(object)` — for blob.body: find first Mesh/SkinnedMesh,
report { uvOk (uv attr exists & 0..1 bounded), triCount, materialCount }.
3. Blob slot specifics (in your createBlob hook):
- The delivered mesh's material `map` is replaced by PaintSkin's canvas
texture at integration (skin binds to whatever mesh is present); ensure
the hook exposes the swapped-in paintable mesh as `blob.mesh` (the skin
and buffs read it). If the GLB is a SkinnedMesh with `idleClip`, drive an
AnimationMixer in a fixed-step System (NOT onFrame — hidden-tab rule, see
telegraph.ts docstring for why).
- UV warning: if `paintableInfo.uvOk` is false, console.warn and keep the
procedural sphere instead (paint must never silently break).
4. `assets/manifest.json` — ship EMPTY `{}` (packs come later via integration).
5. Demo `demos/lane-i.html` + `src/demo/lane-i.ts`: a stage that builds every
slot twice — fallback vs a test manifest pointing at the four existing farm
GLBs (`assets/meshes/*.glb`) — side by side, with per-slot load status text
and the paintability report for blob.body.
## Acceptance
- `npm run build` green. With `{}` manifest: pixel-identical construction paths
(verify: no registry code path alters any existing geometry parameters —
the fallback builders are the ORIGINAL code moved, not rewritten).
- Demo loads all four farm GLBs into sensible slots (boot, bucket, toaster
scenery, blob body) with fit transforms committed in the demo's test manifest.
- Blob body swap: paint stamps visibly land on the swapped mesh in the demo
(drive a scripted splat at a known point).
- Failure paths: bad url → fallback + single warn; malformed manifest → `{}`.
- Slot-hook edits to the four foreign files are mesh-construction-only (diff
must show no collider/physics/logic lines changed).
## Contract friction
Report, never edit frozen files. If a slot needs data the hook can't reach,
document it for integration rather than widening interfaces.