Maya 4.0 API Quick Programming Tutorial
Maya API BasicsGreat, you finished the entire input side of your exporter! Now you can goof-off the rest of the week because you already got half your work on the exporter done, right? Ok, no, you can't. Look at that crazy API. ( You can find the Maya API documentation in the help files under maya/docs/en_US/html/DevKit/Dev_Tools_TOC.html ) Its not even clear where you start. How do you even get a reference to the scene graph, or its root object? To understand these things you should take the time to read the Maya API White Paper. Its kind of long and boring and you might be tempted not to read it, but trust me, you should read it. Its actually not that long, about 10 pages, and everything is much clearer after that.
But you didn't go read it did you? Ok, I'll tell you whats in it, but then go back and read it later.
The central data structure in Maya is the DAG, or Directed Acyclic Graph. It is what is known to almost all programmers as a "tree". Much of the data you are interested in is expressed as nodes of data on this tree. Each node has a type. Each one has a parent node ( except for the root node ), and each node may have an arbitrary number of children. There are hundreds of types of nodes, and at least in theory, any node can be the parent or the child of a node of any type. The DAG is guaranteed to be a tree. So there are no cycles in it. If you visit the nodes using an orderly tree walking algorithm you can visit every node, and you won't have to worry about infinite loops. Not all of the data is stored in the DAG, but vertices, meshes, transforms, and a model's skeletal system are stored in the DAG.
The other data structure, far scarier, is the DG or Dependency Graph. Like the DAG, this is a set of nodes that refer to each other, but here there are no restrictions. DG nodes can refer to any other node, and cycles are possible. This is where the textures, materials, and animation information is stored. Technically the DAG is a subset of the DG, but getting data out of the DG is somewhat more complicated than getting stuff out of the DAG, so its convenient to think about nodes that are in the DAG, and then all the other nodes.
Finally, the way the API works is unlike anything you are probably used to. There are two major object types in the Maya API: dependency nodes and function sets. There are a large number of "dependency node" objects, each is a different type of handle to a node in the DG or DAG. Think of them as pointers into the DG. The different kinds allow you to refer to nodes with greater or lesser specificity and to iterate over the nodes in different ways. ( MItDag iterates over the DAG, MItDepenencyNodes iterates over all the nodes in the DG - including all the DAG nodes ).
The function set objects represent interfaces to the nodes. You construct these interfaces by calling that function set's constructor with a node reference as an argument to its constructor. Not every node supports every function set, but it's legal to construct any function set using any node. If the given node does not support the function set you are trying to create then the function set object will just be created in an invalid state, which should be checked. A typical function set is MFnMesh, which allows you to extract mesh information ( vertices, polygons etc. ) from a node that contains this kind of information.
Writing an exporter requires you to pretty much just iterate over the DG or DAG in various ways tracking down nodes, and then using them to create function sets that allow you to extract the data.
Its also good to remember that almost all of the objects that you can create through the Maya API are handles to the real objects that are linked into the DG and that Maya manages itself. You normally create all these objects as temporaries and let them get cleaned up automatically when they go out of scope. Simple.
So lets write a quick function to find all the vertices in a model. We will assume that we are interested in extracting all the vertices in the file:
The code is pretty self explanatory. dagIter iterates through all the nodes in the DAG, where your vertices are. Everytime you find a node that supports the MFn::kMesh type, and is not a transform, or an intermediate node, then you use it to make an MFnMesh object, and extract vertices. Notice when go to get the vertices themselves, you request what space you want them represented in. MSpace::kWorld will convert the vertex into world space for you ( by multiplying the vertex by all the transforms from the root of the DAG on down ). You can also ask it to give you the vertices in any number of local spaces if you are extracting skeletal data as well.void extractVertices()
// we assume here that Maya has been initialized and the file in
// question has already been loaded.
MItDag dagIter( MItDag::kBreadthFirst, MFn::kInvalid, &stat );
for ( ; !dagIter.isDone(); dagIter.next())
stat = dagIter.getPath( dagPath );
if ( stat )
MFnDagNode dagNode( dagPath, &stat );
// this object cannot be intermediate, and it must be a mesh
// and it can't be a transform.
// Intermediate objects are special meshes
// that are not drawn used for mesh morphs or something.
if ( dagNode.isIntermediateObject()) continue;
if ( !dagPath.hasFn( MFn::kMesh )) continue;
if ( dagPath.hasFn( MFn::kTransform )) continue;
MFnMesh fnMesh( dagPath );
// get the vertices that are part of the current mesh
fnMesh.getPoints( vertexList, MSpace::kWorld );
// iterate through all the vertices
for ( u32 i = 0; i < vertexList.length(); i++ )
MPoint point = vertexList[i];
// here is your data... now go do whatever you want with
// it. If you need a unique identifier for this vertex,
// use its index in the mesh, and some kind of mesh id.
// These stay constant while exporting ( so long as the file is
// not edited )
processVertex( point.x, point.y, point.z );