Skip to content

Motion, all of it: people, metrics, matching, the character, physical animation, combat (#853) - #913

Merged
nicolas-maman merged 8 commits into
mainfrom
feat/anim-next
Nov 2, 2026
Merged

nicolas-maman merged 8 commits into
mainfrom
feat/anim-next

Conversation

@nicolas-maman

@nicolas-maman nicolas-maman commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

All of the remaining motion and animation work in one pull request (#853), until motion is right; other engine work waits for it. Issues are linked as "Part of", because this PR may be merged partway, as #891 and #905 were. Each issue is closed by hand, with its evidence, when its own list is met, and its box is ticked then.

The order

  1. People in the pictures and a README that shows the engine (README, docs and images that show what ae3d is for #902).
  2. A metrics library a game can call (M0 ground truth: a real human in CI (Quaternius mannequins, UAL clips), motion metrics, a viewer #854): Siltreach's scorecard and our own tests on the same definitions.
  3. Motion matching that holds on real motion capture (Motion matching: its changes turn joints 42% faster than the takes do #906).
  4. The character component (A character component: a moving, aiming, reacting person in a few lines #903) with usable objects (Usable objects and motion warping: a seat, a tool, a door a figure can use #908).
  5. Physical animation and get-ups (M4 physics asset and physical animation with per-body weights (L4) #858, M5 ragdoll and get-up from a pose snapshot into a matched clip (L5) #859), behaviours (M7 behaviours on physical animation, optional (L6) #861).
  6. Combat (Combat on the pipeline: mocap fight moves as takes, held to numbers #907) and CMU's motion capture (CMU's ASF/AMC read beside BVH #912).
  7. Moving the old motion code onto the pipeline (M6 migration onto the animation pipeline; old writers retired #860), crowds and animation LOD (M8 crowds and animation LOD #862).
  8. Two-person scenes (Two-person scenes: handing over, carrying, grappling, talking #909), animation over the network (Animation over the network: the graph's inputs and events, not poses #911), faces (Faces: blend shapes, blinks, saccades, eyes that look, visemes #910).

Part of

On this branch so far

  • People on the pipeline (0b3fcbd):
    • six CC0 people in resources/people;
    • pose maps Quaternius's modular naming;
    • a rig keeps every skin over its joints, and pose.flesh_extreme finds a joint's flesh across them;
    • a foot with no toe joint is measured from its flesh: 25.9 and 24.2 cm heel to toe, a planted toe sliding 0.00004 mm on the Rigify gaits retargeted onto them.
  • examples/street_life (ecf369f, 5ef5059): people on the street at half past five, every one on the pipeline, with takes retargeted once per skeleton and shared. The street it stands in is now examples/lib/street, shared with street_drive, and takes how wet it is: a dry evening road is matte, not a mirror.
  • The README (b8fda90): the street as its hero, an animated GIF of the people, and what ae3d is for. tools/gif.ae makes the GIF, 75 frames in 2.5 MB.

test_anim_pipeline: 87 checks, all passing.

test_anim_pipeline: 92 checks, all passing.

… their flesh (#902, #855, #857)

Six textured CC0 people in resources/people (Quaternius's Ultimate
Modular Men and Women; the owner's decision of 2026-11-02 for #902's
pictures; sources and hashes in its README), and what it took for the
pipeline to carry them:

- pose maps their naming (SCHEME_QUATERNIUS: Hips, Abdomen, Torso,
  Chest, UpperArm.L, UpperLeg.L...): the women's 51 of 53 slots, all but
  the toes they do not have; the men's 41 (their fingers number from 2).
- A rig keeps the skeleton it was read from and every other skin over
  its joints (pose.add_skin; the animator adds a figure's). These people
  are made in parts, a skin a part over the same joints, and the first
  skin carries only the arms.
- pose.flesh_extreme: the farthest point of a joint's flesh along a
  direction, over every skin.
- ikrig measures a foot with no toe joint from its flesh (flesh_feet):
  heel and toe the flesh's farthest back and ahead, the pad 0.72 of the
  way, in the foot's own frame. Read from its joints alone the foot was a
  point at the ankle. The figure's forward, with no toes to read it from,
  is square to the hips' line: a fixed axis of the rig's space was
  sideways for an armature as Blender exports it, and measured the feet
  across, 4 to 7 cm.

test_anim_pipeline (87 checks, all passing) holds them: the man's and
the woman's feet 25.9 and 24.2 cm heel to toe, and on the Rigify gaits
retargeted onto them with their feet locked, a planted toe slides
0.00004 mm at worst, 1.5 to 4 m/s. test_ik_constraints and test_matching
pass unchanged.
…902)

A new example: the zombie street at 17:30, its pavements walked and stood
on by the six CC0 people of resources/people, every one on the
animation pipeline. Their walk, jog and idle are the Rigify mannequin's
clips retargeted once for each skeleton the people come in and shared by
everyone on it (animator.share_take: sixteen people, two skeletons, six
takes); the walk and jog blended by the speed they are moved at; their
feet held where they land and rested on the paving, kerbs and road as
the physics world has them; standers talking, their heads turned to each
other. No bone is written by the example. AE3D_TIME, AE3D_PEOPLE and the
camera's AE3D_CAMX/Y/Z and AIMX/Y/Z, with AE3D_AWAY=1 to face away from
the sun.

The street it stands in is street_drive's, now in examples/lib/street
(block, slab, lamp_under and the street's own random sequence) and used
by both, so the street the car drives and the street people walk are
built one way. street_drive keeps its sequence, and with it its look:
534 bodies, 128 props, its bystanders as before.

Its first frames are the candidates for the README's hero (#902): into
the low sun, the road gold under it, the people backlit; or with
AE3D_AWAY=1, the sun on their faces.
…s for (#902)

The README opens on what the engine is for, open worlds full of
people, with a picture of one: examples/street_life at 17:30, the wet
road gold under the low sun, Quaternius's CC0 people on the pavements
(docs/images/hero.jpg, an unedited Vulkan frame at 1920x1080). The
collage of four unrelated demos and the lone mannequin on a grey plane
are gone. Characters and animation lead "What's inside", with an
animated GIF of the street's people walking toward the camera
(docs/images/street-life.gif), and say what the pipeline does in plain
lists. The gallery shows the night horde in the city where it showed a
crowd on a flat field. street_life is in the quick start and the
examples. The roadmap names #903, #906 and #907. The credits name the
people and 100STYLE.

tools/gif.ae makes the GIF from a burst of frames (AE3D_SNAPSHOT_BURST),
in Aether:
- one palette for every frame, by median cut over all of them, so a
  still part of the scene keeps its colours;
- a light ordered dither (AE3D_GIF_DITHER, 0 to 8);
- unchanged pixels left transparent, so a still camera's frames are runs
  LZW codes in a few bits: 75 frames of 640x360 in 2.8 MB, where every
  pixel coded was 11.8.
The output decodes frame by frame in Windows' own decoder.

street_life, from what its first GIF showed:
- Walkers keep to lanes, one each way a pavement, spaced along them at
  nearly one pace, and talkers stand by the buildings. With no avoidance
  yet, people walked through each other.
- The people's materials are made matte. Quaternius's export marks every
  one 40% metal at 0.27 roughness, and their clothes mirrored the street
  in blotches. The assets themselves are unchanged.
The road was too reflective (the owner, watching it). It was built as
street_drive's rainy night road in every scene that used the street:
- half a mirror (reflectivity 0.5);
- the export's rain-glossy material, lit by a low sun into a gold band;
- the wet-road reflections pass (shaders.ssr) laying its puddles over it
  at 0.75.

examples/lib/street's block takes how wet the ground is, 0 dry to 1
soaked. Wet, the road keeps the export's material and mirrors at 0.5,
so street_drive (wet 1) is unchanged. Dry, the road is matte tarmac
(roughness 0.9, reflectivity 0.06). street_life is dry by default, with
AE3D_WET to wet it; its SSR strength is the street's wetness times
street_drive's 0.75, so a dry road has no puddles. At 0.15 they still
showed, with #920's stepped edge.

The README's hero and walking GIF are made again on the dry road.
@nicolas-maman nicolas-maman changed the title Animation, next: people in the pictures, a metrics library, matching, the character (#853) Motion, all of it: people, metrics, matching, the character, physical animation, combat (#853) Nov 2, 2026
ae3d.metrics measures any figure from the pose that is drawn (pose.read),
whoever wrote it: an animator, ikrig, a ragdoll, a game posing by hand.
A Meter keeps the worst of each measure over a window and when it came:
joint pops, toe skating a stance, the hands' and pelvis's jerk, a sole
into the ground, the legs' gap, knees out or in, the chest's twist, the
forearm's roll, a foot crossing the hips' middle, and how far from a goal.
Contact is the pad within 15 mm of the ground, soles and toe tips from an
ikrig feet probe, so toeless rigs are measured from their flesh.

tests/test_metrics holds it to telling right from wrong on the Rigify
mannequin: the locked walk skates 0.09 mm against 88 mm bare, sinks 0
against 29.5 mm, keeps the legs 17.8 cm apart with no foot crossing, a
cut change pops 83 rad/s against the takes' own 35.5 while an eased one
stays at 35.5, and a still pose put on by hand measures still.

tools/anim_showcase now reports through the library instead of its own
measures, and flags each figure's defects by name.

ikrig: a lock that slipped is eased out only while the clip still has the
foot down; a release begun by lifting the foot ends when it lands, so the
new footprint is not drawn back toward the old (27 mm on the 2D blend).

ci.sh: the examples' shared code gets the same search path as every
program; examples/lib/street imports ae3d.physics and failed to resolve
aephysics on the module check.

Part of #854, part of #857, part of #902.
…857)

Measured with ae3d.metrics on the showcase, the worst a player would see
over its first 10 s, four runs:

- the female, brought to rest by a cut to her idle: a planted toe slid
  600 mm into the idle's stance; now 3.0 mm;
- the rifleman turning to aim: 16 mm, now 3.4 mm;
- the hit figure: its foot 120 mm into its block for its first 0.3 s;
  now on it from the first frame.

Feet that step (feet_set_steps) step only while the figure stands: its
object travelling under STEP_STILL (0.2 m/s), measured from one frame it
moved on to the next (the engine moves a figure on its fixed step and
draws it in between), still once it has not moved for 0.1 s. Moving, its
feet are the clip's steps and a lock slips as before, so a figure can
walk, stop and turn with steps on throughout. Stepping as it walked, a
trailing foot stepped in the walk's double support, crossed the other and
skated 20 mm. feet_still reads it.

A step rises before it travels and travels before it comes down (the
first and last STEP_RISE of its time), turns as it travels, lifts higher
the longer it is (STEP_LIFT_REACH), and rises from where its lock holds
it (a foot locked on its toe keeps the toe there). It lands into a lock
where it came down, on the heading it was turned to: held on the heading
of its lock before the step, its toe swung 17 cm round the ankle as it
landed.

feet_stand: the next solve stands the feet on the ground at once, the
pelvis down as they need. animator.set_feet asks it of feet put on a
figure before its first frame; on a figure already seen they still ease
onto ground the clip does not have them on, so no joint pops.

metrics (asked by Siltreach, wiring the meter into its motion cycle):
take_fastest(take, rig) reads a take's own fastest joint turn from the
take, the reference a pop is judged by, so a game need not play every
take alone (35.5377 rad/s read against 35.5378 played); meter_joint names
the joint each worst was at (the joint that popped, the foot that
skated).

The showcase's circling figures have steps on.

Part of #857, part of #854.
…d in the palms (#857, #907, #908)

Defects found with ae3d.metrics on the showcase, on stills of it, and on
Siltreach's pacer (the same graph as the showcase's stick).

Feet (ikrig):
- Posed where it is drawn. The engine draws what the fixed steps move
  between its last two steps (#563), but IK solved against where the steps
  left the figure, so a planted foot was drawn a step's share off its spot
  (23 mm at a walk, 6 cm at a jog) on any display faster than the steps.
  The engine gives core the frame's fraction before its pose phase
  (core.interp_set_frame); ikrig converts between the rig's space and the
  ground's through core.model_drawn_transform, and reads the floor, as the
  meter does, the same way. Scripts still read where the steps left
  things. The fastest joint turns uncapped: 400 to 1000 rad/s, now 70 to
  160 (the clips' own at 60 Hz).
- Locked on the heel. A landing foot was locked on its ankle; rolled down
  about it (the ankle stands 8 cm over the heel) the toe slid 28 mm. It is
  locked on its heel's sole until the ball is down, then on the ball. The
  stick's walk: 22 to 31 mm a stance, now about 5.
- Let go without a snap. A foot a 50/50 walk-and-jog blend left half down
  was dragged 25 cm behind the body with its heel peeled up, then let go
  from a lock it was never drawn at: 72 rad/s at the foot. A lock lets go
  from where the ankle was drawn; a locked point drawn LOCK_MISS (3 cm)
  off its lock lets go; a slipped foot is held again once every clip has
  it down (LOCK_AGAIN), or at once by a figure that stands; a peel eases
  out only when the clip lifts the foot (eased about the ankle while the
  foot was down, it swung the toe over the floor). That case: 21 rad/s,
  the clips' own 16.5. feet_miss reads how far the locked point was drawn
  off its lock.

Shoulders (retarget): a clavicle is carried as a turn from each rig's own
rest, not aligned to the source's direction. Where a skeleton puts its
clavicle is its build: 100STYLE's runs straight out, the UE5 mannequin's
38 degrees back, and aligned it swung each shoulder 15 cm forward
(shoulders 8.8 cm ahead of the neck, the mannequin's own clips 5.5 to 7.6
behind; now 5.2 behind), as Unreal's retargeter copies such a bone's turn
from each retarget pose.

Holding (ikrig hands, #907, #908):
- hands_hold: the palm's middle on a point, facing what it holds, the
  fingers along a way, closed by a curl; the hand frame measured from the
  rig's rest. Put on by the wrist with the clip's fingers, a box floated
  between two wrists and a rifle lay over two open hands.
- hands_fingers: each finger closed its own way (a grip's hand shape).
- hands_trigger: the index on a trigger, spread at its knuckle and bent at
  knuckle and middle joint each its own way (searched): its pad 0.9 mm off
  a pistol's trigger.
- hands_hold_box: what to hold a box with no handle by, from its size and
  weight (one hand round it if it fits a hand and is light, both on its
  sides, or from under it when heavy); hands_load leans the trunk back
  against the weight (5.4 degrees for 6 kg at the belly).
- hands_palm reads a palm's middle and facing, for a prop held in it.

metrics: meter_moved, for a figure put down elsewhere (a teleport, a course
begun again): measured through it, the climber skated 2 m.

The showcase:
- the captured walker on motion matching over 100STYLE's walks, side
  steps, turns, start, stops and a stand, instead of a cut from its
  forward walk to its backward walk (still skating at 60 Hz: open);
- the stick at the takes' own speeds (0.86, 1.86 m/s), as the pacer asks;
- the climber carries a 6 kg parcel by its sides, the rifleman a rifle
  with a pistol grip and a trigger, the index on it; props are placed in
  a pose phase after the figures', not a frame late;
- the climber's course restart resets its feet, interpolation and meter;
- the fastest joint turn and its joint in each figure's report;
  AE3D_WATCH_DIST and AE3D_WATCH_AT for a close look.

The showcase against de1a66a (worst toe slide a 10 s window, 60 Hz tick):
the stick's walk 4.3 mm (29.2 before), its stop and turn 16.6 (82.7), the
female's stop 2.4 and the rifleman 2.4 to 3.7 as before. Still open, as
before: the runner's and the female's sprint (20 to 24 mm), the
climber's course, and the captured walker (291 to 386 mm).

tests: test_anim_pipeline holds a 6 kg parcel in both palms (0.16 mm, the
trunk leaned 5.4 degrees) and a pistol grip's trigger under the index
(0.9 mm); 92 checks.

Part of #857, part of #906, part of #907, part of #908, part of #854.
… the leg's reach (#857, #906)

The captured walker on motion matching skated 291 to 386 mm a stance on a
60 Hz tick (uncapped runs hid it): asked to walk back, the matcher held its
stand a moment while the mover drew the body off its planted feet, the
right lock let go, and the release eased the foot 39 cm along the floor in
0.15 s to where the clip had it.

- A lock let go while the clip still has the foot down (a slip, or a leg
  that cannot reach its lock) steps the foot to where the clip has it over
  SLIP_STEP (0.28 s): risen, carried over, set down, lifted higher the
  further it goes, as a standing figure's steps are, and off the ground for
  its soles and tilt as they are. Lifted by the clip mid-step, it carries on
  from where the step is. The captured walker: 291 to 386 mm, now 5.5 mm a
  stance; the stick's stop and turn: 16.6, now 2.3.
- A lock's miss (LOCK_MISS) is now the leg's reach: the ankle drawn off the
  goal it was asked for, after a peel and before the soles and toes are set
  on the ground. Measured at the locked point, toes bent up out of a ramp's
  or a stair's surface drew the tip 3 to 6 cm off its lock and the lock let
  go every stride; with slips now steps, a foot down on the 20 degree ramp
  stood 100 mm off it. Now 4.5 mm.

The showcase: the walker's and the stick's stand is the stop take's
standing with its root under its hips (clips.extract_root), as the
pipeline test builds it; cropped from the capture as it was, the stand kept
the actor's hips 30 to 40 cm off the root every other take has under them.

Showcase on a 60 Hz tick, worst toe slide a 10 s window: the walker 5.5 mm,
the stick 2.3 to 4.3, the female's stop 2.4, the rifleman 2.4 to 3.7, the
runner 0.2. Still open: the runner's and the female's sprint (20 to 24 mm)
and the climber's course (41 to 99 mm).

Part of #857, part of #906.
@nicolas-maman
nicolas-maman merged commit 045b715 into main Nov 2, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant