import fnmatch import nuke # martin@keller.io __version__ = '1.0' MODES = ['MergeBackIntoStream','SelectOnly', 'ContactSheet'] PATTERNDEFAULT = "*direct* !*indirect* !*default" INIT_DONE = False class ChannelSelect(KellerNukePlugin): def configurePlugin(self): self.menu = nuke.menu('Nodes') m = self.menu.addMenu('Workgroup','Toolbar_Workgroup.png') m.addCommand('ChannelSelect', 'import ChannelSelect; ChannelSelect.createChannelSelect()', icon=':qrc/images/ToolbarChannel.png') nuke.addKnobChanged(onKnobChanged) def unconfigurePlugin(self): self.menu.removeItem('ChannelSelect') nuke.removeKnobChanged(onKnobChanged) pass # events def onKnobChanged(): global INIT_DONE if not INIT_DONE: # not ready return k = nuke.thisKnob() # do a refresh on these knobs refresh_resolved = ['pattern','update'] if k.name() in refresh_resolved: print('ChannelSelect: Info. Event refresh triggered.') # Config changed, loading new tasks n = nuke.thisNode() pattern = n['pattern'].value() resolved = resolvePattern(n, pattern) if resolved: resString = '\n'.join(resolved) if not resolved: n['resolved'].setValue('NO MATCH') else: n['resolved'].setValue(resString) pass def createChannelSelect(): # get selected Node (for connecting our node below) try: s = nuke.selectedNode() except: s = None # deselect all Nodes for a in nuke.allNodes(): a.knob('selected').setValue(False) # create Group n = nuke.collapseToGroup(show = False) n.setName('ChannelSelect') # % (r * 255, g * 255, b * 255, 1), 16): change r g and b to your liking hexColour = int('%02x%02x%02x%02x' % (0 * 255, 0 * 255, 0 * 255, 1), 16) n['tile_color'].setValue(hexColour) if s is not None: n.setInput(0, s) n.setXYpos(s.xpos(), s.ypos() + 100) # tab ChannelSelect k = nuke.Tab_Knob('select', 'Select') n.addKnob(k) # Pattern k = nuke.EvalString_Knob('pattern', 'Pattern') n.addKnob(k) n.knob('pattern').setTooltip('*diffuse* *SSS*\nwith exclude (! or ^):\n*direct* !*indirect*') n.knob('pattern').setValue(PATTERNDEFAULT) # Update k = nuke.PyScript_Knob('update', 'Set', 'ChannelSelect.updatePattern(nuke.thisNode())') n.addKnob(k) n.knob('update').setTooltip('Creates Nodes in Group based on Search Pattern') # Resolved Channels k = nuke.Multiline_Eval_String_Knob('resolved', 'Resolved') n.addKnob(k) n.knob('resolved').setTooltip('These AOVs are found based on your search Pattern above') # Divide and Conquer k = nuke.Text_Knob('divider', '') n.addKnob(k) # Exposure k = nuke.Double_Knob('exposure', 'Exposure') n.addKnob(k) n.knob('exposure').setRange(-2,2) # Multiply k = nuke.Color_Knob('multiply', 'Multiply') n.addKnob(k) n.knob('multiply').setValue([1, 1, 1]) # Gamma k = nuke.Double_Knob('gamma', 'Gamma') n.addKnob(k) n.knob('gamma').setValue(1) n.knob('gamma').setRange(0.2, 2) # Compensate Mult k = nuke.Boolean_Knob('compensate', 'Compensate Mult') n.addKnob(k) n.knob('compensate').setValue(True) n.knob('compensate').setFlag(nuke.STARTLINE) # Reset Values k = nuke.PyScript_Knob('reset', 'Reset', 'ChannelSelect.resetValues(nuke.thisNode())') n.addKnob(k) n.knob('reset').setTooltip('Reset Color Correct values') n.knob('reset').setFlag(nuke.STARTLINE) # Divide and Conquer k = nuke.Text_Knob('divider', '') n.addKnob(k) # Mode k = nuke.Enumeration_Knob('mode', 'Mode', MODES) n.addKnob(k) k.setTooltip('MergeBackIntoStream: Shuffles the resolved aovs, applies the grading options and merges the result back to main comp stream\n\nSelectOnly: Just shuffles the resolved aovs and adds them together.\n\nLayerContactSheet: Displays the LayerContactSheet with layer names. Note. You need to Press Set in order for this to work. An empty Pattern is possible') n.knob('mode').clearFlag(nuke.STARTLINE) # refresh again n.knob('pattern').setValue('*key*') n.knob('pattern').setValue(PATTERNDEFAULT) global INIT_DONE INIT_DONE = True return None def resetValues(n): n['exposure'].setValue(0) n['multiply'].setValue([1, 1, 1]) n['gamma'].setValue(1) n['compensate'].setValue(True) def resolvePattern(n, pattern): if pattern == "": return None channels = n.channels() layers = list(set([c.split('.')[0] for c in channels])) layers.sort() matchChannels = [] ignoreChannels = [] for p in pattern.split(' '): matchChannels += (fnmatch.filter(layers, p)) # excludes if p[0] == '!' or p[0] == '^': ignoreChannels += (fnmatch.filter(layers, p[1:])) matchChannels = list(set(matchChannels) - set(ignoreChannels)) matchChannels.sort() return matchChannels def updatePattern(n): V_OFFSET = 250 # n = node pattern = n['pattern'].value() resolved = resolvePattern(n, pattern) # create nodes # input and output of Group input = nuke.toNode('Input1') output = nuke.toNode('Output1') # delete old nodes g = nuke.toNode(n.name()) oldNodes = [i for i in g.nodes() if '__' in i['name'].value()] for node in oldNodes: nuke.delete(node) # dot input dot_input = nuke.nodes.Dot(xpos=input.xpos() , ypos=input.ypos()+100) dot_input['name'].setValue('dot_input__') dot_input['label'].setValue('[value name]') #dot_input['note_font_size'].setValue(25) # connect to the input1 node # dot input 2 dot_input2 = nuke.nodes.Dot(xpos=dot_input.xpos()+1500 , ypos=dot_input.ypos()) dot_input2['name'].setValue('dot_input2__') dot_input2['label'].setValue('[value name]') #dot_input2['note_font_size'].setValue(25) # connect to the input1 node # dot resolved dot_resolved = nuke.nodes.Dot(xpos=dot_input.xpos() , ypos=dot_input.ypos() + V_OFFSET) dot_resolved['name'].setValue('dot_resolved__') dot_resolved['label'].setValue('[value name]') #dot_resolved['note_font_size'].setValue(25) # dot resolved2 dot_resolved2 = nuke.nodes.Dot(xpos=dot_resolved.xpos() - 100, ypos=dot_resolved.ypos()) dot_resolved2['name'].setValue('dot_resolved2__') dot_resolved2['label'].setValue('[value name]') #dot_resolved2['note_font_size'].setValue(25) # dot resolved3 dot_resolved3 = nuke.nodes.Dot(xpos=dot_resolved2.xpos(), ypos=dot_resolved.ypos() + V_OFFSET) dot_resolved3['name'].setValue('dot_resolved3__') dot_resolved3['label'].setValue('[value name]') #dot_resolved3['note_font_size'].setValue(25) # merge from merge_from = nuke.nodes.Merge2(xpos=dot_input.xpos()+500 , ypos=dot_input.ypos() ) merge_from['name'].setValue('merge_from__') merge_from['operation'].setValue("from") # exposure exposure = nuke.nodes.EXPTool(xpos=dot_resolved.xpos() , ypos=dot_resolved.ypos() + V_OFFSET) exposure['name'].setValue('exposure__') # grade grade = nuke.nodes.Grade(xpos=exposure.xpos() , ypos=exposure.ypos() + V_OFFSET) grade['name'].setValue('grade__') # merge plus merge_plus = nuke.nodes.Merge2(xpos=grade.xpos() , ypos=grade.ypos() + V_OFFSET) merge_plus['operation'].setValue("plus") merge_plus['name'].setValue('merge_plus__') # dot input From dot_input_from = nuke.nodes.Dot(xpos=merge_from.xpos() , ypos=merge_plus.ypos()) dot_input_from['name'].setValue('dot_input_from__') dot_input_from['label'].setValue('[value name]') #dot_input_from['note_font_size'].setValue(25) # contact_sheet_all contact_sheet_all = nuke.nodes.LayerContactSheet(xpos=merge_plus.xpos() +250 , ypos=merge_plus.ypos() + 200) contact_sheet_all['name'].setValue('contact_sheet__') # switch switch = nuke.nodes.Switch(xpos=merge_plus.xpos() , ypos=merge_plus.ypos() + V_OFFSET) switch['name'].setValue('switch__') # dot output dot_output = nuke.nodes.Dot(xpos=dot_input.xpos() , ypos=dot_input.ypos() + V_OFFSET) dot_output['name'].setValue('dot_output__') dot_output['label'].setValue('[value name]') #dot_output['note_font_size'].setValue(25) # create new nodes i = 1 j = 0 offset = 100 merge_resolved = None if resolved: if len(resolved) > 0: for r in resolved: # shuffle shuffle = nuke.nodes.Shuffle(xpos=dot_input.xpos() - (len(resolved)/2)*offset + (i * offset), ypos=dot_input.ypos() + offset) shuffle['in'].setValue(resolved[i - 1]) shuffle['in2'].setValue('rgba') shuffle['out'].setValue('rgb') shuffle['name'].setValue('shuffle__' + str(i)) # connect to the dot dot_input shuffle.setInput(0, dot_input) # create plus if i == 1: merge_resolved = nuke.nodes.Merge2(xpos=dot_input.xpos(), ypos=dot_output.ypos() + 50) merge_resolved['operation'].setValue("plus") merge_resolved['name'].setValue("merge_resolved__") # connect plus # skip Mask Input if j==2: j+=1 # connect input merge_resolved.setInput(j, shuffle) # next please i += 1 j += 1 # connect to the input1 node dot_resolved.setInput(0, merge_resolved) else: # shuffle shuffle = nuke.nodes.Shuffle(xpos=dot_input.xpos() + (i * offset), ypos=dot_input.ypos() + offset) # create black in rgb and a shuffle['in'].setValue('none') shuffle['in2'].setValue('none') shuffle['out'].setValue('rgb') shuffle['name'].setValue('shuffle_empty__') shuffle.setInput(0, dot_input) # remove old input connections #for r in range(output.inputs()): # output.setInput(r, None) # connect to the input1 node dot_resolved.setInput(0, shuffle) # connect the nodes dot_input.setInput(0, input) dot_input2.setInput(0, dot_input) dot_input_from.setInput(0, merge_from) dot_resolved2.setInput(0, dot_resolved) dot_resolved3.setInput(0, dot_resolved2) merge_plus.setInput(1, grade) merge_plus.setInput(0, dot_input_from) grade.setInput(0, exposure) exposure.setInput(0, dot_resolved) merge_from.setInput(0, dot_input2) merge_from.setInput(1, dot_resolved) contact_sheet_all.setInput(0, dot_input2) switch.setInput(0, merge_plus) switch.setInput(1, contact_sheet_all) switch.setInput(2, dot_resolved3) dot_output.setInput(0,switch) output.setInput(0, dot_output) # link the nodes exposure['mode'].setValue('Stops') exposure['gang'].setValue(0) exposure['red'].setExpression('parent.exposure') exposure['green'].setExpression('parent.exposure') exposure['blue'].setExpression('parent.exposure') # init grade['multiply'].setValue([1,1,1,1]) grade['multiply'].setExpression('parent.multiply.r',0) grade['multiply'].setExpression('parent.multiply.g',1) grade['multiply'].setExpression('parent.multiply.b',2) grade['multiply'].setValue(1, 3) # enable Compensate Mult by default grade['white'].setExpression('parent.compensate == 1 ? 1/((parent.multiply.r+parent.multiply.g+parent.multiply.b)/3) : 1') grade['gamma'].setExpression('parent.gamma') contact_sheet_all['width'].setExpression('parent.width') contact_sheet_all['height'].setExpression('parent.height') contact_sheet_all['showLayerNames'].setValue(1) # again. Weird Update Issues contact_sheet_all['showLayerNames'].setExpression('parent.mode == 0 ? 0 :1') switch['which'].setExpression('parent.mode == 0 ? 0 :(parent.mode == 1 ? 2 : 1)') # arrange the nodes weird_dot_offset = 0 dot_input.setXYpos(input.xpos()+weird_dot_offset, input.ypos() + 100) if merge_resolved: merge_resolved.setXYpos(input.xpos(), input.ypos() + 350) dot_resolved.setXYpos(input.xpos()+weird_dot_offset, input.ypos() + 500) dot_resolved2.setXYpos(dot_resolved.xpos()-250+weird_dot_offset, dot_resolved.ypos()) dot_resolved3.setXYpos(dot_resolved2.xpos()+weird_dot_offset, dot_resolved2.ypos() + 500) exposure.setXYpos(dot_resolved.xpos(), dot_resolved.ypos()+150) grade.setXYpos(exposure.xpos(), exposure.ypos() + 150) merge_plus.setXYpos(grade.xpos(), grade.ypos() + 150) switch.setXYpos(merge_plus.xpos(), merge_plus.ypos() + 150) dot_output.setXYpos(switch.xpos()+weird_dot_offset, switch.ypos() + 150) output.setXYpos(dot_output.xpos(), dot_output.ypos() + 150) merge_from.setXYpos(dot_input2.xpos(), dot_resolved.ypos()) dot_input_from.setXYpos(merge_from.xpos()+weird_dot_offset, merge_plus.ypos()) n['label'].setValue('[value pattern]') return None