Navigating CATRig Hierarchies using MAXScript
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 knowing
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
(arm or leg). 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
Using Bone Addresses
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.
EXAMPLE
|
--Select a bone, for example left elbow:
theAddress =$[3].address
--the result will be something like:
"SceneRootNode.Hub.Spine[0].Hub.Limb[0].LimbBone[1].BoneSeg[0]"
|
Once you have an address, it 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:
EXAMPLE
|
--Selectthe CATParent of a rig and call:
$.GetBoneByAddresstheAddress
--the result will be something like:
$Object:MaramaLForeArm1 @ [18.374180,2.811419,91.353607]
|
Getting all the Bones that make up the CATRig
The CATParent provides a property called CATRigNodes that contains a list of all the
bones in a CATRig.
EXAMPLE
|
--Select the CATParent of a CATRig andexecute:
$.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 property called CATRigLayerCtrls that contains an array of all the layer controllers in a CATRig.
EXAMPLE
|
-- Select the CATParent of a CATRig andexecute:
$.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.
Navigating Bones
Each bone in a CATRig is controlled by a controller.
This controller exposes the CATNodeControl interface.
This means you can access information about that bone.
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].address
"SceneRootNode.Hub.Spine[0].Hub.Limb[0].LimbBone[0].BoneSeg[2]"
|
EXAMPLE
|
To get the layer controller for a CATBone, use the following line:
|
$[3].layertrans.controller
Controller:LayerMatrix3
|
Note that some bones do not have a layertrans controller such as, the bones in the
procedural spine.
|
Navigating Hubs
Hubs are the "joiners" of the 3ds Max CAT hierarchy.
Hubs can have limbs (legs and arms), tails, spines, and extra bones attached to them.
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.
|
-- Select the CATParent of a CATRig andexecute:
$[3].limbs.count
2
|
Navigating Limbs
Limbs are made up of the following types of controllers: Limb, Collarbone, Bone, BoneSeg, Palm, IKTarget, and Upvector.
The Limb controller 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.
EXAMPLES
|
--To get the limb controller from a limb bone use the following:
$[3].Limb
Controller:CATLimbData2
--If you have a Limb bone selected and you want to know how many
--bones in the Limb use the following:
$[3].Limb.bones.count
4
--To get the second bone in the Limb use the following:
$[3].Limb.bones[2]
Controller:CATBoneData
--If a limb bone is selected and you want to select all the bones
--in the limb, you can use the following:
select (for o in $[3].Limb.bonescollect o.node)
OK
|
EXAMPLE
|
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
"MarineLUpperarm1"
--Here is a quick overview of bone hierarchy access:
$--> accesses the selected bone
$Object:MarineLUpperarm3 @ [32.745049,27.963869,14.721746]
$[3]--> accesses the selected bone's transform controller
SubAnim:Transform
$[3].limb--> accesses the selected bone's tail controller
Controller:CATLimbData2
$[3].limb.bones[1]--> accesses the 1st bone in the limb controller
Controller:CATBoneData
$[3].limb.bones[1].node--> accesses the node of the 1st bone.
$Object:MarineLUpperarm1 @ [30.869087,27.893547,14.979661]
$[3].limb.bones[1].node.name--> accesses the name of the node of the 1st bone in the Limb.
"MarineLUpperarm1"
|
Navigating Tails
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.
EXAMPLE
|
--To get the TailData controller from a tail bone use the following:
$[3].tail
--If you have a Tail bone selected and you want to know how many bones
--arein the tail, use the following:
$[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:
select (for o in $[3].Limb.bones collect o.node)
OK
|
EXAMPLE
|
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
--Here is a detailed overview of the hierarchy access:
$--> 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.
|