tech maya python

BroTools Snippets #04 - Mirror FFD Box

2017-01-18

*Different small parts of my expanding collection of Maya tools and scripts, from small scripts to large rigging and simulation systems.

Another small script that can be of any help for others. It mirrors FFD box shape from one FFD to another. Useful for, for example, cartony eye rigs. I think anyone who knows a little python in maya should be able to write this one, but for those who just start with Python in Maya or those who are just lazy, here it is. Tried to comment the hell out of it.

'''
Mirror ffd deformer shape. Useful for cartoon eyes.
by Michael Davydov

Currently only works with mirroring across X axis.

Usage:
    1. Copy your ffdLattice and ffdBase objects. Change TranslateX to the opposite value. 
    This means that you basically need to multiply current TranslateX by (-1). 
    Basically just add a minus sign if it's positive value, and remove it if it's negative value.

    2. Change names in the script below, FROM and TO.
    3. Run the script.
'''

import maya.cmds as cmds # As usual, import maya's python bindings
import time # This is Python's time module, just for fun

FROM = "ffd_eye_R_Lattice" # Change this to the FFD object name you need to mirror FROM
TO = "ffd2Lattice" # Change this to the FFD object name you need to mirror TO


# Now, let's get number of S, T and U divisions from the "FROM" lattice.
# I'm going to use dictionary here, to save some space and time.
divisions = {}
for ax in ['s','t','u']:
    divisions[ax+"Divisions"] = cmds.getAttr(FROM+".{0}Divisions".format(ax))


# Make sure that target have the same number. Here we have two options,
# we can either force change number of divisions for user, or we can
# warn him. I'll do both. Why not?

# For each division check if there is the same number.
matching = True
confirm = None
for division, value in divisions.iteritems():
    if cmds.getAttr(TO+"."+division) != value:
        matching = False

if not matching:
    # Call the confirm dialog, thank you maya for built-in command.
    confirm = cmds.confirmDialog( title='Confirm', message='Number of divisions do not match. Script will only work with matching FFD divisions. Change division number or target ffd to match the source?', button=['Yes','No'], defaultButton='Yes', cancelButton='No', dismissString='No' )

if confirm=="Yes" or matching:
    for division, value in divisions.iteritems():
        cmds.setAttr(TO+"."+division, value)

    # Iterate over all points
    for S in range (0,3):
        for T in range (0,3):
            for U in range (0,3):
                print "//", i,o,p
                # Get position of a point
                pos = cmds.xform('{0}.pt[{1}][{2}][{3}]'.format(FROM, S,T,U), q=1, ws=1, t=1)
                pos[0] = pos[0]*(-1) # Reverse x position value
                cmds.xform('{0}.pt[{1}][{2}][{3}]'.format(TO, 2-S,T,U), ws=1, t=pos) # Paste position to another object's opposite point
                # Make progress visible.
                cmds.refresh() # Refresh the viewport to see what's going on
                time.sleep(0.03) # Wait for a few milliseconds, otherwise the script would work instantly. Remove these two lines for production use.

else:
    print "// User canceled"