Files
jeremy 31ba2911df init: animation pipeline hub — Mac↔PC bridge for converting and implementing animations
Skills (.claude/skills/): animation-creation, iclone-video-mocap,
pose-estimation (MediaPipe models via LFS), retarget-animations (deprecated).
Tools: cc/mixamo/kevin/mocap retargeters + composite baker.
Plans: animation-gen-pipeline + skills-adoption.
exchange/: incoming-fbx, converted-glb, reference-video (LFS for fbx/glb/mp4).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 10:29:48 -07:00

4.3 KiB

gltf-transform recipes

Headless GLB surgery with gltf-transform (donmccurdy, gltf-transform.dev) — inspect, merge, transplant, and shrink animation packs with no Blender required. Same Quaternius skeleton across all game GLBs, so node names line up 1:1 and clip moves are mechanical.

Install / run

CLI (no global install — npx pulls it on demand):

npx @gltf-transform/cli --help
npx @gltf-transform/cli inspect assets/quaternius/kevin/kevin_male_social.glb

Or as a JS SDK from a node script (mirror the layout of the existing tools/bake_run_punch.mjs):

import { NodeIO } from "@gltf-transform/core";
import { prune, dedup, resample } from "@gltf-transform/functions";
const io = new NodeIO();
const doc = io.read("path/to/file.glb");

Install once into the repo's node tooling: npm i -D @gltf-transform/core @gltf-transform/functions (the orchestrator installs — you author the script only).

Inspect clips

# Full report: meshes, animations, channels, duration.
npx @gltf-transform/cli inspect assets/quaternius/kevin/kevin_male_social.glb

For a quick clip-name list without the SDK, the pure-node JSON-chunk one-liner already in SKILL.md (Path 1) reads the GLB's JSON chunk and prints animations[].name — use it to confirm a clip exists before importing/transplanting.

Clip transplant between same-skeleton GLBs

Both files share the Quaternius skeleton, so every channel's target node matches by name. Sketch: read source + target, copy the source Animation into the target document, then re-point each channel's targetNode to the target-doc node with the same name.

// verify against gltf-transform v4 API before first use
import { NodeIO } from "@gltf-transform/core";
import { copyToDocument } from "@gltf-transform/functions";

const io = new NodeIO();
const target = io.read("assets/quaternius/mixamo/northern_soul.glb");   // pack to extend
const source = io.read("downloads/clip_to_add.glb");                     // same skeleton

const srcAnim = source.getRoot().listAnimations()
    .find(a => a.getName() === "NewClip");
const targetNodes = new Map(target.getRoot().listNodes().map(n => [n.getName(), n]));

copyToDocument(target, source, [srcAnim]);          // copy the Animation property
const dstAnim = target.getRoot().listAnimations().at(-1);
for (const ch of dstAnim.listChannels()) {
    const srcNode = ch.getTargetNode();
    if (srcNode) ch.setTargetNode(targetNodes.get(srcNode.getName()));  // re-point by name
}
io.write("assets/quaternius/mixamo/northern_soul.glb", target);        // overwrites — back up first

Common variations: copy all source animations (iterate listAnimations()), rename on the way in (dstAnim.setName("Wave01")), or strip a clip (remove its channels then prune). If copyToDocument isn't the right entry point in your installed version, fall back to walking srcAnim.listChannels() and rebuilding each (sampler interpolation + input/output accessors + targetNode/path) on a fresh target.createAnimation().

Shrink / clean

Standard transforms, applied in this order for pack size:

npx @gltf-transform/cli optimize <in.glb> <out.glb>      # resample + prune + dedup combined
# or individually:
npx @gltf-transform/cli <in.glb> <out.glb> resample      # drop redundant keyframes
npx @gltf-transform/cli <in.glb> <out.glb> prune         # remove unreferenced data
npx @gltf-transform/cli <in.glb> <out.glb> dedup         # merge identical accessors/meshes

In the SDK: await doc.transform(prune(), dedup(), resample()). resample takes an optional tolerance/ratio to trade fidelity for size — retargeted clips have clean curves and tolerate aggressive resampling; mocap-sourced clips do not.

When to prefer this over Blender

Prefer gltf-transform when there's no new motion to author:

  • Same-skeleton clip moves (one clip from a downloaded GLB into a shipped pack).
  • Batch renames (fix a pack of *_Loop suffixes, normalize clip names).
  • Pack merges / splits (combine two assets/quaternius/mixamo/ packs).
  • Headless size passes before shipping (resample/prune/dedup).

Reach for Blender (Path 2 retarget / Path 5 authoring) when the source uses a different skeleton (needs the world-rotation-delta retarget), or when you must author, IK-solve, or layer new motion that exists nowhere.