Skip to content

Instantly share code, notes, and snippets.

@patwooky
Last active March 7, 2019 08:07
Show Gist options
  • Save patwooky/5f12207670c2743c75ced2553c2cc60f to your computer and use it in GitHub Desktop.
Save patwooky/5f12207670c2743c75ced2553c2cc60f to your computer and use it in GitHub Desktop.
Maya Arnold light filters apply to many (Arnold compatible) lights with Maya UI
'''
connectAiLightFilters
Written by: Patrick Woo ([email protected])
Version: v02
Date: 20190307
--- Description ---
This tool connects Arnold light filters (decay, gobo, blocker, barn door) to Arnold lights
This is especially useful when we want to duplicate many lights, which are all connected to the same
Arnold decay or light blocker filter. This tool saves us from having to manually work the interface
to choose to connect each light to the Arnold light filter node.
---
changelog:
V02
---
- lightsAttrList building now has a check for the aiFilters attr, in a list comprehension format
V01
---
- this is the ui version of the previous which contained only the core function.
- increase execution by 248.512 times from previous versions which also
generated a lot of warning lines from Maya like this:
# Warning: pymel.core.general : Could not create desired MFn. Defaulting to MFnDagNode. #
'''
from pymel.core import *
from functools import partial
class aiFilterUtils(object):
def __init__(self):
self.winName = 'aiFilterUtilsWin'
self.winWH = (450, 1)
self.buildUI()
def buildUI(self):
winName = 'aiFilterUtilsWin'
winWH = self.winWH
if (window(self.winName, q=True, exists=True)):
deleteUI(self.winName)
window(self.winName, title='aiFilter Utilities', width=winWH[0], height=winWH[1])
main_cl = columnLayout(w=winWH[0])
frameLayout(w=winWH[0], label='Selection', collapsable=False, borderVisible=False)
tmpw = [x*winWH[0] for x in [0.9, 0.1]]
self.aiFilterGrab_rl = rowLayout(w=winWH[0], numberOfColumns=2, columnWidth2=tmpw)
ann = 'Grab an aiFilter node that can be inserted into an Arnold Light Filters list'
self.aiFilterGrab_tfbg=textFieldButtonGrp(w=tmpw[0], h=30, label='aiFilter Node', text='',
buttonLabel='Grab', editable=False, ann=ann,
columnWidth3=[x*tmpw[0] for x in [0.2, 0.7, 0.1]])
textFieldButtonGrp(self.aiFilterGrab_tfbg, e=True,
buttonCommand=partial(self.grabButtonPressed, self.aiFilterGrab_tfbg))
button(label='select', w=tmpw[1], ann='select the grabbed aiFilter node',
command=partial(self.selectButtonPressed, self.aiFilterGrab_tfbg))
setParent('..')
tmpw = [x*winWH[0] for x in [0.9, 0.1]]
self.aiLightsGrab_rl = rowLayout(w=winWH[0], numberOfColumns=2, columnWidth2=tmpw)
ann = 'Grab Arnold lights or a parent of many Ai Lights<br><br>'
ann += 'If grabbing a light parent, please enable the "<b><i>all descendents</i></b>" checkBox '
ann += 'to target all the children lights below it'
self.aiLightsGrab_tfbg=textFieldButtonGrp(w=tmpw[0], h=30, label='Lights', text='',
buttonLabel='Grab', editable=False, ann=ann,
columnWidth3=[x*tmpw[0] for x in [0.2, 0.7, 0.1]])
textFieldButtonGrp(self.aiLightsGrab_tfbg, e=True,
buttonCommand=partial(self.grabButtonPressed, self.aiLightsGrab_tfbg))
button(label='select', w=tmpw[1], ann='select the grabbed aiLight selection',
command=partial(self.selectButtonPressed, self.aiLightsGrab_tfbg))
setParent('..')
frameLayout(w=winWH[0], label='Settings', collapsable=False, borderVisible=False)
columnLayout(w=winWH[0])
tmpw = [x*winWH[0] for x in [0.475, 0.05, 0.475]]
rowLayout(w=winWH[0], numberOfColumns=3, columnWidth3=tmpw)
ann = 'Turning on will recursively target all children of the grabbed object '
ann += 'for an eligible "aiFilter" attribute.<br><br>'
ann += 'In other words, turn this on to include all children lights.'
self.alldescendents_cb = checkBox(w=tmpw[0], label='All descendents', value=True, ann=ann)
text(label='', w=tmpw[1])
self.disconnect_cb = checkBox(w=tmpw[2], label='Disconnect', changeCommand=self.disconnectCbChanged)
setParent('..')
rowLayout(w=winWH[0], numberOfColumns=3, columnWidth3=tmpw)
self.forceReplace_cb = checkBox(w=tmpw[0], label='Force replace')
text(label='', w=tmpw[1])
self.addToExisting_cb = checkBox(w=tmpw[2], label='Add to existing filters')
setParent(main_cl)
text(label='', w=winWH[0], h=20)
ann = 'Execute the connect/disconnect with the existing options'
self.doit_b = button(label='DO IT', w=winWH[0], h=50, enable=False, ann=ann,
command=self.doitButtonPressed)
text(label='[email protected]', w=winWH[0], align='right', enable=False)
showWindow(winName)
window(winName, e=True, w=1, h=1)
# end buildUI()
def grabButtonPressed(self, whichButton, *args):
if not ls(sl=True):
textFieldButtonGrp(whichButton, e=True, text='')
else:
if whichButton == self.aiFilterGrab_tfbg:
# grabbing a filter
# check that there is only 1 filter
textFieldButtonGrp(whichButton, e=True, text=str(ls(sl=True)))
else:
# grabbing lights
textFieldButtonGrp(whichButton, e=True, text=str(ls(sl=True)))
# check if both aiFilter and lights textfields are filled up
# check that number of aiFilters attrs are not zero
# then enable the 'Do it' button
buttonEnableFlag = False
if textFieldButtonGrp(self.aiFilterGrab_tfbg, q=True, text=True) and \
textFieldButtonGrp(self.aiLightsGrab_tfbg, q=True, text=True):
buttonEnableFlag = True
button(self.doit_b, e=True, enable=buttonEnableFlag)
return
# end grabButtonPressed
def selectButtonPressed(self, whichField, *args):
textFieldContent = textFieldButtonGrp(whichField, q=True, text=True)
cmd = 'select({})'.format(textFieldContent)
# print ('built command is: {}'.format(cmd))
exec(cmd)
pass
# end selectButtonPressed
def doitButtonPressed(self, *args):
# assemble parameters for self.connectAiLightsFilters
filterNode = textFieldButtonGrp(self.aiFilterGrab_tfbg, q=True, text=True)
lightsNodesList = textFieldButtonGrp(self.aiLightsGrab_tfbg, q=True, text=True)
exec('filterNode = {}'.format(filterNode))
filterNode = PyNode(filterNode[0])
exec('lightsNodesList = {}'.format(lightsNodesList))
# print('filterNode is {}'.format(filterNode))
self.connectAiLightFilters(
lightFilterNode = filterNode,
lightsList = lightsNodesList,
allDescendents = checkBox(self.alldescendents_cb, q=True, value=True),
disconnect = checkBox(self.disconnect_cb, q=True, value=True),
forceReplace = checkBox(self.forceReplace_cb, q=True, value=True),
addToExistingFilters = checkBox(self.addToExisting_cb, q=True, value=True))
# end doitButtonPressed
def disconnectCbChanged(self, *args):
cbState = checkBox(self.disconnect_cb, q=True, value=True)
enableFlag = False
if not cbState:
enableFlag = True
checkBox(self.forceReplace_cb, e=True, enable=enableFlag)
checkBox(self.addToExisting_cb, e=True, enable=enableFlag)
# end disconnectCbChanged
# Apply Maya Arnold light filters to many (Arnold compatible) lights
def connectAiLightFilters(self, lightFilterNode=None, lightsList=None,
allDescendents = True,
disconnect = False,
forceReplace = False,
addToExistingFilters = False,
*args):
'''
allDescendents - <bool> when True, all children lights will be considered
disconnect - <bool> when True, will disconnect all light filters connected to the aiFilters attr
forceReplace - <bool> disconnect and replace any existing connection first if already exist
this means that the list will be cleared
addToExistingFilters - <bool> add the aiFilter as an additional entry in the list of items
'''
import types
import timeit
print ('connectAiLightFilters: flags are:')
print ('lightFilterNode = {}'.format(lightFilterNode))
print ('lightsList = {}'.format(lightsList))
print ('allDescendents: {}'.format(allDescendents))
print ('disconnect: {}'.format(disconnect))
print ('forceReplace: {}'.format(forceReplace))
print ('addToExistingFilters: {}'.format(addToExistingFilters))
if type(lightsList) is not types.ListType:
# lightsList is not a list, casting it into a list
lightsList = list(lightsList)
# print ('lightsList is {}'.format(lightsList))
if allDescendents:
# add add all descendents of the initial list to lightsList
lightsList += listRelatives(lightsList, ad=True)
# remove transform nodes
lightsList = [x for x in lightsList if 'transform' not in nodeType(x)]
print('lightsList is\n{}'.format(lightsList))
startTime = timeit.default_timer()
lightsAttrList = [y.attr('aiFilters') for y in [x for x in lightsList] \
if attributeQuery('aiFilters', node=y, exists=True)]
print ('time taken building lightsAttrList: {}'.format(timeit.default_timer() - startTime))
print ('collected {} aiFilters attrs'.format(len(lightsAttrList)))
if not lightsAttrList:
msg = 'No lights to connect! The selection does not contain any eligible lights with an aiFilter list'
print (msg)
return
for thisAttr in lightsAttrList:
if disconnect:
# disconnect flag merely disconnects the attr
thisAttr.disconnect()
else:
'''
with a multi-element (or compound) attr like aiFilters which is a list,
when there are more than 1 item, it becomes an indexed array
aiFilters[0], aiFilters[1], etc.
in this case where you have more than 1 element connected, only the items on aiFilters[x]
will take affect.
If at this point we connect another input to aiFilters with the "force" flag,
the new connection will connect to the main aiFilters attr,
But only the aiFilters[x] items will have an effect on the light.
Also, only the aiFilters[x] items will not be listed in the aiFilters list in the interface
Hence under the circumstances there is no benefit when we use the force flag when connecting.
'''
newElementIdx = 0
if forceReplace:
# when there's only a single connection, the "force" flag will disconnect
# existing connection from the main aiFilters attr and replace it
# with the lightFilterNode
thisAttr.disconnect()
# reconnection will happen outside of this if block
elif addToExistingFilters:
# and user wants to add to the list of existing aiFilters
# find out how many entries exist, then set this aiFilter's index at the end
# we do not check if the same connections already exist.
# we only add this to the end of the list.
# Maya actually allows for double entry of the same item,
# so we o will not stop the user from doing so
newElementIdx = len(thisAttr.listConnections())
# build the actual attribute[index]
thisAttr = PyNode('{}[{}]'.format(thisAttr.name(), newElementIdx))
# make the connection
lightFilterNode.message.connect(thisAttr)
return
# end connectAiLightFilters
# end class aiFilterUtils
# main start
aiFilter_instance = aiFilterUtils()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment