Home Coding, Scripting, Shaders

Maya Python Script Performance Issues

thefelixone
polycounter lvl 4
Offline / Send Message
thefelixone polycounter lvl 4

Hello! I just finished doing a small script that is really helpful to encode proximity data from scalp to cards. The issue I have right now is that the script is really... REALLY heavy, like it takes a long time to process 50k tries. Anyone has tips on how to improve the result? Thanks alot!

import maya.cmds as cmds
import math


total = []
selected = cmds.ls(sl=True)


obj = selected[0]
furVtx = cmds.ls(obj+'.vtx[*]', fl=True)


obj = selected[1]
skinVtx = cmds.ls(obj+'.vtx[*]', fl=True)
   
for vertsFur in furVtx:
    furVtxPos = cmds.xform(vertsFur, q=True, ws=True, t=True)
                
    for vertsSkin in skinVtx:
        skinVtxPos = cmds.xform(vertsSkin, q=True, ws=True, t=True)


        d = math.sqrt((skinVtxPos[0] - furVtxPos[0])**2 + (skinVtxPos[1] - furVtxPos[1])**2 + (skinVtxPos[2] - furVtxPos[2])**2)
        #print('Distance between:', vertsFur, vertsSkin, d)
        total.append(d)
        
    r= min(total)
    total.clear()


    cmds.select(vertsFur)
    
    cmds.polyColorPerVertex(r=r, cdo=True)

Replies

  • poopipe
    Offline / Send Message
    poopipe grand marshal polycounter

    I don't do much Maya scripting anymore because I hate it.


    But.


    Maya is really slow when dealing with lists of vertices and other similar objects.

    Using openmaya will help.


    Alternatively using the string name representation of the vertices rather than the object itself can help in certain circumstances. Anything to avoid nested loops of actual Maya objects is good basically

  • neilberard
    Offline / Send Message
    neilberard polycounter lvl 18

    I would use Open Maya 2.0 and iterate on MfnMesh points. Should be a lot faster.

    https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=Maya_SDK_py_ref_index_html

  • Finnn
    Offline / Send Message
    Finnn greentooth

    You are having a loop inside a loop. If both list of vertices have 50k entries, that means you have to iterate 2,500,000,000 times. And everytime you do a (nested) squareroot operation with list accesses etc.

    There is no way that you can use another library and it will be fast all of the sudden. You have to rethink your problem and figure out a way to solve in a different way to achieve actual improvement of performance.

  • poopipe
    Offline / Send Message
    poopipe grand marshal polycounter

    Using openmaya will make it orders of magnitude faster which is usually enough.


    As I said above the problem is in the way Maya's standard python API deals with lists of Maya objects - which is slow. Openmaya sidesteps that

  • Finnn
    Offline / Send Message
    Finnn greentooth

    Would be very interesting to see what difference it actually makes. :)

  • Deadly Nightshade
    Offline / Send Message
    Deadly Nightshade polycounter lvl 10

    Huge per-vertex operations are typically slow when going with just maya.cmds and pymel due to the nature of the programming language (Python). What I would suggest is to use the Maya API instead.

    Easiest stepping-stone into that would be the Python bindings (OpenMaya). Create a vertex iterator (MItMeshVertex class) and init it from an MSelectionList of vertices.

    Not only do you get vastly faster looping speeds (due to the underlying C++ code running) but you also get access to the mesh function set's (MFnMesh) utility functions which can be used to apply updated component data to all components (in this case: vertices) to the entire mesh, in one single go without having to loop.

    Having only previous expereience of high-level programming, it can feel daunting to dive into the Maya API which due to it being more low-level, also has much higher complexity. But the API is powerful, the Python bindings are great and the documentation is up-to-date and well written. Most things you need are found in the function sets (classes by the name of MFn), the iterators (classes by the name MIt), the MSelectionList and MDagPath objects and the MGlobal static class.

    The concepts that was the hardest for me to understand was MObjects and how they work with function sets. (MObjects are just generic containers of raw data - and depending on the data they are compatible with only some function sets (MFn)) - that and MPlugs (which are like interfaces to an object's attributes). 90% of the classes in the docs you will most likely never touch. If you can learn all the bold classes mentioned above you will have a very solid foundation of the Maya API.

  • foxagon
    Offline / Send Message
    foxagon polycounter lvl 10

    quick OpenMaya example of using a mesh intersector to get closest point on surface


    import maya.OpenMaya as OpenMaya


    def getDagPath(obj):

       selectionList = OpenMaya.MSelectionList()

       try:

          selectionList.add(obj)

       except RuntimeError:

          return None

       dagPath = OpenMaya.MDagPath()

       try:

          selectionList.getDagPath(0, dagPath)

       except RuntimeError:

          return None

       return dagPath


    def distToSurface(src, trg):

       srcDagPath = getDagPath(src)

       if not srcDagPath:

          return

       if not srcDagPath.hasFn(OpenMaya.MFn.kMesh):

          return

       trgDagPath = getDagPath(trg)

       if not trgDagPath:

          return

       if not trgDagPath.hasFn(OpenMaya.MFn.kMesh):

          return


       if trgDagPath.apiType() != OpenMaya.MFn.kMesh:

          trgDagPath.extendToShape()

       trgNode = trgDagPath.node()


       matrix = trgDagPath.inclusiveMatrix()

       intersector = OpenMaya.MMeshIntersector()

       intersector.create(trgNode, matrix)


       iterVert = OpenMaya.MItMeshVertex(srcDagPath)

       pointInfo = OpenMaya.MPointOnMesh()

       colors = OpenMaya.MColorArray()

       colors.setLength(iterVert.count())


       while not iterVert.isDone():

          pos = iterVert.position(OpenMaya.MSpace.kWorld)


          intersector.getClosestPoint(pos, pointInfo)

          hitPoint = OpenMaya.MPoint(pointInfo.getPoint().x, pointInfo.getPoint().y, pointInfo.getPoint().z) * matrix


          # TODO: what unit scale ?

          dist = pos.distanceTo(hitPoint)


          colors[iterVert.index()].r = dist

          colors[iterVert.index()].g = 0

          colors[iterVert.index()].b = 0

          colors[iterVert.index()].a = 1


          iterVert.next()


       meshFn = OpenMaya.MFnMesh(srcDagPath)

       if not meshFn.numColorSets():

          meshFn.createColorSetWithName("colorSet1")


       vertices = OpenMaya.MIntArray()

       compFn = OpenMaya.MFnSingleIndexedComponent()

       compFn.create(OpenMaya.MFn.kMeshVertComponent)

       compFn.setCompleteData(meshFn.numVertices())

       compFn.getElements(vertices)


       meshFn.setVertexColors(colors, vertices)

       meshFn.setDisplayColors(True)




    distToSurface("pHelix1", "pSphere1")

  • poopipe
    Offline / Send Message
    poopipe grand marshal polycounter

    In theory 5-10k times faster..

    You're absolutely not wrong though, the fastest way to do this would be to do it differently

Sign In or Register to comment.