yan
yan

Reputation: 649

Getting the name of the mesh that shader was assigned to

How do I get the name of the mesh that the shader was assigned to, using python?

Example, lambert02 --> AreaA_01_geo, lambert03 --> AreaA_03_geo, lambert04 --> AreaA_04_geo

I tried using

Shader = cmds.ls(type = 'surfaceShader')
for i in Shader:
    con = mc.listConnections('%s.outColor' % i)
    name = cmds.listConnections(Shader, type="mesh")

But I was unable to get anything out of name variable

Upvotes: 3

Views: 7022

Answers (3)

Patrick Woo
Patrick Woo

Reputation: 61

After posting my previous answer, I chanced upon an unexpected behaviour in Maya.

I found out that when I try to select a shadingEngine node (shading group node) from the Hypershade Window, you get the Shading Engine attributes in the attribute editor like so: Shading Group Node selected in Hypershade Window

Now if you hit the 'Select' button at the bottom of the Attribute Editor to actually try and select that shadingEngine node, you get the following result: Selecting the Shading Group Node with code and inside Attribute Editor selects the assigned meshes!

Maya actually went ahead and selected the assigned mesh objects for us.

So I tried to do the same thing with this single-line code:

select('aiHeiyuVarABelts2Shader', replace=True)

Maya actually went ahead and selected those exact same mesh nodes that the shader was assigned to.

This is the exact test scene as the one in my previous answer. When in my previous code, listConnections() returned a list of transform nodes connected to the shadingEngine nodes, selecting the shadingEngine node using these 2 new methods above, Maya actually selected mesh objects for us.

So making use of this seemingly inconsistent behaviour, I can revise my code to specifically get a list of the meshes you were asking for.

for shEngine in ls(type='shadingEngine'):
    select(shEngine, replace=True)
    print 'shading engine:', shEngine

    for connection in ls(sl=True):
        print '->', connection, '(%s)' % nodeType(connection)

    print ''

Running this on the same sample scene in my previous reply, I get this:

shading engine: aiHeiyuVarALashesShaderSG
    -> C_heiYuA_Mesh_lod200_v007:L_lower_eyelash_geoShape (mesh)
    -> C_heiYuA_Mesh_lod200_v007:L_upper_eyelash_geoShape (mesh)
    -> C_heiYuA_Mesh_lod200_v007:R_lower_eyelash_geoShape (mesh)
    -> C_heiYuA_Mesh_lod200_v007:R_upper_eyelash_geoShape (mesh)

shading engine: aiHeiyuVarAPantsShaderSG
    -> C_heiYuA_Mesh_lod200_v007:pants_geoShape (mesh)

shading engine: aiHeiyuVarASkinShaderMapSG
    -> C_heiYuA_Mesh_lod200_v007:body_geoShape (mesh)

shading engine: aiTopShaderSG
    -> C_heiYuA_Mesh_lod200_v007:top_geoShape (mesh)

shading engine: initialParticleSE

shading engine: initialShadingGroup
    -> C_heiYuA_Mesh_lod200_v007:upperTeeths_geoShape (mesh)
    -> C_heiYuA_Mesh_lod200_v007:lowerTeeths_geoShape (mesh)
    -> C_heiYuA_Mesh_lod200_v007:gums_geoShape (mesh)
    -> C_heiYuA_Mesh_lod200_v007:L_nail_geoShape (mesh)
    -> C_heiYuA_Mesh_lod200_v007:R_nail_geoShape (mesh)

shading engine: phong1SG

shading engine: useBackground1SG

The code is shorter now, and all the selected objects are solely mesh objects.

To obtain the shader nodes / surface materials that are connected to the shadingEngine node, we can then use:

listConnections(shadingEngineNode, type=['shadingDependNode', 'THdependNode'])

Personally I am inclined to stay away from this method because somehow I feel that it is not elegant, having to rely on a select(), then a ls(sl=True) function to retrieve the selected objects.

Am I paranoid? With this method I would also be insecure, always worried if Maya will continue to consistently support this behaviour in future versions.

I have just tested this in Maya 2014 and Maya 2016. The code works exactly the same way in both versions.

Bottom line is, it works. Please feel free to exploit it if you so choose to :)

Edit: After doing more digging and searching, I realised that the shadingEngine is a child class of objectSet. This "unexpected behaviour" is actually a standard Maya behaviour when we deal with sets (selection sets, render sets, light-linking sets, deformer membership sets, etc).

Thus we can directly use sets(setName, q=True) to query the set members. This is more sensible, elegant and quicker compared to the above mentioned method of depending on the select() command, then querying the selection to get the members of the set.

Here's the final piece of code:

from pymel.core import *

for shEngine in ls(type='shadingEngine'):
    print 'shading engine:', shEngine
    for connection in sets(shEngine,q=True):
        print '\t->', connection, '(%s)' % nodeType(connection)
    print ''

Upvotes: 0

Patrick Woo
Patrick Woo

Reputation: 61

I ran into this same problem in my attempt to generate a list of existing shaders in my scene, and the objects (meshes, shapes, transforms) that each shader is assigned to.

from pymel.core import *

def getShaders():
    for shEngine in ls(type='shadingEngine'):
        print 'shading engine:', shEngine

        for connection in shEngine.listConnections():
            connectionNodeTypeList = nodeType(connection, i=True)

            if [x for x in connectionNodeTypeList if 'ependNode' in x]:
                # tests for shader nodes. they inherit the 'shadingDependNode' type
                # I am using Arnold renderer. Arnold shading nodes inherit the 'THdependNode' type
                print '\t<shader> ->', connection, '(%s)'% nodeType(connection)

            if 'dagNode' in connectionNodeTypeList:
                # tests for transform, geometry and shape node
                # any transform, mesh, shape, curves, vertices, edges, faces
                # will inherit from the 'dagNode' type
                print '\t<dagNode> ->',connection, '(%s)'% nodeType(connection)
        print ''
getShaders()

This will generate a sample print out as follows (when run on a test scene):

shading engine: aiHeiyuVarALashesShaderSG
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:L_lower_eyelash_geo (transform)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:L_upper_eyelash_geo (transform)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:R_lower_eyelash_geo (transform)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:R_upper_eyelash_geo (transform)
    <shader> -> aiHeiyuVarALashesShader (aiStandard)

shading engine: aiHeiyuVarAPantsShaderSG
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:pants_geo (transform)
    <shader> -> aiHeiyuVarAPantsShader (aiStandard)

shading engine: aiHeiyuVarASkinShaderMapSG
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:body_geo (transform)
    <shader> -> aiHeiyuVarASkinShaderMap (aiStandard)

shading engine: aiTopShaderSG
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:top_geo (transform)
    <shader> -> aiHeiyuVarATopShader (aiStandard)

shading engine: initialParticleSE
    <shader> -> lambert1 (lambert)
    <shader> -> particleCloud1 (particleCloud)

shading engine: initialShadingGroup
    <shader> -> lambert1 (lambert)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:R_nail_geo (transform)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:L_nail_geo (transform)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:gums_geo (transform)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:lowerTeeths_geo (transform)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:upperTeeths_geo (transform)

shading engine: phong1SG

shading engine: useBackground1SG

From here it is easy to arrive at the associated mesh objects if you are looking specifically for meshes.

Edit:

After reading up some more, I figured out that we can use the following command to get a list of the mesh objects that has been assigned the shader.

sets(shadingEngine, q=True)

Thus the final form of the

from pymel.core import *

def getShaders():
    for shEngine in ls(type='shadingEngine'):
        print 'shading engine:', shEngine

        for connection in [x for x in shEngine.listConnections()]:
            myType = nodeType(connection,i=True)
            if ('shadingDependNode' in myType or 'THdependNode' in myType):
                # found a shader, printing it out
                print '\t<shader> ->', connection, '(%s)'% nodeType(connection)

        for connection in sets(shEngine, q=True):
            # prints out the list of members that the shader is assigned to
            print '\t<dagNode> ->',connection, '(%s)'% nodeType(connection)
        print ''
getShaders()

This latest method enables us to get at the mesh nodes as well as the shader nodes.

Here's a sample output:

shading engine: aiHeiyuVarAPantsShaderSG
    <shader> -> aiHeiyuVarAPantsShader (aiStandard)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:pants_geoShape (mesh)

shading engine: aiHeiyuVarASkinShaderMapSG
    <shader> -> aiHeiyuVarASkinShaderMap (aiStandard)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:body_geoShape (mesh)

shading engine: aiTopShaderSG
    <shader> -> aiHeiyuVarATopShader (aiStandard)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:top_geoShape (mesh)

shading engine: initialParticleSE
    <shader> -> lambert1 (lambert)
    <shader> -> particleCloud1 (particleCloud)

shading engine: initialShadingGroup
    <shader> -> lambert1 (lambert)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:upperTeeths_geoShape (mesh)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:lowerTeeths_geoShape (mesh)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:gums_geoShape (mesh)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:L_nail_geoShape (mesh)
    <dagNode> -> C_heiYuA_Mesh_lod200_v007:R_nail_geoShape (mesh)

Upvotes: 2

joojaa
joojaa

Reputation: 4434

The shader is connected to one or more shading sets which house the assignments. So this is not a 1:1 assignment but rather one to many, and then one to many again (granted you don't see it that often). Please note that you use 2 namespaces when you only should need one.

import maya.cmds as mc

Shader = mc.ls(type = 'surfaceShader')
for i in Shader:
    con = mc.listConnections('%s.outColor' % i)
    names = mc.listConnections(con, type="mesh")
    print i, "->", ", ".join(names)

Upvotes: 3

Related Questions