Mixamo characters
Mixamo and Fuse characters brought in, their materials fixed, and drawn through anim.
A Mixamo or Adobe Fuse character reaches a game through the anim rig file: the rig names the body under bodies, and the bake plays every clip of the rig onto it in Blender, packs it with gltfpack and writes a glb to the loader model entry of the same name. A character that is a new kind with rules of its own, and not only a new body for a kind that exists, is designed first, as Designing a kind says.
Setting up
The bake reads BLENDER, PATCHWORK_LIBRARY and GLTFPACK, the native gltfpack 1.3 or later, from the shell, else from .env at the repository root, copied from .env.example. yarn health checks each and says the fix for any that fails.
Inspecting the FBX
- Open the new body and the body it replaces in Blender. Its bones are
mixamorig:<Name>, the same names as the clips'; a rig with other names takes no clip. - Its height is a person's, about 1.6 to 1.9 m. A body of other proportions still plays every clip, since only the hips' position is keyed.
- Images named
…_diffuse,…_gloss,…_specularand…_normalmark a Fuse or Mixamo spec/gloss body, whose materials the bake converts.
Swapping or adding a body
- Put the character in the library,
PATCHWORK_LIBRARY, else~/patchwork-library, ascharacters/<name>/<name>.fbx. The library holdsfarmer_woman,farmer_man,farmhand,mechworker,soldier,soldier_woman,xbot(Mixamo's X Bot, meshesAlpha_*) andybot(Y Bot,Beta_*). - The rig file's
bodiesis{ <model entry>: "<fbx under the library>" }. To swap a body, point its existing key at the new FBX; the kind, the catalogue entry, the glb path and every scene keep their names. - To add one, add a key, a
modelentry of that name inworld.loader.assets, and aworld.anim.kinds.<kind>naming it. - Bake every body in one run, from the repository root:
The rig's/Applications/Blender.app/Contents/MacOS/Blender -b --factory-startup --python plugins/anim/anim.export.py -- \ <rig.json> <scratch>/table.json <body>=<out.glb> [<body>=<out.glb> ...]tableis measured on the first body named and serves all of them. Each body's bone ends are measured too, intotable.ends.jsonbeside the table. - Each body's export prints
PACK <out.glb> raw=<bytes> packed=<bytes> meshes=<names>. A glb whoseasset.generatoris notgltfpackwas not made by the bake. - Write the measured table and ends back into the rig file, keeping the rig's layout, so only the numbers and the
bodieslines change.
A new kind that walks and is animated
Start from the kind in the game that already walks on walker and is drawn by anim: its rule, its walker, anim, hitscan3d and ragdoll3d entries in the scene, and its rig file. Then decide each of these in the scene:
- Its rig. A body that moves as the existing kind moves is one more key in that rig's
bodies. A body that moves differently, unarmed, a worker or a civilian, gets a rig file of its own beside its glb, with its ownclips,roles(stillalone for a body that never leaves the ground) andstances, and only the clips it plays: every clip in a rig is baked onto every body of it. - Its clips.
~/troupe-collection/packs/holds the packs, such asMale Locomotion Pack 30FPSfor an unarmed idle and walk, andpacks/mixamo-motions/single clips such asTalking At Watercooler.fbx. A walk is a gear (loop,gear: 0). An idle or a talk played in place is aloopplayed as an act, or a gesture on the upper channel over thestillrole. - What it is solid to.
world.walker.kinds.<kind>.bodieslists the kinds it walks round, and another kind walks round it only if that kind'sbodieslists it. A bystander that must not block the player lists only its own kind. - Whether it can be shot. A kind is a body to hitscan3d only with a
world.hitscan3d.kindsentry. Scenery has none, nohpand nohitact. - Where it stands and walks to. Rows in
actors, never a list onworldor in its definition: its own, and for the places it walks between, a kind of their own with oneactorsentry each. The place a row has taken is a field of that row, and a row chooses only among places no other row holds.
Materials
Blender reads a spec/gloss material as fully metallic with the gloss map as roughness, which draws cloth and skin as polished metal. The bake fixes every material whose roughness is fed by an image named gloss: metallic 0, roughness from that image inverted, no specular map, and alpha from the base colour image clipped at one half, or none when it is opaque. It logs METAL_ROUGH <material> alpha=clip|opaque for each.
- Check the bake printed a
METAL_ROUGHline for each material of each Fuse body. - In the glb, every material has
"metallicFactor": 0and a…_gloss_roughimage, and"alphaMode": "MASK"where hair, lashes or brows are. - Render the baked glbs in their
idleclip, three to a picture. Blender's glTF importer refuses a packed glb, so each is unpacked first, its meshopt buffers decoded with three.js's decoder. Cloth is matte, skin is skin and hair is cut out. A metallic sheen means a material's roughness is fed by an image not namedgloss.
Props on a bone
world.anim.kinds.<kind>.propsis a list of{ model, bone, at, turn }:atin metres andturnin degrees, a three.js'XYZ'Euler, in the bone's frame without scale.- Two Mixamo bodies roll their hand bones differently, often by 10 to 25 degrees, so refit each prop for the new body. Pose both bodies on the first frame of the clip the prop is held in and find the
atandturnthat keep the prop where it was. Three.js's'XYZ'is Blender's'ZYX'. - Write the new numbers into every scene that holds the kind's
animtable.
Checking it
- Look at the body in a gym scene: the game's own scene with the bodies standing from the first step, render3d's camera (
prefabs.render3d.cameraatandlook, in metres) a few metres in front at eye height, and noplayer, vehicle or HUD. A body in the scene'sactorswithgroundedtrue stands on the ground on its first step. - Run the tests beside the kinds that draw the body, the scenes whose props were refit, and the gym's own
<gym>.sim.test.ts.