This page attempts to document the LucasArts MODL mesh format, file extension ".3do". Work is ongoing, so no guarantees are made regarding any information on this page.
The MODL format comes in two flavours: binary, and ASCII. The ASCII flavour is used in the Grim Fandango demo, and the binary one is used in the full version. It follows that the demo version contains ASCII-versions of models that appear in the full version. Such models should be approached with care, because it's not unlikely that corresponding models from the two versions contain slightly different data.
This document is concerned with the binary flavour of the mesh files.
TODO -- add more games that use MODL, if there are any; upload samples.
This document employs several terms and conventions. Most of these are concerned with data types, of course.
- float - IEEE floating point value
- vectorN - an N-dimensional array that consists of N floats
- geoset - a collection of meshes
- string - character array padded with zeros (one or more) at the end
- All values are little-endian unless otherwise specified
- Fields marked with "(?)" have not been verified. this should not be confused with an ordinary question mark "?", which is a valid part of a field's description.
The MODL format, at the highest level, has the following layout:
header geoset(s) \mesh(es) \face(s) node(s) footer
That is, mesh primitives with no relational information are specified first through geosets, meshes, and faces. Relations between mesh primitives are represented through nodes, which are specified last.
The header is formatted as follows:
"MODL" FOURCC |4 bytes number of materials used|4 bytes
This is followed by material name fields, one per material:
material name|32 bytes, string
Followed the rest of the header:
3d model name|32 bytes, string
The geoset header is formatted as follows:
unknown |4 bytes number of geosets|4 bytes
This is followed by (not very interesting) geoset structures, one per geoset:
number of meshes|4 bytes
A mesh is formatted as follows:
mesh name |32 bytes, string unknown |4 bytes geometry mode |4 bytes lighting mode |4 bytes texture mode |4 bytes number of mesh vertices |4 bytes number of texture vertices |4 bytes number of faces |4 bytes mesh vertex data |vector3 * number of mesh vertices texture vertex data |vector2 * number of texture vertices extra light data (see notes) |float * number of mesh vertices unknown |4 bytes * number of mesh vertices face data (see below) |... mesh vertex normal data |vector3 * number of mesh vertices has shadow |4 bytes unknown |4 bytes mesh radius |4 bytes unknown |vector3 unknown |vector3
face id (?) |4 bytes face type |4 bytes geometry mode |4 bytes lighting mode |4 bytes texture mode |4 bytes number of vertices |4 bytes unknown |4 bytes has texture? |4 bytes has material? |4 bytes unknown |vector3 extra light |4 bytes, float unknown |vector3 normal vector |vector3 mesh vertex indices |4 bytes, one per vertex if has texture, texture vertex indices|4 bytes, one per texture vertex if has material, material index |4 bytes
The nodes header looks like this:
unknown |4 bytes number of nodes|4 bytes
Followed by node structures, one per node:
name |64 bytes, string flags |4 bytes unknown |4 bytes type |4 bytes mesh id |4 bytes depth |4 bytes has parent? |4 bytes number of children |4 bytes has children? |4 bytes has sibling? |4 bytes pivot |vector3 position |vector3 pitch |4 bytes, float yaw |4 bytes, float roll |4 bytes, float unknown |48 bytes (whoa!) if has parent, parent id |4 bytes if has child, child id |4 bytes if has sibling, sibling id|4 bytes
The final piece is the footer, which contains some more information about the model:
model radius |4 bytes, float insertion offset (see notes)|vector3
- Extra-light values are used to as additional brightness values that are added to their corresponding vertex brightness value. This way, for example, for a given vertex v, an illumination value of 0.5 will increase v's brightness by 0.5. Illumination values (are floats, and) are in the range [0, 1]. All encountered models so far have all illumination values of 0.00.
- In order to conserve space, faces only refer to vertices in their enclosing mesh. Therefore a "vertex index" is simply the vertex's position in the vertex array, starting at 0.
- A "node id" refers to the node's position in the breadth-first traversal of the node tree.