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
- the “full” version of
- all triangles following coordinates
- the “full” version of
.obj
can handle n-gons - … and other types of geometry
- the “full” version of
Okay, so, my plan for developing this was to;
- write a sample model file by hand
- test/check import sample to Blender / Unity
- 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.
[return].obj
is not a format you should load at runtime. Bake your assets for your engine.