Previously, I went over the process of writing an Away3D parser but skipped how to actually interpret the data in a file. In this post, I’m going to talk about the structure of an OBJ file (less complicated than SOUR), how it’s typical of all 3D files, and how in Away3D or any 3D engine one would turn its data into a usable 3D object.

First, let’s actually look at a 3D file, a simple cube saved by Blender. This cube has sides 2 Blender units long and is centered perfectly. (Hence all the 1’s and -1’s.) It has a material file and is UV-mapped. Unlike SOUR, it uses quads (four-pointed faces like rectangles, diamonds, or squares).

Alright, let’s go through it.

#
The lines that begin with a pound sign (#) are comments. They can be deleted and it will have no effect on the file.

mtllib
The lines that begin with “mtllib” refer to the MTL material files exported alongside the OBJ file. In my experience, MTL files are fairly useless, so I’m not going to cover how they work here except to say that they do not contain the PNG or JPG file used for the texture, and that you can delete that line with little effect. Same for “useMtl” lines.

v
The lines that begin with “v” are vertices. You can see that each has an X, Y, and Z value. Furthermore, they are “indexed”. More on that later.

dao_mesh

vt
The lines that begin with “vt” are texture vertices. They’re in 2D space, so they only have X and Y coordinates. They will for the rest of this post be called “UVs”, since that’s more common.

dao_UV

useMtl, s
Next are a few more useless lines. The line with “useMtl” was discussed above. The one with “s” has to do with smoothing groups, which applies to 3DS Max only.

f
Next up are the lines starting with “f”. F is for face. This is where it gets interesting. Remember how I said the vertices were indexed? Each face has two numbers. One refers to a vertex, a “v”, and the second refers to a UV, a “vt” from the above lists of vertices and UVs. You’ll see that there are eight vertices and each of the face number-sets’ first numbers is in the range 1-8. See where this is going? The first face is a quad with the vertices 1,2,3, and 4, or:

By plotting those four points in 3D space, we reconstruct the first of the six quad faces of a cube. “Indexing” means listing each of the eight vertices only once and then referring to that vertex by its “index” number in the face information. This saves space and bandwidth. The alternative would be listing the actual “v” vertex information per face. The UVs are also indexed.

So, in a custom parser, if you were parsing line by line like the OBJ and SOUR parsers do, you’d read a line, separate the bits by the spaces in the line via something like AS3’s String.split() function, throw out the first one, and use the rest to form a vertex or UV. Once you had a list of vertices, UVs, and whatever other data was relevant, you’d use your 3D engine’s methods/functions to construct the Mesh object and put it onscreen. In Away3D, that looks like:

And that’s really it. Parsing is just taking data in one form, and making it readable to whatever needs to use it.

A big part of creating the SOUR format was writing a parser to get the models into Away3D. (A parser is a part of a program which reads data in a storage format and changes it into a usable format.) In this article, I’m going to go through the basics of writing an Away3D parser.

The Parts

An Away3D parser has six fundamental functions you need to use:

  • the constructor¬†and super() in it
  • proceedParsing()
  • hasTime()
  • supportsType()
  • supportsData()
  • finalizeAsset()

The Parent Class: ParserBase

You’ll need to extend Away3D’s ParserBase class to get access to the functions mentioned above, and to make your parser compatible with the AssetLibrary.

The Constructor

Like any AS3 constructor, the constructor function will have the same name as a parser class. So for our parser, SOURParser, the constructor is SOURParser(). The only thing you need to worry about doing in the constructor (though you can do more if you so choose) is using the super() function. ParserBase, the parent class, takes a single argument, either ParserDataFormat.PLAIN_TEXT or ParserDataFormat.BINARY, depending on what kind of 3D file is being parsed.

Compatibility Checking

Away3D parsers attempt to determine whether the data being fed to them is compatible in two ways: by examining the file extension and by checking the contents of the file for certain strings which are a part of the file’s formatting. supportsType() does the former and supportsData() the latter. Both return a boolean. Override them and modify them as is appropriate for your 3D file format.

The Actual Parsing

How a parser reads your data is really up to you. For SOUR, I chose to do a line-by-line approach in a while loop, much like the OBJ parser does. All the parsing gets done in proceedParsing().

One nice feature of Away3D’s ParserBase though, is that it has the hasTime() function, which allows you to do the parsing over the course of several frames rather than all at once. Why does that make a difference? If you’re attempting to read a file all at once and the file is too large to be processed in the time in between frame renders, the parsing will continue until it’s done and THEN the next frame will get rendered, causing frame loss. Using hasTime(), you can determine if you’ve exceeded the time limit between frames yet, in order to break the job of parsing down into smaller pieces, and not lose frames. If everything gets preloaded, it won’t make a difference, but if some assets are loaded on the fly, it’s rather important.

When you finish reading data from your 3D file and reconstructing it as a Mesh, Geometry, Animation, Texture, etc., you’ll want to pass that 3D-formatted data back to your application. You do that with finalizeAsset(), which takes the data itself, automatically figures out the AssetType, and delivers it back to your application via an AssetEvent. The process of turning a 3D file into Mesh data is the subject of another article.