Game Developer
I started final project last week. Essentially it marks the end of our education, and now begins a different form of learning. The kind others are only privileged to experience in the industry or in post grad studies. We'll be pitching two game ideas this Wed, one of which will be fleshed out into a Game Bible, accompanied by Gantt charts, intense UML diagrams, and than development into a polished product (well I hope). Despite lack of final exams, coding labs, we are busier than ever. I particularly took on the role as project officer, and my main job has been to keep the team on track (I was lucky enough to be part of the ultimate dream team of programmers, and this has been largely a non-issue). But I think most importantly my job has been to motivate and inspire others.
My approach has never involved, criticism (constructive or not), and it has always been about hands off leadership. In certain cases this is very dangerous, and I would recommend this for everyone. My method is based on inspiration. I work hard everyday, and don't ask the same of others. If you wait long enough, people begin to feel uneasy, and soon enough you'll be shocked and amazed at the sort of things that they will do without even saying a word. I think the job of any project leader is to do one thing and one thing only. Make the job of other easier, and more productive. As long as you can justify what your doing as such, your safe, and in time you'll gain the sort of respect that doesn't go away. Better yet, you make friends.
I've got a new website up btw.
http://www.michaelkofman.com/
It's in development-ish, I don't do web design anymore, and I actually had to spend a good minute to get back into CSS yet alone learn some PHP (I just can't take it seriously, no matter how hard I try). Hopefully in the coming months I'll beef it up just a bit, and feed my obsession for perfection.
My immediate goals are to drive the game project forward, and keep up with my other duties ( a Mars based education game for NASA, a research internship for AI, and the ever difficult task of maintaining a proper living environment; cooking, cleaning, laundry, paying the bills ).
I hope that Artician is doing well without me trolling the forums and new submissions. Eric is a great guy that is determined and I'd go as far as to say destined for great things. Along with Henry, one of the few people who I can truly be myself around, I wish you guys the best and of course keep on doing what you guys do!
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.
I’m excited, no thrilled, I’m so anxious I can’t find the words to describe how I feel right now. Its the first day of school again. Oh the antcipation. You know when you go to sleep right away, in hopes of making tomorrow come faster, only to wake up hours before thinking and dreaming about nothing else.
We finally made it to Final Project! The next five months will be the most important months for me at FullSail. They will define all that I’ve learned, they will define all that I’m capable of. These last months will be the last stepping stones towards breaking into my dream job. There’s nothing more to say but this.
No matter what happens, no matter what goes wrong, no matter what challanges lie ahead. Our final project will be amazing, I know it. And with the months to come, I think I’ll convince you!
I recently wrote a paper on my research into procedural animation. We are only a few days from final project and my big game proposal is largely animation based. I thought it would be a good idea to put together a simple demonstration of Inverse Kinematics before Wednsday.
Although I was fairly successful at implementing the basic hierarchy, I am miles away from true IK. At the moment I’m attempting to comprehend Jacobians, but I feel my understanding for Calculus is really holding me back, as I’m having a hard time understanding fundemental concepts such as partial derivatives and how to represent derivites in code. Of course these are only set backs that I will resolve in the coming days.
For one I am glad I decided to keep this Unit Test very light and keep the process very itterative. This is usually quite the opposite to how I approach problems, but in this case it was largely to my benefit, as it allows for the implementation of small features before tackling the monster that is IK.
The steps I took are as follows:
- Render a joint on screen using D3DXCreateTeapot (later changed to D3DXCreateSphere)
- Add simple camera controls
- Build a simplified skeleton architectures. This involves joints, and bones.
- A joint is packed with a 4×4 local and global matrix representing both orientation and translation.
- A local matrix is relative to its parent or @ origin (identity matrix) if no parent is present.
- A global matrix is set or computed by taking the inverse of the local matrix. (this step will refined later)
- A joint contains a list of children (bones). This allows us to climb down the hierarchy.
- A joint contains an index into it’s parent (joint). This allows us to climb up the hierarchy.
- A bone contains an index into it’s fromJoint, and an index into it’s toJoint.
- If a a bone contains no toJoint it is assumed as the endeffector.
- Build a DummySkeleton function that initializes a simple arm hierarchy as seen above. This is fast and easy, and would be later replaced with a filestream and a tool.
- Render the skeleton.
- Rendering joints as sphere will allow for simple collision tests when regarding picking
- Bones are rendered through D3DPT_LINELIST.
- Utilization of recursive breadth first traversal and ID3DXMatrixStack for push, and pop functionality.
- Add picking (simply changing the appearance color of the joints for visual confirmation)
- This will probably involve having to add additional information to an already rather large joint class.
- Add highlighed, and selected states to the joints.
- Again I opted to keep these inside the joints
- When selected, translate the joint based on cursor position (should already be taken care of inside the picking algorithm). There is again some heavy use of inverses in this function, and this is where we can draw relationships between the data, to hopefully create better data structures during the next iteration.
- Although imo, a minor step before attempting true IK. Implementing bounds based on bone length and DOF values for the axis, we will be officially at the milestone of Dynamic Control.

