Search code examples
pythonmaya

How can I perform shader assignment to a geo efficiently and non-destructively?


My goal is to set a shader to each parts of a geo. The parts of the geo are typically a group of attached faces. I do this to bake an ID map and then use it in Substance Painter.

So far, I separate the parts, then assign a shader to each and unite them back. I eventually delete the history to clean the outliner and the mesh itself.

import maya.cmds as mc
import random

selection = mc.ls(sl=True)[0]
parent = mc.listRelatives( selection, ap = True)[0]
mc.polySeparate(selection)
parts = mc.ls(sl=True)

for p in parts:
    sha = mc.shadingNode('lambert', asShader=True, name=p+"_lambert")
    sg = mc.sets(empty=True, renderable=True, noSurfaceShader=True,  name=p+"_SG")
    mc.setAttr(sha+".color", random.uniform(0.0, 1.0), random.uniform(0.0, 1.0), random.uniform(0.0, 1.0))
    mc.connectAttr( sha+".outColor", sg+".surfaceShader", f=True)
    mc.sets(p, e=True, forceElement=sg)

result = mc.select( selection, r = True )
sel = mc.ls( sl=True )
parts = mc.listRelatives( sel[0], c = True )
mc.polyUnite(parts, muv = True, name = selection)
mc.DeleteHistory()

I've got my shaders on parts but lose my original group under which the mesh was parented, and it doesn't feel right to do it this way.

Is there a way to create sets of parts without using 'separate' and assign a shader to each set?


Solution

  • I am assuming that you are assigning shaders on a combined object but my script should give you the idea : (run it and it will assign random shader on the combined sphere)

    Just to resume Im listing the faces of the object selected. I take one face, grow the selection until it cant anymore Assign a shader on them, removing the selection of faces from the main list and do it again in a loop at the end im producing some kind of history by creating a set per object and sub set per lambert assigned

    import maya.cmds as cmds
    import random
    
    def growComponent(component):
        # component = vertices[0]
        check = component.split('.')
        if len(check) != 2:
            cmds.error('please select one component in order to grow the selection')
    
        mySel = [component]
        counterSel = len(cmds.ls(component, fl = True))
        myFinalSel = 0
    
        while myFinalSel != counterSel:
            mySel = component
            counterSel = len(mySel)
            vert = cmds.polyListComponentConversion(mySel, tv = True)
            component = cmds.polyListComponentConversion(vert, tf = True)
            component = cmds.ls(component, fl = True)
            myFinalSel = len(component)
    
        component = cmds.polyListComponentConversion(component, tv = True)
        return component
    
    # =========================
    #just to illustrate an exemple selection of 10 sphere combined
    psph = [cmds.polySphere()[0] for i in range(10)]
    for i in psph:
        cmds.setAttr(i+'.t', random.uniform(0.0, 3.0), random.uniform(0.0, 3.0), random.uniform(0.0, 3.0))
    sph_comb = cmds.polyUnite(psph)
    cmds.delete(sph_comb, ch=True)
    # ===============================
    
    
    selection = cmds.ls(sph_comb)[0]
    
    faceSet = cmds.sets(em = True, name = '{}_sg_faceset'.format(selection))
    
    combinedFaceGrp = cmds.ls(selection + '.f[:]', fl = True)
    
    partsFace = []
    x = 0
    while combinedFaceGrp or x>10000:
    
        len(combinedFaceGrp)
        component = growComponent(combinedFaceGrp[0])
        faces = cmds.polyListComponentConversion(component, tf = True)
        partset = cmds.sets(faces, name='part{}_{}_set'.format(x, selection))
        cmds.sets(partset, edit = True, fe = faceSet)
        x+=1
    
        # remove face selected
        faces_fl = cmds.ls(faces, fl=True)
        combinedFaceGrp = list(set(combinedFaceGrp)-set(faces_fl))
    
        # assign shader
        sha = cmds.shadingNode('lambert', asShader=True, name="{}_{}_lambert".format(selection, x))
        sg = cmds.sets(empty=True, renderable=True, noSurfaceShader=True,  name="{}_{}_sg".format(selection, x))
        cmds.setAttr(sha+".color", random.uniform(0.0, 1.0), random.uniform(0.0, 1.0), random.uniform(0.0, 1.0))
        cmds.connectAttr( sha+".outColor", sg+".surfaceShader", f=True)
        cmds.sets(faces, e=True, forceElement=sg)