224 lines
28 KiB
Markdown
224 lines
28 KiB
Markdown
> **FABLE'S ADJUDICATION (2026-07-26) — read before §1.**
|
||
> This spike is ratified: the mechanism is proven on our own vendored bytes, and the tiering
|
||
> stands. **One verdict in it is over-claimed and is corrected here.** The spike calls the four
|
||
> module-scope furniture GLB fetches under `?classic=1` "a live covenant breach in shipped
|
||
> code". They are not a breach: `bench`/`bin`/`bus_shelter`/`food_cart` entered
|
||
> `furniture.js` in **Lane B round 6 (`24cef5a`), which is tagged v2.0-alpha through v2.0** —
|
||
> git-verified. They are part of the v2 baseline the covenant preserves, so classic fetching
|
||
> them is classic being *correct*. "Zero fetch delta" has always meant zero delta **against
|
||
> the documented v2 baseline**, not zero requests.
|
||
>
|
||
> **What the spike really found, and it is valuable:** (a) `classic_regression` carries **no
|
||
> network assertion at all**, so it could not detect a future fetch-surface widening — which
|
||
> is exactly the R36 species (five DJ bodies widened classic's fetch surface and every pinned
|
||
> number stayed green; only a NEW smoke caught it); and (b) `furniture.js`'s module-init load
|
||
> pattern is a **trap for new assets** — anything added to `GLB_FILES` fetches under classic
|
||
> silently. Both corrections are binding on the fauna work: `fauna.js` is dynamically imported
|
||
> behind its construction guard or carries a CLASSIC-derived module-scope guard stated in its
|
||
> header, and `classic_regression` grows a network **allow-list** (the four v2 depot GLBs plus
|
||
> same-origin), never a deny-list. A deny-list only catches what you already thought of.
|
||
>
|
||
> Also noted: the spike could not reproduce the 280/300 walked draw figure (it measures
|
||
> 191 default / 167 classic by teleport-walk). **Re-pinning the budget of record is now wave
|
||
> 0's first job**, and no proposal spends against 280 until a walked re-measure settles it.
|
||
|
||
---
|
||
|
||
# ANIMALS — THE ANSWER
|
||
|
||
*PROCITY fauna. Answering: "yeah idk about rigged animal models, i will see if i can find some to use online, they have different rigging too right? diff skeleton? what do you suggest here"*
|
||
|
||
---
|
||
|
||
## 1. Short answer
|
||
|
||
**Yes, every animal has a different skeleton — and it turns out not to matter.** There is no mixamorig for quadrupeds. There is no standard animal rig at all. Every animal you download ships a bespoke skeleton with bone names its author invented that morning: `leg_fl`, `Bone.003`, `Hind_L_01`, whatever. That part of your instinct is exactly right.
|
||
|
||
What is wrong is the assumption underneath it — that a different skeleton is a *problem*. It's only a problem if you need to make **one clip play on a different body**. That's retargeting, and it's the reason our human stack exists: 24 human bodies share 8 clips, so every body must speak `mixamorig`. An animal that ships its **own** animations inside its **own** file doesn't need any of that. The GLB binds its clips to its own bones, through its own mixer, and plays. Zero retargeting.
|
||
|
||
We measured it rather than assumed it. There is no real animated non-mixamo GLB anywhere on the fleet — I scanned all of `~/Documents`: 2,486 GLBs, 81 with animations, **81/81 mixamorig, 0 non-mixamo skins**. So we authored a test asset byte by byte: a 10-bone quadruped named `root/spine/neck/head/tail_01/tail_02/leg_fl/leg_fr/leg_bl/leg_br`, 96 tris, two embedded clips named Blender-style (`Armature|CritterWalk`, `Armature|CritterIdle`). Then loaded it through **our actual vendored three.js r175 bytes** and **our actual `web/js/core/loaders.js`**, with the repo untouched, and rendered it on a real `WebGLRenderer`.
|
||
|
||
Result — 27 assertions, all green:
|
||
|
||
- A bare `new THREE.AnimationMixer(gltf.scene)` + `clipAction(gltf.animations[0]).play()` — no canon, no `buildFigure`, no `_rotOnly`, no MIRPAMO — moved `leg_fl` by quaternion delta 0.252 then 0.310 across two `mixer.update()` calls.
|
||
- The GPU agreed. Rendered to a 200×150 render target and read the pixels back: 1,804 lit pixels, and 704–1,015 of them changed between frames. Not "the matrices moved" — **actual different pixels came off the GPU**.
|
||
- `SkeletonUtils.clone` works on the foreign rig: separate skeletons, **shared geometry and shared material**, so one file feeds N animals. (It maps by object identity, not by name — `SkeletonUtils.js:388-425` — so "foreign names are fine" is structural, not luck.)
|
||
- Our own `loadGLB()` returned the identical object unmodified. **No change to any existing module.**
|
||
|
||
And the mirror image, which is the important half: if you fed that animal to `web/js/citizens/rigs.js`, `_canon()` would silently do nothing (no `mixamorigN` names to normalise), and then `buildFigure()` would measure the quadruped's "feet-to-crown" span at **0.36 m**, normalise it to human height, and **scale the dog 4.9×**. No error. A giant dog.
|
||
|
||
So the law is: **animals get a new sibling module, `web/js/citizens/animal_rig.js`, that imports nothing from the human rig stack.** About forty lines. The human fleet is classic-covenanted and never gets touched.
|
||
|
||
**The real constraint is draws, not skeletons.** Measured on a real renderer: **1.00 draw call per skinned animal, 1:1, all the way to N=80.** three.js has no instancing for animated skinned meshes and there is no impostor tier for animals (our impostor bake frames square from height — `impostor.js:57-66` — which crops a long low animal). So a skinned animal is a full draw at every distance it's visible, or it's culled outright. Nothing in between.
|
||
|
||
Against that: **sixty birds on wires cost one draw between them**, because static instanced meshes share one material and one draw call. That asymmetry — 1 draw per dog vs 1 draw per *sixty birds* — is the entire design.
|
||
|
||
---
|
||
|
||
## 2. The recommendation
|
||
|
||
Four tiers. Only two of them ship this epoch.
|
||
|
||
### TIER 0− — the heard animal · **+0 draws, +0 triangles** · **SHIP**
|
||
|
||
A cockatoo you hear at dusk and never see costs literally nothing and does most of the work. New entries in `PACK` (`pipeline/gen_audio.py:688`), ~30 lines of numpy each, deterministic, original, 🟢 by construction. Played at a **seeded anchor**, so it's the same dog behind the same fence every time you walk that street.
|
||
|
||
This is the highest charm-per-minute item in the entire document. A town with **zero visible animals** reads as a place with animals in it. Nothing can regress; it inherits the existing `smoke_audio` gate for free.
|
||
|
||
### TIER 0a — the flock · **+1 draw for the whole town** · **SHIP — this is wave 1**
|
||
|
||
One `InstancedMesh`, one geometry (two crossed planes = 4 tris), one material, one **1024 WebP species atlas**. Cockatoo, galah, corella, lorikeet, perched magpie, ibis are all cells in the same sheet — which means **every future bird species is free**. That single decision is the most valuable thing in the plan.
|
||
|
||
This is not a new trick: it's exactly what already renders the town's entire tree canopy (`furniture.js:98-101`). Matrices are static; the buffer refills only when you cross a 64 m chunk boundary. Zero per-frame CPU.
|
||
|
||
And it is genuinely free when empty — verified to the vendored line. `three.module.js:2193-2199` returns from `renderInstances` before the GL call, and `render.calls++` lives at `three.module.js:4094` *inside* `info.update()`, downstream of that return. A town with no birds, or the daytime species at night, costs **exactly zero**, not "almost zero".
|
||
|
||
Live cap 96 instances → 384 triangles. Against ~76k spare triangles that is a rounding error.
|
||
|
||
### TIER 0b — the near props · **+1 draw per species** · **wave 1.5, one species**
|
||
|
||
Real low-poly meshes for the ones you walk within two metres of, where a billboard reads as cardboard. **The ibis, head down in a council bin** — it's the named proof species from the last review and it lands next to the existing `council-bin` prop. One town-wide instanced mesh, ≤450 tris, cap 16.
|
||
|
||
### TIER 1 — static mesh + scripted path · **+1 draw** · **the magpie, later**
|
||
|
||
The already-approved swooping magpie: position is a pure function of `(seed, elapsed time)` — the tram's exact contract — with the wing wave riding `wind.js`'s existing amplitude uniform, which is byte-identical at amplitude 0.
|
||
|
||
**One correction that's binding:** the review caught that the magpie's territory has to scale with the road graph. On the synthetic golden town there are 370 pole anchors; on `bendigo_real` there are **10,014 poles and 13,730 tree anchors across 2,593 edges**. A capacity-4 magpie town-wide on bendigo is a swooping town where you never meet the magpie. That was already ruled on once (`REVIEW_CITY_PATTERNS.md:111`) and this plan has to honour it, not quietly re-open it.
|
||
|
||
### TIER 2 — genuinely skinned · **+1 draw each, uninstanceable** · **CUT for this epoch**
|
||
|
||
The mechanism works. We proved it. **We are still not shipping it, and the argument that killed it came from our own plan.**
|
||
|
||
The justification for a skinned animal was: a quadruped **gait** is the one motion every human eye detects as wrong before it can read, so we can't fake a walking dog. True. But the actual spec anchored the dog at `shopFront` — **a dog tied outside the shops**, with a static tied dog as its `?noassets=1` fallback. A tied dog doesn't trot. The walk clip, the sole and entire reason the tier exists, would never play.
|
||
|
||
So either the dog walks — which needs a path, footpath alignment, a collision story and a sim hook that the design explicitly disclaims ("the ped sim never learns animals exist") — or the tier buys an **idle animation for one uninstanceable draw each**, which is exactly what tier 0b already delivers inside a draw it shares with fifteen others.
|
||
|
||
**Cut. Along with the download that fed it.** Revisit only with a *walking* dog spec that owns its path, its cull and its sim hook. Different round, different charter.
|
||
|
||
### The ledger
|
||
|
||
| tier | mechanism | draws | ship? |
|
||
|---|---|---|---|
|
||
| 0− heard | numpy synth, seeded anchor | **0** | **this week** |
|
||
| 0a flock | 1 instanced mesh, 1 atlas, town-wide | **+1** | **wave 1** |
|
||
| 0b prop | 1 instanced mesh, 1 species (ibis) | +1 | wave 1.5 |
|
||
| 1 scripted | 1 instanced mesh (magpie) | +1 | later, graph-scaled |
|
||
| 2 skinned | uninstanceable, +1 each | +1..2 | **CUT** |
|
||
| the wire | town-wide instanced spans | +1 | see §6 |
|
||
|
||
**Wave-1 spend: +1 draw.** The original plan said +7. The review cut it to +1 and the cut holds.
|
||
|
||
**One thing to fix before any of this is charter law:** the budget of record says the walked worst frame is 280/300 draws. Re-measuring headless I get **191 default / 167 classic** by teleport-walk, and can't reproduce 280 — it's almost certainly a transient during the chunk-dispose window, which teleport sampling can't hit. It stands as the conservative number, but spending against a figure nobody can currently reproduce is the weakest joint in the whole thing. Re-pin it first. Fauna helps here: its layers are **town-wide, not per-chunk**, so they take no part in the dispose window at all — their cost is steady-state.
|
||
|
||
---
|
||
|
||
## 3. INTAKE CHECKLIST — one screen, for while you're browsing
|
||
|
||
### ✅ ACCEPT ON SIGHT
|
||
- **CC0 / public domain.** No argument, no attribution obligation. Still record it in `SOURCES.md` — our discipline, not their demand.
|
||
- **Single `.glb`, glTF 2.0**, or a `.gltf` sitting next to `.fbx` (Quaternius ships both).
|
||
- **A named clip list you can actually read**: "Walk", "Idle", "Run", "Gallop". Not the word "rigged" on its own.
|
||
- **Under 3,000 triangles.** Under 900 for anything that'll be a bird.
|
||
- **One mesh, one material.** Draws are the scarce resource.
|
||
- **Textures embedded in the file.**
|
||
|
||
### ❌ REJECT ON SIGHT
|
||
- **A "Buy" or "Add to cart" button.** TurboSquid, CGTrader, Sketchfab **Store**, paid Fab. See the licence rules — **paying does not fix this**, and this is the most expensive avoidable mistake on the list.
|
||
- **"Rigged" with no clip list.** A rig with no animations is a static mesh with extra bones, and we cannot animate a quadruped ourselves — MIRPAMO is biped-only by construction, Mixamo's auto-rigger is verified humanoid-only. Worth exactly zero to us. We can make a static mesh in three minutes for free.
|
||
- **Five- or six-figure triangle counts**, and the photogrammetry/museum-scan look that produces them. The free "White Ibis" on Sketchfab is **238,200 triangles** — one bird exceeding our entire 200k street budget — and it doesn't move. Research and heritage-scan accounts are a reject-on-sight *category*, not individual bad models.
|
||
- **Draco / KTX2 / meshopt compression** (look for `extensionsRequired`). We vendor no decoders. Verified: the loader throws, our fail-soft wrapper (`loaders.js:38-44`) turns the throw into `null`, and the animal **silently does not exist**. A `console.warn` nobody reads. Downloaded assets in the wild are heavily Draco-compressed — this is the single most likely silent failure.
|
||
- **Right common name, wrong animal.** The free "White Ibis" is *Eudocimus albus*, the American one — not our bin chicken (*Threskiornis molucca*). "Magpie" on stock sites is almost always the European bird, which looks nothing like ours. Australian fauna is badly served by international libraries, and a wrong-species bird reads as wrong to every Australian and is invisible to everyone else.
|
||
- **NC / ND / "free for personal use" / "editorial use only".** Instant reject including when it costs nothing.
|
||
- **CC-BY-SA or GPL-on-art.** Share-alike is viral, and we retexture and rescale *everything* — we'd always be making derivatives. Would compromise a provenance table that's been clean for seven epochs.
|
||
- **No licence stated anywhere** — page, description, or the README in the zip. Default under copyright is all rights reserved. Silence is not permission. Close the tab; don't email and negotiate.
|
||
- **Real brands, sponsor decals, team logos, recognisable people.** `AUDIT.md` already quarantined branded assets once.
|
||
|
||
### 🟢 THE LICENCE RULES
|
||
|
||
- **🟢 GREEN — CC0.** Accept freely. Record URL, author, licence string and download date in `SOURCES.md` anyway.
|
||
- **🟡 AMBER — CC-BY.** Usable commercially forever, but the attribution obligation is **perpetual and travels with every build**. Hard rule: the `SOURCES.md` line goes in the **same commit** as the file, never "later". CC-BY is the one licence class that becomes a breach through nothing worse than forgetfulness.
|
||
- **🔴 RED — every paid marketplace royalty-free licence, structurally, at any price.** This is the finding that saves money rather than spending it. TurboSquid states it outright: stock media "must be contained in proprietary formats so that they cannot be opened or imported in a publicly available software application or framework, or extracted without reverse engineering" — then names the only exceptions: WebGL exports from Unity, Unreal, Lumberyard and Stingray. PROCITY is hand-rolled three.js with **no build step**, serving raw `.glb` over plain HTTP. A player presses F12 and saves the file. That is the named prohibited case. Sketchfab Store forbids letting third parties "use, download, extract or access" the asset — same wall. CGTrader forbids passing it on "in the form it is downloaded from the site" — same wall, blurrier wording, which is its own reason to stay out. **A $18 rigged possum is exactly as unusable as a free one.**
|
||
- **"Free" is not a licence.** It means "costs zero dollars" and says nothing about rights. Free3D's own terms cap "substantially similar creations" at personal use, max 5 instances.
|
||
- **Capture provenance at download time, not audit time.** Epic is folding Sketchfab into Fab, and **CC0, CC-BY-SA, CC-BY-NC and CC-BY-ND cannot migrate** — those licences don't exist on Fab. Free downloads have been on borrowed time since 2025. itch.io packs go paid or vanish without notice. Archive the file in-repo **and** record the exact licence string the same day. A dead URL with no recorded licence is an asset we can no longer prove we may use.
|
||
- **Internal conflicts = walk away.** If the itch.io page says CC0 and the README in the zip says "credit me", don't adjudicate and don't negotiate. Take the unambiguous asset instead. Ambiguity is what eventually contaminates the table.
|
||
|
||
---
|
||
|
||
## 4. VETTED SOURCES
|
||
|
||
| source | URL | licence | animated? | verdict |
|
||
|---|---|---|---|---|
|
||
| **Quaternius** | quaternius.com | **CC0 1.0** | **Yes, genuinely** — "Ultimate Animated Animal Pack": 12 animals × 12+ named clips | **The only one that matters.** glTF shipped alongside FBX so no conversion. Low-poly, single-material, flat-shaded — our exact budget shape. Roster is Cow/Donkey/Deer/Alpaca/Bull/Fox/Shiba/Stag/Husky/Wolf/Horse. **Zero Australian natives, zero birds.** |
|
||
| **itch.io** (Ariescar) | itch.io/game-assets/free/tag-3d/tag-animals | CC0 (per pack — read carefully) | Yes | "10 free CC0 rigged & animated low-poly animals (GLB) for Three.js" — literally built for our stack. Highest signal-to-noise after Quaternius. Licence sloppiness is the risk: page body and zip README often disagree. |
|
||
| **Poly Pizza** | poly.pizza | **Mixed CC0 / CC-BY per model** | Mostly no | Fast browse surface for Quaternius, not a source in its own right. The site most likely to sneak a CC-BY model into a folder of CC0 ones — read the badge on the individual model, never the bundle header. |
|
||
| **Kenney** | kenney.nl/assets/animal-pack-redux | CC0 | **No** — animals are static | **Skip.** Static is the one thing we already make locally for free. Excellent for props/UI/audio, just not this. |
|
||
| **Sketchfab (CC filter)** | sketchfab.com — filter `licenses=CC0` | Per model, CC0→CC-BY-NC-ND all coexist; **CC-BY is the default** | Sometimes | Only big library where you can filter Animated + Downloadable + permissive at once. Two warnings: **Store ≠ Sketchfab** (any "Buy" button is forbidden to us), and downloads are on borrowed time due to the Fab migration. |
|
||
| **OpenGameArt** | opengameart.org | CC0/CC-BY/**CC-BY-SA**/OGA-BY/**GPL** | Thin, dated | **The share-alike trap lives here.** Filter to CC0 and CC-BY only. |
|
||
| **Icosa Gallery** | icosa.gallery | Mostly CC-BY | **No** — the rescued Poly corpus is Tilt Brush/Blocks, completely motionless | Cross it off. Anyone calling it "the Google Poly successor" hasn't checked whether the models move. |
|
||
| **Blender Studio** | studio.blender.org/characters | CC-BY / some CC0 | Rigged, but `.blend` CloudRig control rigs, not baked GLB clips | Wrong shape. Film-density rigs meant to be hand-animated. Quality benchmark, not a download. |
|
||
| **TurboSquid** | — | Paid RF | Yes, incl. a rigged brushtail possum | **🔴 STRUCTURALLY FORBIDDEN.** See licence rules. Do not spend money. |
|
||
| **CGTrader** | — | Paid RF + free tier under RF terms | Yes | **🔴 Same wall, blurrier wording.** A provenance table wants "clearly permitted", not "probably defensible". |
|
||
| **Free3D** | — | Mixed and hostile | Sometimes | Per-asset licence archaeology on every file, real chance the answer is "no commercial" after you've downloaded. Not worth opening. |
|
||
| **Meshy / Tripo** | — | Free tier = CC-BY | Largely no | **Competitors to our own pipeline, not suppliers.** We generate static meshes locally for $0 with provenance we control end to end. Trading that for a murkier third-party line gains nothing. |
|
||
| **Mixamo** | — | Free w/ Adobe account | **Biped only — confirmed** | Auto-rigger requires a humanoid in T/A-pose and rejects quadrupeds. Adobe stopped developing it years ago. No path here. |
|
||
| **Anything World** | anything-world.gitbook.io | SaaS, ~$50/mo | Yes, incl. animals | Technically the closest thing to a magic wand, still a no: cloud dependency on a startup, and $50/mo for what turns out to be a one-dog problem we've now cut. |
|
||
| **Blender Rigify** | docs.blender.org | GPL (the *tool*; your rigs are yours) | Ships quadruped, cat, wolf/dog, horse and **bird** metarigs | **Worth knowing, and it corrects our own record.** We wrote down "we cannot locally rig a non-humanoid". True of MIRPAMO; **not true of the fleet** — Blender 5.0.1 is already on ultra. The gap is animator hours, not tooling. That's a scheduling fact, not an architectural wall, and the door stays open if you ever want an afternoon in Blender. |
|
||
|
||
---
|
||
|
||
## 5. SPECIES SHORTLIST
|
||
|
||
| species | tier | download? | why |
|
||
|---|---|---|---|
|
||
| **Sulphur-crested cockatoo** | 0a flock | **No — generate** | Screaming on a wire at dusk. All the charm is silhouette, placement and noise. Perched = zero locomotion. Five cockatoos for **one draw**, which no rigged model can ever match. |
|
||
| **Galah** | 0a flock | **No** | Same mesh as the cocky, second skin. `flux_local` does a skin in ~5.7 s — **the second bird is nearly free**. |
|
||
| **Corella** | 0a flock | **No** | Third cell on the same sheet. Free. |
|
||
| **Rainbow lorikeet** | 0a flock | **No** | A screeching mob in a flowering gum. Instanced statics. |
|
||
| **Australian white ibis** | 0b prop | **No** | The joke is entirely **postural** — head in the bin, or standing on one leg looking personally aggrieved. Nobody has ever laughed at an ibis's walk cycle. The named proof species; keep it in wave 1.5. Note: nothing usable exists to download — the free one is the wrong continent at 238k tris, and the correct 4.2k-tri one is a Store listing. |
|
||
| **Magpie** (swooping) | 1 scripted | **No** | Already approved. Static mesh on a seeded parabola, wing wave via the existing wind uniform. Being swooped in September is shared national trauma. Must scale territory with the road graph. |
|
||
| **Brushtail possum** | 1 scripted | **No** | Frozen on a fence rail at night with eyeshine — which is what a real possum actually does. The cheapest implementation is also the most accurate. Only proper rigged one found is $18 TurboSquid, forbidden at any price. |
|
||
| **Brush turkey** | 0b later | **No** | Bonus pick, and a strong one — the canonical town is redhill_godverse, Red Hill, Brisbane. Genuine local menace. Signature behaviour is standing next to a mound scratching, not walking. Too regional to exist in any stock library anyway. |
|
||
| **Blue-tongue / cane toad / flying fox** | 0a/1 later | **No** | Long tail, all near-zero cost. Toad = scripted hop. Flying fox = the magpie's parabola with different geometry, reused for free. |
|
||
| ~~Dog (kelpie/heeler)~~ | ~~2~~ | ~~Quaternius Shiba~~ | **CUT.** The only species that ever earned a download, and it was refuted by its own spec — anchored tied outside the shops, so the walk clip never plays. Also: a Shiba's curled tail and prick ears are **geometry, not texture** — `flux_local` retexturing gives you a tan Shiba, not a kelpie. The dog survives at tier 0− as a **bark behind a fence**, which is free and, honestly, more 1990s Australian than a visible dog. |
|
||
| ~~Gull, pigeon, cat, flies, crow~~ | — | — | **CUT.** Nothing about any of them says Australia, let alone 1990s. |
|
||
|
||
**And the thing that isn't an animal:** the single most 1990s-Australian-country-town object on this whole list is the **overhead powerline**. A main street with streetlights and no wires reads as a 2010s estate. The wire is worth more period-load per draw than any two bird species on the sheet — and it's the thing the birds are *for*. Wave 1 perches on the streetlight arm (`furniture.js:77-80`, y≈4.1) because it's free and period-plausible; a real wire is +1 draw, town-wide instanced, and needs the pole-pairing worked out first (poles **alternate sides every 18 m**, so same-side spans are 36 m and must not cross edge boundaries or you get wires cutting through intersections).
|
||
|
||
---
|
||
|
||
## 6. Two things that block, independent of animals
|
||
|
||
**1. `?classic=1` is already fetching four off-origin GLBs.** I verified the mechanism directly: `index.html:52` statically imports `chunks.js`, which imports `furniture.js`, whose module body fires `loadGLB()` at line 63-67 guarded by **`NOASSETS` only** — never by `CLASSIC` (`index.html:94`). ES module bodies run on import; construction guards are irrelevant to them. Measured: **179 requests, 25 `.glb`, 4 off-origin depot fetches** (bench, food cart, bin, bus shelter) under `?classic=1`. And `classic_regression` (`flags_check.py:128-241`) has **no network assertion at all**, so it goes green.
|
||
|
||
This is a live covenant breach in shipped code, and it matters here because that module-init pattern is precisely what the fauna plan proposed to copy. Binding: `fauna.js` is either dynamically imported behind its construction guard or carries a CLASSIC-derived module-scope guard, stated in the file header — and `classic_regression` grows a network **allow-list** (the four known depot GLBs plus same-origin), not a fauna-shaped deny-list. A deny-list only catches what you already thought of, which is exactly how this got through.
|
||
|
||
**2. `normalize.py` silently eats rigs.** Line 95 deletes every non-MESH object (its own comment says "empties, armatures, cameras, lights"), line 101 joins, line 105 clears armature modifiers. Point the existing pipeline at a rigged animal and you get a T-posed statue with the clips gone **and a green run**. Same for `decimate_props.py:37-45` and `bake_lowpoly.py`. Write it into their docstrings and make the intake tool refuse to run them.
|
||
|
||
Worth landing regardless of whether an animal ever ships: the spike's deliberate-failure fixtures (`bad_draco`, `bad_cm`, `bad_zup`, `bad_rootmotion`, `bad_2mat`, `bad_exttex`) move into `pipeline/_fixtures/` and become a permanent regression suite for free.
|
||
|
||
---
|
||
|
||
## 7. NEXT ACTION
|
||
|
||
### What Fable can start unattended this week, $0, no download, nothing to approve
|
||
|
||
**Tonight — the audio batch.** Six numpy synth functions into `gen_audio.py`'s `PACK`: magpie clack and carol, cockatoo screech, dog bark (fence and near), ibis grunt, crow. ~30 lines each, deterministic, 🟢 original, **zero draws and zero triangles**. This is the highest charm-per-minute item in the entire design and it cannot regress anything. A town with no visible animals starts reading as a place with animals in it.
|
||
|
||
**Then — the flock atlas, about ten minutes of GPU.** Sixteen `flux_local` bird sprites at ~5.7 s each → `bg_remove_local` → packed into one 1024 WebP 4×4 sheet. **No TRELLIS, no meshes** — a billboard needs an image, not geometry, so sixteen sprites cost two minutes where eight meshes would cost twenty-five. And it's not a new asset class: `manifest.skins.facade` is already twenty-five shipped, audited flux images used as textures. This single batch unlocks the whole +1-draw flock: cockatoo, galah, corella, lorikeet, perched magpie, ibis.
|
||
|
||
**Then — the ibis prop, six minutes.** `flux_local` → `bg_remove_local` → `trellis2_mlx` → `normalize`, decimated to ≤450 tris, head down.
|
||
|
||
Everything above is unattended, zero-risk and gate-free in the sense that nothing existing can break.
|
||
|
||
### If you want to go shopping anyway
|
||
|
||
Nothing in the plan blocks on it, and after the tier-2 cut there is **no download on the critical path**. But if you're browsing:
|
||
|
||
1. **Go to Quaternius first.** Everything else is downstream of it. CC0, glTF included, low-poly single-material by design.
|
||
2. **Before writing a single `SOURCES.md` line, open the `.gltf` and confirm `animations` is non-empty.** The number one silent failure of free packs is clips that live only in the `.blend` or `.fbx` while the `.gltf` ships an empty array. Fifteen minutes with tools we've already built: `pipeline/animal_intake.py` for the file checks, and the spike harness at `scratchpad/spike_run.py` — it takes a filename — to confirm a bare `AnimationMixer` actually moves a bone. **If the clips aren't in the glTF, walk away.** The pack's entire value to us is the walk cycle.
|
||
3. **The one thing genuinely worth grabbing while you're there is not an animal.** Quaternius's **"Universal Animation Library"** — CC0, 130+ clips on a Mixamo-compatible humanoid rig. That's fuel for our **human** fleet's 8-clip bank, which is the actual thin spot. It touches `rigs.js`, which is classic-covenanted, so it's its own round with its own gates. Don't fold it into the animal work.
|
||
4. **Do not spend money.** Not on TurboSquid, not on CGTrader, not on Sketchfab Store, not on a $50/mo auto-rigger. Their licences forbid serving an open `.glb` over plain HTTP, which is the only thing PROCITY does. Paying doesn't unlock them.
|
||
|
||
### The two-sentence version
|
||
|
||
Yes, every animal ships its own bespoke skeleton with its own bone names, and that turns out to be completely fine — a self-contained GLB binds its own clips to its own bones through its own mixer, so retargeting, the thing that needs a shared skeleton, is exactly what we must *not* do. What actually limits animals is **draw calls**: a skinned animal can't be instanced so it costs one draw each against maybe twenty spare, while sixty birds on wires cost one draw between them — so generate a bird sprite sheet and a handful of animal sounds this week for $0, and buy nothing, because the one species that would have justified a download was a walking dog and our own spec had it tied up outside the shops. |