Search code examples
pythonpipelinemaya

Create shader from file selection and apply to selected object


You select a folder with a bunch of images, select the one you wish to use from a dropdown menu, then select your object and hit apply. My issue is that I cannot do this to multiple objects in a scene, it only changes the material on the first object that is selected. When I try to do this to another object in my scene it just replaces the image on the first object and doesn't create another shader. The goal is to be able to select any object in the scene and apply a texture from the selected list to any of the objects in the scene. Help would be greatly appreciated. I provided the tool down below.

import maya.cmds as cmds
from os import listdir

class TextureImport():
    def __init__(self):
        if cmds.window(TextureImport, q=True, exists=True):
            cmds.deleteUI(TextureImport)
        GUI=cmds.window(title="Texture Import Tool", widthHeight=(250,160), s=True, tlb=True)
        cmds.rowColumnLayout(numberOfColumns=1, columnAlign=(1, 'center'), columnAttach=(1, 'both', 0), cw=(1,250))
        cmds.button(label="Select Directory", command=self.select_dir)
        cmds.separator(style='in', h=20)
        cmds.optionMenu('optionMenu', label="File List")
        cmds.button(label="Clear List", command=self.clear_list)
        cmds.separator(style='in', h=20)
        cmds.text('Select your object, then:', h=25)
        cmds.button(label="Apply Texture", command=self.apply_texture)
        cmds.setParent('..')
        cmds.showWindow()

    def select_dir(self, *args):
        basicFilter = "Image Files (*.jpg *.jpeg *.tga *.png *.tiff *.bmp *.psd)"
        self.myDir = cmds.fileDialog2 (fileFilter=basicFilter, dialogStyle=2, fm=3)
        myFiles = listdir(self.myDir[0])

        for items in myFiles:
            fileEndings = ('.psd','.PSD','.jpg','JPG','.jpeg','.JPEG','.tga','.TGA','.png','.PNG','.tiff','.TIFF','.bmp','.BMP')
            if items.endswith(fileEndings):
                cmds.menuItem(items)
            else:
                cmds.warning(items + 'This is not a valid image type, you fool.')
        print myFiles

    def clear_list(self, *args):
        fileList = cmds.optionMenu('optionMenu', q=True, itemListLong=True)
        if fileList:
            cmds.deleteUI(fileList)

    def apply_texture(self, *args):
        object = cmds.ls(sl=True)
        selectedMenuItem = cmds.optionMenu('optionMenu', q=True, value=True)
        cmds.sets(name='imageMaterialGroup', renderable=True, empty=True)
        shaderNode = cmds.shadingNode('phong', name='shaderNode', asShader=True)
        fileNode = cmds.shadingNode('file', name='fileTexture', asTexture=True)
        cmds.setAttr('fileTexture'+'.fileTextureName', self.myDir[0]+'/'+selectedMenuItem, type="string")
        shadingGroup = cmds.sets(name='textureMaterialGroup', renderable=True, empty=True)
        cmds.connectAttr('shaderNode'+'.outColor','textureMaterialGroup'+'.surfaceShader', force=True)
        cmds.connectAttr('fileTexture'+'.outColor','shaderNode'+'.color', force=True)
        cmds.surfaceShaderList('shaderNode', add='imageMaterialGroup')
        cmds.sets(object, e=True, forceElement='imageMaterialGroup')
TextureImport()

The goal is to be able to select any object in the scene and apply a texture from the selected list to any of the objects. For example, an artist could set up multiple planes to apply reference images on. This tool would create the shader from the selected files which would make their job very easy. Help on this would be greatly appreciated.


Solution

  • Your problem lies in the way you try to connect the attributes:

    fileNode = cmds.shadingNode('file', name='fileTexture', asTexture=True)
    cmds.setAttr('fileTexture'+'.fileTextureName', ..., type="string")
    

    You use the explicit name of the file node: 'fileTexture'. The result is that if the node exists, the exisiting node is used instead of your newly created one. You have to build the attribute with the fileNode variable this way:

    fileNode = cmds.shadingNode('file', name='fileTexture', asTexture=True)
    cmds.setAttr(fileNode+'.fileTextureName', ..., type="string")
    

    The same should be changed in the other connectAttr() functions.