Maya Python - for loop parenting issue

I don’t understand why this is not working. I am trying to parent all the joints under the shape nodes :

import maya.cmds as mc

#create variable for the selected joints
selected = mc.ls(sl=True)

#clears the selection
mc.select(cl=True)

#create empty list for the new controls
CK = []

#for the joints in selection, run the following code
for s in selected:
    
    #create variable for the position/rotation of the joints
    pos = mc.xform(s, q=True, t=True, ws=True)
    rot = mc.xform(s, q=True, rotation=True, ws=True)
    
    #create 3lvl controls and position them on top of joints
    ctrl = mc.circle(n=str(s + '_ctrl'), radius=0.6,nr=(1,0,0), ch=False) 
    mc.group(n=str(s + '_sdk'))
    offset = mc.group(n=str(s + '_offset'))
    
    #snap the controls to the position of the joint
    mc.xform(offset, translation=pos, ws=True)
    mc.xform(offset, rotation=rot, ws=True)
    
    #append controls to a list
    CK.append(ctrl)
    
#list all the shape nodes within a list
for s in CK:
    derp = mc.listRelatives(s, s=True)
    
#parent all the joints within the list under the shape nodes from Derp variable 
for l in selected:
    mc.parent(derp, l, relative=True, shape=True)


   

try:

mc.parent(derp, l, add=True, shape=True)
and indent the last 2 lines

I think your ‘ctrl’ variable is causing the issue. Since you’re creating it in a for loop, by the time you exit the loop, Maya thinks you are only referring to the last controller when you try and append your list to ‘CK’. Instead of adding all your controls within one list, you are adding lists within a larger list.

Example : you are doing this “CK = [[ctrl], [ctrl], [ctrl], [ctrl], etc.]” rather than “CK = [ctrl, ctrl, ctrl, ctrl, etc.]”

The solution would be to simply add ‘[0]’ to ‘ctrl’ when you append everything to ‘CK’.

Another problem is you’re trying to toss your joints under your controller’s shape nodes (or at least that’s what you say you are trying to do, right?). The issue with this is you are using ‘mc. parent’ which only works with transform nodes. I’m assuming you are trying to “hide” your joints within your controllers so they don’t show up in the outliner—otherwise, I’m not sure why this would be desirable.

I haven’t tried seeing if there is another way to do parent your joints under your shape nodes but if you want to try the code below, maybe that will give you what you’re after. Everything works at least in terms of function and hierarchy but like I explained above, you can still see your joints despite them being parented under your control’s shape nodes.

import maya.cmds as mc

#create variable for the selected joints
selected = mc.ls(sl=True)

#clears the selection
mc.select(cl=True)

#create empty list for the new controls
CK = []
offsetList = []

#for the joints in selection, run the following code
for s in selected:
    
    #create variable for the position/rotation of the joints
    pos = mc.xform(s, q=True, t=True, ws=True)
    rot = mc.xform(s, q=True, rotation=True, ws=True)
    
    #create 3lvl controls and position them on top of joints
    ctrl = mc.circle(n=str(s + '_ctrl'), radius=0.6,nr=(1,0,0), ch=False)   
    mc.group(n=str(s + '_sdk'))
    offset = mc.group(n=str(s + '_offset'))
    
    #snap the controls to the position of the joint
    mc.xform(offset, translation=pos, ws=True)
    mc.xform(offset, rotation=rot, ws=True)
    
    #append controls and offsets to list
    CK.append(ctrl[0]) # add '[0]' otherwise you will nest lists within list
    offsetList.append(offset)  
    
#list all the shape nodes within a list
for s in CK:
    derp = mc.listRelatives(CK, s=True) # list shape for all controls
    
#store length of shape nodes
shapeCount = 0

#parent all the joints within the list under the shape nodes from Derp variable 
for each in selected:
    mc.select(selected[shapeCount], derp[shapeCount], replace=True, noExpand=True)
    mc.parent()
    shapeCount += 1
    
#store length of shape nodes
shapeCount = 1

#parent 'offset' groups to joints
for each in selected:
    mc.select(offsetList[shapeCount], each, replace=True)
    mc.parent()
    shapeCount += 1
    
    # add one to 'shapeCount' until it reaches last 'offset' group
    if shapeCount > len(offsetList) -1:
        #delselect all. generate warning
        mc.select(offsetList[0], replace=True) # select top 'offset'
        mc.warning("All joints have been parented.")
        break

One last thing to keep in mind is your control’s orientation depends on the orientation of your joints. That said, if you are finding any of the controls are misaligned or not oriented the right way, it’s due to your joints. In that case I would simply orient things like you want and then your controls will be oriented accordingly. Hope that helps.
-Bryan

is this intended to create broken hierarchy (offset > sdk > joint > offset > sdk > joint…)?

[QUOTE=Theodox;28319]is this intended to create broken hierarchy (offset > sdk > joint > offset > sdk > joint…)?[/QUOTE]

Good question, Theodox. I was actually wondering the same. I assumed the hierarchy would have to be broken in order to keep everything within a single chain but if each ‘offset’ group is meant to sit independently then the code I wrote above would have to be modified by removing the last ‘shapeCount’ and the two suites that follow. If this is the case Badcat, you would end up with something like this:


Let us know. :):

That’s pretty much what I saw. This might work better as two parallel hierarchies – the deformation skel and the FK skel – joined up by constraints

is this intended to create broken hierarchy (offset > sdk > joint > offset > sdk > joint…)?

Yes. That is exactly what I wanted. I’ve seen the error of my ways though…

[QUOTE=bgodoy;28317]I think your ‘ctrl’ variable is causing the issue. Since you’re creating it in a for loop, by the time you exit the loop, Maya thinks you are only referring to the last controller when you try and append your list to ‘CK’. Instead of adding all your controls within one list, you are adding lists within a larger list.

One last thing to keep in mind is your control’s orientation depends on the orientation of your joints. That said, if you are finding any of the controls are misaligned or not oriented the right way, it’s due to your joints. In that case I would simply orient things like you want and then your controls will be oriented accordingly. Hope that helps.
-Bryan[/QUOTE]

Amazing explanation! Thank you so much for going through this. The only reason I tried this method is because I thought that the usual parent constraint method would be slower as opposed to controls replacing joints.

I was told that I shouldn’t be afraid of using parent constraints as they are pretty cheap performance wise, so this is what I ended up with: ( If you guys have some suggestions for improvement, please tell me)

I am also aware of the joint orient aspect. I usually re-orient all the joints anyway with X always being the aim axis.


import maya.cmds as mc

#create variable for the selected joints
selected = mc.ls(sl=True)

#clears the selection
mc.select(cl=True)

#create empty list for the new controls
CL = []
CK = []

#for the joints in selection, run the following code
for s in selected:
    
    #create variable for the position/rotation of the joints
    pos = mc.xform(s, q=True, t=True, ws=True)
    rot = mc.xform(s, q=True, rotation=True, ws=True)
    
    #create 3lvl controls and position them on top of joints
    ctrl = mc.circle(n=str(s + '_ctrl'), radius=0.6,nr=(1,0,0), ch=False) 
    mc.group(n=str(s + '_sdk'))
    offset = mc.group(n=str(s + '_offset'))
    
    #snap the controls to the position of the joint
    mc.xform(offset, translation=pos, ws=True)
    mc.xform(offset, rotation=rot, ws=True)
    
    #parent constraint joints to ctrls
    mc.parentConstraint(ctrl, s)
    
    #append offset nodes to CL
    CL.append(offset)
    CK.append(ctrl)
    
offsets = CK
controls = CL

#create a zip loop - no fucking idea how this works, but it does
for offset, control in zip(offsets, controls[1:]):
      mc.parent(control, offset)

You should definitely not be afraid to use constraints in scripts for positioning, it saves a lot of lines and is faster to write and maintain. You should also make sure to transfer the rotation order from the joints. Here’s an example, I haven’t tested it but I think it should work :slight_smile:

#Create empty lists controllers
ctrls = []
ctrlExtraGrp = []
ctrlXformGrp = []

#For each joint in the current selection, run the following code
for jnt in mc.ls(sl=True, type="joint"):
    #Create the hierarchy for the current controller: Control -> Extra Ctrl Group -> Xform Grp
    ctrl = mc.circle(name=(jnt + '_ctrl'), radius=0.6,nr=(1,0,0), ch=False)[0]
    grp1 = mc.group(ctrl, name=(jnt + '_extra_grp'))
    grp2 = mc.group(grp1, name=(jnt + '_xform_grp'))

    #Set the rotateOrder of the group/controller
    mc.setAttr((ctrl + '.rotateOrder'), mc.getAttr(jnt + '.rotateOrder'))
    mc.setAttr((grp1 + '.rotateOrder'), mc.getAttr(jnt + '.rotateOrder'))
    mc.setAttr((grp2 + '.rotateOrder'), mc.getAttr(jnt + '.rotateOrder'))

    #Position the controller
    mc.delete(mc.parentConstraint(jnt, grp2, maintainOffset=False))
    #Parentconstraint the current joint to be driven by the current controller
    mc.parentConstraint(ctrl, jnt)

    #Append nodes to variables
    ctrls.append(ctrl)
    ctrlExtraGrp.append(grp1)
    ctrlXformGrp.append(grp2)

#Create the hierarchy for the joint-controllers
for ctrl, offset in zip(ctrls, ctrlXformGrp[1:]):
    mc.parent(offset, ctrl)

You might also do shape parenting, the command for this is:

mc.parent("derpShape", "joint1", shape=True, add=True)

[QUOTE=badcat;28338]The only reason I tried this method is because I thought that the usual parent constraint method would be slower as opposed to controls replacing joints.[/QUOTE]
Constraints will slow down your rig, so it’s a good idea to see if it’s possible to achieve what you want without them. It’s totally ok to have constrained FK joints and such, but be careful with having too many extra entire hierarchies for convenience. I once looked into having a separate joined joint hierarchy for my character rigs, it’s practical when skinning and if you need to export your rig, but the speed costs was huge. I don’t remember the exact numbers, and it was roughly 150-200 joints (pretty dense), I think the FPS dropped down to 50% or so. But that’s an entire rig :slight_smile:

Here’s the result of a speed test I did comparing skinclusters, constraints, direct connections and normal parenting, might be useful:
[vimeo]69356751[/vimeo]