Introduction

 This document describes the format of the Maya(tm) ASCII file structure so far I have determined it's content. I only analyze and describe the properties I'm personally interested in, so this document is bound to be incomplete and subject to changes as I determine new properties of these files.

Trademarks: Any names used in this documents are registered trademarks of their respective owners.

A good description of the general layout of  the MAya ASCII format can be found at http://caad.arch.ethz.ch/info/maya/manual/FileFormats/FileFormatsTOC.fm.html

This document was written by Gerhard W. Gruber <sparhawk@gmx.at>. If you have any additional information that should be included please contact me. You can also contact me to tell me if you find this document usefull, or whats wrong with it, or spelling mistakes or any thing you might consider usefull (or not so usefull :) ).


General

Any Maya ASCII file starts with '//Maya'. If this is not the case it is not a valid Maya ASCII file. Comments are donated by // and are removed if saved by Maya. This means you can edit the file as you wish and add comments but they will not be retained once you do any changes. The following 5 lines show the start of any Maya ASCII file using a small example of a polygon cube.

Example (line numbers are added for reference):
  1. //Maya ASCII 4.0 scene
  2. //Name: Cube.ma
  3. //Last modified: Wed, Mar 27, 2002 08:57:20 PM
  4. requires maya "4.0";
  5. currentUnit -l centimeter -a degree -t film;
Line 1 identifies as valid Maya ASCII file. Line 2 identifies the name of the workfile and Line 3 shows the modification date. These are the only comments that are retained by Maya. Line 4 shows the version of Maya which created the file. Depending of the data you created this means that you need this version to properly load the data. If you create simple geometry with not features that are specific to Maya 4.0 you can safley edit this line to i.e. 3.0 when you need to load it in another Maya version. Of course this is not supported by Alias/Wavefront(tm) and it might damage properties of the object, so you should create a backup if you try this. For my purpose I can ignore this entry since I'm only interested in polygon meshes, vertex data and texture coordinates. Line 5 shows the units that are used in the Maya project settings for this file. For further explanation of these values you can refer to your documentation.

Maya usualy safes only values that are needed because they are different from the default settings. If a value should contain a default value then it is not safed but it is still created within an object. i.E. a transformation node contains information about transforms (who whould have guessed that). The default position of an object is at 0/0/0 (x/y/z) with no rotation or scaling. Since this is the default the appropriate settings for the objects are not safed to the file but they are still set to 0.

Attributes and node can appear in any order with the only restriction that data that belongs to each other or has a relationship, this relationship is stored in the order the values are needed. This means that a child always comes after a parent node and never before, but not neccessarily directly after the parent as there can be other nodes in between not related to each other.
Attributes belonging to each other can be stored in any order but also data belonging to each other follows in the logical order and directly after it. This means that a vertex set, which is split up into several commandlines has a header giving the total size of the entries needed, and then following the entries itself. Probably there can be other attributes intermixed because the attributes are always identifiable by a name and optional an index but Maya doesn't save it this way. You should be prepared that this can happen.

Each commandline (except comments) are seperated by ';'. This means if you read in the file line by line then you should add up the line until the ';' character appears which will end that command. Certain commands can produce quite a big number of values (like vertex data) which will be split up into several commands each of course seperated by the ';' character. In case a line is split up it is always split on a element boundary. Meaning that if an array has three values per element (like vertices do) then alwas three elements are within a given line. In the examples I show line numbers only for complete commandlines and not as they appear in the file. This means that the exmaple shows the file layout with multiple lines but I only use one line number per command. Many of the data are refering to other data via indixes. The size of an attribute, if it needs an array, is stored in such a way that it can be read before the actual data starts, thus a properly sized array can be preallocated and subsequently used with the values.

When reading this document you should have an understaing of how to use indexes and arrays, because some of the important  structures I describe here use indizes to reuse storage space efficiently.

As in the example above I will use line number for better reference. These line numbers are only for the purpose of documentation and are not contained in the file itself.


Filedescription with an example

A node is an object within Maya, but don't confuse geometric objects with objects in general. An object can be any kind of object that is needed for your scene like cameras, lights, meshes, transformations etc.. Since I'm only interested in polygon objects I will only explain these things so far. There are different types of nodes, since they create an object, and each node contains an identifier what it is used for, along with a name and an optional parent node. This parent node is important as it allows Maya to create hierarchies of objects which correspond to the view in the Outliner Window of Maya. The node for a geometric object starts with a transform and a name.

Example:
  1. createNode transform -n "pCube1";
  2.    setAttr ".t" -type "double3" 5.9668819370872228 0.56982039305093213 4.3060871548908901 ;
  3.    setAttr ".r" -type "double3" -12.338352729591465 37.799999999999422 0 ; 
  4.    setAttr ".s" -type "double3" 1.5 0.4 0 ;
  5. createNode mesh -n "pCubeShape1" -p "pCube1";
  6.     setAttr -k off ".v";
  7.     setAttr ".vir" yes;
  8.     setAttr ".vif" yes;
  9.     setAttr ".uvst[0].uvsn" -type "string" "map1";
  10.     setAttr -s 14 ".uvst[0].uvsp[0:13]" -type "float2" 0 0 1 0
            0 1 1 1 0 2 1 2 0 3 1 3 0 4 1 4 2 0 2 1 -1 0 -1 1;
  11.     setAttr ".cuvs" -type "string" "map1";
  12.     setAttr -s 8 ".vt[0:7]"  -0.5 -0.5 0.5 0.5 -0.5 0.5 -0.5
            0.5 0.5 0.5 0.5 0.5 -0.5 0.5 -0.5 0.5 0.5 -0.5 -0.5 -0.5 -0.5 0.5 -0.5 -0.5;
  13.     setAttr -s 12 ".ed[0:11]"  0 1 0 2 3
            0 4 5 0 6 7 0 0 2 0 1
            3 0 2 4 0 3 5 0 4 6 0
            5 7 0 6 0 0 7 1 0;
  14.     setAttr -s 6 ".fc[0:5]" -type "polyFaces"
            f 4 0 5 -2 -5
            mu 0 4 0 1 3 2
            f 4 1 7 -3 -7
            mu 0 4 2 3 5 4
            f 4 2 9 -4 -9
            mu 0 4 4 5 7 6
            f 4 3 11 -1 -11
            mu 0 4 6 7 9 8
            f 4 -12 -10 -8 -6
            mu 0 4 1 10 11 3
            f 4 10 4 6 8
            mu 0 4 12 0 2 13 ; 
Each node contains parameters and properties that are valid for this particular node. Each node is completely described until another createNode command appears. There is no mixing of node data, such that you can create a node and load all the data in your code that belongs to a node. Once another createNode command is encountered, the previous node is finished and will no attributes will be added or modified for this node. Each value that is added to a Node is called an attribute (of that node) and is donated with a setAttr command

Line 1 instructs us to create a new transform Object named pCube1. The command '-n' followed by a string donates the name of the object (i.e. -n "<objectname>"). The following attributes are used to change the default position of theobject. ".t" - transform, ".r" - rotate, ".s" - scale. These attributes are followed by a type modifier to tell which types the following data is supposed to be, followed by three values indicating the respective (x/y/z) modification of the attribute. This attributes are important in object hierarchies in order to maintain the porper alignment of the objects. Suppose you have a figure that is constructed with a head, torso, two arms and two legs. Each of this objects will be ideally a sperate object which can be modified indpendently. In this example you could create the torso with no transformation and then attach the head and transform it to it's final position. The above transformations then would be the relative position that is needed to retain the position of the head where it should be. Otherwise the head would end up in the middle of the torso being at position 0/0/0 instead at the top where it rightfully belongs.
Line 5 creates another node with type mesh. This node type contains the actual geometric data attributes like vertices, edges, faces, texture coordinates and others. As you can see this node is parented to the previous node (-p "<ParentName>") meaning that the transformations of the parent apply to this node. Each node that is parented to another always follows the parent. I never encountered a file where a parented node has been created before the parent node, so I assume this to be a rule.
Line 9 introduces a texture coordinate mapping, but I have no detail yet about this command.
Line 10 introduces the actual texture coordinates. I assume that it is possible to have multiple texture settings for the same object because there is an index assoicated with the command (.uvst[index number]). The attribute has shows the size of the array that is needed for the values in this case 14 entries. Each entry of this attribute has two values associated (x/y coordinates). The array values are for the indexes 0-13 which means the entire array. As explained earlier, data can be split up into several commands it the amnount of data is quite large for a given attribute. As shown here this is done by issuing multiple attributes with the same types only the array index values differ.

Example:
  1. setAttr -s 26 ".uvst[0].uvsp";
  2. setAttr -s 14 ".uvst[0].uvsp[0:13]" -type "float2" 0 0 1 0  0 1 1 1
  3.     0 2 1 2 0 3 1 3 0 4 1 4 2 0 2 1 -1 0 -1 1;
  4. setAttr -s 12 ".uvst[0].uvsp[14:25]" -type "float2" 0 0 1 0  0 1 1 1
  5.      0 2 1 2 0 3 1 3 0 4 1 4 2 0 2 1 -1 0 -1 1;
This example shows two attribute lines connected to the same attribute but split up into two different lines. The first set goes from index 0-13 and the second set from 14-25 but the belong to the same array. If such a splitting occurs then prior to the first attibute there is another attribute that has the same name (or identifer) as the following attributes but only giving the total size of the array (26 in our case). The size for each data line only gives the size for the actual data contained in this line. The size always gives the size of the structure and not of the number fo arguments. This means that in this case we have a total of 52 values to read because we have 26 pair of coordinates. It is to be noted that the first line in this example is only used when the data is spread over several commandlines. Otherwise the arraysize is given directly in the setAttr command, though I guess that it doesn't hurt if the line is still present but redundant in that case.

Line 12 gives the vertex data ".vt" in triples. For our cube we need 8 points to be constructed each point described by three values (x/y/z). The vertex coordinates are always specified from the 0/0/0 position of the object (probably not when Freeze Transformation is used, I have to check that). When objects are in a hierarchy and translated relative to each other then the vertex data has still the same values but must be modified by the translation/rotation/scale values in the node itself. With vertex data the same rule applies for splitting up as for texture coordinates. When the line is to long the data is split up in several commandlines each giving the array entries for a particular part of the array as described above.

Line 13 gives the Edges for our cube ".ed". The format is the same as for texture coordinates and vertex data. Each entry consists of three values to denote a line(edge) of the cube and an additional value that is curently unknown to me. The first two values of an entry contain the indicies into the vertex array giving the vertices that are needed to connect that line. The direction of the line is always from V1 to V2. The third value contains always 0 or 1 (at least I have seen no other values by now). My guess is that this may have something to do with outlines that are needed for some computations where it might be neccessary to know which lines are on the outer side of a polygon or such.

Line 14 now gives the faces of the object ".fc". The format is the same as described above. Each  face is described by two lines which are donated by "f" for the face edges and "mu"/"mf". "f" is started by a number that says how many edges are following describing the polygon. In our case we have 4 edges per face. The numbers after are the actual edge values, denoting indicies into the edge array. If the number is positiv, the edge is taken as it is in the array. If the number is negativ this means that the direction of the indicated edge is to be reveresed. The index is acquired by converting the number to a positive value and subtracting 1 from it. This is neccessary because obviously -0 is not a valid number. In order to draw the object you have to loop through the face array indexing into the edge array which in turn indexes into the vertice arrray. The "mu"/"mf" values contain the indicies into the texture coordinate array. In case of "mu" the first value gives the texture coordinate array that is to be used (i.e. .uvst[index]) and then the number of indicies as for "f" and afterwards the actual indicies into the array itself.



createNode

SYNOPSIS: createNode {transform|mesh} -n "<Name>" [-p "<Parent>"];

DESCRIPTION: createNode creates a node of the specified Type and a name. Optional it is possible to parent a node to another one, that has to exist already in the nodetree. A node that has a parent is always safed after the node it is parented to.

INPUT:
transform - simply describes a transform node which contains information on how to translate, rotate and scale the following objects. This is a general node and the transformations should apply to all nodes that are childs of this node.
mesh - desribes a node which contains mesh data attributes like vertices, edges, faces and texture coordinates. A mesh node may also contain transformation attribute like the transform node.

setAttr

SYNOPSIS: setAttr [-s <Number of elements>] {attribute identifier}[-type {"<ValueType>"}] value[s];

DESCRIPTION: setAttr creates attributes for a given node. this command applies to the current node is is only encountered in that context. Attributes can have either a single value assigned to it or a list of values. A single value doesn't neccessarily mean a single number, rather it means the neccessary set of values of the specified attribute. i.E. the value for single vertex contains three numbers namely the x/y/z coordinates. This means that in this case the number of elements will be specified as 1 while it contains three numbers. The attribute identifier specifies the type attribute that is to be set, optionally a type can be associated with an attribute.

INPUT:
Attribute identifiers:

The following table gives an overview of the attributes I know about and what data they contain. The attributes are alwas enclosed in " when saved to the file, the same applies to the type. For some attributes the type is not specified in the file. In this case the type is fixed and is mentioned in the Value column, otherwise the type may change depending on the actual attribute/node type. If the "Type used" syas yes then the type can vary and I list only the onbe I know about.

Attribute Size spezified
Type used
Value
Comment
.t
No
double3
Three numbers per element as specified by the type.
Translation values x/y/z
.r
No
double3
Three numbers per element as specified by the type.
Rotation values x/y/z
.s
No
double3
Three numbers per element as specified by the type.
Scaling values x/y/z
.uvst[MappingIndex].uvsp[ElementFrom:ElementTo]
Yes
float2
Two numbers per Element.
Contains the texture coordinates x/y. Since the array can be rather large a header can be applied giving the total size of the elements for the given texture index. Multiple texture arrays can be specified identified by "MappingIndex". Each of these contain a complete set of texture coordinates. When to many elements are in the array the coordinates are split up over multiple lines. In that case each block shows the  array indicies it belongs to by ElementFrom:ElementTo. If the line is split up it still contains two numbers per element and is not split within an element.
.uv[ElementFrom:ElementTo]
Yes
No
Two float numbers per element.
Contains the texture coordinates x/y as above but only a single texture coordinate array is used.
.vt[ElementFrom:ElementTo]
Yes
No
Three numbers per element type float/double
Contains the vertex data for the object x/y/z values.
.ed[ElementFrom:ElementTo]
Yes
No
Three numbers per element type signed integer.
Contains the indicies into the vertex array that build a specific edge. The first two numbers per element are the indicies into the vertex array while the third number is unknown to me.
.fc[ElementFrom:ElementTo]
Yes
Yes
polyFaces, Two lines per element with varying number of values.
"f" specifies the indicies into the edges. The first parameter is the number of edges for this face. The indicies can be either positive or negativ. A negative index means the reversal of the edge associated to it and is calculated by converting to a positive number and subtracting 1. After that <N> numbers are following specifying each one an index into ".ed".
"mu" specifies the mapping index into a texture coordinate array ".uvst[MappingIndex]". The next number gives the number of  texture coordinates following and afterwards are the actual texture indicies each an index into .uvst[MappingIndex].uvsp[TextureIndex].
"mf" Some objects have this texture mapping command applied. This is the same as "mu" except that it has no MappingIndex. Following the command is the number of the texture indicies. This is used when the texture coordinates are specified by ".uv[ElementFrom:ElementTo]". For each face attribute there is either "mu" or "mf" depending on the texture coordinates.