CRIR
A Control Rig is a RigVM execution graph plus a rig-element hierarchy — unit nodes wired by
named pins, bones and controls arranged in a skeleton, all stored in a binary
UControlRigBlueprint you can only really read by opening the Control Rig editor and
tracing wires across the canvas. CRIR is that same rig as compact, line-based text: every node named,
every pin wire spelled out, the whole bone-and-control hierarchy listed. And because CRIR is
round-trip, your assistant does not just read it — it edits a few lines and compiles the result
straight back into the asset.
Why it matters
Diffable in version control
A rig change becomes a handful of changed lines in a pull request instead of an opaque binary blob. The same text is written as a crir.txt sidecar in the asset-dump cache, so rig history is greppable in git.
Reviewable like code
Execution is explicit: %localId node ids, full struct-path unit identity, and wire_in_<Pin>=%src.pin links. You see what a node does and where its inputs come from without following wires by eye.
A format an AI can author
One consistent, keyword-first language covering the full RigVM opcode set and the rig hierarchy — so your assistant writes whole graphs and control setups in a single call instead of dozens of blind create-node / connect-pin RPCs.
Faithful and deterministic
Two consecutive decompiles are byte-equal, and a decompile → compile → decompile round-trip is byte-equal. Node identity survives via struct paths, and authored @(x, y) positions are kept. Text back to graph is a real compile, not a lossy approximation.
A taste
A rig_hierarchy block lists bones and controls; one rig_graph per RigVM model
holds the executable nodes, with unit opcodes keyed by their full struct path and wired by
wire_in_* arguments:
rig_hierarchy {
bone "root" parent="" location=(0.0, 0.0, 0.0) rotation=(0.0, 0.0, 0.0) scale=(1.0, 1.0, 1.0)
bone "spine_01" parent="root" location=(0.0, 0.0, 12.0) rotation=(0.0, 0.0, 0.0) scale=(1.0, 1.0, 1.0)
control "spine_ctrl" parent="root" shape="Circle_Thick" location=(0.0, 0.0, 12.0) rotation=(0.0, 0.0, 0.0) scale=(1.0, 1.0, 1.0)
}
rig_graph "RigVMModel" {
%fwd = unit /Script/ControlRig.RigUnit_BeginExecution() @(-400, 0)
%get = unit /Script/ControlRig.RigUnit_GetControlTransform(Control="spine_ctrl", Space=GlobalSpace) @(-150, 0)
%set = unit /Script/ControlRig.RigUnit_SetBoneTransform(
Bone="spine_01",
wire_in_ExecuteContext=%fwd.ExecuteContext,
wire_in_Transform=%get.Transform
) @(150, 0)
}
How your assistant uses it
It reads the current rig out with controlrig.decompile_crir, edits the text, and compiles
the change back in with controlrig.compile_crir — a single call that replaces a long
sequence of node-create and connect-pin operations:
You: In CR_Mannequin, drive spine_01 from the spine_ctrl control.
call("controlrig.decompile_crir", {assetPath:"/Game/CR_Mannequin"})
→ {text:"rig_hierarchy { ... }\nrig_graph \"RigVMModel\" { ... }"}
call("controlrig.compile_crir", {context:"/Game/CR_Mannequin", save:true,
text:"rig_graph \"RigVMModel\" {\n %fwd = unit /Script/ControlRig.RigUnit_BeginExecution()\n %get = unit /Script/ControlRig.RigUnit_GetControlTransform(Control=\"spine_ctrl\", Space=GlobalSpace)\n %set = unit /Script/ControlRig.RigUnit_SetBoneTransform(Bone=\"spine_01\", wire_in_ExecuteContext=%fwd.ExecuteContext, wire_in_Transform=%get.Transform)\n}"})
→ {assetPath:"/Game/CR_Mannequin", blocksCompiled:1, nodesCreated:3, warnings:[]}
Done. spine_01 now follows spine_ctrl.
# TODO unsupported marker rather than round-tripping. Decompile an existing rig to learn the
exact shape before authoring an unfamiliar pattern.