Some maya api questions (multiple iterators)

hey im pretty new to the api but have been a long time python user, and am just hitting a simple snag.
how would i implement a iterator that goes through the vertices of a face being iterated on.
where it says “do stuff” is where i would want a MItmeshVertex() but not sure how you make it work on just the current polygon, and not the whole mesh.


import maya.OpenMaya as OpenMaya

#get mSelection List

sel = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(sel)

#MItSelectionList iteritor for meshes
#filter with mfn class

iterMesh = OpenMaya.MItSelectionList(sel, OpenMaya.MFn.kGeometric)

# selection iterator
while not iterMesh.isDone():
    polyList = []
    dagPath = OpenMaya.MDagPath()
    iterMesh.getDagPath(dagPath)

    # get selection as MObject
    mObj = OpenMaya.MObject()
    iterMesh.getDependNode(mObj)

    # create polys iterotor
    iterPolys = OpenMaya.MItMeshPolygon(mObj)
    # iterate through polys
    while not iterPolys.isDone():
        # get current poly material
        print '
defaultmat'
        # do stuff here

        iterPolys.next()

    iterMesh.next()

Take a look at: polyListComponentConversion command

I’m not sure what your end goal is, but you can use getVertices(MIntArray &vertices) on your iterPolys object to get a list of vertexIds.

@btribble im trying to keep atleast this part of hte script within maya.OpenMaya and it;s methods, since things would be too slow using maya.cmds.

@cluce maybe kinda hoping to get something back as a list of objects i can iterate over, since im wanting to do things like get the vertex positions, normals, and uv’s, the reason why im iterating over faces, than trying to iterate over the verts with in that loop, is due to the file structure of the formate im trying to build a exporter for.

would there be a way of getting a MItMeshVertex object for all the vertices of the polygon being worked on. since that class has all the methods i would ever need for getting the wanted data.

[QUOTE=passerby;19543]im wanting to do things like get the vertex positions, normals, and uv’s, the reason why im iterating over faces, than trying to iterate over the verts with in that loop.[/QUOTE]

This can all be achieved using the MFnMesh class http://docs.autodesk.com/MAYAUL/2013/JPN/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html and using the vertexIds. You can also get the list of polygonIds from this class as well.

I typically find this approach easier unless you are iterating through selected components, etc.

Seconding cluce’s recommendation of using MFnMesh.

That being said, you can get an MItMeshVertex of the verts of the currently iterated polygon, but it’s a bit convoluted (At least this is what I just pieced together, maybe there’s a simpler way?)

import maya.OpenMaya as om

sel = om.MSelectionList()
om.MGlobal.getActiveSelectionList(sel)

mobj = om.MObject()
# DagPath is required when you want to iterate over it's components
mdag = om.MDagPath()
components = om.MFnSingleIndexedComponent()
vertIntArray = om.MIntArray()

itermesh = om.MItSelectionList(sel, om.MFn.kGeometric)
while not itermesh.isDone():
    itermesh.getDependNode(mobj)
    itermesh.getDagPath(mdag)
    
    iterpoly = om.MItMeshPolygon(mobj)
    while not iterpoly.isDone():
        print "Face id: %d" % iterpoly.index()

        iterpoly.getVertices(vertIntArray)
        # Creates an MObject of type kMeshVertComponent
        componentMObj = components.create(om.MFn.kMeshVertComponent)
        # Add the vertex ids to the component MObject
        components.addElements(vertIntArray)
        
        # Will iterate over the vertex ids specified in componentMObj
        itervert = om.MItMeshVertex(mdag, componentMObj)
        while not itervert.isDone():
            print "Vertex id: %d" % itervert.index()
            itervert.next()
            
        iterpoly.next()
        
    itermesh.next()

This may give you a head start:


import maya.OpenMaya as OpenMaya

#get mSelection List

sel = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(sel)

#MItSelectionList iteritor for meshes
#filter with mfn class

iterMesh = OpenMaya.MItSelectionList(sel, OpenMaya.MFn.kGeometric)

# selection iterator
while not iterMesh.isDone():
    polyList = []
    dagPath = OpenMaya.MDagPath()
    iterMesh.getDagPath(dagPath)

    fnMesh = OpenMaya.MFnMesh(dagPath)
    
    for polyId in range(fnMesh.numPolygons()):
        print 'PolyId: ', polyId
        vertIds = OpenMaya.MIntArray()
        fnMesh.getPolygonVertices(polyId, vertIds)
        
        for vertId in vertIds:
            print 'VertId: ', vertId
            vertPos = OpenMaya.MPoint()
            fnMesh.getPoint(vertId, vertPos)
            print 'VertPos: ', vertPos.x, vertPos.y, vertPos.z
        print

    iterMesh.next()

thanks, that pretty much allows me to get the information i want, and in the proper layout, with face ID than the 3 sets of verts and the verts potions that make that face.

im just rather concerned with the speed of it, since the iterations are being done via python, and not via the compiled code from the api.

works fast enough on a the 12 tris of a cube, but im a little worried how long it will take when it gets a say 12k tris mesh to work on.

If all you want is the face id and position of the verts, you can use MItMeshFaceVertex (MItMeshFaceVertex Class Reference).

It has faceId, vertId, and position (returns the vertex position) methods

ya that could work too, the data im needing is vertex positions, vertex normals, uv’s per vertex.

Yeah sorry, I didn’t notice you were trying to do this in openMaya only.

I know that pymel uses MItGeometry for a lot of this stuff:

http://download.autodesk.com/us/maya/2011help/API/class_m_it_geometry.html

alright went back to using a method like what cluce posted with mfnMesh, which im prolly going to work with till it supports everything i want from the file translator, i never knew printing to console with print or print >> sys.stderr, was drastically slower than just printing right into a file, so as it stand right now even for 12k tris it takes no longer than 3 seconds to export. so will have to keep checking back up on performance as i add more data for it to retrieve and export.

thanks for all the help guys.

also wondering, since my output needs to be all triangles, am i better off just running polyTriangulate from the maya.cmds first, or is there a way to actully just iterate over the rendering triangles in OpenMaya, and the verts of those tris?

You could skip using numPolygons and instead use MFnMesh::getTriangles(MIntArray & triangleCounts, MIntArray & triangleVertices)

Or

If your mesh is 100% triangles or quads (which has been enforced almost everywhere I worked that had QA checks), I believe that the following is true for quads (3 sided polygons are trivial):


vertIds = OpenMaya.MIntArray()
fnMesh.getPolygonVertices(polyId, vertIds)
triangle0 = vertIds[:3]
triangle1 = vertIds[1:]

Note: I haven’t done this for a while though and you should probably test it to make sure it holds.

will try, but i never seen anyplace enforce all quads, most just don’t want ngons, but ya everything being modeled is all quads and tris. highpoly meshes i use ngons all the time in, but one of those will never hit this exporter since im olny sending those out to xnormal to get some nice normals and cavity maps.

Sorry, 3 or 4 sided polygons are enforced, not just quads.

hitting a few new snags when it comes to vertex normals.

was trying to grab them like this.


vertNrm = OpenMaya.MVector()
fnMesh.getVertexNormal(vertId, vertNrm)
print >> sys.stderr, vertNrm.x, vertNrm.y, vertNrm.z

but even know im working on a cube with all hard normals, im getting averaged results. im assuming this is happening since im access a vertex instead of a vtxFace vertex?, and im also assuming this same issue will bite me in the ass when it come to getting the UV data, since just like normals, UV’s could possible have multiple values per vertex, where there are spilts.

the rest of the script is more or less cluce posted on post #7.

so does the vertID method of using fnMesh only work on the visible verts in maya, and doesn’t count vertex splits for things like normals and UV’s

i also tried this, to get things per triangle, but it didnt seem to work, as im getting hte exact same resutls as i was before in getting information for 4 verts of hte quad not info for the triangles in the quad. it also prints out info for 14 faces and gives me this error as it tries to do more “Error: RuntimeError: (kInvalidParameter): No element at given index” test is a cube so it should olny give me 12.


import maya.OpenMaya as om

#get mSelection List
sel = om.MSelectionList()
om.MGlobal.getActiveSelectionList(sel)

#MItSelectionList iteritor for meshes
#filter with mfn class
iterMesh = om.MItSelectionList(sel, om.MFn.kGeometric)

# selection iterator
while not iterMesh.isDone():
    dagPath = om.MDagPath()
    iterMesh.getDagPath(dagPath)
    fnMesh = om.MFnMesh(dagPath)

    triCount = om.MIntArray()
    triVerts = om.MIntArray()
    fnMesh.getTriangles(triCount, triVerts)

    for polyId in triVerts:
        # print >> sys.stderr, 'PolyId: ', polyId
        vertIds = om.MIntArray()
        fnMesh.getPolygonVertices(polyId, vertIds)

        for vertId in vertIds:
            # print >> sys.stderr, 'VertId: ', vertId
            # make point var for pos, and get pos
            vertPos = om.MPoint()
            fnMesh.getPoint(vertId, vertPos)
            # make vector var for normal and get normal
            vertNrm = om.MVector()
            fnMesh.getVertexNormal(vertId, vertNrm)
            # print out data
            print >> sys.stderr, 0, vertPos.x, vertPos.y, vertPos.z,
            print >> sys.stderr, vertNrm.x, vertNrm.y, vertNrm.z
        print >> sys.stderr, 'end'
        print >> sys.stderr

    iterMesh.next()

getVertexNormal() returns the averaged normal of a vertex, not the face-vertex, and is not actually stored on the mesh.

From the docs:

Per-vertex: You can also access a normal for each vertex of the mesh, independent of any polygons. Such normals are not stored in the object, but instead are calculated by Maya upon request as the average of all the per-polygon normals for the polygons adjacent to the vertex. These normals are what is returned by MFnMesh::getVertexNormal or MItMeshVertex::getNormal.

If you want the normals per face-vertex, you can use MFnMesh.getFaceVertexNormal(faceIndex, vertexIndex, MVector).

vertNrm = OpenMaya.MVector()
fnMesh.getFaceVertexNormal(faceId, vertId, vertNrm)
print vertNrm.x, vertNrm.y, vertNrm.z

UVs are the same way, getPolygonUV and getPolygonUVid both operate on a face-vertex level (they take a faceId and vertId)

i also tried this, to get things per triangle, but it didnt seem to work, as im getting hte exact same resutls as i was before in getting information for 4 verts of hte quad not info for the triangles in the quad. it also prints out info for 14 faces and gives me this error as it tries to do more “Error: RuntimeError: (kInvalidParameter): No element at given index” test is a cube so it should olny give me 12.

It looks like you’re using the 2nd return value of getTriangles as a list of polygon ids when it’s actually a list of vertex ids for each triangle.

Parameters:
[out] triangleCounts The number of triangles for each polygon face
[out] triangleVertices The triangle vertex Ids for each triangle

So for a cube you’ll end up with something like:

triangleCounts: [2, 2, 2, 2, 2, 2]
triangleVertices: [0, 1, 2, 2, 1, 3, 2, 3, 4, 4, 3, 5, 4, 5, 6, 6, 5, 7, 6, 7, 0, 0, 7, 1, 1, 7, 3, 3, 7, 5, 6, 0, 4, 4, 0, 2]

Where each element in triangleCounts is the number of triangles in the polygon with the same index*. So [[0, 1, 2], [2, 1, 3]] are the vertex ids of the two triangles in the first polygon.

If you need to know which polygon and triangle the vert is from, you could group them into a nested list of vertex ids per-triangle, per-polygon with something like:

vertTriplets = zip(*[iter(triVerts)]*3)
start = 0
vertids = []
for num in triCount:
    next = start + num
    vertids.append(vertTriplets[start:next])
    start = next

print vertids
# [[(0, 1, 2), (2, 1, 3)], [(2, 3, 4), (4, 3, 5)], [(4, 5, 6), (6, 5, 7)], [(6, 7, 0), (0, 7, 1)], [(1, 7, 3), (3, 7, 5)], [(6, 0, 4), (4, 0, 2)]]

So if I’m understanding what you’re after, something like this prints out Poly id, Triangle id, Vertex id, and the position and normal of each vertex.

import maya.OpenMaya as om

#get mSelection List
sel = om.MSelectionList()
om.MGlobal.getActiveSelectionList(sel)

#MItSelectionList iteritor for meshes
#filter with mfn class
iterMesh = om.MItSelectionList(sel, om.MFn.kGeometric)

triCount = om.MIntArray()
triVerts = om.MIntArray()
# selection iterator
while not iterMesh.isDone():
    dagPath = om.MDagPath()
    iterMesh.getDagPath(dagPath)
    fnMesh = om.MFnMesh(dagPath)

    fnMesh.getTriangles(triCount, triVerts)
    
    # Zips the list of verts into triplets: [[0, 1, 2], [2, 1, 3],...]
    vertzip = zip(*[iter(triVerts)]*3)
    # Groups the triplets into n-length lists based on the number of tris per-poly from triCount
    start = 0
    vertids = []
    for num in triCount:
        next = start + num
        vertids.append(vertzip[start:next])
        start = next
    
    for polyId, tris in enumerate(vertids):
        print 'Poly id: %d' % polyId
        for triNum, vertIds in enumerate(tris):
            print 'Triangle: %d' % triNum

            for vertId in vertIds:
                print 'Vert id: %d ::' % vertId,
                # make point var for pos, and get pos
                vertPos = om.MPoint()
                fnMesh.getPoint(vertId, vertPos)
                # make vector var for normal and get normal
                vertNrm = om.MVector()
                fnMesh.getFaceVertexNormal(polyId, vertId, vertNrm)
                # print out data
                print 'POSITION:', vertPos.x, vertPos.y, vertPos.z,
                print 'NORMAL:', vertNrm.x, vertNrm.y, vertNrm.z

    iterMesh.next()

Maybe there’s a better way to do this… Haha, but it’s a start at least.

Also, if speed is an issue, don’t call MfnMesh.getPoint() on every vertex (it’s actually being called multiple times for the same vertices because triangles share verts), call MFnMesh.getPoints() and look up each vertex id by index in the returned MPointArray.

  • The docs say that it “Returns the number of triangles for every polygon face”, so I’m making the assumption that it returns them in ID order, so that triCount[2] is the number of triangles for the polygon with an index of 2.

thanks for hte help ya the information im trying to grab is triangleID, and the vertID’s positions, normals, and uv’s per tri.

ya guess the position would be easy to optimize so your not calling for it multiple times, since it is known the position will always be the same, but UV’s and normals it would depend on where the vertex spilts for havieng multiple UV’s and normals per vertex are.

just find the maya api under pyhton a bitch to wrap my head around, even know i got tons of python experience with doing web devlopmeant, and with the maya.cmds.