Criso
Criso

Reputation: 397

Getting all materials attached to object throughout all render layers [Maya]

Issue

Getting all materials attached to an object without switching render layers; getting the materials from other render layers for the particular object.

Code to get material from object

# Gets all shaders attached to the current renderLayer for all objects in "group1"
import maya.cmds as cmds

cmds.select("group1")
allChildren = cmds.listRelatives(ad=1)
for eachChild in allChildren:
    # Get the shader groups attached to this particular object
    shaderGroups = cmds.listConnections(cmds.listHistory(eachChild))
    if shaderGroups is not None:
        # Get the material attached to the shader group
        materials = [x for x in cmds.ls(cmds.listConnections(shaderGroups), materials=1)]
print materials

The only other way I know to get information for the placed in other render layers is to switch to them and then run the same code... Anyone have any other suggestions?

Edit:

cmds.listConnections() and cmds.listHistory() and the combination of the two above seem to provide the render layers, themselves, if attached to the objects that I'm getting materials from; I.E. - materials = [u'Car_Paint_Material', u'RenderLayer_BluePaint', u'RenderLayer_RedPaint'].

Edit #2:

Screenshots!

defaultRenderLayer -> Red material for selected object defaultRenderLayer -> Red material for selected object.

layer1             -> Green material for selected object layer1 -> Green material for selected object.

Run above script to get material Run above script to get material.

Run un-optimized to get object + renderLayer + material Run un-optimized to get object + renderLayer + material. It works, but it has to switch render layers to get that information.

I need a solution that gets the above information without switching renderlayers.

Upvotes: 2

Views: 8049

Answers (3)

Kaveen Perera
Kaveen Perera

Reputation: 464

Problem in your approach

materials = [x for x in cmds.ls(cmds.listConnections(shaderGroups), materials=1)] only returns the shader name of the selected layer.

Solution

Following script will store all the materials from all the models of all the layers in a dictionary in the following order;

meshName : {layerName : shader/MaterialName}

It is stored in a dictionary called objectList

The script was tested with a scene with 1203 objects and 2 render layers (+the default render layer). It executes in a fraction of a second.

meshList = cmds.ls(type='mesh')
shaderList = {}
for mesh in meshList:
    shaderList[mesh]={}

#get render layers
renderLayers = [x for x in cmds.ls(type="renderLayer")]

for eachLayer in renderLayers:
    currentRenderLayerName = eachLayer
    attrib = currentRenderLayerName+'.outAdjustments'
    numberOfConnectedMeshes = len(cmds.getAttr(attrib, multiIndices=True))

    for currentMeshIndex in range(numberOfConnectedMeshes):

        queryConnection = currentRenderLayerName + '.outAdjustments['+str(currentMeshIndex)+']'
        # sometimes if you delete an object, the connection might still be active.
        # validating it with an if condition avoids errors
        if cmds.listConnections(queryConnection+'.outPlug') !=  None:

            # following line should technically return a mesh name, however it returns the transform object name
            currentTransformObject = cmds.listConnections(queryConnection+'.outPlug')[0]
            # following line is important as the 'currentTransformObject' does is the not mesh name.
            # if it does return the mesh object then just change 'currentTransformObject' to 'currentMeshName'
            # and comment out the following line
            currentMeshName = cmds.listRelatives(currentTransformObject)[0]

            currentMeshShadingGroup = cmds.listConnections(queryConnection+'.outValue')[0]
            currentShadingGroupSurfaceShader = cmds.listConnections(currentMeshShadingGroup+'.surfaceShader')

            shaderList[currentMeshName][currentRenderLayerName] = currentShadingGroupSurfaceShader

# If you only have objects on the defaultRenderLayer
# above code ignored those objects.
# following will add them to the shaderList dict
for eachMesh in shaderList:
    if shaderList[eachMesh]=={}:
        currentRenderLayerName  = "defaultRenderLayer"
        materialAppliedToObject = cmds.ls(cmds.listConnections(cmds.listConnections(
                                          cmds.listHistory(cmds.listRelatives(eachMesh, parent=1)))), 
                                          mat=1)[0]
        shaderList[eachMesh][currentRenderLayerName] = materialAppliedToObject 

this returned on the sample scene with three objects the following.

shaderList
# Result: 
{
    u'pSphereShape0':
        {
        u'defaultRenderLayer': [u'lambert2'],
        },
    u'pSphereShape1': 
        {
        u'defaultRenderLayer': [u'anisotropic1'], 
        u'layer2': [u'anisotropic3'], 
        u'layer1': [u'anisotropic2']
        }, 
    u'pCubeShape1':
        {
        u'defaultRenderLayer': [u'phong1'], 
        u'layer2': [u'phong3'], 
        u'layer1': [u'phong2']
        },
    u'pConeShape1':
        {
        u'defaultRenderLayer': [u'blinn1'], 
        u'layer2': [u'blinn3'], 
        u'layer1': [u'blinn2']
        }
} # 

to query a surface shader of a given mesh to the corresponding layer use the following form

shaderList['pCubeShape1']['layer2']

# Result: [u'phong3'] 
# notice it returns a list.
# you have to use the index if you need only the name 
#`objectList['pCubeShape1']['layer2'][0]`

How to customise?

You can re arrange the order of the dict by directly editing the code, or a better alternative is you can query the shaders list of a selected object or a group of objects, and store that in a new variable in the desired order.

Important

If an object is not added to a render layer, a corresponding shader information will not be returned.

Tested with

Mental Ray / Software / Hardware 2.0 Renderers (Please add to the answer if any of you have success with other rendereres)

Upvotes: 3

Criso
Criso

Reputation: 397

I have a temporary solution:

# Gets all shaders attached to the current renderLayer for all objects in "group1"
import maya.cmds as cmds

# Create a dictionary for {"object1": ["material1", "material2"]}
objectToMaterials = {}

# List of renderlayers
renderLayers = [x for x in cmds.ls(type="renderLayer")]

# Pick each renderLayer and switch to it


cmds.select("group1")
allChildren = cmds.listRelatives(ad=1)
for eachChild in allChildren:
    if cmds.objectType(eachChild)=="mesh":
        eachChild = cmds.listRelatives(eachChild, parent=1)
        for eachRenderLayer in renderLayers:
            cmds.editRenderLayerGlobals(crl=eachRenderLayer)
            # Get the shader groups attached to this particular object
            shaderGroups = cmds.listConnections(cmds.listHistory(eachChild))
            if shaderGroups is not None:
                # Get the material attached to the shader group
                materials = [x for x in cmds.ls(cmds.listConnections(shaderGroups), materials=1)]

            objectToMaterials.setdefault(eachChild[0], []).append(eachRenderLayer)
            objectToMaterials.setdefault(eachChild[0], []).append(materials[0])

print(objectToMaterials)

# objectToMaterials = {"object1": ["defaultRenderLayer", "RenderLayer_RedPaint"], ["secondRenderLayer", "RenderLayer_BluePaint"], "object2": ["defaultRenderLayer", "RenderLayer_BluePaint"]}

Upvotes: 0

Ari Gold
Ari Gold

Reputation: 1548

# we need a sandbox case   
collection = []
for layer in cmds.ls(type = "renderLayer"):
    members =  cmds.editRenderLayerMembers( layer, query=True)
    for i in members:
        shader_engine = cmds.listConnections(cmds.listHistory(i), type = 'shadingEngine')
        _shaders = cmds.ls(cmds.listConnections(shader_engine), materials= True)
    collection.append(dict(object = members, layer = layer, engine = shader_engine, shaders = _shaders))
print collection

[{'engine': [u'blinn2SG'], 'layer': u'defaultRenderLayer', 'object': [u'pSphere1', u'pSphereShape1', u'pSphere2', u'pSphereShape2'], 'shaders': [u'blinn2']}, {'engine': [u'lambert2SG'], 'layer': u'layer1', 'object': [u'pSphere1'], 'shaders': [u'lambert2']}, {'engine': [u'blinn2SG'], 'layer': u'layer2', 'object': [u'pSphere2'], 'shaders': [u'blinn2']}]

Upvotes: 0

Related Questions