← All text IRs

CRIR

Round-trip Control Rig (RigVM) graphs

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.
Early Access. CRIR covers the full RigVM opcode set and round-trips bones, nulls, controls, and sockets; a few hierarchy element kinds (curves, connectors, physics elements) decompile to a # TODO unsupported marker rather than round-tripping. Decompile an existing rig to learn the exact shape before authoring an unfamiliar pattern.

← Back to all text IRs