Skip to content

Wavefront OBJ (.obj)

The Wavefront OBJ geometry format: a line-oriented ASCII format with v (vertices), vt/vn (texture/normal coordinates), f (faces) and g (groups).

Format nameobj
Extensions.obj
Read / Write✓ / ✓
Extra dependencies

Reading & writing

python
import meshioplusplus

mesh = meshioplusplus.read("model.obj")
meshioplusplus.obj.write("out.obj", mesh)

write takes no keyword arguments.

File structure

Blank/#-comment lines are skipped.

  • v x y z — a point row (all tokens after v are parsed as floats; extra columns are simply appended, not rejected).
  • vn ... / vt ... — vertex normal / texture-coordinate rows (arbitrary column count, stored raw).
  • s 1 / s off — smooth-shading toggle, ignored.
  • f i1[/t1[/n1]] i2/... ... — a face; only the part before the first / of each token is used as the vertex index (1-based). All faces in a "run" must have the same vertex count; the run breaks (opening a new cell block) as soon as the count changes, or a new g line appears.
  • g <name> — starts a new group and increments a running group-id counter (starting at -1, so faces before any g get group id -1). Only the group's running id is kept — the name string itself is not stored.
  • Any other keyword is silently ignored.

Empty trailing groups (e.g. a g line with no faces after it) are dropped after the full file is scanned.

Cell types

Faces are grouped by vertex count into triangle (3), quad (4), or polygon (else).

Data mapping

  • point_data["obj:vn"] — vertex normals, present only if any vn lines were seen.
  • point_data["obj:vt"] — texture coordinates, present only if any vt lines were seen.
  • cell_data["obj:group_ids"] — one int array per cell block, the g-index that produced that block (-1 if before the first g).

Quirks & limitations

  • The /vt//vn per-face index references (e.g. f 1/2/3) are discarded entirely — only the leading vertex index survives; there is no per-face UV/normal index array, only the flat per-vertex vn/vt blocks.
  • A new g line always starts a fresh cell block, even if the vertex count is unchanged from the previous group — group boundaries and vertex- count changes both independently trigger a new block.

Notes

  • tests/meshes/obj/elephav.obj — a real triangle-mesh scan (vertex-only v lines plus f triangle lines).
  • Fully handled by the C++ core.

Released under the MIT License.