Last active
March 7, 2019 08:07
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
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