Menu

Status Update and GC

We are finally back to working on AGE; picking up where we left off, the OBJ loader. After all, who wants to hard-code all of those vertices!?

There's not much documentation on the format, but it's that simple! The current state of implementations is pretty sorry with respect to Garbage Generation; ArrayList<Float> is a path to ruin, even for a small model. There's that, then there's use of some parser like StringTokenizer or worse yet, Scanner! We looked at the Java 7 source, there's lots of "state" in there! This is compounded by the fact that these are immutable on the input, so you have to create lots of these heavyweight beauties (multiple per line in some cases).

Since one of our top goals is to avoid inducing GC, a better implementation is warranted. So let's go over what we absolutely cannot have in the implementation:

  • ArrayList<any boxed type>
  • Scanner, StringTokenizer, etc. because they are laden with state and new.
  • Pattern or anything related to Regex for similar reasons.

That's pretty strict, but not actually. Within a line of input, there are only two delimiters:

  • Whitespace (space, tab)
  • Slash "/" (within "Face" vertex)

Given the simple tests involved, a straightforward scan-ahead-until-separator function is employed. Using the start and one-after-end that this function returns, are a perfect match for passing to String.substring() to get the necessary string. This may seem like extra work, but it avoids any superfluous string creation by any of our friends up in the other list. We defer the rest of the "checking" to Float.parseFloat() and Integer.parseInt().

So the actual parsing part of it is now covered without any "parser" overhead, we can look at how are we going to store all of these floats?

Well, that's pretty obvious: we use float[]. But we use it in a smart way: with a page-oriented wrapper class that handles array re-sizes when we are about to overflow. We use initial capacity and expand-by in the constructor, and call its put() method to append 2/3/4 tuples into the array.

So that's good for getting the raw data staged up. Recall there may be Position, Normal, Texture coordinates in the OBJ file, and normals and textures are optional. We have one of our "page buffers" for each attribute. Now comes the fun part: the Faces.

The final "stage" of the OBJ file contains the 3-tuples for triangles or 4-tuples for quads; we only consider triangles here. A quad can be decomposed; good luck maintaining winding.

Let's consider why we are loading this data: to create Vertex Attribute Arrays for OpenGL! We are going with the "interleaved" format, where all of a vertex's data is stored in one "entry" of contiguous floats, of a specific total size, or "stride".

Given all of the setup above, a face is one, two, or three indices into the corresponding arrays. They are one-based, so "1" would refer to vertex zero of each attribute, in order Position, Texture, Normal:

  • Position "1"
  • Position+Texture "1/1"
  • Position+Normal "1//1"
  • Position+Texture+Normal "1/1/1"

The parser builds an AGE InterleavedVertexGeometry instance based on traversing the faces, and creating the vertex attributes according to the above indices. This process is facilitated by another "page buffer" for the final data. This geometry in turn can be used in one or more DrawableGameObject GOs with varying materials, most likely TextureMaterial. There is no color information directly generated in OBJ files, and the IVG won't contain any. Update: this actually isn't an issue, because you can associate a PerVertexMaterial with the geometry, and everything "just works". This was a pleasant surprise!

Notice we are also avoiding using IndexedVertexGeometry since this format has some limitations, requires the index array itself, and complicated models don't benefit much, since all the attributes must match the same index, and this would require much "shuffling" or re-staging to get everything to "line-up". We decided it's easier to just "unwind" the faces directly. There are also limits to the number of triangles submitted, so a very complicated model must decompose into multiple Vertex Attribute Arrays, and now you have index re-basing problems on top of everything else!

These changes are upcoming, along with completing the texturing implementation. In AGE, texturing consists of:

  • Texture (GL Resource)
  • TextureMaterial (interacts with Shader, owns a Texture)

Currently you can make textures from drawable resources in your APK, but obviously you can get a Bitmap from many sources!

So there it is! Starting exporting your models now!

Posted by g-dollar 2014-03-19

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.