Monday, July 13, 2015

Force Deformation Order

The other day I ran into a seemingly simple problem. I was trying to add a new blendshape to my rig but for some reason I wasn't able to get some verts to behave properly. To make a long story short, I wrote this:

(Shelf Button)
 

If you're like me and ever ran into this issue, you might have noticed that Maya's input window doesn't always solve the problem like you might expect. This tool is meant to provide an alternative. 
At the moment I'm still testing things out with just about every rig I can find to see if I run across any bugs. Currently the only deformers this tool supports are tweaks, blendshapes, and skinClusters (since that's all I needed at the time) but I plan on adding more features if it makes creating blendshapes and reordering deformers overall, easier.

If anyone's interested in looking at the code or using the tool yourself, below is the script written in Python. To throw this thing on your shelf you can use the following three lines. Let me know if this tool becomes of use to anyone and any comments or suggestions would be great too!

Download the script here:
http://bit.ly/1I315zV




# =====================================
# Force Deformation Order - BETA v.1.0
# =====================================

import maya.cmds as mc

# ==============
# Window
# ==============

# If window already exists, it will be replaced by new window
FDOName = "forceDOrder_UI"
if mc.window("forceDOrder_UI", exists=True):
    mc.deleteUI("forceDOrder_UI", window=True)

def FDO_UI():
    # QuiCam UI
    FDOWin = mc.window(FDOName, title="Force Deformation Order", menuBar=True)
    parentLayout = mc.columnLayout(adj=True)
    instructionMenu = mc.menu(label="Instructions")
    mc.menuItem(label="Open Instruction Window", parent=instructionMenu, command="forceDeformationOrder.instructionsWindow()")
    mc.setParent("..")
    mc.separator(parent=parentLayout, height=10, style="in")
    mc.button(parent=parentLayout, label="Load List", width=100, height=30, command="forceDeformationOrder.loadList()")
    mc.separator(parent=parentLayout, height=10, style="in")
    childLayout = mc.rowLayout(parent=parentLayout, numberOfColumns=2, adjustableColumn=True)
    mc.textScrollList("textBox")
    mc.columnLayout(parent=childLayout, adj=True)
    mc.button(label="Move Up", width=100, height=40, command="forceDeformationOrder.moveUp()")
    mc.button(label="Move Down", width=100, height=40, command="forceDeformationOrder.moveDown()")
    mc.columnLayout(parent=parentLayout, adj=True)
    mc.separator(height=10, style="in")
    mc.button(label="Remove All", width=100, height=30, command="forceDeformationOrder.removeAllList()")
    mc.separator(height=10, style="in")
    mc.separator(height=10, style="in")
    mc.button(label="Force Deformation Order", width=100, height=30, backgroundColor=[1, 0.2, 0.2], command="forceDeformationOrder.forceOrder()")
    mc.showWindow(FDOWin)

# Instructions Window
def instructionsWindow():
    # If window already exists, it will be replaced by new window
    instructionsName = "instructWin_UI"
    if mc.window("instructWin_UI", exists=True):
        mc.deleteUI("instructWin_UI", window=True)
        
    # Instructions
    instructionsWin = mc.window(instructionsName, title=" FDO - Instructions")
    mc.columnLayout(adj=True, width=500)
    mc.text(wordWrap=True, al="center", label="1.) Select one piece of geometry to rearrange your object's deformation order.")
    mc.text(wordWrap=True, al="center", label="2.) When you have made your selection, load your list and specify the order of your inputs.")
    mc.text(wordWrap=True, al="center", label="3.) Once you are satisfied with the order of your list, click 'Force Deformation Order'.")
    mc.text(wordWrap=True, al="center", font="boldLabelFont", label="Note: Make sure your geometry is selected before running any commands.")
    mc.separator(height=10, style="in")
    mc.separator(height=10, style="in")
    mc.text(wordWrap=True, al="center", font="boldLabelFont", backgroundColor=[0.8, 0.35, 0.35], label="Suggestions or Comments - http://b-animated.blogspot.com/")
    mc.showWindow(instructionsWin)

# ==========
# Commands
# ==========

# Load List
def loadList():
    # Store geo variable for selection...
    selectedGeo = mc.ls(sl=True)
    
    # Query geometry - obtain deformer inputs
    queryGeo = mc.deformer(query=True, geometry=True)
    for i in queryGeo:
        geoShape = i
    listDeforms = mc.listSets(type=2, object=geoShape)
    mc.select(listDeforms, replace=True, noExpand=True) # needs 'noExpand' for sets
    
    masterList = []
    
    # Check for "skinCluster" nodes... 
    for everySkin in listDeforms:
        listConnects = mc.listConnections(everySkin, type="skinCluster")
        masterList.append(listConnects)
    
    # Check for "blendShape" nodes...
    for everyBS in listDeforms:
        listConnects = mc.listConnections(everyBS, type="blendShape")
        masterList.append(listConnects)
    
    # Check for "tweak" nodes...
    for everyTweak in listDeforms:
        listConnects = mc.listConnections(everyTweak, type="tweak")
        masterList.append(listConnects)
    
    # Delete "None" objects..
    for everyNone in masterList:
        masterList.remove(None) # ... run twice to make sure
        masterList.remove(None)

    # Add to list
    mc.textScrollList("textBox", query=True, allItems=True)
    for everyInput in masterList: 
        mc.textScrollList("textBox", edit=True, append=everyInput)
    listCount = len(masterList)
    mc.select(selectedGeo, replace=True) # select geo before spitting warning
    mc.warning(listCount, " Deformer(s) Added To List.")

# Remove all from list
def removeAllList():
    mc.textScrollList("textBox", query=True, allItems=True) 
    mc.textScrollList("textBox", edit=True, removeAll=True)       
    mc.warning("All Items Removed From List.")

# List Control
def moveUp():
    # Set up index to move item up
    currentIndex = mc.textScrollList("textBox", query=True, selectIndexedItem=True) # current index
    selInt = currentIndex.pop() - 1 # pop 'list' out of brackets - now 'int'
    
    # Set up string to specify which item to move up
    selStr = mc.textScrollList("textBox", query=True, selectItem=True) # shape node for selected item
    moveItem = [selInt] + selStr # new list
    
    # Move item up
    mc.textScrollList("textBox", edit=True, appendPosition=moveItem) # ...requires one integer value and one string
        
    # Delete selected item to aviod duplicates
    removeDup = mc.textScrollList("textBox", query=True, selectIndexedItem=True) # produce index
    mc.textScrollList("textBox", edit=True, removeIndexedItem=removeDup) # remove duplicate from list
    mc.textScrollList("textBox", edit=True, selectIndexedItem=selInt) # select new list item
    
def moveDown():
    # Set up index to move item up
    currentIndex = mc.textScrollList("textBox", query=True, selectIndexedItem=True) # current index
    selInt = currentIndex.pop() + 2 # pop 'list' out of brackets - now 'int'
    
    # Set up string to specify which item to move up
    selStr = mc.textScrollList("textBox", query=True, selectItem=True) # shape node for selected item
    moveItem = [selInt] + selStr # new list
    
    # Move item up
    mc.textScrollList("textBox", edit=True, appendPosition=moveItem) # ...requires one integer value and one string
    
    # Delete selected item to aviod duplicates
    removeDup = mc.textScrollList("textBox", query=True, selectIndexedItem=True) # produce index
    mc.textScrollList("textBox", edit=True, removeIndexedItem=removeDup) # remove duplicate from list
    mc.textScrollList("textBox", edit=True, selectIndexedItem=selInt -1) # select new list item
    
# Force order based on list
def forceOrder():
    # Store geo variable for selection...
    selectedGeo = mc.ls(sl=True, long=True) # GEO (long name)
    
    # Compare new list to old defomation order
    listShapes = mc.textScrollList("textBox", query=True, allItems=True) # LIST
    
    # Convert shape nodes to transform nodes
    listAll = [] # empty list
    
    for everyItem in listShapes:
        wrapQuotes = everyItem.__str__() # wrap in quotes
        listAll.append(wrapQuotes) # toss everything into empty list
        
    # Slice itemsi in 'listAll' so we are only dealing with two items at a time (needed for reorderDeformers)
    splitTwo = -2 # set to -2 to set up sliceCount 
    
    for i in listAll:
        splitTwo += 1 # add 1 for every iteration
        sliceCount = listAll.__getslice__(splitTwo, splitTwo +2) # slice tuple     
        sliceTotal = len(sliceCount) # gather length
        
        if sliceTotal == 2: # if there are 2 values returned... move forward
            RDList = sliceCount.__getslice__(0,2) # isolate deformer's list - shape nodes   
            RDFinals = RDList[0], RDList[1], selectedGeo[0].__str__() # convert...
            
            # *** For every tuple, run reorderDeformers command ***
            RDMaster = map(str, RDFinals) # covert type tuple to list...
            mc.reorderDeformers(RDMaster[0], RDMaster[1], RDMaster[2]) # * REORDER DEFORMERS *
            # three values (Dform1, Dform2, geo affected) - Dform2 placed over 1
            
    # Once order has been changed, we'll reselect geo to refresh input list
    mc.select(clear=True)
    mc.select(selectedGeo, replace=True)
    mc.warning("Deformation Order has been rearranged.") # final warning
            
# Script by Bryan Godoy
# http://b-animated.blogspot.com/
# https://twitter.com/b_animates

No comments:

Post a Comment