Character rigging and animation for img2threejs, as an img2 plugin.
img2 add img2threejs/plugin-characterAn img2threejs reconstruction produces a static mesh. This plugin takes it from there: a skeleton read out of a GLB, skin conditioning across overlapping parts, clips measured rather than guessed at, and a twelve-check gate where an unmeasured check is never a pass.
The base skill stays the entry point. Without this plugin a character reference still reconstructs — with no rig, no skinning and no bind. Less exact is the intended trade; useless is not.
Nothing about a rig is believed until it has been evaluated at a sampled time and compared against the value the track says it should have. A clip that exists is not a clip that plays.
Two bugs in the pre-plugin build each produced a plausible scene with zero motion: eleven clips held actions, the mixer held state, buttons dispatched, nothing moved. Neither was visible in code review; both were found only by measuring. Every gate here traces to a failure that was measured, not imagined.
Every threshold is a fraction of figure height H, never an absolute unit. Numbers marked
single-subject came from one rig and are starting values, not constants.
One gate row, not twelve. img2_core.gate_runner is sequential and stop-the-line: the first
non-passing blocking row halts the run and the rest are recorded as skipped. Twelve rows would surface
one failure and eleven skips. The aggregate preserves the point of the suite, which is seeing every
violation at once.
unevaluated maps to error, and never appears in status. A check whose input is absent is not
a pass. The verdict parser accepts only pass|fail|error, and an unrecognised status makes it discard
every reason supplied — so the distinction lives in reasons and evidence instead. Four of the
twelve checks have no input producer yet, so error is the honest verdict on a real run today.
python3 -m unittest discover -s teststests/test_oracle_replay.py is the correctness oracle for the extraction: a byte comparison against
a frozen run on a real 113-node GLB. It is deliberately a byte comparison — a semantic assertion has
to enumerate what matters, and the point of an oracle is to catch the change nobody predicted.
Apache-2.0.
