Search code examples
pythonmaya

Maya Python Create joints Hierachy


I'm trying to create o hierarchy of joints for a skeleton in maya python. And I'm doing this

def makeSkelet(args):

    helperSkelet('Root_Locator', 'root_Joint')
    helperSkelet('Pelvis_Locator', 'pelvis_Joint')
    helperSkelet('Spine_Locator', 'spine_Joint')
    helperSkelet('Spine01_Locator', 'spine01_Joint')
    helperSkelet('Spine02_Locator', 'spine02_Joint')
    helperSkelet('Neck_Locator', 'neck_Joint')
    helperSkelet('Head_Locator', 'head_Joint')
    mc.select(cl=True)
    helperSkelet('ArmL_Locator', 'armL_joint')
    helperSkelet('ElbowL_Locator', 'elbowL_Joint')
    helperSkelet('HandL_Locator', 'handL_Joint')
    mc.select(cl=True)
    helperSkelet('ArmR_Locator', 'armR_joint')
    helperSkelet('ElbowR_Locator', 'elbowR_Joint')
    helperSkelet('HandR_Locator', 'handR_Joint')
    mc.select(cl=True)
    helperSkelet('HipL_Locator', 'hipL_joint')
    helperSkelet('KneeL_Locator', 'kneeL_Joint')
    helperSkelet('AnkleL_Locator', 'ankleL_Joint')
    helperSkelet('FootL_Locator', 'footL_Joint')
    mc.select(cl=True)
    helperSkelet('HipR_Locator', 'hipR_joint')
    helperSkelet('KneeR_Locator', 'kneeR_Joint')
    helperSkelet('AnkleR_Locator', 'ankleR_Joint')
    helperSkelet('FootR_Locator', 'footR_Joint')

Now this works fine, because the joints must be created in this order. (the helper skelet is a function where i create the joint with the reference to a locator position)

I was wondering if there is a more optimized way to do this considering the order or creation must be kept .

Thank you


Solution

  • If by "optimize" you mean getting better performace, I agree with what @downshift said.

    If what you meant was instead making your code "cleaner" (more general or scalable or simply more pythonic), here's another way you can do the same, which is a bit more compact (and separates the logic from your input):

    def helperSkeletGroup(group, symmetric=False):
        # quick workaround to capitalize a word, leaving the following letters unchanged
        capitalize = lambda s: s[:1].upper() + s[1:]
    
        symmetric_group = []
        for elem in group:
            if symmetric:
                symmetric_group.append('{0}R'.format(elem))
                elem = '{0}L'.format(elem)
            # format locators and joints
            loc, joint = '{0}_Locator'.format(capitalize(elem)), '{0}_Joint'.format(elem)
            helperSkelet(loc, joint)
        cmds.select(cl=True)
        if symmetric_group:
            helperSkeletGroup(symmetric_group)
    
    helperSkeletGroup(['root', 'pelvis', 'spine', 'spine01', 'spine02', 'neck', 'head'])
    helperSkeletGroup(['arm', 'elbow', 'hand'], True)
    helperSkeletGroup(['hip', 'knee', 'ankle', 'foot'], True)
    

    This comes with a few advantages:

    • it handles symmetry for you
    • the code doesn't grow too much, as the number of joints increases
    • if at some point you want to change the naming convention for locators and joints, you can do it by changing a single line

    Alternatively, you could go with an OOP approach. Here's an example:

    class Skeleton:
    
        def __init__(self):
            self.joint_groups = []
    
        def add_joint_group(self, group, symmetric=False):
            # quick workaround to capitalize a word, leaving the following letters unchanged
            capitalize = lambda s: s[:1].upper() + s[1:]
    
            processed, processed_symmetric = [], []
            for elem in group:
                if symmetric:
                    processed_symmetric.append('{0}R'.format(elem))
                    elem = '{0}L'.format(elem)
                processed.append(('{0}_Locator'.format(capitalize(elem)), '{0}_Joint'.format(elem)))
            self.joint_groups.append(processed)
            if processed_symmetric:
                self.add_joint_group(processed_symmetric)
    
        def helper_skelet(self, loc, joint):
            # your helper logic goes here
            print loc, joint
    
        def build(self):
            for group in self.joint_groups:
                for loc, joint in group:
                    self.helper_skelet(loc, joint)
                cmds.select(cl=True)
    
    skeleton = Skeleton()
    skeleton.add_joint_group(['root', 'pelvis', 'spine', 'spine01', 'spine02', 'neck', 'head'])
    skeleton.add_joint_group(['arm', 'elbow', 'hand'], True)
    skeleton.add_joint_group(['hip', 'knee', 'ankle', 'foot'], True)
    
    from pprint import pformat
    print pformat(skeleton.joint_groups)
    
    skeleton.build()
    

    Here the code is a bit longer but it is all contained in a single object, where you could store additional data, which you get only at construction time and which you might need later on.

    EDIT (to answer @Giakaama's question in the comment):

    If you save the class in a separate file skeleton_class.py, you can import the class in your main.py (or whatever you want to call it), as such:

    from skeleton_class import Skeleton

    where the lower-case skeleton_class refers to your module (read: file) and Skeleton is the class itself. Once you've done that, you can do the same as above:

    skeleton = Skeleton()
    skeleton.add_joint_group(['root', 'pelvis', 'spine', 'spine01', 'spine02', 'neck', 'head'])
    skeleton.add_joint_group(['arm', 'elbow', 'hand'], True)
    skeleton.build()