Now with the “materials” dealt with for the time being I move on to the simpler and more obvious vertices and faces. The header indicates the sizes of each entry for vertices and faces. For vertices they appear to be 24bytes or 36bytes. Cars tend to be 36bytes and the course appears to be 24bytes. Faces all tend to be 2bytes, so shorts. This does mean that we’re limited to 65535 vertices for face entries which would limit our detail. However the fact the format allows for face sizes I suspect we can set this to 4 and use integers instead.

Both vertices entries start with Vertices X, Y, Z. Then the last 3 values contain an integer which looks like it contains ARGB and then 2 floats which have to be U and V for the textures. (Doing this has improved my understanding of 3d model formats. So if I sound like I know what I’m talking about, trust me, I’ve only learned the basics and still learning).

Now for the 36byte Vertices entry. The additional 3 floats are normals. Which I had to learn what these were and why they were needed. Without normals the model appears low poly but with them the model looks higher poly. Turns out this is what normals do. They blend between the faces so that it isn’t harsh. Making the model more pleasing to the eye. But this does mean that cars are going to look better than course parts as course parts don’t have normals.

Here are the structures for the vertices that I’ve added to the MMDL class:

@dataclass
class Vertice1:
    X: float = 0.0
    Y: float = 0.0
    Z: float = 0.0
    Colour :int = 0
    U: float = 0.0
    V: float = 0.0

@dataclass
class Vertice2:
    X: float = 0.0
    Y: float = 0.0
    Z: float = 0.0
    NX: float = 0.0
    NY: float = 0.0
    NZ: float = 0.0
    Colour :int = 0
    U: float = 0.0
    V: float = 0.0

I wont get too much into the code here. My github would be the place for that. So this side project can be found here: https://github.com/tofuman0/mmdl-processing

With the class made, I create a readmmdl.py script that will load an MMDL and then output it to a txt file. This is on my github. The syntax is:

python readmmdl.py -m mmdlfile.mmdl -l logfile.log

The output is something like:

So now that I have the MMDL file in my own structure I can convert it to another format. It appears that the OBJ is the obvious choice. It’s an open format and every 3d modelling program supports it.

OBJ files are actually pretty simple to create. They can be opened in a text editor and support comments so can be easy to follow. A simple OBJ file contain vertices entries (v) and face entries (f). The vertices lines are formatted as:

v X Y Z

Faces are formatted as (using 3 values from the face entry table):

f 1 2 3

So time to create a MMDL to OBJ script. Which I name mmdl2obj.py. Much like the readmmdl.py it loads the MMDL file but outputs an OBJ with the vertices and face data. The syntax for the mmdl2obj.py is:

python mmdl2obj.py -m mmdlfile.mmdl -o objfile.obj

I then loaded the OBJ in Paint 3D (of all things) and the output:

Paint 3D, it turns out isn’t the best utility for this task but at least I can see that a model is in fact generated. Blender would be the better choice. However as I’m not grouping the faces the model would become unwieldy so I move onto grouping faces. After all, I have the Object tables which contains the vertices offset and face offset for each material in Object3.

In addition to face groups. I should also add UV and Normals to the mmdl2obj.py script. These attributes are vertices texture (vt) and vertices normals (vn). These are as follows:

vt U V
vn NX NY NZ

Now that I’ve added UV and Normals to the file the face lines must also include these so the line are changed as follows (Vertice Face/Normal Face/Texture Face):

f 1/1/1 2/2/2 3/3/3

To group faces are can use objects (o), materials (usemtl). There is also “Smoothing Groups” (s) which we can turn off using “s off”. I use the “Name” value from the parent Object2 for the object (o) name, and use “MaterialName” from Object3 for the material name (usemtl). As the UV and Normals are in the Object3 entries the face value is the same for all face types. Which makes it simple:

o AE86T3-00body_A_AE86T3_A
s off
usemtl mat_CC1_KAN1_AE86T3_A
f 1/1/1 2/2/2 3/3/3
f 2/2/2 4/4/4 3/3/3
f 3/3/3 4/4/4 5/5/5
f 4/4/4 6/6/6 5/5/5
f 5/5/5 6/6/6 7/7/7
f 6/6/6 8/8/8 7/7/7
f 7/7/7 8/8/8 9/9/9

Now to load it into Blender:

It is now possible to hide the groups so the car model can be visualised. Now all we’re missing is textures!

Categories: Game Assets