Accessing Bones in the CATRig Skeleton
Accessing bones by name in a skeleton tends to cause errors. For example, if an artist renames a bone, your scripts might stop working. 3ds Max CAT is designed to let you navigate hierarchies from a script without necessarily know the names of the bones in the rig. This means that you can write scripts that will work on any CATrig, regardless of the naming conventions used.
The CATRig is structured like a tree. Each bone has child bones that you can navigate to and get their children. There are special controllers in CAT that hold parts of the tree together, but do not show up in the hierarchy you see in 3ds Max. For example, there is a controller called “Limb” whose job is to maintain the limb. Each bone in the limb is generic and contains only information related to that specific bone. If you want information about the limb, you can access the limb controller.
The basic tree structure looks like this: From a hub controller you can get to a limb controller, and from a limb controller you can get to the collarbone controller. From a hub controller you can get to a tail controller, and from a tail controller you can get to any of the tailbone controllers. From a hub controller you can get to a spine controller, and from a spine controller you can get to any of the spine bone controllers, or to the hub controller at the tip of the spine.
Hubs Hubs->Limbs Hubs->Limbs->Collarbone Hubs->Limbs->Limb Bones Hubs->Limbs->Limb Bones->Bone Segments Hubs->Limbs->Palm/Ankle Hubs->Limbs->Palm/Ankle->Digits Hubs->Limbs->Palm/Ankle->Digits->Digit Bones Hubs->Tails Hubs->Tails->Tail Bones Hubs->Spines Hubs->Spines->Spine Bones Hubs->Spines->Tip Hub
The 3ds Max CAT scripting interface includes methods that let you access bones using bone “addresses.” Each bone in a CATRig contains a unique address.
Select a bone and enter the following into the Listener:
$[3].address
The result looks something like this:
"SceneRootNode.Hub.Spine[0].Hub.Limb[0].LimbBone[1].BoneSeg[0]"
Once you have an address, then is is easy to get the same bone on a different rig. There is a method on the CATParent for retrieving a bone from the skeleton using an address. This method is called GetBoneUsingAddress.
Select the CATParent of a CATRig and enter the following into the listener. In this example, we have selected a CATRig’s left elbow.
$[3].address
The result looks something like this:
"SceneRootNode.Hub.Spine[0].Hub.Limb[0].LimbBone[1].BoneSeg[0]"
Onne you have a bone address, you can use that address on the same or a different CATRig to retrieve the same bone. For example, you can use the preceding address to find the left elbow on any CATRig:
$.GetBoneByAddress "SceneRootNode.Hub.Spine[0].Hub.Limb[0].LimbBone[1].BoneSeg[0]"
$Object:MaramaLForeArm1 @ [18.374180,2.811419,91.353607]
Getting all the Bones that make up the CATRig
The CATParent provides a method, CATRigNodes, that generates a list of all the bones in a CATRig.
Example: Select the CATParent of a CATRig and enter the following into the Listener.
$.CATRigNodes
#($Object:MaramaPelvis @ [0.643538,12.460782,56.982174], $IKTarget:MaramaLPlatform @ [-9.479717,45.484272,2.016279], $Object:MaramaLThigh @ [-5.399519,13.610688,52.234413], $Object:MaramaLCalf @ [-5.073243,42.623035,42.755692], $Object:MaramaLAnkle @ [-9.923340,42.132290,12.445667], $Object:MaramaLToe @ [-8.354513,46.355228,5.667924], $IKTarget:MaramaRPlatform @ [22.299629,-26.312172,2.125263], $Object:MaramaRThigh @ [7.615567,12.286880,53.555431], $Object:MaramaRCalf @ [18.307613,1.576054,27.048307], $Object:MaramaRAnkle @ [15.838839,-24.331221,10.763937], $Object:MaramaRToe @ [21.849598,-24.039907,5.285047], $Object:MaramaSpine1 @ [0.084267,13.617851,61.769054], $Object:MaramaSpine2 @ [0.155154,17.742903,70.302628], $Object:MaramaSpine3 @ [0.838520,20.302643,79.403381], $Object:MaramaRibcage @ [1.721320,22.587023,88.560089], $IKTarget:MaramaLIKTarget @ [-14.076582,23.387424,73.722427], $Object:MaramaLCollarbone @ [-0.265896,27.761074,90.442078], $Object:MaramaLUpperArm @ [-7.214790,19.994431,91.494965], $Object:MaramaLForeArm1 @ [-22.375963,8.502111,91.433647], $Object:MaramaLForeArm2 @ [-18.546179,13.426756,83.416336], ...)
All the bones in the rig have been collected and put into an array for you. This makes it unnecessary to learn how to navigate CATRig hierarchies.
Getting All the Layer Controllers in a CATRig
The CATParent also provides a method, CATRigLayerCtrls, that generates a list of all the layer controllers in a CATRig.
Example: Select the CATParent of a CATRig and enter the following into the Listener.
$.CATRigLayerCtrls
#(Controller:LayerWeights, Controller:LayerMatrix3, Controller:LayerFloat, Controller:LayerWeights, Controller:LayerFloat, Controller:LayerFloat, Controller:LayerFloat, Controller:LayerMatrix3, Controller:LayerMatrix3, Controller:LayerWeights, Controller:LayerMatrix3, Controller:LayerFloat, Controller:LayerMatrix3, Controller:LayerMatrix3, Controller:LayerWeights, Controller:LayerFloat, Controller:LayerFloat, Controller:LayerFloat, Controller:LayerMatrix3, Controller:LayerMatrix3, ...)
The Pose Mixer utility uses this function to find all the controllers that will be keyframed.
Every bone in a CATRig is controlled by a controller. This controller exposes the CATNodeControl interface. This means you can access information about that bone.
$[3].address
Example: To get the layer controller for a CATBone using the following line:
$[3].layertrans.controller
Hubs are the “joiners” of the 3ds Max CAT hierarchy. Hubs have legs and arms, tails, spines and extra bones attached.
For any hub, you can see how many limbs are attached and then you can access the limb and get the bones from that limb.
Example: If a hub bone is selected and you want to know how many limbs are attached to the hub, use the following line of script.
$[3].limbs.count
Limbs are made up of four types of controllers: Limb, Collarbone, Bone, BoneSeg, Palm, IKTarget, and Upvector. Limb holds all the information about the whole limb, such as its name, and also keep a pointer to each bone in the limb, collarbone, ankle, and the IKTarget.
From one limb bone you can access everything in a CATRig.
To get the limb controller from a limb bone use the following line of script:
$[3].Limb
If you have a Limb bone selected and you want to know how many bones in the Limb use the following line of script.
$[3].Limb.bones.count
To get the second bone in the Limb use the following
$[3].Limb.bones[ 2 ]
if a limb bone is selected and you want to select all the bones in the limb, you can use the following line:
for bone in $[3].Limb.bones do selectMore bone.node
If a limb bone is selected and you want to get the name of the first bone in the limb, you can use the following:
$[3].limb.bones[1].node.name
$ -> accesses the selected bone $[3] -> accesses the selected bone's transform controller $[3].limb -> accesses the selected bone's tail controller $[3].limb.bones[1] -> accesses the 1st bone in the limb controller $[3].limb.bones[1].node -> accesses the node of the 1st bone. $[3].limb.bones[1].node.name -> accesses the name of the node of the 1st bone in the Limb.
Tails are made up of two types of controllers: TailData and TailTrans. TailData holds all the information about the whole tail, such as its name, and it also keep a pointer to each bone in the tail.
From one tail bone you can access everything in a CATRig.
To get the TailData controller from a tail bone use the following line of script:
$[3].tail
If you have a Tail bone selected and you want to know how many bones in the tail, use the following line of script:
$[3].tail.bones.count
To get the second bone in the tail, use the following:
$[3].tail.bones[ 2 ]
if you have one tail bone selected and you want to select all the bones in the tail, you can use the following line:
for bone in $[3].tail.bones do selectMore bone.node
If you have a tail bone selected and you want to get the name of the first bone in the tail, you can use the following:
$[3].tail.bones[1].node.name
$ -> accesses the selected bone $[3] -> accesses the selected bone's transform controller $[3].tail -> accesses the selected bone's tail controller $[3].tail.bones[1] -> accesses the 1st bone in the tail controller $[3].tail.bones[1].node -> accesses the node of the 1st bone. $[3].tail.bones[1].node.name -> accesses the name of the node of the 1st bone in the tail.