makepad/libs/game/assets/examples/chars.rs
Admin 0ac21bdc9d A real cast: 36 rigged characters across 3 shared rigs — and Kenney IS rigged
THE FINDING THAT CHANGES THE PREMISE: "1 rigged model in 4,442" was measured
with the BROKEN GLB probe (the gLUF magic bug). With the probe fixed the
library holds 36 rigged models across three rigs:
  41 joints — 9 KayKit heroes + undead, up to 95 clips
   7 joints — 22 KENNEY civilians (male/female a-f, orc, human, archer, shop
              employees, skaters, soldiers), 32 clips
   6 joints — 5 Kenney platformer characters, 25 clips
So a village can be populated with 22 visually distinct civilians TODAY, with
no third-party pack at all. Every conclusion drawn from that probe before it
was fixed needs re-checking, not just this one.

KayKit: 9 characters fetched (Adventurers + Skeletons), pinned by commit +
sha256, 37 MB, gitignored. CC0 verified by READING LICENSE.txt at each pinned
commit, recorded in the script header and CREDITS.toml.

THE SHARED RIG HOLDS ACROSS PACKS, proven rather than assumed: hashing the
joint-name list of all nine files yields the SAME digest — 41 joints, same
names, same order — despite two separate repositories. Skeleton clips are a
strict superset (95 = the adventurers' 76 + 19 undead extras: awaken,
resurrect, spawn, taunt). So a clip authored for the knight plays on the
skeleton warrior and one animation path drives the cast. A test pins this,
including that both packs are present, so a version bump cannot silently break
it.

The texture trap that cost the Kenney fetch three attempts does NOT apply:
KayKit GLBs EMBED their atlas (image/png in a bufferView), verified by parsing
all nine.

tests/rigged.rs parses all 36 rigged models through makepad_game_render::skin
— the loader the app actually runs — and asserts the index's joint and clip
counts match it. Deliberate: the index's own probe was wrong for the entire
library once and survived because the fixture encoded the same error.

find_cast groups by JOINT COUNT rather than pack, because the valuable fact is
cross-pack interchangeability. Cast states are the INTERSECTION, not the union
— advertising a state one member cannot perform is worse than a shorter list.
Added the state words the skeletons needed (spawn/resurrect/taunt/use):
Skeletons_Awaken_Floor previously matched nothing, so "an undead that rises
from the ground" was unfindable.

One bug found in its own work: casts_to_json emitted a doubled closing brace —
malformed JSON that still looked fine in a log. Fixed with a structural test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 11:43:42 +02:00

28 lines
1.2 KiB
Rust

//! What rigged characters the library holds, and what the AI is told about them.
fn main() {
use makepad_game_assets::agent;
let idx = makepad_game_assets::AssetIndex::build(std::path::Path::new(
"apps/arcade/resources",
));
for c in idx.casts() {
println!(
"rig {:>2} joints — {} members, up to {} clips",
c.joints, c.members.len(), c.max_clips
);
println!(" states: {}", c.shared_states.join(", "));
println!(" members: {}", c.members.join(", "));
if c.richest.len() < c.members.len() {
println!(" richest ({} clips): {}", c.max_clips, c.richest.join(", "));
}
println!();
}
let json = agent::casts_to_json(&agent::execute_cast(&idx, None), 6);
println!("find_cast JSON ({} bytes):\n{}", json.len(), json);
println!();
for q in ["a character that can attack", "a skeleton enemy", "someone who can run and jump"] {
let hits = idx.find(q);
let top: Vec<&str> = hits.iter().filter(|h| h.entry.rigged).take(3)
.map(|h| h.entry.id.as_str()).collect();
println!("{q:<34} -> {}", top.join(", "));
}
}