Figuring out the Maya Python API

I’ve been doing a lot of Python lately. Mainly to script Maya to better optimize things that would normally take too long, but also outside of it, and I think I’ve gotten the hang of it by now.

So I figured I’d head into the API stuff now, wanting to make deformers mainly (animator background) but also rigging aids and some other cool stuff.However scripting and using the API in python actually seems very different from one another, and what is even worse is that there doesn’t really seem to be that much to go on if one tries to learn about it.

So far, I’ve gone ahead and bought the David Gould books and figured out some C++ to go along with that. I’ve got some websites going, like this one,
this http://www.rtrowbridge.com/blog/
this http://www.macaronikazoo.com/
this http://www.chadvernon.com

and the Maya docs to try and piece things together. but ultimately it seems I’m going to have to do it the old fashioned way. Learning by doing and that’s why I’m posting here today. I’m looking for some pieces of guidance as I try out some stuff by following the API docs and C++ tutorials from Goulds books. Some of which work real nice, others however do not.

Like this thing.

import maya.OpenMaya as OpenMaya
sel = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(sel)

dagPath = OpenMaya.MDagPath()
component = OpenMaya.MObject()
vertCount, vertIndex = 0, 0

iter = OpenMaya.MItSelectionList(sel)
while not iter.isDone():
    dagPath = iter.getDagPath( dagPath, component )
    
    meshIter = OpenMaya.MItMeshVertex( dagPath, component ) # <--- Here it fails
    
    iter.next()

It doesn’t really do anthing, I haven’t gotten that far yet. I get that in C++, the dagPath and component objects are both references and that I probably should use MScriptUtil somehow to fake that and pass it along, but I fail to see the logic.

The whole thing returns:

Error: RuntimeError: (kInvalidParameter): Object is incompatible with this method

What am I not thinking of?

Here’s your fix!

import maya.OpenMaya as OpenMaya
sel = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(sel)

dagPath = OpenMaya.MDagPath()
component = OpenMaya.MObject()
vertCount, vertIndex = 0, 0

iter = OpenMaya.MItSelectionList(sel)
while not iter.isDone():
    iter.getDagPath( dagPath, component ) # dagPath and component are passed as references; the function itself returns null, so you don't want to store its return value in dagPath
    meshIter = OpenMaya.MItMeshVertex( dagPath, component )
    iter.next()

As a note, MScriptUtil is only in question when you have a simple type (int, short, enum, double, etc.) passed by reference.

If you’re looking for other stuff, I have plenty of free Python plugin nodes/commands on my website. Some of them are a little old and sloppy, but for example my mesh ribbon node/command and expose transform node/command plug-ins are much cleaner, reasonably documented, and may be helpful. Sometime soon I’l try to update the other stuff

Short and sweet. Just what I was looking for. Thank you. :slight_smile:

As a result, my first Python script utilizing the API, converted from the second Complete Maya Programming book:

import maya.OpenMaya as OpenMaya

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

dagPath = OpenMaya.MDagPath()
component = OpenMaya.MObject()

vertCount, vertIndex = 0, 0

txt = ''

iter = OpenMaya.MItSelectionList(sel)
iter.reset()
while not iter.isDone():
    iter.getDagPath( dagPath, component )
    meshIter = OpenMaya.MItMeshVertex( dagPath, component )
    
    try:
         txt = txt + dagPath.fullPathName() + "
"
         
         vertCount = meshIter.count()
         txt = txt + "# Vertices: " + str(vertCount) + "
"
         
         while not meshIter.isDone():
             pt = OpenMaya.MPoint(meshIter.position( OpenMaya.MSpace.kWorld ))
             
             vertIndex = meshIter.index()
             
             txt = txt + " " + vertIndex + ": " + pt.x + ", " + pt.y + ", " + pt.z + "
"
    except:pass
    
    iter.next()

print txt 

Oh joy. :slight_smile:

And the maya.cmds version:

import maya.cmds as mc
pm.polyEvaluate(mc.ls(sl=1)[0], v=1)

Hehe, couldn’t resist.

edit: Oops. looks like it did a bit more than print out the number of vertices!

correct code:

import maya.OpenMaya as OpenMaya

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

dagPath = OpenMaya.MDagPath()
component = OpenMaya.MObject()

vertCount, vertIndex = 0, 0

txt = ''

iter = OpenMaya.MItSelectionList(sel)
iter.reset()
while not iter.isDone():
    iter.getDagPath( dagPath, component )
    meshIter = OpenMaya.MItMeshVertex( dagPath, component )
    
    try:
         txt = txt + dagPath.fullPathName() + "
"
         
         vertCount = meshIter.count()
         txt = txt + "# Vertices: " + str(vertCount) + "
"
         
         meshIter.reset()
         while not meshIter.isDone():
             pt = OpenMaya.MPoint(meshIter.position( OpenMaya.MSpace.kWorld ))
             
             vertIndex = meshIter.index()
             
             txt = txt + " " + str(vertIndex) + ": " + str(pt.x) + ", " + str(pt.y) + ", " + str(pt.z) + "
"
             
             meshIter.next()
    except:pass
    
    iter.next()

print txt

prints out

|pCube1
# Vertices: 8
 0: -0.5, -0.5, 0.5
 1: 0.5, -0.5, 0.5
 2: -0.5, 0.5, 0.5
 3: 0.5, 0.5, 0.5
 4: -0.5, 0.5, -0.5
 5: 0.5, 0.5, -0.5
 6: -0.5, -0.5, -0.5
 7: 0.5, -0.5, -0.5

Black magic!

Allright, next problem!

I’m trying to convert some c++ code of a mel command that essentially draws your wireframe in the form of rods and spheres. Generating the actual vertices and faces in code, rather than using the polySphere command and such. Awesome!

However, i ran across this thing for which I’m a bit confused over.

polyConnects.append( linearlndex( zeni, azi, nZenithPts, nAzimuthPts ) );

I understand that it somehow converts a matrix into a single row list? How would I translate that into python you think?

edit: Realizing you couldn’t read my mind, I was talking about the “linearIndex()” function and what it did and how to do it. :slight_smile:

In the tutorial, he says:

Each polygon in the sphere is defined by defining the vertices that make it up. Because the vertices have been constructed in a consistent fashion, the polygons are defined using the simulated 2D array’s row and column indices. Because the polygons are to be displayed in a right-handed coordinate system, the vertices are specified in a counterclockwise order. This also ensures that the normals point in the correct direction.

and the more complete code looks like this:

	// Calculate the number of polygons
	nPolys = nAzimuthSegs * nZenithSegs;
	
	// Each face has four points
	polyCounts.setLength( nPolys );
	int i;
	for( i=0; i < nPolys; i++ )
		polyCounts[i] = 4;
	
	// Generate the faces
	for( zeni=0; zeni < nZenithSegs; zeni++ )
	{
		for( azi=0; azi < nAzimuthSegs; azi++ )
		{
			//MGlobal::displayInfo( MString( "
" ) + azi + "," + zeni );
			
			polyConnects.append( linearIndex( zeni, azi, nZenithPts, nAzimuthPts ) );
			polyConnects.append( linearIndex( zeni, azi+1, nZenithPts, nAzimuthPts ) );
			polyConnects.append( linearIndex( zeni+1, azi+1, nZenithPts, nAzimuthPts ) );
			polyConnects.append( linearIndex( zeni+1, azi, nZenithPts, nAzimuthPts ) );
		}
	}	

Still, the linearIndex. What do I write in Python? :scared:

edit: Sorry, my bad. It was defined earlier in the code… -.-