Wavefront .Obj

Notes from adding (WaveFront) .obj export to a Unity3D plugin I’m working on.


Motivation

I want to … use … geometry … elsewhere.

I want to use Unity3D as a (sort of) map editor thing that I could pull the data out of and mess with it. Specifically, I’m trying to export geometry for “asset time”1 manipulation in the form of old-school .bsp files. I want to play with this in [PureScript]() (or [whatever]()) before loading it in a C/++ application. I want something I can dump from a Unity plugin, pull into a more suitable programming language, then re-export as a final baked format.

I’m using a .zip file with all geometry as a series of (merged) .obj files and a .png file per-merged object.

Crude stuff.

Plan

I’ll use an (otherwise conformant) subset of the .obj format that has;

  • all vertex coordinates and texture coordinates
    • the “full” version of .obj can handle more data
    • I don’t care about normals - they can be recomputed anyway[^unbake]
    • Tangents (et al) aren’t present in .obj AFAIK
  • all triangles following coordinates
    • the “full” version of .obj can handle n-gons
    • … and other types of geometry

Okay, so, my plan for developing this was to;

  1. write a sample model file by hand
  2. test/check import sample to Blender / Unity
  3. code and test Unity plugin based on this

As always Paul Bourke’s webpage is my go-to reference http://paulbourke.net/dataformats/obj/

It looks like the format can hold a lot of stuff, but, I’ll only need;

flag type usage
# comment starts comments that are otherwise ignored
v vertex coordinate it can be a lot of things, but, I’ll just do x/y/z
vt texture coordinate it can hold up to three! but I only need the two
f face will be the three v/vt of my triangles

My sample file came out like this, and, it worked in Blender and Unity. I needed the latter to check the texture coordinates by applying a material.

v 1 0 0
vt 1 0

v 0 1 0
vt 0 1

v 0 0 1
vt 0 0

f 1/1 2/2 3/3

Code

With the sample, I learned that the indices start at 1 (like animals would do it) and that I didn’t need the full v/vt/vn form. I also don’t need any sort of naming thing … which was surprising. I whacked together a C# section in an existing exporter (that already did the .png writing and .zip) and it looked like this;

... // do something prior to find/collect the/a scene tree/s you want in this surface brush


// write the obj geometry
using (var writer = new StreamWriter(file.CreateEntry("brush/" + surface + ".obj").Open()))
{
    writer.Write("# bsp brush '" + surface + "'");

    // where we record the vertex/texture
    var v = new Dictionary<string, int>();
    var t = new Dictionary<string, int>();

    // where we record the faces - i'm appending them (all) since that should make loading simpler
    var f = new LinkedList<string>();

    // ====
    // write the vertices and textures

    // write a vertex coordinate
    int put_vertex(Vector3 xyz)
    {
        var txt = xyz.x + " " + xyz.y + " " + xyz.z;

        if (!v.ContainsKey(txt))
        {
            v[txt] = v.Count + 1;
            writer.Write("\nv " + txt);
        }

        return v[txt];
    }

    // write a texture coordinate
    int put_texture(Vector2 uv)
    {
        var txt = uv.x + " " + uv.y;

        if (!t.ContainsKey(txt))
        {
            t[txt] = t.Count + 1;
            writer.Write("\nvt " + txt);
        }

        return t[txt];
    }

    // loop through all triangles
    foreach (var triangle in data[surface])
    {
        // start the line
        var l = "\nf";

        // append the verticies
        for (int i = 0; i < 3; ++i)
            l += " " + put_vertex(triangle[i].xyz) + "/" + put_texture(triangle[i].uv);

        // put it in the list
        f.AddLast(l);
    }

    // ====
    // write the faces now
    foreach (var l in f)
        writer.Write(l);
}

Conclusion

So … I can now write .obj files from geometry. I can get some data out of Unity3D scenes for other tools.


  1. .obj is not a format you should load at runtime. Bake your assets for your engine.

    [return]
comments powered by Disqus
Peter LaValle avatar
Peter LaValle
Any links probably include affiliate ids for that sweet sweet kickback - and some programs require that I tell you. The contents of this blog are likely unrelated - as they include games, paints, and build tools.