| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| 1.5.2_ Support VFX _ Animation for character source code.tar.gz | 2026-09-03 | 1.4 MB | |
| 1.5.2_ Support VFX _ Animation for character source code.zip | 2026-09-03 | 1.6 MB | |
| README.md | 2026-09-03 | 9.0 kB | |
| Totals: 3 Items | 3.1 MB | 1 | |
Changelog
[1.5.2] — 2026-09-03
Theme: a clip that exists is not a clip that plays, and rigging may only add. Rigging failures in 1.5.1 were total and silent: clips held actions, the mixer held state, buttons dispatched, and nothing moved. This release turns the measured repair pipeline into executable modules, wires it into the workflow, and freezes the source geometry so binding cannot silently damage the model.
Workflow integration
Added
animated-characterworkflow profile (forge/_shared/workflow_state.pyRIG_STEPS,forge/state.py). Nine Stage R steps join the checklist: read the contract, read the rig from the GLB, repair the mesh, freeze it, validate the payload, bind, verify parity, measure the clips, run the gates. Everyforge/stage5_rig/module was callable before this and nothing in the workflow ever told anyone to call one —next.pywalks the checklist, so a gate absent from the checklist never runs, and a gate that never runs reports a clean verdict forever.- The Stage R order is enforced by
forge/tests/test_rig_workflow_steps.py, not by convention. Repair precedes the freeze; the freeze precedes every rig step; parity is verified after the bind. Moving the freeze later would let a bind rewrite vertices and then certify the rewritten buffer, so the manifest would attest to the damage instead of catching it. forge/stage5_rig/mesh_parity.py— freeze the geometry, then prove it survived. Hashes are taken over packed bytes rather than JSON text, so a formatter changing its mind is not a reported change while a changed number cannot be formatted into a collision.position,normal,uvandindexare frozen;skinIndexandskinWeightare deliberately not, because adding them is the entire legal purpose of rigging — freezing them would fail every successful rig and the gate would be switched off within a day. A single nudged float is caught and located to the mesh, the attribute, the element and both values.forge/stage5_rig/glb_rig_reference.py— read the skeleton, the skin's joint ordering, the inverse bind matrices and the clips FROM the GLB, and sample its own animation channels into the payloadclip_featuresconsumes. Joint correspondence to a procedural skeleton is matched by measured position, never by name, and reportsusable: falsewhen any joint on either side is unmatched. A GLB's channels target its node indices and itsskinIndexvalues address its joint array; fed to a procedurally authored skeleton they index a different rig entirely.docs/GLB_ANIMATED_CHARACTER_PROMPT.md— one copy-paste prompt carrying a subject from a GLB to a rigged, animated, gate-cleared character in a single pass.- Add reusable build, GLB measurement, polish, and VFX prompts under
docs/standard-prompts/, with the VFX pass driven by a measured event table rather than timing guessed from clip names.
Fixed
forge/stage5_rig/emit_rig.pyconstructedTHREE.SkeletonbeforeupdateMatrixWorld, socalculateInverses()captured identity matrices, the rest pose never cancelled, and every vertex sat displaced by its bone's offset. That failure compiles, binds, reportsbound: trueand renders a corpse. The main factory emitter already ordered this correctly; only the Milestone-0 emitter did not.forge/stage3_build/generate_threejs_factory.pynow refuses to bake a geometry twice.applyMatrix4mutates in place and is not idempotent, so a second pass applies the world matrix squared and scatters the parts. It throws rather than skipping, because a silent skip would leave a mesh in the wrong space and resurface later as a subtler misplacement.
Changed
docs/pipelines/character-rigging-animation-1.5.2.md§R0.1 carried a claim that did not reproduce. It cited the Lee Sin rig for "clips target technical nodes that are not joints"; re-measured on that asset, all 1,353 channels across all 11 clips target deform joints and zero target technical nodes. The rule stands — it is what keeps the skin's index space intact — but it is now stated as the risk it guards against rather than as an observation, anddeformVsTechnicalreports the real count per asset.
Core animation pipeline
Added
The animation pipeline — forge/stage5_rig/
clip_features.py: the §1 measurement vocabulary (duration, travel, rise, speed, handRange, footRange, headRise, scaleDelta, poseReturn) sampled at N = 25, the §2 classifier, §3 naming with theinferredhonesty flag, and the §4 loop rule.scaleDeltais a tripwire, not a descriptor: a non-zero value means the source rig scales joints, which changes what R2 may legally do to skin weights, and it is surfaced before anything else proceeds.- The loop rule is corrected, and the old one is pinned as a regression test. 1.5.1 used "a clip
that neither travels nor rises can repeat seamlessly", which contradicted its own data —
idle-gesturetravels 0.121H, six times the idle threshold, and is correctly loopable. Loop is nowposeReturn ≤ 0.5°and‖hip(T) − hip(0)‖ ≤ 0.01H. A host that cannot measureposeReturngetsloop = None, never a defaultedfalse— "measured, does not loop" and "nobody looked" are different answers. skin_conditioning.py: proximity weight blending for characters built from overlapping parts. Dense accumulation before mixing, buffered writes so the result is order-independent, a uniform grid hash at one cell per radius (≤ 27 buckets per query), and interior vertices left bit-identical. Coincidence-welding is kept as an executed negative result: it closes one crack and leaves 28 frames cracked, because adjacent parts overlap rather than share a rim.- The R2 trade is stated wherever it runs rather than buried: blending closes holes (974 px in 30 blobs → 287 px in 15) and makes creases ~16% worse (31,316 px → 36,470 px). A hole shows the background, a crease shows skin. A later stage must not "fix" the crease count by disabling it.
action_design.py: §R4 target bands, topology-driven chain resolution (arms, legs and spine resolved without reading a single joint name), the medial/lateral gate that catches a mirrored rig, gait as a phase machine with real stance/swing intervals, andfoot_slide— the gate that catches a gait reading as "floaty" or "skating" while being hard to name by eye.rig_gates.py: the G1–G12 runner. A gate whose input is absent reportsunevaluated, never a pass, and any unevaluated gate makes the whole report not-ok.emit_animation_runtime.py: emits the R1/R5 runtime as TypeScript — identity bind in attached mode, display offset from mesh bounds alone,refreshTickers()for lazily-built characters, the explode-offset order around the tick loop, and the controller contract. A clip whose loop was never measured emitsLoopOncewith a comment saying so, never a silentLoopRepeat.docs/pipelines/character-rigging-animation-1.5.2.md(the derivation and the failure log),forge/stage5_rig/CONTRACT_1.5.2.md(module map and payload shape) andgrimoire/readiness/animation_contract.md(the routing file read at Stage R).
Fixed
- §R4's limb bands were unsatisfiable as written, and the implementation proved it. §1 defines
handRange/footRangeas world-space ranges; under that definition a forward-travelling gait givesfootRange ≈ travelandhandRange ≈ travel + swing, so no speed lands in the stated0.15H–0.25H/≈ travel/2bands. The limb bands are hip-relative and now say so; the global features stay world-space, the only frame the §2 classifier is meaningful in. footRange ≈ travel / 2compared two different quantities:travel / 2is the step length between alternating footfalls,footRangeis a positional range. The≈is read as ±30%.
Known limits at this release
- The Stage R checklist references
runtime/scripts/export_mesh_buffers.mjs, butruntime/remains ignored and the exporter is not present in a clean core checkout. Mesh freeze/parity therefore needs a host-provided raw-buffer export until that producer is shipped or the docs are rerouted. - G1, G3, G6, and G10 still have no complete measurement producer. They correctly report
unevaluated, so a full G1–G12 report is expected to remain not-ok rather than claiming coverage that did not run. - The classifier thresholds and the
0.006Hblend radius are calibrated from one rig and remain starting values until they are reproduced on a second subject. - Proximity blending reduces holes at overlapping parts but does not eliminate creases; doing so requires a continuous mesh and a different topology contract.