Home Coding, Scripting, Shaders

[maya] Get material order from mesh

MBS320
polycounter lvl 2
Offline / Send Message
MBS320 polycounter lvl 2
Hey guys,

Sorry- I'm aware this has been asked a hundred times over, but I'm struggling to find a way to get materials from a mesh fast and it doesn't help that I'm very new to pymel. I'm trying to get the materials of a mesh by face order because that is how I believe the material names are stored in an fbx when the fbx is written- but I can't find a way to do it quickly.

This is the best I've got:
def get_materials_by_face_count_selection(transform):
    allFaces= len(transform.f)   
    materials = []

    for face in range(allFaces):
        thisFace = transform + ".f[" + str(face) + "]"
        mc.select(thisFace)
        mc.hyperShade(shaderNetworksSelectMaterialNodes=True)
        material = ''.join(mc.ls(selection=True))
        #print ("material: " + material)
        if material not in materials:
            materials.append(str(material))
            
    return materials
This takes 12 seconds to run on a mesh with 16k verts/faces and over a minute for something with 66k verts ... which makes sense, I'm asking Maya to run select material on this face 66000 times. But I can't think of a better way to do this.

The reason I'm doing this is because I'm trying to setup an export pipeline in our studio for Maya to Unity. You hit export from a custom menu in Maya, Maya studies the mesh and writes the materials (in order) to an XML, this mesh then gets exported to a location in Unity. The Unity Asset Post processor picks up the fbx, studies the xml and automatically assigns materials and then saves this as prefab variant next to the fbx. This is how it has worked for years in 3ds Max currently and I can't get this to work as well in Maya because the get materials part takes ages to run.

Thoughts? Suggestions? Ideas?

EDIT: I also have this, which runs fast- but doesn't get the materials by face order and therefore doesn't work with the pipeline correctly:
def get_materials_on_selected_meshes(mesh):
    all_materials = list()
    
    for shading_engine in mesh.getShape().listConnections(type='shadingEngine'):
        materials = pm.listConnections('%s.surfaceShader' %shading_engine)
        if materials is not None and len(materials) > 0:
            all_materials.extend(materials)
    
    # Swap to a set to force a single instance, then back to a list
    all_materials = list(set(all_materials))
    return all_materials

Replies

  • neilberard
    Offline / Send Message
    neilberard polycounter lvl 18
    It's been a while since I have used Unity, but do recall having to deal with material order since Unity assigns them to the mesh based of lowest face index value per assigned material in Maya.

    Something easy to do is gather all the material assignments and sort them by lowest face index. In this case, I took a 50k sphere with 3 materials assigned to it. 

    initialShadingGroup
    lowest face idx 0
    phong1SG
    lowest face idx 256
    surfaceShader1SG
    lowest face idx 4219
    Time to process 0.0279998779297

    import maya.cmds as cmds
    import time

    def organize_materials(mesh):
        """
        :param mesh: Geometry Transform Node
        :return:
        """
        start = time.time()
        # Get Shader
        shape = cmds.listRelatives(mesh, type='shape')[0]
        sgs = set(cmds.listConnections(shape, type='shadingEngine'))

        for sg in sgs:
            members = cmds.sets(sg, q=True)
            if not members:
                continue

            # Get the lowest index of each material
            lowest_idx = -1
            for item in members:
                item_name, face_idx = item.split(".")
                if mesh == item_name:
                    face_idx = face_idx.split('[')[1]
                    face_idx = face_idx.split(':')[0]
                    face_idx = int(face_idx.split(']')[0])

                    if lowest_idx is -1 or face_idx < lowest_idx:
                        lowest_idx = face_idx

            print sg
            print "lowest face idx {}".format(lowest_idx)
        end = time.time()
        print "Time to process {}".format(end - start)

    mesh = cmds.ls(selection=True)[0]
    organize_materials(mesh)


Sign In or Register to comment.