Armature Bend

examples/armature-bend/

Rigging end to end in the data API — edit_bones chain construction, name-bound vertex groups with smoothstep blend zones, posing, and depsgraph evaluation — bending a ribbed bellows hose through rest, half, and full curl.

Rendered headless by the example itself. Select it to enlarge.

witnesses edit_bones exists only in edit mode (empty in object mode), vertex groups bind to bones strictly by name, and the armature modifier is exactly linear blend skinning: every evaluated vertex must equal Σ wᵢ·(pose_bone.matrix @ bone.matrix_local.inverted()) @ rest, root ring pinned, tip deflected — a straight tube fails.

tags armature depsgraph

runs on 4.5 LTS · 5.1 · 5.2 LTS

blender --background --python examples/armature-bend/armature_bend.py --

falsify it Breaks the contract on purpose and must exit 8

blender --background --python examples/armature-bend/armature_bend.py -- --zero-curl

A runnable example that rigs a tapered, ribbed bellows hose with a four-bone armature chain — edit_bones construction, name-bound vertex groups with smoothstep blend zones, a posed curl — and checks the depsgraph-evaluated result against closed-form linear blend skinning, following mesh-editing-and-bmesh and depsgraph-and-evaluated-data.

What it witnesses: the three contracts AI-generated rigging code most often gets wrong.

  1. edit_bones only exists in edit mode — the collection is asserted empty in object mode and holding the full chain (heads/tails at closed-form positions) inside mode_set(mode='EDIT'). Outside edit mode, reads silently return nothing.
  2. Vertex groups bind to bones by name; a typo deforms nothing without erroring. Every group here is named after its bone and the deform proves the binding.
  3. The armature modifier is exactly linear blend skinning: for every vertex, evaluated == Σ wᵢ · (pose_bone.matrix @ bone.matrix_local.inverted()) @ rest. The check re-implements LBS from the pose matrices and compares the whole evaluated mesh (max error ~5e-7). The root ring must stay pinned and the tip must deflect — a straight tube is a failure.

The armature API is identical on Blender 4.5 LTS and 5.1 — no version gate needed. One portability hazard is baked in as a comment: interleaving color_attributes writes with VertexGroup.add() dangles the attribute reference when the deform layer is allocated (hard crash on 4.5, silent luck on 5.1), so the script finishes all weight writes before creating the color attribute.

The render shows rest → half curl → full curl left to right. The hose is lathed from an (r, z) profile — brass collars at both ends, a ribbed rubber section between — and every weight is a function of z alone, so the ribs change nothing the LBS check asserts; they make the deformation legible, fanning open on the outside of each bend and crowding on the inside. Per-bone weight bands are visualized through a BoneTint color attribute on the rib crests (a Crest attribute darkens the valleys) — the smooth color blends at the joints are the same weights the check asserts. Each hose stands on a render-only steel foot flange; the render path gates framing through examples/gallery_framing.py (exit 10) before writing the still.

Run#

# Cheap correctness check (no render) — the CI check:
blender --background --python armature_bend.py --

# Falsifier: rest pose. Must exit non-zero (tip deflection).
blender --background --python armature_bend.py -- --zero-curl

# Also render a still (EEVEE on a GPU host; use --engine cycles on GPU-less hosts):
blender --background --python armature_bend.py -- --output bend.png
blender --background --python armature_bend.py -- --output bend.png --engine cycles

Exit codes#

Per-script sequential checks. 9 is a valid check code; there is no rule against it.

CodeMeaning
0Success
1Uncaught exception (FATAL wrapper)
2argparse / usage
3edit_bones populated in object mode
4Edit-mode bone chain off closed form
5Evaluated vertex count changed
6Evaluated mesh off closed-form LBS
7Root ring moved
8Tip did not deflect (--zero-curl lands here)
9--output produced no file
10Framing gate violation on the --output path (gallery_framing)

The blender-smoke workflow runs the check on Blender 5.2 LTS and 4.5 LTS (5.1 on the weekly cron, the needs-5.1 PR label, or manual dispatch). Smoke does not pass --output. Its catalog falsifier is --zero-curl (expects exit 8).

Source

examples/armature-bend/armature_bend.py View the script on GitHub →
Three ribbed bellows hoses with brass collars on steel foot flanges, banded teal to amber to coral, standing straight, half curled and fully curled by an armature.