How did I deal with…?
Programming an Automation Tool for Maya Using Python
The aim of this tool is to automate renaming and rigging processes for an XReality prop. The company (Misterspex) makes an augmented reality app for online glasses fitting. My tool provides the staff with automatic conventional nomenclature and rigs the assets with a button click.
After one pass of this automation tool, my rigged mesh is ready for testing in Babylon:
These are the steps I'm taking to write this automation tool:
1. Planning the tool
My tool is building up a whole hierarchy with a button click. Swift requires the app to set the geometry on position and enable the temples' pivots to rotate accordingly. These schematics show what the general logic and nomenclature of this basic rig looks like:
This is what the outliner looks like before using the tool:
The staff is modeling hinges and temples only on one side (doesn't matter which one) for efficiency's sake. This is what the outliner looks like after renaming the pieces, using the tool's buttons:
The tool sends an error message if you try to repeat a name. After renaming:
- Hinges' pivots are centered
- Clicking on the “Create Hierarchy” button creates all groups
- Sets the geometry at the correct position/orientation/scale
- Makes a symmetrical copy of the temples, scales it, and
- Sets the rotation pivots for the hinges.
2. Writing the automation tool in Python (Maya Commands)
After laying out the UI, these are the methods that I'm implementing to accomplish the tasks and link the buttons.
2.1. Selecting and calling methods
First of all, renaming the nodes:
def rename_component(component_name):
component_to_rename = cmds.ls(selection=True)
if cmds.objExists(component_name):
cmds.error("Name already in use.")
else:
cmds.rename(component_to_rename, component_name)
move_pivot_to_origin(component_name)
cmds.delete(constructionHistory=True)
cmds.makeIdentity(apply=True)
cmds.select(clear=True)
Moving all pivots to 0,0,0 coordinates (Origin):
def move_pivot_to_origin(component_name):
if component_name != 'Hinge_':
cmds.move(0, 0, 0, ".scalePivot", ".rotatePivot", absolute=True)
Calling the functions from the “Create Hierarchy” Button:
def create_hierarchy():
rename_after_side()
select_side_geometry()
duplicate_rename_side_geometry()
copy_pivot()
group_temple_after_side()
creating_trim_group()
moving_geometry_pivots_to_origin()
grouping_all_nodes()
scaling_all_by_10x()
lens_names = ['LensRight', 'LensLeft']
temple_group_names = ['TempleRight', 'TempleLeft']
temple_hinge_names = ['HingeRight', 'HingeLeft']
group_names = ['Frame', 'Lenses', 'Temple_', 'Temples']
side_names = ['Right', 'Left']
provisional_names = []
component_names = []
temple_components = []
2.2. Replacing method
The tool finds out on which side of the Z axis our hinge/temple are by:
- Checking out the distance to Origin
- Storing the information, and
- Setting an underscore (_) as placeholder.
def rename_after_side():
cmds.select(clear=True)
cmds.select('Temple*', 'Hinge*', 'Lens*')
temporal_group = cmds.group(name='temp_grp')
temporal_group_duplicated = cmds.duplicate(cmds.ls(selection=True), returnRootsOnly=True)
print temporal_group_duplicated
temporal_group_duplicated += cmds.listRelatives(temporal_group_duplicated, allDescendents=True, fullPath=True)
longNames = cmds.ls(temporal_group_duplicated, long=True)
print longNames
longNames.sort()
side = '_'
for n in longNames[::-1]:
component_position = cmds.objectCenter(n, gl=True, local=True, x=True)
if component_position < 0:
side = 'Right'
elif component_position > 0:
side = 'Left'
else:
component_position = 0
print side
shortname = n.rpartition("|")[-1]
cmds.rename(n, shortname.replace("_", side))
cmds.ungroup('temp*')
cmds.delete('Temple_*', 'Hinge_', 'Lens_')
Here you can see the meshes before applying the renaming method:
2.3. Renaming and Adding Symmetry methods
After renaming the temples' geometry with the characters that my tool can select, and knowing on which side they are (left or right), the method:
- Selects the geometry with the appropriate names
- Makes a loop through the selection, and
- Replaces the placeholders with the correct side name.
def select_side_geometry():
cmds.select('Temple*', 'Hinge*', add=True)
selected_children = cmds.ls(selection=True)
cmds.group(empty=True, name='hub_grp')
cmds.parent(selected_children, 'hub_grp')
cmds.select('hub_grp')
def duplicate_rename_side_geometry():
duplicated_group = cmds.duplicate(cmds.ls(selection=True), returnRootsOnly=True)
print duplicated_group
duplicated_group += cmds.listRelatives(duplicated_group, allDescendents=True, fullPath=True)
longnames = cmds.ls(duplicated_group, long=True)
print longnames
longnames.sort()
print longnames
for n in longnames[::-1]:
shortname = n.rpartition("|")[-1]
if shortname.startswith('TempleRight' or 'HingeRight'):
side = side_names[0]
new_side = side_names[1]
elif shortname.startswith('TempleLeft' or 'HingeLeft'):
side = side_names[1]
new_side = side_names[0]
cmds.rename(n, shortname.replace(side, new_side))
cmds.scale(-1, 1, 1)
cmds.ungroup('hub*')
Here you can see all the pieces, with symmetry, at the right position and renamed after Z axis side:
2.4. Groups and Pivots method
This method creates:
- New parent groups
- Finds out translation and orientation of the hinges' pivots
- Copies the information
- Resets pivots, except the temples group, and
- Pastes the hinges pivots' information on them.
def copy_pivot():
cmds.group(empty=True, name='TempleRight')
cmds.group(empty=True, name='TempleLeft')
hinges = ['HingeRight', 'HingeLeft']
sides = ['TempleRight', 'TempleLeft']
hinges_sides = zip(hinges, sides)
for hinge, side in (hinges_sides):
copy_pivot = cmds.xform (hinge, query = True, worldSpace = True, rotatePivot = True)
cmds.parent(side, hinge)
cmds.makeIdentity(side, apply = True, translate = True, rotate = True, scale = True)
cmds.xform (side, worldSpace = True, pivots = copy_pivot)
cmds.parent(side, world = True)
print side
Here you can see the hinges' geometry with their pivots:
2.5. Grouping Temples method
This method creates, names, and appends the corresponding geometry to the new groups.
def group_temple_after_side():
cmds.select(clear=True)
shapeList = cmds.ls(typ='mesh')
cmds.select(shapeList)
print shapeList
transformList = cmds.listRelatives(shapeList, parent=True, fullPath=True)
print transformList
cmds.select(transformList)
selection = cmds.ls(selection=True)
print selection
cmds.select(deselect=True)
temple_groups = []
temple_group = []
for s in selection:
if s.startswith('TempleRight') or s.startswith('HingeRight'):
temple_group = 'TempleRight'
cmds.parent(s, temple_group)
if s.startswith('TempleLeft') or s.startswith('HingeLeft'):
temple_group = 'TempleLeft'
cmds.parent(s, temple_group)
temple_groups.append(temple_group)
You can see the new groups in the outliner:
2.6. Grouping Trims method
Swift needs the trim geometry inside a group to let the customer hide them behind the ears when online (© Misterspex).
This method looks for the named geometry, selects them and resets pivots.
def creating_trim_group():
cmds.select(clear=True)
cmds.select('TempleRight', hierarchy=True)
cmds.select('TempleRight', deselect=True)
selection = cmds.ls(selection=True)
print selection
for s in selection:
if not s.endswith('Trim'):
cmds.select(s, deselect=True)
else:
continue
cmds.group(name='TrimRight')
cmds.xform(zeroTransformPivots=True, worldSpace=True)
cmds.delete(constructionHistory=True)
cmds.makeIdentity(apply=True)
cmds.select(clear=True)
cmds.select('TempleLeft', hierarchy=True)
cmds.select('TempleLeft', deselect=True)
selection = cmds.ls(selection=True)
print selection
for s in selection:
if not s.endswith('Trim'):
cmds.select(s, deselect=True)
else:
continue
cmds.group(name='TrimLeft')
cmds.xform(zeroTransformPivots=True, worldSpace=True)
cmds.delete(constructionHistory=True)
cmds.makeIdentity(apply=True)
You can see the grouping in the Outliner after applying the method:
2.7. Setting Pivots method
This method resets all the pivots in temples, except for hinge group.
def moving_geometry_pivots_to_origin():
cmds.select(clear=True)
cmds.select(temple_group_names)
cmds.group(name=group_names[3])
cmds.select(clear=True)
selected_children = []
print selected_children
cmds.select(clear=True)
cmds.select('Temple*', 'Hinge*')
cmds.select('TempleRight', 'TempleLeft', deselect=True)
selected_children = cmds.ls(selection=True)
print selected_children
for c in selected_children:
cmds.move(0, 0, 0, ".scalePivot", ".rotatePivot", absolute=True)
The picture shows that all the pivots are in the origin, except for the temples group:
2.8. Finishing Up method
When you have everything renamed and sorted in the hierarchy, it means the time is come to set up the final groups, reset all pivots and transform nodes one last time.
def grouping_all_nodes():
#group rest geometry
cmds.group('Lens*', name=group_names[1])
cmds.select(clear=True)
cmds.select('Eyewires', 'Frame*', add=True)
cmds.group(name=group_names[0])
# group everything
cmds.select(clear=True)
cmds.select('Frame', 'Lenses', 'Temples', add=True)
model_group = cmds.ls(selection=True)
cmds.group(model_group, name='Model')
# move pivot rest geometry to 0
cmds.select('Eyewires', 'Frame*', 'Lens*', '*Trim', 'Trim*', add=True)
selected_components = cmds.ls(selection=True)
print selected_components
for c in selected_components:
cmds.move(0, 0, 0, ".scalePivot", ".rotatePivot", absolute=True)
#delete history and freeze transformations on everything
cmds.select('Model*', 'Frame*', 'Lens*', 'Eyewires', 'Temple*', 'Hinge*', '*Trim', 'Trim*', add=True)
all_nodes = cmds.ls(selection=True)
for n in all_nodes:
cmds.delete(constructionHistory=True)
cmds.makeIdentity(apply=True, preserveNormals=True)
Now all geometry and groups match the schematics.
2.9. Scaling & Cleaning Up method
Swift needs to scale everything 10×:
def scaling_all_by_10x():
cmds.select(clear=True)
cmds.select('Model', hierarchy=False)
cmds.scale(10, 10, 10)
cmds.select('Model', hierarchy=True)
cmds.delete(constructionHistory=True)
cmds.makeIdentity(apply=True)
cmds.select(clear=True)
Also note that I'm applying the tool before any polygon smooth nodes. The reason for this is that we are getting rid of the meshes' history, i.e. we cannot go back to the lowest poly version.
This is an example of using programming and automation for Digital Content Creation (DCC). Click on these case studies if you want to see more examples of programming for real time, UI, and interaction:
Also related: