M2
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.
This file describes their structure in the second expansion "Wrath of the Lich King". It is up to date for build 8820 and most likely following ones. It has been made by Xayo and schlumpf. All structures have been overlooked by schlumpf. Enjoy it.
--schlumpf_ 00:50, 23 August 2008 (CEST)
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.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x000 | char[4] | Magic | "MD20" | |
0x004 | uint32 | Version | 0x80100000 (first digit of the build the format was last updated) | |
0x008 | uint32 | lName | Length of the model's name | |
0x00C | uint32 | ofsName | Offset to the name | |
0x010 | uint32 | GlobalModelFlags | (0,1,3 seen), connected to field 4 of CreatureModelData.dbc? | |
0x014 | uint32 | nGlobalSequences | ||
0x018 | uint32 | ofsGlobalSequences | A list of timestamps. | |
0x01C | uint32 | nAnimations | ||
0x020 | uint32 | ofsAnimations | Information about the animations in the model. | |
0x024 | uint32 | nAnimationLookup | ||
0x028 | uint32 | ofsAnimationLookup | Mapping of global IDs to the entries in the Animation sequences block. | |
0x02C | uint32 | nBones | ||
0x030 | uint32 | ofsBones | Information about the bones in this model. | |
0x034 | uint32 | nKeyBoneLookup | ||
0x038 | uint32 | ofsKeyBoneLookup | Lookup table for key skeletal bones. | |
0x03C | uint32 | nVertices | ||
0x040 | uint32 | ofsVertices | Vertices of the model. | |
0x044 | uint32 | nViews | Views (LOD) are now in .skins. | |
0x048 | uint32 | nColors | ||
0x04C | uint32 | ofsColors | Color definitions. | |
0x050 | uint32 | nTextures | ||
0x054 | uint32 | ofsTextures | Textures of this model. | |
0x058 | uint32 | nTransparency | ||
0x05C | uint32 | ofsTransparency | Transparency of textures. | |
0x060 | uint32 | nTextureanimations | ||
0x064 | uint32 | ofsTextureanimations | ||
0x068 | uint32 | nTexReplace | ||
0x06C | uint32 | ofsTexReplace | Replaceable Textures. | |
0x070 | uint32 | nRenderFlags | ||
0x074 | uint32 | ofsRenderFlags | Blending modes / render flags. | |
0x078 | uint32 | nBoneLookupTable | ||
0x07C | uint32 | ofsBoneLookupTable | A bone lookup table. | |
0x080 | uint32 | nTexLookup | ||
0x084 | uint32 | ofsTexLookup | The same for textures. | |
0x088 | uint32 | nTexUnits | ||
0x08C | uint32 | ofsTexUnits | And texture units. Somewhere they have to be too. | |
0x090 | uint32 | nTransLookup | ||
0x094 | uint32 | ofsTransLookup | Everything needs its lookup. Here are the transparencies. | |
0x098 | uint32 | nTexAnimLookup | ||
0x09C | uint32 | ofsTexAnimLookup | Wait. Do we have animated Textures? Wasn't ofsTexAnims deleted? oO | |
0x0A0 | float | theFloats[14] | Noone knows. Meeh, they are here. | |
0x0D8 | uint32 | nBoundingTriangles | ||
0x0DC | uint32 | ofsBoundingTriangles | Our bounding volumes. Similar structure like in the old ofsViews. | |
0x0E0 | uint32 | nBoundingVertices | ||
0x0E4 | uint32 | ofsBoundingVertices | ||
0x0E8 | uint32 | nBoundingNormals | ||
0x0EC | uint32 | ofsBoundingNormals | ||
0x0F0 | uint32 | nAttachments | ||
0x0F4 | uint32 | ofsAttachments | Attachments are for weapons etc. | |
0x0F8 | uint32 | nAttachLookup | ||
0x0FC | uint32 | ofsAttachLookup | Of course with a lookup. | |
0x100 | uint32 | nAttachments_2 | ||
0x104 | uint32 | ofsAttachments_2 | And some second block. | |
0x108 | uint32 | nLights | ||
0x10C | uint32 | ofsLights | Lights are mainly used in loginscreens but in wands and some doodads too. | |
0x110 | uint32 | nCameras | ||
0x114 | uint32 | ofsCameras | The cameras are present in most models for having a model in the Character-Tab. | |
0x118 | uint32 | nCameraLookup | ||
0x11C | uint32 | ofsCameraLookup | And lookup-time again. | |
0x120 | uint32 | nRibbonEmitters | ||
0x124 | uint32 | ofsRibbonEmitters | Things swirling around. See the CoT-entrance for light-trails. | |
0x128 | uint32 | nParticleEmitters | ||
0x12C | uint32 | ofsParticleEmitters | Spells and weapons, doodads and loginscreens use them. Blood dripping of a blade? Particles. |
Skeleton and animation
Standard animation block
- Many values that change with time are specified using blocks like the following.
Please see this for information.
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)
Global sequences
- nGlobalSequences 32-bit unsigned integers starting at ofsGlobalSequences.
A list of timestamps that act as upper limits for global sequence ranges.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint32 | Timestamp | Entry |
Animation sequences
- nAnimations 0x40-byte records starting at ofsAnimations.
List of animations present in the model.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | AnimationID | Animation id in AnimationData.dbc | |
0x02 | uint16 | SubAnimationID | Sub-animation id: Which number in a row of animations this one is. | |
0x04 | uint32 | Length | The length (timestamps) of the animation. I believe this actually the length of the animation in milliseconds. | |
0x08 | float | MovingSpeed | As 2.x says: moving speed for walk/run animations. | |
0x0C | uint32 | Flags | Most likely. All flags I saw: 0b01101101 | |
0x10 | uint32 | Flags 2 | Only the first 4 bits are the actual flags. The rest is 1. Seen flags: 0,3,6,7 | |
0x14 | uint32 | Unknown 1 | These two are connected. Most of the time, they are 0. | |
0x18 | uint32 | Unknown 2 | But if there is data in one, there is data in both of them. | |
0x1C | uint32 | PlaybackSpeed | Values: 0, 50, 100, 150, 200, 250, 300, 350, 500. | |
0x20 | float | BoundingBox[6] | A Bounding Box made out of 2 vectors. | |
0x38 | float | Radius | The radius. | |
0x3C | int16 | NextAnimation | Id of the following animation of this AnimationID, points to an Index or is -1 if none. | |
0x3E | uint16 | Index | Id in the list of animations. |
Animation Lookup
- nAnimationLookup in 16-bit shorts starting at ofsAnimationLookup.
Lookup table for Animations in AnimationData.dbc.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | AnimationID | Index at ofsAnimations which represents the animation in AnimationData.dbc. -1 if none. |
Bones
- nBones records of 0x58 bytes starting at ofsBones.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | int32 | AnimationSeq | Index into Animation sequences or -1. | |
0x04 | uint32 | Flags | Only known flags: 8 - billborded and 512 - transformed | |
0x08 | int16 | ParentBone | Parent bone ID or -1 if there is none. | |
0x0C | uint16 | GeosetID | A geoset for this bone. | |
0x10 | ABlock | Translation | An animationblock for translation. Should be 3*floats. | |
0x24 | ABlock | Rotation | An animationblock for rotation. Should be 4*shorts, see Quaternion values and 2.x. | |
0x38 | ABlock | Scaling | An animationblock for scaling. Should be 3*floats. | |
0x4C | float | PivotPoint[3] | The pivot point of that bone. Its a vector. |
The bone indices in the vertex definitions seem to index into this data.
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.
Bone Lookup Table
- nBoneLookupTable 16-bit integers starting at ofsBoneLookupTable. (values: 0 to nBones-1)
Lookup table for bones that transform geometry. Referenced in the various geoset definitions.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | Bone | Which bone. -1 if there is none. |
Key-Bone Lookup
- nKeyBoneLookup 16-bit shorts starting at ofsKeyBoneLookup.
Its a lookup table for key skeletal bones like hands, arms, legs, etc. nKeyBoneLookup is 21 for the most models. At static models it is mostly 1.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | Bone | Which bone. -1 if there is none. |
Geometry and rendering
Vertices
- nVertices entries of 48 bytes per vertex (at ofsVertices)
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | float | Position[3] | A vector to the position of the vertex. | |
0x0C | uint8 | BoneWeight[4] | The vertex weight for 4 bones. | |
0x10 | uint8 | BoneIndices[4] | Which are referenced here. | |
0x14 | float | Normal[3] | A normal vector. | |
0x20 | float | TextureCoords[2] | Coordinates for a texture. | |
0x28 | float | Unknown[2] | Null? |
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).
I was wrong. --Hergonan
Views (LOD)
Since there is no ofsViews anymore, this data is now stored in .skin files. More information about them here: M2/WotLK/.skin.
Render flags
- nRenderFlags (uint16, uint16) pairs starting at ofsRenderFlags
Offset | Description | |
---|---|---|
0x00 | Flags | |
0x02 | Blending mode |
- Flags:
Flag | Meaning | |
---|---|---|
0x01 | Unlit | |
0x02 | Unfogged? | |
0x04 | Two-sided (no backface culling if set) | |
0x08 | ? (probably billboarded) | |
0x10 | Disable z-buffer? |
- 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.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | int16 | Unit | Values are -1, 0 or 1. See below. |
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 nTexUnits 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.
Replacable texture lookup
- nTexReplace 16-bit integers starting at ofsTexReplace.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | int16 | TextureID | Array indices (0 to n-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.
Colors and transparency
Colors
- nColors records starting at ofsColors, followed by data referenced in these records.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | ABlock | Color | Three floats. One for each color. | |
0x14 | ABlock | Alpha | A short for the alpha value. 0 - transparent, 0xFFFF - opaque. |
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 constant.
Transparency lookup table
- nTransLookup 16-bit integers starting at ofsTransLookup.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | TransparencyId | Array indices (0 to n-1). |
Contains indices into the Transparency block. Used by the texture unit definitions in the LOD block.
Transparency
- nTransparency AnimationBlocks at ofsTransparency, followed by data referenced in these records.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x10 | ABlock | Alpha | A short for the alpha value. 0 - transparent, 0xFFFF - opaque. |
Specifies global transparency values in addition to the values given in the Color block. I assume these are multiplied together eventually.
Textures
- Textures are defined globally in a list, additionally, a lookup table is given, referenced during rendering, to select textures.
Texture lookup table
- nTexLookup items starting at ofsTexLookup.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | TextureId | Array indices (0 to n-1). |
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, which seems to have the same structure like the MOTX-Chunk in WMOs(see WMO), the "empty aligtments" are just the zeros were non "0 - hardcoded" type textures point too.
Offset | Type | Name | Description |
---|---|---|---|
0x000 | uint32 | Type | The type of the texture. See below. |
0x002 | uint16 | Unknown | What do I know? Most likely just padding. |
0x004 | uint16 | Flags | Textures have some flags. See below too. |
0x008 | uint32 | lenFilename | Here is the length of the filename, if the type is not "0 - hardcoded" then it's just 1 and points to a zero |
0x00C | uint32 | ofsFilename | And the offset to the filename. |
Texture Types
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 #1 | |
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 items starting at ofsTexAnimLookup.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | AnimatedTextureId | Array indices (0 to n-1). -1 for 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 0x9C bytes starting at ofsRibbonEmitters, followed by data referenced in these records.
The records have the following structure:
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint32 | Unknown | Always (as I have seen): -1. | |
0x04 | uint32 | BoneID | A bone to attach to. | |
0x08 | float | Position[3] | And a position, relative to that bone. | |
0x14 | int32 | nTextures | Number of referenced textures. | |
0x18 | int32 | ofsTextures | Offset to the referenced textures. | |
0x1C | int32 | nUnkRef | Number of some referenced integers. | |
0x20 | int32 | ofsUnkRef | Offset to the integers. | |
0x24 | ABlock | Color | A color in three floats. | |
0x38 | ABlock | Opacity | And an alpha value in a short, where: 0 - transparent, 0xFFFF - opaque. | |
0x4C | ABlock | Above | The height above. | |
0x60 | ABlock | Below | The height below. Do not set these to the same! | |
0x74 | float | Resolution | This defines how smooth the ribbon is. A low value may produce a lot of edges. | |
0x78 | float | Length | The length aka Lifespan. | |
0x7C | float | Gravity | some sort of; values seen: 0.5 and -0.5 | |
0x80 | short | UnknownShorts[2] | Maybe these are blending modes. Pairs of {1,1} for example. | |
0x84 | ABlock | UnknownABlock1 | The value on these two are integers. First one is 0 all the time. | |
0x98 | ABlock | UnknownABlock2 | And the second one is 1. |
Some models that contain ribbon emitters and are viewable in the game world are: Wisps in BFD, Al'ar the Phoenix in Tempest Keep and any other phoenix models and the energy trails in the COT (not the actual instance, but the entrance cave in Tanaris Desert). Other models with ribbon emitters are spells and effects.
Parameters from the MDL format that are probably in here somewhere: emission rate, rows, cols ...?
Particle emitters
- nParticleEmitters records starting at ofsParticleEmitters, followed by data referenced in these records.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x000 | uint32 | Unknown | Always (as I have seen): -1. | |
0x004 | uint32 | Flags | There are more flags now. Known: 0x1000 - do not billboard. | |
0x008 | float | Position[3] | The position. Relative to the following bone. | |
0x014 | uint16 | Bone | The bone its attached to. | |
0x016 | uint16 | Texture | And the texture that is used. | |
0x018 | uint32 | nReferences1 | The first number of integers referenced. | |
0x01C | uint32 | ofsReferences1 | And the matching offset. | |
0x020 | uint32 | nReferences2 | The second number of integers referenced. | |
0x024 | uint32 | ofsReferences2 | And the matching offset again. | |
0x028 | uint8 | BlendingType | A blending type for the particle. Maybe the same as at the renderflags. | |
0x029 | uint16 | EmitterType | 1 - Plane (rectangle), 2 - Sphere, 3 - Spline? (can't be bothered to find one) | |
0x02B | uint16 | ParticleType | Found below. | |
0x02D | uint8 | Padding | ||
0x02E | uint16 | TextureTileRotation | Rotation for the texture tile. (Values: -1,0,1) | |
0x030 | uint16 | TextureRows | How many different frames are on that texture? People should learn what rows and cols are. | |
0x032 | uint16 | TextureCols | Its different everywhere. I just took it random. | |
0x034 | ABlock | EmissionSpeed | All of the following blocks should be floats. | |
0x048 | ABlock | SpeedVariation | Variation in the flying-speed. (range: 0 to 1) | |
0x05C | ABlock | VerticalRange | Drifting away vertically. (range: 0 to pi) | |
0x070 | ABlock | HorizontalRange | They can do it horizontally too! (range: 0 to 2*pi) | |
0x084 | ABlock | Gravity | Fall down, apple! | |
0x098 | ABlock | Lifespan | Everyone has to die. | |
0x0AC | uint32 | unknownPadding | I don't know what these two values should do.. | |
0x0B0 | ABlock | EmissionRate | Stread your particles, emitter. | |
0x0C4 | uint32 | unknownPadding2 | It could have been an array without them.. | |
0x0C8 | ABlock | EmissionAreaLength | Well, you can do that in this area. | |
0x0DC | ABlock | EmissionAreaWidth | ||
0x0F0 | ABlock | Gravity2 | A second gravity? Its strong. | |
0x104 | FBlock | ParticleColor | This one points to 3 floats defining red, green and blue. | |
0x114 | FBlock | ParticleOpacity? | Looks like opacity (short) --Igor; Most likely they all have 3 timestamps for {start, middle, end}. | |
0x124 | FBlock | ParticleSizes | It carries two floats per key. (x and y scale) | |
0x134 | int32 | UnknownFields[10] | Always 0 as far as I see. Indices into the tiles on the texture? | |
0x15C | float | UnknownFloats1[3] | They have something to do with the spread. | |
0x168 | float | Scale[3] | Wheey, its the scale! | |
0x174 | float | Slowdown | Slowpoke is slow. | |
0x178 | float | UnknownFloats2[2] | More unknown fields. | |
0x180 | float | Rotation | As a single value? Most likely only one axis then.. | |
0x184 | float | UnknownFloats3[2] | More unknown fields. | |
0x18C | float | Rot1[3] | Model Rotation 1 | |
0x198 | float | Rot2[3] | Model Rotation 2 | |
0x1A4 | float | Trans[3] | Model Translation | |
0x1B0 | float | UnknownFloats4[6] | Unknown, unknown, unknown, unknown, unknown... | |
0x1C8 | ABlock | EnabledIn | Maybe this has integers again. Has been in the earlier documentations. Enabled Anim Block. |
About Slowdown: For a non-zero 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. Speed is multiplied by exp( -slowdown * t ).
About particle rotation: 0 for none, 1 to rotate the particle 360 degrees throughout its lifetime.
Rotation can be a float value greater or less one. Results look better if use it as a "phase shift": particle_rotate = randfloat(-sys->rotation * pi, sys->rotation * pi); --Igor
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) |
ParticleType is always 0 and, maybe, now (Flags & 0x40000) != 0 means "particles from origin to position". --Igor
The Fake-AnimationBlock
- Its pretty much like the real one but without the "header".
Offset | Type | Name | Description | |
---|---|---|---|---|
0x000 | uint32 | nTimestamps | The number of timestamps. | |
0x004 | uint32 | ofsTimestamps | And the offset to them. | |
0x008 | uint32 | nKeys | The same number again. This time its the number of Keys / Values. | |
0x00C | uint32 | ofsKeys | And their offset. |
But it doesn't point to a substructure like the real one.
Miscellaneous
Bounding volumes
- For some models a simplified bounding volume is given. This is probably used for collision detection?
Vertices
- nBoundingVertices vertices at ofsBoundingVertices.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | float | Coordinate[3] | This defines a vertex in x,y and z values. |
Triangles
- nBoundingTriangles triples of uint16s at ofsBoundingTriangles.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | Index[3] | Specifies three vertices of the list above to build a triangle. |
The number nBoundingTriangles once again contains the number of indices used, so divide by 3 to get the number of triangles.
Normals
- nBoundingNormals normals at ofsBoundingNormals.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | float | Vector[3] | This defines the normal for each vertex. |
Each vertex also has a corresponding normal vector. Therefore, it should be true that nBoundingVertices = nBoundingNormals = nBoundingTriangles / 3.
Lights
- nLights records of 0x9A bytes starting at ofsLights, followed by data referenced in these records.
The records have the following structure:
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | Type | Types are listed below. | |
0x02 | int16 | Bone | If its attached to a bone, this is the bone. Else here is a nice -1. | |
0x04 | float | Position[3] | Where is this light? | |
0x10 | ABlock | AmbientColor | The ambient color. Three floats for RGB. | |
0x24 | ABlock | AmbientIntensity | A float for the intensity. | |
0x38 | ABlock | DiffuseColor | The diffuse color. Three floats for RGB. | |
0x4C | ABlock | DiffuseIntensity | A float for the intensity again. | |
0x60 | ABlock | AttenuationStart | This defines, where the light starts to be. | |
0x74 | ABlock | AttenuationEnd | And where it stops. | |
0x88 | ABlock | Unknown | Its an integer and usually 1. |
Two light types:
Value | Description | |
---|---|---|
0 | Directional | |
1 | Point light |
Cameras
- nCameras records of 0x64 bytes starting at ofsCameras, followed by data referenced in these records.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint32 | BoneID | -1 if attached to none? Its also documented as id elsewhere. | |
0x04 | float | FOV | No radians, no degrees. Multiply by 35 to get degrees. | |
0x08 | float | FarClipping | Where it stops to be drawn. | |
0x0C | float | NearClipping | Far and near. Both of them. | |
0x10 | ABlock | TranslationPos | How the cameras position moves. Should be 3*floats. | |
0x24 | float | Position[3] | Where the camera is located. | |
0x30 | ABlock | TranslationTar | How the target moves. Should be 3*floats. | |
0x44 | float | Target[3] | Where the camera points to. | |
0x50 | ABlock | Scaling | The camera can have some roll-effect. Its 0 to 2*Pi. |
These blocks are 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. The reason that non-mainmenu and non-geometry M2s have cameras was is you can see the unit's portrait.
Camera lookup table
- nCameraLookup 16-bit integers starting at ofsCameraLookup.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | CameraId | All cameras. Array indices (0 to n-1). |
Attachments
Block 1
- nAttachments records of the following structure starting at ofsAttachments, followed by data referenced in these records.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint32 | Id | Just an id. Is referenced in the lookup-block below too. | |
0x04 | uint32 | Bone | Somewhere it has to be attached. | |
0x08 | float | Position[3] | Relative to that bone of course. | |
0x14 | ABlock | Data | Its an integer in the data. It has been 1 on all models I saw. Whatever. |
This block specifies a bunch of locations on the body - hands, shoulders, head, back, knees etc. It is 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 / Mountpoint | 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.dbc. They take the ids 0 to 4. Mounts take the id 0 for their rider.
Attachment Lookup
- nAttachLookup 16-bit integers starting at ofsAttachLookup.
Offset | Type | Name | Description | |
---|---|---|---|---|
0x00 | uint16 | Attachment | From 0 to nAttachments-1 |
Block 2
- nAttachments_2 records of 0x14 bytes starting at ofsAttachments_2, 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 | Name | Description | |
---|---|---|---|---|
0x00 | char | Identifier[4] | Some kind of ID, starts with '$'. Some identifiers below. | |
0x04 | uint32 | Id | Just an id. Is referenced in the lookup-block below too. | |
0x08 | uint32 | Bone | Somewhere it has to be attached. | |
0x0C | float | Position[3] | Relative to that bone of course. | |
0x08 | uint16 | InterpolationType | This is some fake-AnimationBlock. | |
0x0A | uint16 | GlobalSequence | Built up like a real one but without values. What the fuck? | |
0x0C | uint32 | nTimestamps | See the documentation on AnimationBlocks at this topic. | |
0x10 | uint32 | ofsTimestamps | Will most likely be the same shit again as in the real ones. |
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.