M2 |
From WoWDev
Contents |
M2 Files
M2 files (also called MDX) contain model objects. Each M2 file describes the vertices, faces, materials, texture names, animations and properties of one model. M2 files don't have a chunked format like most other WoW formats.
Models are used for doodads (decoration objects), players, monsters and really everything in the game except for Terrain and WMOs.
Overview
- Global texture list
- Global vertex list
- Position
- Blend weights
- Blend indices
- Normal
- Texture coordinates
- Per LOD (There are four of these in each model?)
- Vertex indices (vertices used by this LOD. Index into global vertex list)
- Face indices (three per triangle, these index into Vertex indices above)
- Vertex properties (one per vertex index, usage unknown)
- Submeshes (one per material, usually)
- Texture units (refer to a submesh, an index into the global texture list, and a texture unit number to bind to)
- Global bounding volume (used for collisions?)
- Animations, global sequences
- Skeleton
- Additional rendering information: colors, transparency, texture animations, blending modes etc.
- Effects: ribbons, particles
- Lights
- Cameras
Header
The header has mostly the layout of number-offset pairs, containing the number of a particular record in the file, and the offset. These appear at fixed places in the header. Record sizes are not specified in the file.
The unknown blocks have (mostly arbitrary) alphabetic names until we figure out what they are.
| Offset | Type | Description |
|---|---|---|
| 0x000 | char[4] | "MD20" |
| 0x004 | uint8[4] | Version (00 01 00 00 before Burning Crusade, 04 01 00 00 after BC) |
| 0x008 | uint32 | model name length (including \0) |
| 0x00C | uint32 | model name offset |
| 0x010 | uint32 | Model type? always 0, 1 or 3 (mostly 0) |
| 0x014 | uint32 | nGlobalSequences - number of global sequences |
| 0x018 | uint32 | ofsGlobalSequences - offset to global sequences |
| 0x01C | uint32 | nAnimations - number of animation sequences |
| 0x020 | uint32 | ofsAnimations - offset to animation sequences |
| 0x024 | uint32 | nC |
| 0x028 | uint32 | ofsC |
| 0x02C | uint32 | nD - always 201 or 203 depending on WoW client version |
| 0x030 | uint32 | ofsD |
| 0x034 | uint32 | nBones - number of bones |
| 0x038 | uint32 | ofsBones - offset to bones |
| 0x03C | uint32 | nF - bone lookup table |
| 0x040 | uint32 | ofsF |
| 0x044 | uint32 | nVertices - number of vertices |
| 0x048 | uint32 | ofsVertices - offset to vertices |
| 0x04C | uint32 | nViews - number of views (LOD versions?) 4 for every model |
| 0x050 | uint32 | ofsViews - offset to views |
| 0x054 | uint32 | nColors - number of color definitions |
| 0x058 | uint32 | ofsColors - offset to color definitions |
| 0x05C | uint32 | nTextures - number of textures |
| 0x060 | uint32 | ofsTextures - offset to texture definitions |
| 0x064 | uint32 | nTransparency - number of transparency definitions |
| 0x068 | uint32 | ofsTransparency - offset to transparency definitions |
| 0x06C | uint32 | nI - always 0 |
| 0x070 | uint32 | ofsI |
| 0x074 | uint32 | nTexAnims - number of texture animations |
| 0x078 | uint32 | ofsTexAnims - offset to texture animations |
| 0x07C | uint32 | nK |
| 0x080 | uint32 | ofsK |
| 0x084 | uint32 | nRenderFlags - number of blending mode definitions |
| 0x088 | uint32 | ofsRenderFlags - offset to blending mode definitions |
| 0x08C | uint32 | nY - bone lookup table |
| 0x090 | uint32 | ofsY |
| 0x094 | uint32 | nTexLookup - number of texture lookup table entries |
| 0x098 | uint32 | ofsTexLookup - offset to texture lookup table |
| 0x09C | uint32 | nTexUnits - texture unit definitions? |
| 0x0A0 | uint32 | ofsTexUnits |
| 0x0A4 | uint32 | nTransLookup - number of transparency lookup table entries |
| 0x0A8 | uint32 | ofsTransLookup - offset to transparency lookup table |
| 0x0AC | uint32 | nTexAnimLookup - number of texture animation lookup table entries |
| 0x0B0 | uint32 | ofsTexAnimLookup - offset to texture animation lookup table |
| 0x0B4 | 14 * float | float values ... ? (in range -1000...1000, mostly in -20...30) |
| 0x0EC | uint32 | nBoundingTriangles |
| 0x0F0 | uint32 | ofsBoundingTriangles |
| 0x0F4 | uint32 | nBoundingVertices |
| 0x0F8 | uint32 | ofsBoundingVertices |
| 0x0FC | uint32 | nBoundingNormals |
| 0x100 | uint32 | ofsBoundingNormals |
| 0x104 | uint32 | nO |
| 0x108 | uint32 | ofsO |
| 0x10C | uint32 | nP |
| 0x110 | uint32 | ofsP |
| 0x114 | uint32 | nQ |
| 0x118 | uint32 | ofsQ |
| 0x11C | uint32 | nLights - number of lights |
| 0x120 | uint32 | ofsLights - offset to lights |
| 0x124 | uint32 | nCameras - number of cameras |
| 0x128 | uint32 | ofsCameras - offset to cameras |
| 0x12C | uint32 | nCameraLookup |
| 0x130 | uint32 | ofsCameraLookup |
| 0x134 | uint32 | nRibbonEmitters - number of ribbon emitters |
| 0x138 | uint32 | ofsRibbonEmitters - offset to ribbon emitters |
| 0x13C | uint32 | nParticleEmitters - number of particle emitters |
| 0x140 | uint32 | ofsParticleEmitters - offset to particle emitters |
Skeleton and animation
Standard animation block
Many values that change with time, such as bone animation, etc. are specified using blocks like the following:
(size is 0x1C bytes)
| Offset | Type | Description |
|---|---|---|
| 0x00 | int16 | interpolation type |
| 0x02 | int16 | global sequence ID or -1 |
| 0x04 | uint32 | number of (int, int) interpolation ranges |
| 0x08 | uint32 | offset to interpolation ranges |
| 0x0C | uint32 | number of (int) timestamps |
| 0x10 | uint32 | offset to timestamps |
| 0x14 | uint32 | number of values |
| 0x18 | uint32 | offset to values |
The values can have various types depending on the kind of data required, like floats, vectors, quaternions etc.
Usually, if animation is used, interpolation ranges will be given for each animation in the global animation list. These ranges refer to keyframes that have given time and data values. The number of timestamps should therefore be equal to the number of data values.
If a global sequence is used, it means there is an implicit interpolation range across all values, and a time range from 0 to the proper global sequence timestamp.
If the interpolation type is 0, in some cases that might mean that no animation is given (like for bones), in other cases it means that a single constant data value should be used (like for colors and effect paramters)
Interpolation types:
| Value | Description |
|---|---|
| 0 | none / static value |
| 1 | linear |
| 2 | (spline?) in addition to values, in/out tangents are given |
Maybe the interpolation types here are the same as the WC3 MDL files? (bezier, etc)
Global sequences
nGlobalSequences 32-bit unsigned integers starting at ofsGlobalSequences
A list of timestamps that act as upper limits for global sequence ranges.
Animation sequences
List of animations present in the model.
Based on linghuye's work.
nAnimations 68-byte records starting at ofsAnimations
| Offset | Type | Description |
|---|---|---|
| 0x00 | uint32 | Animation ID in AnimationData.dbc |
| 0x04 | uint32 | Start timestamp |
| 0x08 | uint32 | End timestamp |
| 0x0C | float | moving speed (for walk/run animations?) |
| 0x10 | uint32 | 1 for non-looping? |
| 0x14 | uint32 | Flags? |
| 0x18 | uint32 | ? |
| 0x1C | uint32 | 0 ? |
| 0x20 | uint32 | Playback speed? |
| 0x24 | float[6] | Bounding box (2 vectors)? |
| 0x3C | float | radius? |
| 0x40 | int16 | Index of the next animation of the same Animation ID. (ex: PCs have multiple different copies of the Dance (id:69) animation). -1 if there are no other animations of this ID. |
| 0x44 | uint16 | ? |
Bones
nBones records of 0x6C bytes starting at ofsBones, followed by data referenced in these records.
The bone indices in the vertex definitions seem to index into this data.
Based on linghuye's work:
| Offset | Type | Description |
|---|---|---|
| 0x00 | uint32 | index into block F or -1 |
| 0x04 | uint32 | Flags |
| 0x08 | uint16 | Parent bone ID or -1 |
| 0x0A | uint16 | ? |
| 0x0C | uint32 | ? (unknown, found in Client 2.0 M2s) |
| 0x0C | AnimationBlock (float, float, float) | Animation data for translation |
| 0x28 | AnimationBlock (float, float, float, float) | Animation data for rotation |
| 0x44 | AnimationBlock (float, float, float) | Animation data for scaling |
| 0x60 | float[3] | Pivot point |
Keyframe data: float[3] vectors for translation and scaling, float[4] quaternions for rotation.
In Client 2.0 the Quaternion values are shorts, that need to be converted by Floats by Float = (Short > 0 ? Short-32767 : Short+32767)/32767.0; Backwards short to float scaling?
Flags:
| Value | Description |
|---|---|
| 8 | billboarded |
| 512 | transformed |
The billboarding bit is used for various things:
- Light halos around lamps must always face the viewer
- The cannonball stack model (in the Deadmines or Booty Bay), where each cannonball is a crude hemisphere, they always face the viewer to create the illusion of actual cannonballs (one wonders why Blizzard bothered with this, is a small sphere so hard to model? -_-)
Geometry and rendering
Vertices
48 bytes per vertex
Models, too, use a Z-up coordinate systems, so in order to convert to Y-up, the X, Y, Z values become (X, -Z, Y).
| Offset | Type | Description |
|---|---|---|
| 0x00 | float[3] | Position |
| 0x0C | uint8[4] | Bone weights (0 to 255) |
| 0x10 | uint8[4] | Bone indices (0 to nBones-1) |
| 0x14 | float[3] | Normal vector |
| 0x20 | float[2] | Texture coordinates |
| 0x28 | float[2] | 0? |
The fields marked with ? probably have to do with skeletal animation.
++++ Guest: Considering "standard" GPU sceletal animation (matrix palette, 4 weights/bones per vertex):
then "usually 255" should be an array of 4 unsigned chars defning the vertex weight for 4 bones
tehn "vertex group" should be an Array of 4 unsigned chars indexing 4 bones ++++
Views (LOD)
Every model has four of these, at 0x2C bytes each. I'm not sure if it is LOD because each one of them has the same amount of vertices for all four data sets. (???) First I thought this might be different vertex orderings from different views, to ease polygon sorting for transparent faces, but that's not it either. Hmm.
Starting at ofsViews, there are four View (LOD?) header records followed by the data for all four.
| Offset | Type | Description |
|---|---|---|
| 0x00 | uint32 | number of elements in the index list |
| 0x04 | uint32 | offset to the index list |
| 0x08 | uint32 | number of elements in the triangle list (this is 3* the number of triangles to be drawn) |
| 0x0C | uint32 | offset to the triangle list |
| 0x10 | uint32 | number of elements in the vertex property list |
| 0x14 | uint32 | offset to the vertex property list |
| 0x18 | uint32 | number of elements in the submesh list |
| 0x1C | uint32 | offset to the submesh list |
| 0x20 | uint32 | number of elements in the texture list |
| 0x24 | uint32 | offset to the texture list |
| 0x28 | uint32 | LOD distance or something? |
About Views(By ArtsyLee)
There are four views. They are bone LOD, not model LOD. There is a uint32 at '0x28' offset each of them which representatives the maximum bone amounts that can be used in view submesh. The first is 256, the second 75, the third 53, and the fourth is 21. The amounts of submesh may be different and usually the back ones have more than that in front ones.
You can choose which view to use according to the biggest shader registers your video card can support. For either high-end video card or low-end video card, you can be free to calculate the skeletal animations without cutting bones.
The shaders in the BLS files max out at 21 (0-20), so they are using view 4. The bone indices in BlockY are list of bone subsets needed each submesh. These bone subsets tell you which bones to load into the shaders for each submesh.
Indices
A 16-bit unsigned integer per index, specifies vertices from the global vertex list.
Triangles
For every triangle, three 16-bit unsigned ints. These refer to the index list given above, not the global vertex list.
Vertex properties
4 bytes per vertex. Usually 0? Purpose unknown.
Submeshes
32 bytes per submesh. 48 bytes per submesh after client 2.0 (Burning Crusade expansion)
| Offset | Type | Description |
|---|---|---|
| 0x00 | uint32 | Mesh part ID |
| 0x04 | uint16 | Starting vertex number |
| 0x06 | uint16 | Number of vertices |
| 0x08 | uint16 | Starting triangle index (that's 3* the number of triangles drawn so far) |
| 0x0A | uint16 | Number of triangle indices |
| 0x0C | uint16 | Number of elements in Block Y |
| 0x0E | uint16 | Starting index in Block Y |
| 0x10 | uint16 | Highest number of bones needed at one time in this Submesh |
| 0x12 | uint16 | Unknown |
| 0x14 | float[3] | 3 float values? |
| 0x20 | float[4] | 4 float values? (only in Client 2.0 M2s) |
Mesh part ID: for character models, each hairstyle/thick armor/etc is present in the mesh, so to render a character with a specific set of looks, some of the submeshes should be ommitted based on this ID.
Reference to Block Y: the base number seems to increase per LOD, and the numbers in Block Y, in turn, point to bones (?) - indices to Block E. Maybe this could be an indicator of bones used in a submesh, or an animation sequence or something?
About LOD: Creatures\AncientProtector\AncientProtector.m2 is the first model I found where the LOD levels have a significant difference. The 4th LOD level still has the same number of vertices/polygons, but it has more submeshes (more sophisticated animation/lighting or something? I dunno)
Texture units
More specifically, textures for each texture unit. Based on the current submesh number, one or two of these are used to determine the texture(s) to bind.
24 bytes per record.
| Offset | Type | Description |
|---|---|---|
| 0x00 | uint16 | Flags |
| 0x02 | int16 | Render order (used in skyboxes to ditch the need for depth buffering) |
| 0x04 | uint16 | Submesh index |
| 0x06 | uint16 | Submesh index (repeated?) |
| 0x08 | int16 | Color index or -1 |
| 0x0A | uint16 | Index into render flags table |
| 0x0C | uint16 | Texture unit number (0 or 1 - index into the texture unit lookup table) |
| 0x0E | uint16 | always 1? |
| 0x10 | uint16 | Texture to use (index into the texture lookup table) |
| 0x12 | uint16 | Texture unit number (repeated?) |
| 0x14 | uint16 | Transparency (index into transparency lookup table) |
| 0x16 | uint16 | Texture animation (index into the texture animation lookup table) |
Flags: usually 16 for static textures, and 0 for animated textures.
Render flags
nRenderFlags (uint16, uint16) pairs starting at ofsRenderFlags
The first value contains flags.
| Flag | Meaning |
|---|---|
| 0x01 | Unlit |
| 0x02 | Unfogged? |
| 0x04 | Two-sided (no backface culling if set) |
| 0x08 | ? |
| 0x10 | Disable z-buffer? |
The second value specifies the blending mode
| Value | Meaning |
|---|---|
| 0 | Opaque |
| 1 | Alpha testing only |
| 2 | Alpha blending |
| 3 | Additive? |
| 4 | Additive alpha? |
| 5 | Modulate? |
| 6 | Used in the Deeprun Tram subway glass, supposedly (src=dest_color, dest=src_color) (?) |
Most of these blend values are taken from the MDL docs, but they sort of work (like additive blending for light shafts and such)
Texture unit lookup table
nTexUnits 16-bit integers starting at ofsTexUnits. (values: -1, 0, 1)
For models that use multitexturing, this maps given texture unit numbers into actual texture unit numbers (0 or 1).
Values of -1 seem to mean environment mapping.
One model is of special interest, Creature/KelThuzad/KelThuzad.m2, which is the only one that has an nL of 3, and has three texture units specified for some of its submeshes. Sure enough, two of those map to 0 and 1, and one maps to -1.
More confusion thanks to my favorite "weird" model, World/Generic/Gnome/Passive Doodads/GnomeMachine/GnomeSubwayGlass.m2, which is the translucent, environment mapped glass tunnel in the Deeprun Tram. It only has a single value in this block, -1, which is used for the single texture layer in both render operations in the model. This and the magic with rendering flags/blend modes make up the neat transparent-reflective glass effect, but confuse me even more about how envmapping and such is handled. (and where it seems to get the bluish color from - is it in the model (no color blocks in this particular model), the wmo, a solid background color, or simply the result of the blending used?)
As a side note, on my (dated) system WoW does every texture unit in a single pass.
Colors and transparency
Colors
nColors records of 0x38 bytes starting at ofsColors, followed by data referenced in these records.
For some swirling portals and volumetric lights, these define vertex colors. Referenced from the Texture Unit blocks in the LOD part. Contains a separate timeline for transparency values. If no animation is used, the given value is assumed to be constant.
| Offset | Type | Description |
|---|---|---|
| 0x00 | AnimationBlock (float,float,float) | Data for RGB color values |
| 0x1C | AnimationBlock (short) | Data for opacity values |
Opacity values: 32767 = opaque, 0 = transparent.
Transparency lookup table
nTransLookup 16-bit integers starting at ofsTransLookup. (values: 0 to nH-1 ?)
Contains indices into the Transparency block. Used by the texture unit definitions in the LOD block.
Transparency
Specifies global transparency values (in addition to the values given in the Color block - I assume these are multiplied together eventually?)
nTransparency records of 0x1C bytes starting at ofsTransparency, followed by data referenced in these records.
| Offset | Type | Description |
|---|---|---|
| 0x00 | AnimationBlock (short) | Data for transparency values |
Transparency: 32767 for fully opaque, 0 for fully transparent
Textures
Textures are defined globally in a list, additionally, a lookup table is given, referenced during rendering, to select textures.
Texture lookup table
Just a list of 16-bit integers. nTexLookup items starting at ofsTexLookup.
Texture definitions
First is a list of nTextures texture definition records, 16 bytes per record, starting at ofsTextures. After it comes a string block with the texture filenames.
| Offset | Type | Description |
|---|---|---|
| 0x00 | uint32 | Texture type (aka replacable id) |
| 0x04 | uint16 | Unknown, usually 0 |
| 0x06 | uint16 | Flags |
| 0x08 | uint32 | Filename length |
| 0x0C | uint32 | Offset to filename |
Texture type is 0 for regular textures, nonzero for skinned textures (filename not referenced in the M2 file!) For instance, in the NightElfFemale model, her eye glow is a type 0 texture and has a file name, the other 3 textures have types of 1, 2 and 6. The texture filenames for these come from client database files:
DBFilesClient\CharSections.dbc
DBFilesClient\CreatureDisplayInfo.dbc
DBFilesClient\ItemDisplayInfo.dbc
(possibly more)
| Value | Meaning |
|---|---|
| 0 | Texture given in filename |
| 1 | Body + clothes |
| 2 | Cape |
| 6 | Hair, beard |
| 8 | Tauren fur |
| 11 | Skin for creatures |
| 12 | Skin for creatures #2 |
| 13 | Skin for creatures #3 |
Flags:
| Value | Meaning |
|---|---|
| 1 | Texture wrap X |
| 2 | Texture wrap Y |
Texture animation lookup table
nTexAnimLookup 16-bit integers starting at ofsTexAnimLookup. (values: -1, 0 to nTexAnims-1)
Contain indices into the texture animations list, or -1 meaning a static texture.
Texture animations
This block contains definitions for texture animations, for example, flowing water or lava in some models. The keyframe values are used in the texture transform matrix.
nTexAnims records of 0x54 bytes starting at ofsTexAnims, followed by data referenced in these records.
| Offset | Type | Description |
|---|---|---|
| 0x00 | AnimationBlock (float, float, float) | Translation |
| 0x1C | AnimationBlock (float, float, float ???) | Rotation? |
| 0x38 | AnimationBlock (float, float, float) | Scaling? |
The three subrecords specify texture transforms. Translation seems to work, producing nice flowing lava and waterfalls.
Effects
Ribbon emitters
nRibbonEmitters records of 0xDC bytes starting at ofsRibbonEmitters, followed by data referenced in these records.
These are called ribbon emitters in the strings buried in wow.exe. I guess these are some sort of ribbon-like visual effects similar to particles. In Warcraft 3 (of which the WoW engine is quite loosely based off of) there was also the possibility of ribbon-like effects in trails and flowing clothing. This is also how in WoW the feathers of the gryphons, hippogryphs, and bats flow in the wind as you rise and descend.
Note: the models that contain ribbon emitters and are viewable in the game world are: wisps in BFD, and energy trails in the COT (not the actual instance, but the entrance cave in Tanaris Desert). The only other models with ribbon emitters are spells and effects. Gryphons/etc don't have ribbon emitters, the feathers are part of their geometry.
What about the ribbons in the model of the Ziggurat (or spirit tower, haven't played warcraft 3 in awhile) that seem to swirl around the Ziggurat's crystal in Stratholme? (It's probably a good idea to play this in WoW to see what i'm talking about.) -DG
- must be one of those server-side specified objects because I can't see them in wowmapview.
The records have the following structure:
| Offset | Type | Description |
|---|---|---|
| 0x00 | int32 | ID? always -1 |
| 0x04 | int32 | Bone ID |
| 0x08 | float[3] | position |
| 0x14 | int32 | Number of texture refs |
| 0x18 | int32 | Offset to texture refs (ints) |
| 0x1C | int32 | Number of ints #2 |
| 0x20 | int32 | Offset to list of ints #2 |
| 0x24 | AnimationBlock (float, float, float) | Color |
| 0x40 | AnimationBlock (short) | Opacity |
| 0x5C | AnimationBlock (float) | Height above |
| 0x78 | AnimationBlock (float) | Height below |
| 0x94 | float | Something about length/lifespan? |
| 0x98 | float | Something about length/lifespan? |
| 0x9C | float | Usually 0 |
| 0xA0 | short[2] | (blending modes maybe?) (1,1) |
| 0xA4 | AnimationBlock (int) | ? (always 0) |
| 0xC0 | AnimationBlock (int) | ? (always 1) |
Parameters from the MDL format that are probably in here somewhere: life span, emission rate, rows, cols ...?
Particle emitters
nparticleEmitters records of 0x1F8 bytes starting at ofsParticleEmitters, followed by data referenced in these records.
The records have the following structure:
| Offset | Type | Description |
|---|---|---|
| 0x000 | int32 | id (always -1?) |
| 0x004 | int32 | flags |
| 0x008 | float[3] | position |
| 0x014 | int16 | Bone ID |
| 0x016 | int16 | Texture ID |
| 0x018 | uint32 | Number of ints referenced #1 |
| 0x01C | uint32 | Offset to list of ints #1 |
| 0x020 | uint32 | Number of ints referenced #2 |
| 0x024 | uint32 | Offset to list of ints #2 |
| 0x028 | int16 | Blending mode |
| 0x02A | int16 | Emitter type |
| 0x02C | int16 | Particle type |
| 0x02E | int16 | Texture tile rotation (-1,0,1) |
| 0x030 | int16 | Rows on texture |
| 0x032 | int16 | Columns on texture |
| 0x034 | AnimationBlock (float) [10] | Parameters |
| 0x14C | float[36] | unknown float values - more parameters? |
| 0x14C | float | Midpoint in lifespan? (0 to 1) |
| 0x150 | uint32[3] | ARGB colors (start, mid, end) |
| 0x15C | float[3] | Particle sizes (start, mid, end) |
| 0x168 | short[10] | Indices into the tiles on the texture? |
| 0x17C | float[3] | Unknown |
| 0x188 | float[3] | Something about particle scaling? Hm. |
| 0x194 | float | Slowdown |
| 0x198 | float | Particle rotation |
| 0x19C | float[10] | Unknown (usually all 0) |
| 0x1C4 | float[6] | Unknown, usually (2.5, 0.7, 7, 0.9, 0, 0) |
| 0x1DC | AnimationBlock (int) | unknown |
About slowdown: for nonzero values, instead of travelling linearly the particles seem to slow down sooner. I can't work out the exact function but for a value of, say, 10, the particles pretty much stay in place. Not the same effect as gravity, though. Update: thanks to nsz for the formula. Speed is multiplied by exp( -slowdown * t )
About particle rotation: 0 for none, 1 to rotate the particle 360 degrees throughout its lifetime.
The ten animatable particle parameters are:
| Number | Description |
|---|---|
| 0 | Emission speed |
| 1 | Speed variation (range: 0 to 1) |
| 2 | Spread? (range: 0 to pi) |
| 3 | ? (range: 0 to 2*pi) |
| 4 | Gravity |
| 5 | Lifespan |
| 6 | Emission rate |
| 7 | Emission area length |
| 8 | Emission area width |
| 9 | Gravity? (much stronger) |
Emitter types:
| Value | Description |
|---|---|
| 1 | Plane (rectangle) |
| 2 | Sphere |
| 3 | (Spline? can't be bothered to find one) |
Particle types:
| Value | Description |
|---|---|
| 0 | "normal" particle |
| 1 | large quad from the particle's origin to its position (used in Moonwell water effects) |
| 2 | seems to be the same as 0 (found some in the Deeprun Tram blinky-lights-sign thing) |
Flags:
| Value | Description |
|---|---|
| 0x1000 | do not billboard (used for some water wake ripple effects) |
Miscellaneous
Bounding volumes
For some models a simplified bounding volume is given. This is probably used for collision detection?
The nBoundingVertices vertices used by the bounding volume's faces start at ofsBoundingVertices. Each vertex is given by 3 floats.
The faces start at ofsBoundingTriangles. The number nBoundingTriangles once again contains the number of indices used, so divide by 3 to get the number of triangles. Each triangle is given by 3 16-bit integers that index the bounding vertices list. Each face also has a corresponding normal vector (?), these start at ofsBoundingNormals. Therefore, it should be true that nBoundingNormals * 3 = nBoundingTriangles.
Lights
nLights records of 0xD4 bytes starting at ofsLights, followed by data referenced in these records.
The records have the following structure:
| Offset | Type | Description |
|---|---|---|
| 0x00 | uint16 | type |
| 0x02 | uint16 | Bone ID |
| 0x04 | float[3] | coordinates |
| 0x10 | AnimationBlock (float, float, float) | Ambient color |
| 0x2C | AnimationBlock (float) | Ambient intensity |
| 0x48 | AnimationBlock (float, float, float) | Diffuse color |
| 0x64 | AnimationBlock (float) | Diffuse intensity |
| 0x80 | AnimationBlock (float) | Attenuation start? |
| 0x9C | AnimationBlock (float) | Attenuation end? |
| 0xB8 | AnimationBlock (int) | usually 1 |
Some light types:
| Value | Description |
|---|---|
| 0 | Directional |
| 1 | Point light |
Cameras
These blocks seem to be present in the "flyby" camera models which completely lack geometry, and the main menu backdrop models which are supposed to have a fixed camera. Additionally, characters and monsters also have this block (I wonder why)
nCameras records of 0x7C bytes starting at ofsCameras, followed by data referenced in these records.
| Offset | Type | Description |
|---|---|---|
| 0x00 | int32 | id or maybe a parent bone? usually -1 |
| 0x04 | float | FOV |
| 0x08 | float | Far clipping plane |
| 0x0C | float | Near clipping plane |
| 0x10 | AnimationBlock (float, float, float) | Translation for position? |
| 0x2C | float[3] | Position? |
| 0x38 | AnimationBlock (float, float, float) | Translation for target? |
| 0x54 | float[3] | Target? |
| 0x60 | AnimationBlock (float) | Rotation or roll? (usually 0 or 2*pi) |
For static cameras the animatable data items are all 0. The FOV is not in radians, but some weird scale... Multiplying by about 35 seems to give correct-ish results in degrees.
The reason that non-mainmenu and non-geometry M2s have cameras (atleast in Warcraft 3) was so you could see the unit's portrait. In WoW, these models have cameras so that when you enter the Character menu (press "C") you see your character regardless of what model you currently have. Also, I believe that one of the main menu models has two cameras in it (I believe it's the orc one...).-DG
Oh, that's right. Neat. I added menu backdrops using these cameras. Obviously it's still missing particle emitters, but that can be fixed eventually... - Z.
Camera lookup table
Lookup table for cameras? It seems to be persent only if a camera is present too.
nCameraLookup 16-bit integers starting at ofsCameraLookup. (values: 0..nCameras-1)
Unknown blocks
Block C
nC 16-bit integers starting at ofsC (values: -1 to nAnimations-1)
Lookup table for animations?
Block D
nD records of (int16, int16) starting at ofsD
Maybe a lookup table for animations? Since the numbers happen to be in fixed positions. The first short seems to increase with the position for models with all animations (like characters), the second seems to be flags or a modifier? Or something.
Block F
nF 16-bit integers starting at osfF (values: -1 to nBones-1)
nF is 27 for most characters/models with lots of animation. For static models it's 1. Intermediate values are rare.
Lookup table for bones?
Block K - Replacable texture lookup
nK 16-bit integers starting at ofsK. (values: -1 or 0 to nTex-1)
A reverse lookup table for 'replaced' textures, mapping replacable ids to texture indices or -1. Only goes up to the maximum id used in the model.
Block Y
nY 16-bit integers starting at ofsY. (values: 0 to nBones-1)
Lookup table for bones that transform geometry? Referenced in the various geoset definitions.
Block O - Attachments
nO records of 0x30 bytes starting at ofsO, followed by data referenced in these records.
| Offset | Type | Description |
|---|---|---|
| 0x00 | uint32 | id (position of the record in block P) |
| 0x04 | uint32 | bone |
| 0x08 | float[3] | position |
| 0x14 | AnimationBlock (int) | Data |
Much like Block Q this one also specifies a bunch of locations on the body - hands, shoulders, head, back, knees etc. Maybe this one is actually used to put items on a character. This seems very likely as this block also contains positions for sheathed weapons, a shield, etc.
Here's the list of position slots (by ID, or index in block P) for character models:
| ID | Description | ID | Description | ID | Description |
|---|---|---|---|---|---|
| 0 | Left wrist (shield) | 12 | Back | 24 | - |
| 1 | Right palm | 13 | - | 25 | - |
| 2 | Left palm | 14 | - | 26 | Right back sheath |
| 3 | Right elbow | 15 | Bust | 27 | Left back sheath |
| 4 | Left elbow | 16 | Bust | 28 | Middle back sheath |
| 5 | Right shoulder | 17 | Face | 29 | Belly |
| 6 | Left shoulder | 18 | Above character | 30 | Left back |
| 7 | Right knee | 19 | Ground | 31 | Right back |
| 8 | Left knee | 20 | Top of head | 32 | Left hip sheath |
| 9 | - | 21 | Left palm | 33 | Right hip sheath |
| 10 | - | 22 | Right palm | 34 | Bust |
| 11 | Helmet | 23 | - | 35 | Right palm |
For weapons, usually 5 of these points are present, which correspond to the 5 columns in ItemVisuals.dbc, which in turn has 5 models from ItemVisualEffects.dbc. This is for the weapon glowy effects and such. The effect ID is the last column in ItemDisplayInfo.cdb. (I should document these cdb files properly somewhere, sigh)
Block P
nP 16-bit integers starting at ofsP. (values: -1, 0 to nO-1)
Lookup table for whatever is in block O, I guess :)
Block Q - Attachments
nQ records of 0x2C bytes starting at ofsQ, followed by data referenced in these records.
This might be definitions for weapon attachment slots or something like that... Mostly present on characters, creatures and items.
| Offset | Type | Description |
|---|---|---|
| 0x00 | char[4] | some kind of ID, starts with '$' |
| 0x04 | uint32 | database ID? |
| 0x08 | uint32 | bone ID |
| 0x0C | float[3] | floats (coordinates?) |
| 0x18 | int16[2] | animation block header? |
| 0x1C | uint32 | number of (int, int) ranges |
| 0x20 | uint32 | offset to (int, int) ranges |
| 0x24 | uint32 | number of (int) values |
| 0x28 | uint32 | offset to (int) values |
The records seem to contain a truncated animation block. The int values look like timestamps, and there are no data values. The ranges reference more integers than are given in the list (there's always 1 less item). Weird.
Each record specifies a transformation matrix for attaching another model to a certain point. By translating to the attachment position and then applying the transform matrix of the parent bone, the other model at its default origin will snap right into place. This is how weapons are affixed to the hands, helmets and shoulder armor attached, and spell effects are also positioned this way.
Some position identifiers:
| ID | Description |
|---|---|
| $TRD | Crotch |
| $CCH | Bust |
| $BTH | In front of head |
| $CHD | Head |
| $SHL, $SHR | Left/right shoulder |
| $CSL, $CSR | Left/right hand |
| $BWP, $BWR | Right hand (for weapons maybe?) |
The rest are either copies of the crotch position, or down on the floor. I suppose these are used to position spell effects (like a levelup flash or something) and damage effects.

