Rig

June 24, 2026 · View on GitHub

Overview

Rig is the central entity for armature serialisation and deserialisation in MPFB. It provides a full round-trip between a JSON rig definition file and a live Blender armature object.

Import path (JSON → Blender): Load a rig definition with from_json_file_and_basemesh, then call create_armature_and_fit_to_basemesh to build and position the armature in the scene. Internally, this calls create_bones (which invokes get_best_location_from_strategy for every bone end), update_edit_bone_metadata (parent/connect/inherit flags), and rigify_metadata (rigify type strings and parameters).

Export path (Blender → JSON): Load an existing armature with from_given_basemesh_and_armature or from_given_armature_context, which calls add_data_bone_info, add_edit_bone_info, and add_pose_bone_info to populate rig_definition, then match_bone_positions_with_strategies to replace raw coordinates with strategy dicts.

Positioning strategy system: Each bone end stores a named strategy that determines how its 3-D world position is resolved at fit time. Supported strategies are:

StrategyResolution
CUBEMean position of a named joint vertex group on the basemesh
VERTEXPosition of a single basemesh vertex
MEANMean position of two basemesh vertices
XYZOne vertex per axis channel (used for Rigify heel markers)
DEFAULTFalls back to the stored default_position coordinate

get_best_location_from_strategy resolves the actual [x, y, z] coordinate using cached KD-trees built by build_basemesh_position_info.

Parent-child rig concept: Subrigs reference a parent Rig via the parent keyword argument. This is used when resolving cross-rig constraint targets using the JOINTS strategy, where the subtarget bone in the parent armature is located by tracing joint-cube head/tail pairs.

Schema versioning: _upgrade_definition handles JSON schema migration (version 100 → 110). The main change in version 110 is the replacement of integer Rigify layer indices with named bone collection references.

Private helper methods (prefixed _) handle tasks such as constraint encoding/decoding, KD-tree caching, roll alignment, JOINTS strategy resolution, and bendy-bone serialisation. They are not part of the public API.

For the JSON file format this entity reads and writes, see the Rig definition file format.

Source

src/mpfb/entities/rig.py

Dependencies

  • Blender: bpy, bmesh
  • Math: math, random, typing, re, mathutils (Vector, Matrix, Euler, Quaternion, KDTree), bl_math.lerp, itertools.accumulate
  • MPFB services: LogService, ObjectService, RigService
  • MPFB entities: GeneralObjectProperties

Attributes

AttributeTypeDescription
basemeshbpy.types.ObjectThe mesh object the armature deforms
armature_objectbpy.types.Object or NoneThe Blender armature object; None until create_armature_and_fit_to_basemesh is called
parentRig or NoneParent rig for subrigs; used in cross-rig constraint resolution
position_infodictCache of vertex coordinates and joint-cube centre locations built by build_basemesh_position_info
rig_definitiondictPer-bone data: head/tail positions, strategies, roll, constraints, rigify parameters
rig_headerdictFile-level metadata: format version, bone collections, scale_factor, extra_bones, rigify UI
lowest_pointfloatMinimum Z coordinate among all basemesh vertices
bad_constraint_targetssetBone names whose constraint targets could not be resolved during export
relative_scalefloatScale factor relative to the parent rig

Public API

Factory methods (static)


from_json_file_and_basemesh(filename, basemesh, *, parent=None)

Create and populate a Rig from a JSON rig file and a basemesh object.

ArgumentTypeDescription
filenamestrPath to the JSON rig definition file
basemeshbpy.types.ObjectThe mesh object the armature will deform
parentRig or NoneParent rig, required if loading a subrig

Returns: Rig — the populated instance with build_basemesh_position_info already called.


from_given_armature_context(armature_object, *, is_subrig=None, operator=None, empty=False, fast_positions=False, rigify_ui=False)

Create a Rig from the active armature in the current scene context. Automatically detects whether the armature is a subrig and constructs a parent rig if needed.

ArgumentTypeDescription
armature_objectbpy.types.Object or NoneThe armature to export; None returns None
is_subrigbool or NoneOverride subrig detection if provided
operatoroperator or NoneBlender operator instance used for error reporting
emptyboolIf True, skip bone data extraction (returns a skeleton Rig with only position info)
fast_positionsboolIf True, use fast strategy matching (skips edit-bone roll and saved-strategy restore)
rigify_uiboolIf True, also extract Rigify UI state

Returns: Rig or NoneNone if no mesh context can be found.


from_given_basemesh_and_armature(basemesh, armature, *, parent=None, empty=False, fast_positions=False, rigify_ui=False)

Create a Rig from an explicit basemesh and armature object pair. The armature must be the active object in the scene context unless fast_positions=True.

ArgumentTypeDescription
basemeshbpy.types.ObjectThe mesh the armature deforms
armaturebpy.types.ObjectThe armature object to export
parentRig or NoneParent rig for subrigs
emptyboolSkip bone data extraction
fast_positionsboolUse fast strategy matching
rigify_uiboolAlso extract Rigify UI state

Returns: Rig — the populated instance.


Import — JSON → Blender


create_armature_and_fit_to_basemesh(for_developer=False, add_modifier=True)

Build the Blender armature object from rig_definition, fit it to the basemesh, and optionally attach an armature modifier.

ArgumentTypeDescription
for_developerboolIf True, also call save_strategies to persist strategy data on bones
add_modifierboolIf True, add or update the armature modifier on the basemesh

Returns: bpy.types.Object — the newly created armature object (also stored in self.armature_object).


get_best_location_from_strategy(head_or_tail_info, use_default=True)

Resolve a strategy dict to a world-space [x, y, z] coordinate by querying the cached KD-trees.

ArgumentTypeDescription
head_or_tail_infodictStrategy dict with keys strategy, and strategy-specific keys (cube_name, vertex_index, or vertex_indices) plus an optional offset
use_defaultboolIf True and the strategy cannot be resolved, fall back to default_position

Returns: list[float] or None[x, y, z] world position, or None if unresolvable and use_default=False.


create_bone_collections()

Create Blender bone collections on the armature from the collections list in rig_header, replacing any existing collections.

Returns: None


create_bones()

Create all edit bones from rig_definition, set head/tail positions using get_best_location_from_strategy, and apply roll values and roll strategies.

Returns: None


reposition_edit_bone(*, developer=False)

Refit all bones to the current state of the basemesh by reapplying strategies. Also resets STRETCH_TO, CHILD_OF, and ARMATURE constraint state as needed.

ArgumentTypeDescription
developerboolIf True, also rebuild ARMATURE constraint vertex targets for subrigs

Returns: None


update_edit_bone_metadata()

Set parent, use_connect, use_local_location, use_inherit_rotation, inherit_scale, bone collection assignments, and bendy-bone parameters on all edit bones.

Returns: None


rigify_metadata()

Switch to Pose mode and apply rotation_mode, constraints, rigify_type, and rigify_parameters to all pose bones from rig_definition.

Returns: None


Export — Blender → JSON


build_basemesh_position_info(take_shape_keys_into_account=True)

Populate position_info with all basemesh vertex coordinates and joint-cube centre positions. Builds the KD-trees lazily used by find_closest_cube and find_closest_vertex. Respects active shape keys when computing positions.

ArgumentTypeDescription
take_shape_keys_into_accountboolIf True, evaluate a mixed shape key to get deformed vertex positions

Returns: None


add_data_bone_info()

Extract head/tail positions, parent, connect flags, inheritance flags, bone collections, and bendy-bone data from the armature's data layer bones and store them in rig_definition.

Returns: None


add_edit_bone_info()

Extract roll values and roll-strategy custom properties from the armature's edit bones. Must be called while the armature is in Edit mode.

Returns: None


add_pose_bone_info()

Extract rotation_mode, constraints, and Rigify metadata from the armature's pose bones.

Returns: None


cleanup_float_values()

Round all float values in rig_definition (positions, roll, offsets) to 5 decimal places to reduce noise when re-saving.

Returns: None


Developer / strategy tools


save_strategies(refit=False)

Persist the current strategy data from rig_definition into bone custom properties (developer mode). Optionally skips writing edit-bone roll strategy properties.

ArgumentTypeDescription
refitboolIf True, skip saving edit-bone roll strategies

Returns: None


match_bone_positions_with_strategies(fast=False)

For every bone head and tail in rig_definition, search for the best positioning strategy (CUBE, VERTEX, or MEAN) and update the strategy dict.

ArgumentTypeDescription
fastboolIf True, skip MEAN strategy search (faster but less precise)

Returns: None


restore_saved_strategies()

Compare previously saved strategies (stored in bone custom properties) against the auto-detected strategies and apply the saved ones where they are at least as accurate.

Returns: None


list_unmatched_bones()

List bone names that still use the fallback DEFAULT strategy for their head or tail position.

Returns: list[str] — bone names with unresolved positioning strategies.


move_basemesh_if_needed()

Translate the basemesh so that its lowest vertex rests at Z = 0, then apply the transform. Only moves if lowest_point < -0.0001.

Returns: None


Spatial queries


find_closest_cube(pos, max_allowed_dist=0.01)

KD-tree search for the nearest joint cube to a world-space position.

ArgumentTypeDescription
possequence of 3 floatsWorld-space [x, y, z] to search from
max_allowed_distfloat or NoneMaximum search radius; None disables the distance limit

Returns: tuple[str, float] | tuple[None, None](cube_name, distance), or (None, None) if no cube is within range.


find_closest_vertex(pos, max_allowed_dist=0.01)

KD-tree search for the nearest basemesh vertex to a world-space position.

ArgumentTypeDescription
possequence of 3 floatsWorld-space [x, y, z] to search from
max_allowed_distfloat or NoneMaximum search radius; None disables the distance limit

Returns: tuple[int, float] | tuple[None, None](vertex_index, distance), or (None, None) if no vertex is within range.


find_closest_vertex_mean(pos, max_allowed_dist=0.02, search_radius=None)

Find the pair of basemesh vertices whose mean position is closest to the given world-space coordinate.

ArgumentTypeDescription
possequence of 3 floatsWorld-space [x, y, z] to search from
max_allowed_distfloatMaximum mean distance to accept
search_radiusfloat or NoneSearch radius for candidate vertices; defaults to 15% of mesh height

Returns: tuple[list[int] | None, float]([idx1, idx2], mean_distance), or (None, max_allowed_dist) if no suitable pair is found.


Static utilities


apply_bone_roll_strategy(bone, roll_strategy, roll_reference_z=None)

Apply a named roll-alignment strategy to an edit bone in place.

ArgumentTypeDescription
bonebpy.types.EditBoneThe edit bone to adjust
roll_strategystrOne of ALIGN_Z_WORLD_Z, ALIGN_X_WORLD_X, ALIGN_Z_REFERENCE_Z
roll_reference_zlist of 3 floats or NoneReference Z vector for ALIGN_Z_REFERENCE_Z

Returns: None


get_bone_end_strategy(bone, is_tail)

Read the head or tail strategy from a bone's custom properties.

ArgumentTypeDescription
bonebpy.types.Bone or bpy.types.EditBoneThe bone to read from
is_tailboolTrue to read the tail strategy, False for the head

Returns: tuple[dict | None, bool](strategy_dict, is_locked). strategy_dict is None if no strategy is stored.


assign_bone_end_strategy(bone, info, is_tail, *, force=False, lock=None)

Write a head or tail strategy dict to a bone's custom properties.

ArgumentTypeDescription
bonebpy.types.Bone or bpy.types.EditBoneThe bone to write to
infodictStrategy dict to store
is_tailboolTrue to write the tail strategy, False for the head
forceboolOverride the lock flag if set
lockbool or NoneOptionally set the lock flag alongside the strategy

Returns: None


get_armature_constraint_vertex_index(con)

Extract the vertex index encoded in an ARMATURE constraint name using the VERTEX:<n> naming convention.

ArgumentTypeDescription
conbpy.types.ArmatureConstraintThe constraint whose name is parsed

Returns: int or None — the vertex index, or None if the name does not match the convention.


ensure_armature_constraint_vertex_index(con, vertex)

Update (or prepend) the vertex index in an ARMATURE constraint name.

ArgumentTypeDescription
conbpy.types.ArmatureConstraintThe constraint to update
vertexintThe vertex index to encode in the constraint name

Returns: None


Examples

Create an armature from a JSON rig file:

from mpfb.entities.rig import Rig

rig = Rig.from_json_file_and_basemesh("/path/to/rig.json", basemesh_object)
armature = rig.create_armature_and_fit_to_basemesh()

Extract the current armature state to a dict:

from mpfb.entities.rig import Rig
import bpy, json

rig = Rig.from_given_basemesh_and_armature(basemesh, bpy.context.active_object)
print(json.dumps(rig.rig_header, indent=2))