Due to popular request, here is the second installment of my Starcraft 2 .m3 model format series.
First of all, I’ve gotten quite a few mails and comments on this topic. This includes some links to other people also working on the format, so here is a collection of links:
- There is a thread on incgamers with some interesting details. Apparently, they have written a php script that extracts basic model information. See http://starcraft.incgamers.com/forums/showthread.php?p=25589
- Sergey has posted his findings on his blog, and integrated the model format into his engine: http://psytech.spaces.live.com/
- Tigurius has started to write down the format specs in a Wiki: http://www.madx.dk/wowdev/wiki/index.php?title=M3
- There is a Google code hosted library for parsing the m3 format: http://code.google.com/p/libm3/
Thanks everyone for the links!
Today I’ll chat a little about the stored meshes. There are threed parts to rendering out simple meshes: the vertices, the faces, and the regions. But first, we need to find them.
That’s where the MODL chunk comes in. It should be considered the header of the m3 format. It defines where to find what. However, we’re only interested in a few parts. It’s important to note that I’ve found two different versions of the MODL header, version 0x14 and 0x17. 0x14 is slightly different, and I’m not going to talk about it in this post.
struct MODLHeader {uint32 stuff_we_dont_need_atm[17]uint32 flagsuint32 vertex_data_sizeuint32 vertex_taguint32 div_countuint32 div_tag[… some more data we don’t care about…]}
The precious parts here are the reference to the vertices, the reference to the div, and the flags. The flags will become handy when parsing the vertices, as they describe what can be found in the vertex format. In detail:
if flags & 0x40000 == 0:vertex_size = 32else:vertex_size = 36
There are def. other information in the flags, but this is what we need right now. The vertices can now be found at the vertex tag, which is just a collection of bytes. The number of vertices is vertex_data_size / vertex_size. The format is as follows:
struct Vertex {float32 position[3]uint8 boneweights[4]uint8 boneindices[4]uint8 normal[4]uint16 uv[2]if vertex_size == 36: {uint8 unknown[4]}uint8 tangent[4]}
Position is just the object space position of the vertex.
Boneweights range from 0 to 255 and represent the weight factor (divided by the sum of all weights) for each bone matrix.
Boneindices are indices that point to the corresponding bone matrix.
The normal is compressed and can be extracted as c = 2*c/255.0-1 for each component.
The uvs are scaled by 2048, so they need to be divided by 2048 to be used in opengl.
Finally, the tangent is compressed in the same way as the normal.
To get the bitangent and set up a correctly oriented and orthonormal tangent space, we need to take the cross product of the normal and tangent, and then multiply it by the w component of the normal: (n.xyz cross t.xyz)*n.w
The DIV is really just a container for two other important chunks, the faces and the regions:
struct DIV {uint32 indices_countuint32 indices_taguint32 regions_countuint32 regions_tag[… some other stuff …]}
The triangles are just stored as tripplets in the indices U16_ tag. There are indices_count/3 triangles in the mesh.
To correctly render the mesh, we also need the region chunk:
struct Region {uint32 unknown — Note: updated, thanks to NiNtoxicated!uint16 vertex_offsetuint16 vertex_countuint32 index_offsetuint32 index_countuint8 unknown[12]}
Now, to render a mesh, we can iterate through all regions, and for each region, we render index_count/3 triangles with the indices from the indices chunk. This looks something like this:
def drawModel(self):for region in div.regions:GL.glBegin(GL_TRIANGLES)for i in range(region.index_count):v = vertices[div.indices[region.index_offset+i]]GL.glTexCoord(v.uv[0]/2048.0, v.uv[1]/2048.0)GL.glVertex(v.position[0], v.position[1], v.position[2])GL.glEnd()
And that’s it! Setting up the proper textures and materials is rather complex and is def. worth another blog entry 😛 That’s it for now. As usual, let me know if you have any questions or comments!
PS: let me know if you know how to properly format source code on blogger 🙂
All good now <3
Is there any way I can get these .m3 files in to obj or something similar to import in to Maya?
You can check out the php obj exporter scripts at the links i posted. you can then import the obj in maya.