Quantcast
What was you're answer out of curiosity? BaseClass::StaticFunction() -> DerivedClass::StaticFunction()? namespace conflict?

My name is Michael Kofman and
I am a Developer, a Designer, an Architect, an Entrepreneur, an Educator and a Student.

Graduated Full Sail University with a Bachelors of Science in Game Development with a life long background in IT and Web Design.

Exoporting from Maya

Game developers, big and small, are often tasked with content generation. We write tools that range from scripters, effects editors, to the infamous level editors. While level editors serve an important role in the game development cycle, sometimes time and limited man power don’t allow for the creation of a fully featured environment.

In this tutorial we’ll go straight to the source with the creation of our Maya exporter. We will not be covering Mel, or building our own GUI. Instead we’ll be adopting from the MPxFileTranslator, a maya file exporter. We’ll be saving out our code into an XML file format that can then be used as input for character models, or even levels.

Understanding Maya’s naming convention is key to feeling comfortable and eventually predicting variable names when exploring further feature sets of the Maya API.

Nearly all Maya prefixes begin with the letter M; which of course stands for Maya and defines a Maya wrapper class. Next we have MFn; which stands for Maya Function set. For example MFnMesh, contains specific functions and variables that appropriately define a mesh (a mesh is a collection of vertices, normals and uv coordinates in 3D space) object in Maya. The MFn prefix is not limited to only visible objects in Maya. For instance, MFnDependencyNode is a conceptual object that is used to define a node inside the Maya graph architecture (we’ll cover the Maya architecture shortly, so don’t stress it).

Another important prefix in Maya is the MIt prefix that stands for Maya iterator. An iterator is not a difficult concept for those who are familiar with the STL library. It points towards some position in a data set (in this case a tree) and allows us for easy traversal, going to the next object ( itr.next() ) and looping until done ( while(!itr.isDone()) ).

The last and final prefix to take note of is the MPx. It stands for Maya Proxy object (see proxy). Quite simply it provides us with an interface towards integrating into Maya itself. Common uses of MPx are the MPxFileTranslator which we will be focusing on in this tutorial and the MPxCommand which allows the user to create his or her own custom Maya commands. For more information please reference the Maya Documentation.

Now that we are past the formalities, we can begin to talk about how Maya organizes it’s data, so that we can begin to extract from it. Essentially everything in Maya is stored inside a Directed Acyclic Graph or DAG (this one keyword will be very important so please take note of it now). For those familiar with web design this is equivalent to the HTML DOM object, for those not familiar I will refer to the data structure of a graph. The major difference in definition of a graph data structure and the DAG is that child nodes cannot be their own parents. See picture below.

Step 1 – creating a proxy

Create a new class and derive from MPxFileTranslator.

class CFileTranslator : public MPxFileTranslator

You will need to overload the following functions

static void * creator(void);

MStatus writer(const MFileObject& file, const MString& optionString, FileAccessMode mode);

bool haveWriteMethod(void)const { return true; }

MString defaultExtension() const { return “xml”;}

MString filter() const { return “*.xml”;}

Step 2 – creating a an exporter class

Our Maya exporter will execute after pushing file -> export all or file -> export selection. Our entry point is than inside the writer() function that we have overloaded. In order to check for selection we can perform the simple if check for mode == kExportActiveAccessMode else we export all. Since this is more of a feature than base functionality I won’t cover exporting by selection in any great detail but its implementation is not very different from exporting all.

We now have the simple design decision to write all our code inside the CFileTranslator class we created earlier or simply create a new class that will be specific towards our needs. We will want this new class to contain a few basic functions. Such as ClearAndReset(), ExportVertexData() and WriteToFile(). We will call these in that sequence from within the CFileTranslator’s writer() function.

Step 3 – data structures and data members

Next we’ll create some basic data structures that we’ll fill out during exporting. One thing to note is that we’ll be using Maya’s intrinsic data types such as MFloatPoint which is essentially Maya’s version of a 3-tulupe vector that contains floats for the x, y, and z coordinates.

typedef struct _tVertex

{

MFloatPoint point;

MFloatVector normal;

float uv[2];

}UniqueVertex;

Here we created a new datatype that will contain the vertex position (point), the vertex normal (normal) and the UV texture coordinates (uv[2]). As we export our data we’re looking to optimize Maya’s vertex list by creating avoid duplicate vertices and creating a unique vertex list. We’ll than create our own triangle list that store indices into this our array of unique vertices. For a better idea on how index buffers work, reference Chad Vernon’s website at (http://www.chadvernon.com/blog/tutorials/directx9/vertex-and-index-buffers/). So the last important data structure we’ll need to generate is our triangle.

typedef struct _tTriangle

{

unsigned int verts[3];

}Triangle;

std::vector<UniqueVertex>    m_UniqueVertList;

std::vector<Triangle>    m_TriangleList;

Wrapping the above into a mesh structure would allow us to export more than one mesh at a time. These are further improvements that you may consider making to the base exporter.

Step 4 – the loop

The idea is quite simple, but the work may become a little tedious. Our goal is to go through go through Maya’s DAG , find all the Meshes and export them. The tedious part is that we’ll be doing a very similar traversal for just about each part of the export process since the DAG contains everything from Mesh objects to lights and transforms.

Thankfully the iteration process is quite straightforward. We begin by creating a MItDag. We specify that we’ll be traversing in depth first order, and we’ll be looking for Meshes. The code should look something like this.

MItDag dagIt(MItDag::kDepthFirst, MFn::kMesh);

Next we loop through each element, each time checking if the Mesh is an intermediate object, which simply means it’s something left over in Maya history but is not the current model we continue. Else we begin exporting by calling GetMesh(), our own function that will fill out our data structures from earlier.

for (;!dagIt.isDone(); dagIt.next())

{

MDagPath currPath;

dagIt.getPath(currPath);

MFnMesh currMesh(currPath);

if (currMesh.isIntermediateObject())

continue;

GetMesh(currMesh);

}

Step 5 – Creating a unique vertex list from Maya’s data

We’re now inside the GetMesh() function at which point our prime objective is to fill out our m_UniqueVertList and m_TriangleList for our mesh. This is also a good time to go ahead and pull out things like the mesh transform, or any custom attributes if you so wish. Again we will not focus on these features and move onto the actual data.

We begin by creating a MDagPath object which essentially will contain the directory, or path if you will, of the mesh we are attempting to export.

MDagPath path;

currMesh.getPath(path);

Next we’ll create a new itterator to traverse the polygons of our mesh (aka triangles). We’ll also create a few arrays that we’ll populate with Maya’s vertex positions, vertex normals, and UV coordinates.

MItMeshPolygon polyItr(path);

MFloatPointArray _tPoints;

currMesh.getPoints(_tPoints);

MFloatVectorArray _tNormals;

currMesh.getNormals(_tNormals);

MFloatArray _tVArrays;

MFloatArray _tUArrays;

currMesh.getUVs(_tVArrays,_tUArrays);

Something to take note of now, is that the index of the vertex positions, normals, and UVs will all be unique. Although the index of the U and V coordinates is a 1:1, and will use the same value.

If you have kept up with this tutorial so far, congratulations, we’re now in the final stretch and I will simply go ahead and post up the code for generating the unique vertex list and triangle list per mesh. Remember that this code snippet is my own and your coding style may or may not adhere to it. The important part is that you understand the process involved.

for (; !polyItr.isDone(); polyItr.next())

{

Triangle tmpTriangle;

for (int vertIndex = 0; vertIndex < 3; ++vertIndex)

{

UniqueVertex tmpVertex;

tmpVertex.point = _tPoints[polyItr.vertexIndex(vertIndex)];

tmpVertex.normal = _tNormals[polyItr.normalIndex(vertIndex)];

int uvIndex = -1;

if(polyItr.getUVIndex(vertIndex, uvIndex))

{

tmpVertex.uv[0] = _tVArrays[uvIndex];

tmpVertex.uv[1] = _tUArrays[uvIndex];

}

else

continue; // this is a bad place to be!

bool exists = false;

int i = 0;

int size = m_UniqueVertList.size();

for (; i < size; ++i)

{

if(m_UniqueVertList[i].point == tmpVertex.point && m_UniqueVertList[i].normal == tmpVertex.normal &&

m_UniqueVertList[i].uv[0] == tmpVertex.uv[0] && m_UniqueVertList[i].uv[1] == tmpVertex.uv[1] )

{

exists = true;

break;

}

}

if (exists)

{

tmpTriangle.verts[vertIndex] = i;

continue;

}

tmpTriangle.verts[vertIndex] = m_UniqueVertList.size();

m_UniqueVertList.push_back(tmpVertex);

}

m_TriangleList.push_back(tmpTriangle);

}

Since I didn’t cover everything involved with creating a Maya exporter and assumed some familiarity, or rather ability to use the Maya Documentation, please feel free to post comments and ask questions. I’ll be glad to make clarifications, but at the moment I need to get back to work.

2 Responses to “Exoporting from Maya”

  1. VoodooSpecter Says:

    September 6th, 2011 at 6:31 pm

    Hey, I’m just wondering something about the way you determine whether a vertex is unique- you check not only the point, but the normals and the uv’s to see if they are the same. I imagine with multiple objects in a scene it is possible for more than one object to have a vertex at a given point, but with different normals or uv mappings. However, even when I run the algorithm on a single-object scene, the program outputs a unique vertex list that is identical to the original vertex list (with duplicates included). Removing the check for normals and uv coordinates solves the problem. My question for you is: Why do you check all three? If you are going object by object anyway, duplicate vertexes with conflicting texture coordinates or normals should be fairly rare (unless the artist missed a vertex while they were cleaning the model up). I ask purely for educational reasons, I just want to know. Right now I have it checking points only, and I don’t want that decision to come back to bite me later. This tutorial has been extremely helpful.

  2. Michael Kofman Says:

    January 31st, 2012 at 2:51 am

    I really need to fix the CSS on this site for future visitors




Please Reply








XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">