M2: Difference between revisions

From wowdev
Jump to navigation Jump to search
Line 566: Line 566:
====UV-Animation lookup table ====
====UV-Animation lookup table ====
*'''nTexAnimLookup items starting at ofsTexAnimLookup.'''
*'''nTexAnimLookup items starting at ofsTexAnimLookup.'''
{| style="background:#FCFCFC; color:black"
struct
! width="70" | Offset !! width="70" | Type !! width="90" | Name !! width="500" | Description
{
|-
  uint16_t anim_texture_id; // -1 for static
| 0x00 || uint16 || AnimatedTextureId || Array indices (0 to n-1). -1 for a static texture.
} anim_texture_lookup[];
|}


== Ribbon emitters ==
== Ribbon emitters ==

Revision as of 01:24, 11 August 2015

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.

The header can be 0x130 or 0x138 bytes big.

Offset Type Name Description
0x000 char[4] Magic "MD20"
0x004 uint32 Version For Cataclysm this is 263 < version < 272. Files get handled differently depending on this!
0x008 uint32 lName Length of the model's name including the trailing \0
0x00C uint32 ofsName Offset to the name, it seems like models can get reloaded by this name.should be unique, i guess.
0x010 uint32 GlobalModelFlags 1: tilt x, 2: tilt y, 4:, 8: add another field in header, 16:, 32: load .phys data (MoP+); (no other flags as of 3.1.1); list at M2/WotLK/flags; *(p+0x11) & 1 is camera related.
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 nSubmeshAnimations
0x04C uint32 ofsSubmeshAnimations Submesh color and alpha animations definitions.
0x050 uint32 nTextures
0x054 uint32 ofsTextures Textures of this model.
0x058 uint32 nTransparency
0x05C uint32 ofsTransparency Transparency of textures.
0x060 uint32 nUVAnimation
0x064 uint32 ofsUVAnimation
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 nUVAnimLookup
0x09C uint32 ofsUVAnimLookup
0x0A0 Vec3F[2] VertexBox min/max( [1].z, 2.0277779f ) - 0.16f seems to be the maximum camera height
0x0B8 float VertexRadius
0x0BC Vec3F[2] BoundingBox
0x0D4 float BoundingRadius
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 nEvents
0x104 uint32 ofsEvents Used for playing sounds when dying and a lot else.
0x108 uint32 nLights
0x10C uint32 ofsLights Lights are mainly used in loginscreens but in wands and some doodads too.
0x110 uint32 nCameras Format of Cameras changed with version 271!
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.
0x130 uint32 nUnknown This field is getting added in models with the 8-flag only. If that flag is not set, this field does not exist!
0x134 uint32 ofsUnknown It has a array out of shorts. Its related to renderflags.

Skeleton and animation

Global sequences

  • nGlobalSequences 32-bit unsigned integers starting at ofsGlobalSequences.

A list of timestamps that act as upper limits for global sequence ranges.

struct M2Loop
{
  uint32_t timestamp;
} loops[];

Standard animation block

  • Many values that change with time are specified using blocks like the following.

Please see this for information.

Global Sequences

If a block has a sequence >= 0, the block has a completely separate max timestamp. This is the value in the model's ofsGlobalSequences table; index into that table with this sequence value and use that as the block's max timestamp. Blocks that use these global sequences also only have one track, so at the same time as clipping the current timestamp to the max time above, interpolated value should always be taken from track 0 in the block.

This appears to be frequently used by models that don't have more conventional animations (login screen animations, items/weapons with animated effects, etc).

-- Rour, additionally, these sequences can be longer or shorter than whatever animation is running for a given model, so I recommend taking a global scene timestamp and then clipping that value into the given max timestamp range. Otherwise animations will appear to reset when the regular animation loops, which is not good.

Interpolation

  • If the interpolation type is 0, then there is usually only one value and the block always returns that value.
  • If the interpolation type is 1, then the block linearly interpolates between keyframe values (lerp for vectors/colours, slerp for quaternions).
  • If the interpolation type is 2, then hermite interpolation is used (instead of linear).
  • If the interpolation type is 3, then bezier interpolation is used (instead of linear).

Animation sequences

  • nAnimations 0x40-byte records starting at ofsAnimations.

List of animations present in the model.

struct M2Sequence
{
  uint16_t animation_id;         // Animation id in AnimationData.dbc
  uint16_t sub_animation_id;     // Sub-animation id: Which number in a row of animations this one is. 
  uint32_t length;               // The length (timestamps) of the animation. I believe this actually the length of the animation in milliseconds.
  float moving_speed;            // This is the speed the character moves with in this animation.
  uint32_t flags;                // See below.
  int16_t probability;           // This is used to determine how often the animation is played. For all animations of the same type, this adds up to 0x7FFF (32767).
  uint16_t _padding;
  uint32_t _unknown[2];          //  These two are connected. Most of the time, they are 0. But if there is data in one, there is data in both of them.
  uint32_t blend_time;           // The client blends (lerp) animation states between animations where the end and start values differ. This specifies how long that blending takes. Values: 0, 50, 100, 150, 200, 250, 300, 350, 500. 
  CAaBox bounds;
  float bound_radius;
  int16_t next_animation;        // id of the following animation of this AnimationID, points to an Index or is -1 if none.
  uint16_t aliasNext;            // id in the list of animations. Used to find actual animation if this sequence is an alias (flags & 0x40)
} sequences[];

Flags

One thing I saw in the source is that "-1 animationblocks" in bones wont get parsed if 0x20 is not set.

The client loads .anim files if (flags & 0x130 ) == 0. The .anim file to use is "%s%04d-%02d.anim" % (model_filename_without_extension, anim.id, anim.sub_anim_id).

Flag Description
0x01 Sets 0x80 when loaded. (M2Init)
0x02
0x04
0x08
0x10 apparently set during runtime in CM2Shared::LoadLowPrioritySequence for all entries of a loaded sequence (including aliases)
0x20 Looped animation.
0x40 has next / is alias (client skips these following next, until an animation without 0x40 is found.
0x80 Blended animation (if either side of a transition has 0x80, lerp between end->start states, unless end==start by comparing bone values)
0x100 sequence stored in model ?

-- Rour, some animations rely on blending to look right. The MoP mage CM shoulders only animate half of their movement and rely on lerping back to the start position to look correct.

Animation Lookup

  • nAnimationLookup in 16-bit shorts starting at ofsAnimationLookup.

Lookup table for Animations in AnimationData.dbc.

struct
{
  uint16_t animation_id; // Index at ofsAnimations which represents the animation in AnimationData.dbc. -1 if none.
} animation_lookups[];

Bones

  • nBones records of 0x58 bytes starting at ofsBones. (M2Array<M2CompBone>)
struct M2CompQuat
{
  short val[4]; // maps to C4Quaternion. See Quaternion values and 2.x.
};
struct M2CompBone
{
  int32_t key_bone_id;    // Back-reference to the key bone lookup table. -1 if this is no key bone.
  uint32_t flags;           // Only known flags: 0x8 - billborded and 0x200 - transformed; since MoP: 0x400: allow physics to influence this bone. at runtime, also checks for &x1 &x2 &x4 &x8 &x10 &x20 &x40 &x80 &x200 &x400
  int16_t parent_bone;        // Parent bone ID or -1 if there is none.
  uint16_t submesh_id;     //| Mesh part ID
  uint16_t _unknown[2];
  M2Track<C3Vector> translation;
  M2Track<M2CompQuat> rotation;        // compressed values, see Quaternion values and 2.x.
  M2Track<C3Vector> scale;
  C3Vector pivot;         // The pivot point of that bone.
} bones[];

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.

struct 
{
  uint16_t bone;
} bone_lookup[];

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 27 for the most models. At static models it is mostly 1.

struct 
{
  uint16_t bone; // -1 if none
} key_bone_lookup[];

Official list:

  • 00 "ArmL"
  • 01 "ArmR"
  • 02 "ShoulderL"
  • 03 "ShoulderR"
  • 04 "SpineLow"
  • 05 "Waist"
  • 06 "Head"
  • 07 "Jaw"
  • 08 "IndexFingerR"
  • 09 "MiddleFingerR"
  • 10 "PinkyFingerR"
  • 11 "RingFingerR"
  • 12 "ThumbR"
  • 13 "IndexFingerL"
  • 14 "MiddleFingerL"
  • 15 "PinkyFingerL"
  • 16 "RingFingerL"
  • 17 "ThumbL"
  • 18 "$BTH"
  • 19 "$CSR"
  • 20 "$CSL"
  • 21 "_Breath"
  • 22 "_Name"
  • 23 "_NameMount"
  • 24 "$CHD"
  • 25 "$CCH"
  • 26 "Root"
  • 27 "Wheel1"
  • 28 "Wheel2"
  • 29 "Wheel3"
  • 30 "Wheel4"
  • 31 "Wheel5"
  • 32 "Wheel6"
  • 33 "Wheel7"
  • 34 "Wheel8"

Geometry and rendering

Vertices

  • nVertices entries of 48 bytes per vertex (at ofsVertices)
struct M2Vertex
{
  C3Vector pos;
  uint8 bone_weights[4];
  uint8 bone_indices[4];
  C3Vector normal;
  C2Vector tex_coords[2];  // two textures, depending on shader used
};

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).

-- Rour, the WoW vertex shaders all follow the same pattern, "Diffuse_XX_YY" (or sometimes XX, YY and Env). The particular vertex shader that is used chooses which set of texture coordinates to use. So Diffuse_T1 sends T1 texcoords to the fragment shader. Where Diffuse_T1_T2 sends both (for textures 0 and 1) but Diffuse_T1_T1 sends the same coords for both textures. Etc.

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
struct M2Material
{
  uint16_t flags;
  uint16_ blending_mode; // apparently a bitfield
} materials[];
  • Flags:
Flag Meaning
0x01 Unlit
0x02 Unfogged?
0x04 Two-sided (no backface culling if set)
0x08 ? (probably billboarded)
0x10 Disable z-buffer?
0x80 ??? (seen in WoD)
0x400 ??? (seen in WoD)
0x800 prevent alpha for custom elements. if set, use (fully) opaque or transparent. (litSphere, shadowMonk) (MoP+)
  • Blending mode
Value Mapped to Meaning
0 0 Combiners_Opaque (Blend disabled)
1 1 Combiners_Mod (Blend enabled, Src = ONE, Dest = ZERO, SrcAlpha = ONE, DestAlpha = ZERO)
2 1 Combiners_Decal (Blend enabled, Src = SRC_ALPHA, Dest = INV_SRC_ALPHA, SrcAlpha = SRC_ALPHA, DestAlpha = INV_SRC_ALPHA )
3 1 Combiners_Add (Blend enabled, Src = SRC_COLOR, Dest = DEST_COLOR, SrcAlpha = SRC_ALPHA, DestAlpha = DEST_ALPHA )
4 1 Combiners_Mod2x (Blend enabled, Src = SRC_ALPHA, Dest = ONE, SrcAlpha = SRC_ALPHA, DestAlpha = ONE )
5 4 Combiners_Fade (Blend enabled, Src = SRC_ALPHA, Dest = INV_SRC_ALPHA, SrcAlpha = SRC_ALPHA, DestAlpha = INV_SRC_ALPHA )
6 4 Used in the Deeprun Tram subway glass, supposedly (Blend enabled, Src = DEST_COLOR, Dest = SRC_COLOR, SrcAlpha = DEST_ALPHA, DestAlpha = SRC_ALPHA )
7 ? New in WoD, needs research! Example model: World\Expansion05\Doodads\Shadowmoon\Doodads\6FX_Fire_Grassline_Doodad_blue_LARGE.m2

*Blend values are taken from D3D11 debugging of the client

Unknown new block

If this block is present (globalflags&8) and the "shading" flags of a textureunit wont be &0x8000, blending modes wont get mapped to the values above but to the ones in this block.

Instead of Mapping[renderflags->blendingmode] it will be UnknownBlock[textureunit->Shading].

As shading is not &0x8000 and (in their code) needs to be above 0, this may only touch Diffuse_T1.

Texture unit lookup table

  • nTexUnits 16-bit integers starting at ofsTexUnits.
struct 
{
  uint16_t unit; // -1, 0, or 1. see below
} texture_units[];

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.

Colors and transparency

Submesh Animations

  • nSubmeshAnimations records starting at ofsSubmeshAnimations, followed by data referenced in these records.
struct M2Color
{
  M2Track<C3Vector> color; // vertex colors
  M2Track<fixed16> alpha; // 0 - transparent, 0x7FFF - opaque. Normaly NonInterp
} colors[];

This block is the M2 equivalent to the GEOA chunk in MDX files, it represents the vertex color and visibility animations for meshes. Referenced from the Texture Unit blocks in the *.skin. If a texunit belonging to a submesh has a value of -1 then the submesh doesnot use this block. Contains a separate timeline for transparency values. If no animation is used, the given value is constant.

Transparency

  • nTransparency AnimationBlocks at ofsTransparency, followed by data referenced in these records.
struct M2TextureWeight
{
  M2Track<fixed16> weight;
} textureWeights[];

Specifies global transparency values in addition to the values given in the Color block. I assume these are multiplied together eventually.

Transparency lookup table

  • nTransLookup 16-bit integers starting at ofsTransLookup.
struct 
{
  uint16_t transparency;
} transparency_lookup[];

Contains indices into the Transparency block. Used by the texture unit definitions in the LOD block.

Textures

  • Textures are defined globally in a list, additionally, a lookup table is given, referenced during rendering, to select textures.
  • 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.

struct M2Texture
{
  uint32_t type; // see below
  uint32_t flags; // see below
  uint32_t lenFilename; // for non-hardcoded textures (type != 0), this still points to a zero-sized string
  uint32_t ofsFilename; 
} textures[];

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:

Value Meaning
0 - NONE - -- Texture given in filename
1 Skin -- Body + clothes
2 Object Skin -- Item, Capes ("Item\ObjectComponents\Cape\*.blp")
3 Weapon Blade -- Used on several models but not used in the client as far as I see. Armor Reflect?
4 Weapon Handle
5 (OBSOLETE) Environment (Please remove from source art)
6 Character Hair
7 (OBSOLETE) Character Facial Hair (Please remove from source art)
8 Skin Extra
9 UI Skin -- Used on inventory art M2s (1): inventoryartgeometry.m2 and inventoryartgeometryold.m2
10 (OBSOLETE) Tauren Mane (Please remove from source art) -- Only used in quillboarpinata.m2. I can't even find something referencing that file. Oo Is it used?
11 Monster Skin 1 -- Skin for creatures or gameobjects #1
12 Monster Skin 2 -- Skin for creatures or gameobjects #2
13 Monster Skin 3 -- Skin for creatures or gameobjects #3
14 Item Icon -- Used on inventory art M2s (2): ui-buffon.m2 and forcedbackpackitem.m2 (CSimpleModel_ReplaceIconTexture("texture"))
15 Guild Background Color
16 Guild Emblem Color
17 Guild Border Color
18 Guild Emblem

Flags

Value Meaning
1 Texture wrap X
2 Texture wrap Y

Texture lookup table

  • nTexLookup items starting at ofsTexLookup.
struct 
{
  uint16_t texture;
} texture_lookup[];

Replacable texture lookup

  • nTexReplace 16-bit integers starting at ofsTexReplace.
struct 
{
  uint16_t replacement;
} texture_replacements[];

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.

The table may look like this:

Type TextureID
HARDCODED 0
BODY -1
ITEM -1
UNKNOWN3 -1
UNKNOWN4 -1
UNKNOWN5 -1
HAIRBEARD -1
UNKNOWN7 -1
TAURENFUR -1
UNKNOWN9 -1
UNKNOWN10 -1
CREATURESKIN1 2
CREATURESKIN2 1
CREATURESKIN3 -1

Its strange, that HARDCODED is in the list, as a model can have more than one of course. Its just the last one written to the file.

One should investigate the unknown types. Maybe they are usable for something nice.

Effects

UV-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 0x3C bytes starting at ofsTexAnims, followed by data referenced in these records.

struct M2TextureTransform
{
  M2Track<C3Vector> translation;
  M2Track<C4Quaternion> rotation;
  M2Track<C3Vector> scaling;
} textureTransforms[];

UV-Animation lookup table

  • nTexAnimLookup items starting at ofsTexAnimLookup.
struct 
{
  uint16_t anim_texture_id; // -1 for static
} anim_texture_lookup[];

Ribbon emitters

  • nRibbonEmitters records of 0xB0 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 nBlendRef Number of some referenced integers,which look like the blending for the texture
0x20 int32 ofsBlendRef Offset to the blending-integers.
0x24 ABlock Color A color in three floats.
0x38 ABlock Opacity And an alpha value in a short, where: 0 - transparent, 0x7FFF - 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 Emissionangle use arcsin(val) to get the angle in degree
0x80 short Renderflags[2] Perhaps the same as in renderflags
0x84 ABlock UnknownABlock1 (short)
0x98 ABlock UnknownABlock2 (boolean)
0xAC int32 unknown This looks much like just some Padding to the fill up the 0x10 Bytes, always 0

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

This is partly wrong as hell!

  • 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 See Below
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 (see below for WoD multi-texturing).
0x018 uint32 lenModelFilename The lenght of the ModelFilename. Zeroterminated String!
0x01C uint32 ofsModelFilename And the matching offset. This is used for spawning Models. *.mdx
0x020 uint32 lenParticleFileName The lenght of the ParticleFilename. Zeroterminated String!
0x024 uint32 ofsParticleFileName And the matching offset again.This is used for spawning particles of a model. *.mdx!
0x028 uint8 BlendingType A blending type for the particle. See Below
0x029 uint8 EmitterType 1 - Plane (rectangle), 2 - Sphere, 3 - Spline? (can't be bothered to find one)
0x02A uint16 ParticleColorIndex This one is used for ParticleColor.dbc. See below.
0x02C uint8 ParticleType? Found below.
0x02D uint8 HeadorTail 0 - Head, 1 - Tail, 2 - Both
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 (short, vec3f) This one points to 3 floats defining red, green and blue.
0x114 FBlock ParticleOpacity? (short, short) Looks like opacity (short) --Igor; Most likely they all have 3 timestamps for {start, middle, end}.
0x124 FBlock ParticleSizes (short, vec2f) It carries two floats per key. (x and y scale)
0x134 int32 UnknownFields[2] No references. Padding?
0x13C FBlock Intensity Some kind of intensity values seen: 0,16,17,32(if set to different it will have high intensity) (short, short)
0x14C FBlock UnknownReferences1 (short, short)
0x15C float UnknownFloats1[3] They have something to do with the spread.
0x168 float Scale[3] Wheey, its the scale!
0x174 float Drag Air resistance, see below.
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[4] Unknown, unknown, unknown, unknown, unknown...
0x1C0 uint32 nUnknownReference
0x1C4 uint32 ofsUnknownReference Vec3D array
0x1C8 ABlock EnabledIn (boolean) Has been in the earlier documentations. Enabled Anim Block. Appears to be used sparely now, probably there's a flag that links particles to animation sets where they are enabled.

About Drag: 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( -drag * t ). This value is also present in M3 format.

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 Flags

Value Description
0x1 Particles are affected by lighting
0x2
0x4
0x8 Particles travel "up" in world space, rather than model.
0x10 Do not Trail
0x20
0x40
0x80 Particles in Model Space
0x100
0x200
0x400 Pinned Particles, their quad enlarges from their creation position to where they expand.
0x800
0x1000 XYQuad Particles. They align to XY axis facing Z axis direction.
0x2000
0x4000
0x8000
0x10000
0x20000 "Outward" particles, most emitters have this and their particles move away from the origin, when they don't the particles start at origin+(speed*life) and move towards the origin.
0x40000
0x10000000 Particle uses multi-texturing (could be one of the other WoD-specific flags), see multi-textured section.

ParticleColorIndex

This one is used so you can assign a color to specific particles. The value of ParticleColorIndex appears to be an index into ParticleColor.dbc, once you subtract 0x11. Then the particle colour animation track can have its three values replaced by the contents of the table. -- Rour, I played around with blending the particle colour table entry with the base colour but haven't come up with the right answer yet. (Quarantine Shoulderguards from SoO seemed to have a purple-ish colour where the base was blue and the table was red)

Multi-textured particles

A bunch of 6.0.2 particle emitters now have many high bits set in flags (0x10000000, maybe) and this changes the texture field of the emitter structure. Blizzard has packed three texture indices into the one 16-bit field. The following appears to produce the correct three textures:

 int16_t texIndex1 = _def.texture & 0x1f;
 int16_t texIndex2 = (_def.texture >> 5) & 0x1f;
 int16_t texIndex3 = (_def.texture >> 10) & 0x1f;

-- Rour, I haven't found the BLS file with the particle shaders in it, but dumping the ARB shaders from the mac client show that it just multiplies all three texture samples together, then multiplies by 4.0 (ala. mod2x style)

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 Checked and verified --BlinkHawk

Particle Blendings

Value Description
0 glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST);
1 glBlendFunc(GL_SRC_COLOR, GL_ONE);
2 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3 glDisable(GL_BLEND); glEnable(GL_ALPHA_TEST);
4 glBlendFunc(GL_SRC_ALPHA, GL_ONE);

from Modelviewer source -- Rour, some WoD particle effects are using blend mode 0x7 here.

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. The timestamps are shorts! (?)
0x008 uint32 nKeys The same number again. This time its the number of Keys / Values.
0x00C uint32 ofsKeys And their offset.

But they're unable to change between different animations, so they directly point to the data.

Miscellaneous

Bounding volumes

These blocks give a simplified bounding volume for the model. Characters and creatures have just a simple box.

Vertices

Complete and right as of 17-AUG-09 (3.0.9 or higher)

  • nBoundingVertices vertices at ofsBoundingVertices.

This block defines the possible points used for the model. They are referenced in the triangles block later.

Offset Type Name Description
0x00 Vec3F Coordinate This defines a vertex in x,y and z values.

Triangles

Complete and right as of 17-AUG-09 (3.0.9 or higher)

  • nBoundingTriangles/3 triples of uint16s at ofsBoundingTriangles.

The number in the header tells us how many uint16s there are, not how many triangles. To use this better, you should group three of them into an array. The nBoundingTriangles/3 indices will tell you which vertices are used for the triangle then.

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

Complete and right as of 17-AUG-09 (3.0.9 or higher)

  • nBoundingNormals normals at ofsBoundingNormals.

This one defines a normal per triangle. The vectors are normalized, but Blizzard seems to have some problems getting a simple vector normalized leading in several 0,0,0.999999999 ones. Whatever.

As each vertex has a corresponding normal vector, it should be true that nBoundingNormals = nBoundingTriangles / 3.

Offset Type Name Description
0x00 Vec3F Normal This defines the normal for each triangle.

Lights

  • nLights records of 0x9C 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

Complete and right as of 17-AUG-09 (3.0.9 or higher)

  • nCameras records of 0x64 bytes starting at ofsCameras.

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 and the character info tab.

<Cata

struct M2Camera
{
  uint32_t type; // 0: potrait, 1: characterinfo; -1: else (flyby etc.); referenced backwards in the lookup table.
  float fov; // No radians, no degrees. Multiply by 35 to get degrees.
  float far_clip;
  float near_clip;
  M2Track<M2SplineKey<C3Vector>> positions; // How the camera's position moves. Should be 3*3 floats.
  C3Vector position_base;
  M2Track<M2SplineKey<C3Vector>> target_position; // How the target moves. Should be 3*3 floats.
  C3Vector target_position_base;
  M2Track<M2SplineKey<float>> roll; // The camera can have some roll-effect. Its 0 to 2*Pi. 
} cameras[];

>=Cata

struct M2Camera
{
  uint32_t type; // 0: potrait, 1: characterinfo; -1: else (flyby etc.); referenced backwards in the lookup table.
  float far_clip;
  float near_clip;
  M2Track<M2SplineKey<C3Vector>> positions; // How the camera's position moves. Should be 3*3 floats.
  C3Vector position_base;
  M2Track<M2SplineKey<C3Vector>> target_position; // How the target moves. Should be 3*3 floats.
  C3Vector target_position_base;
  M2Track<M2SplineKey<float>> roll; // The camera can have some roll-effect. Its 0 to 2*Pi. 
  M2Track<M2SplineKey<float>> FoV; // Units are not radians. Multiplying by 35 seems to be the correct # of degrees. This is incredibly important, as otherwise e.g. loading screen models will look totally wrong.
} cameras[];

Camera lookup table

Complete and right as of 17-AUG-09 (3.0.9 or higher)

  • nCameraLookup 16-bit integers starting at ofsCameraLookup.

This block lists the different cameras existant in the model. The index in the array is also the type. CameraLookupTable[1] is always the character tab camera.

Offset Type Name Description
0x00 uint16 CameraId All cameras by type. Array indices (0 to n-1).

Attachments

  • nAttachments records of the following structure starting at ofsAttachments, followed by data referenced in these records.

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.

struct M2Attachment
{
  uint32_t id;                      // Referenced in the lookup-block below.
  uint32_t bone;                    // attachment base -- possibly just uint16_t and padding
  C3Vector position;                // relative to bone;
  M2Track<uchar> animate_attached;  // whether or not the attached model is animated when this model is. only a bool is used. default is true.
} attachments[];

Here's the list of position slots (by ID, or index in block P) for character models:

ID Description ID Description ID Description ID Description ID Description
0 Shield / MountMain / ItemVisual0 12 Back 24 Special2 36 Bullet 48 RightFoot
1 HandRight / ItemVisual1 13 ShoulderFlapRight 25 Special3 37 SpellHandOmni 49 ShieldNoGlove
2 HandLeft / ItemVisual2 14 ShoulderFlapLeft 26 SheathMainHand 38 SpellHandDirected 50 SpineLow
3 ElbowRight / ItemVisual3 15 ChestBloodFront 27 SheathOffHand 39 VehicleSeat1 51 AlteredShoulderR
4 ElbowLeft / ItemVisual4 16 ChestBloodBack 28 SheathShield 40 VehicleSeat2 52 AlteredShoulderL
5 ShoulderRight 17 Breath 29 PlayerNameMounted 41 VehicleSeat3 53 BeltBuckle
6 ShoulderLeft 18 PlayerName 30 LargeWeaponLeft 42 VehicleSeat4 54 SheathCrossbow
7 KneeRight 19 Base 31 LargeWeaponRight 43 VehicleSeat5
8 KneeLeft 20 Head 32 HipWeaponLeft 44 VehicleSeat6
9 HipRight 21 SpellLeftHand 33 HipWeaponRight 45 VehicleSeat7
10 HipLeft 22 SpellRightHand 34 Chest 46 VehicleSeat8
11 Helm 23 Special1 35 HandArrow 47 LeftFoot

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. Breath (17) is used by CGCamera::FinishLoadingTarget() aswell as some other one. The name above the head of a Unit (CGUnit_C::GetNamePosition) looks for PlayerNameMounted (29), then PlayerName (18).

Attachment Lookup

  • nAttachLookup 16-bit integers starting at ofsAttachLookup.

The index of the array defines, which type that attachment is of. Its the same as the list above. The lookups and the id of the animations point in a circle.

Offset Type Name Description
0x00 uint16 Attachment From 0 to nAttachments-1

Events

  • nEvents records of 0x24 bytes starting at ofsEvents, followed by timestamps referenced in these records.

These events are used for timing sounds for example. You can find the $DTH (death) event on nearly every model. It will play the death sound for the unit.

The events you can use depend on the way, the model is used. Dynamic objects can shake the camera, doodads shouldn't. Units can do a lot more than other objects.

Somehow there are some entries, that don't use the $... names but identifiers like "DEST" (destination), "POIN" (point) or "WHEE" (wheel). How they are used? Idk.

struct M2Event
{
  uint32_t identifier; // mostly a 3 character name prefixed with '$'.
  uint32_t data; // This data is passed when the event is fired. 
  uint32_t bone;  // Somewhere it has to be attached.
  C3Vector position; // Relative to that bone of course, animated. Pivot without animating.
  M2TrackBase enabled; // This is a timestamp-only animation block. It is built up the same as a normal AnimationBlocks, but is missing values, as every timestamp is an implicit "fire now".
} events[];

Possible Events

There are a lot more of them. I did not list all up to now.

ID data what Type seen to be fired on Description
$AH[0-3] PlaySoundKit (customAttack[x]) soundEffect ID is defined by CreatureSoundDataRec::m_customAttack[x]
$AIM Vehicles CGUnit_C::ComputeMissileTrajectory Position used as MissileFirePos.
$ALT anim_swap_event / DisplayTransition Unit CUnitDisplayTransition_C::UpdateState(1) or CUnitDisplayTransition_C::HandleAnimSwapEvent
$BL[0-3] FootstepAnimEventHit (left) Unit Backwards
$BR[0-3] FootstepAnimEventHit (right) Unit Backwards
$BRT PlaySoundKit (birth) soundEffect ID is defined by CreatureSoundDatarec::m_birthSoundID
$BTH Breath Unit All situations, where nothing happens or breathing. Adds Special Unit Effect based on unit state (under water, in-snow, …)
$BWP PlayRangedItemPull (Bow Pull) Unit LoadRifle, LoadBow
$BWR BowRelease Unit AttackRifle, AttackBow, AttackThrown
$CAH Unit Attack*, *Unarmed, ShieldBash, Special* attack hold? CGUnit_C::HandleCombatAnimEvent
$CCH Unit mostly not fired, AttackThrown CEffect::DrawFishingString needs this on the model for getting the string attachments.
$CFM Unit CGCamera::UpdateMountHeightOrOffset CGCamera::UpdateMountHeightOrOffset: Only z is used. Non-animated. Not used if $CMA
$CHD Unit not fired probably does not exist?!
$CMA Unit CGCamera::UpdateMountHeightOrOffset: Position for camera
$CPP PlayCombatActionAnimKit parry, anims, depending on some state, also some callback which might do more
$CSD soundEntryId PlayEmoteSound Unit Emote*
$CSL release_missiles_on_next_update if has_pending_missiles (left) Unit AttackRifle, SpellCast*, ChannelCast* "x is {L or R} (""Left/right hand"") (?)"
$CSR release_missiles_on_next_update if has_pending_missiles (right) Unit AttackBow, AttackRifle, AttackThrown, SpellCast*, ChannelCast* "x is {L or R} (""Left/right hand"") (?)"
$CSS PlayWeaponSwooshSound sound played depends on CGUnit_C::GetWeaponSwingType
$CST release_missiles_on_next_update if has_pending_missiles Unit Attack*, *Unarmed, ShieldBash, Special*, SpellCast, Parry*, EmoteEat, EmoteRoar, Kick, ... $CSL/R/T are also used in CGUnit_C::ComputeDefaultMissileFirePos.
$CVS  ? Data: SoundEntriesAdvanced.dbc, Sound — Not present in 6.0.1.18179
$DSE DestroyEmitter MapObj
$DSL soundEntryId PlaySoundKit (custom), low priority GO
$DSO soundEntryId PlaySoundKit (custom) GO
$DTH DeathThud + LootEffect Unit Death, Drown, Knockdown """I'm dead now!"", UnitCombat_C, this plays death sounds and more."
$EAC object package state enter 3, exit 2, 4, 5
$EDC object package state enter 5, exit 3, 4, 2
$EMV object package state enter 4, exit 3, 2, 5
$ESD PlayEmoteStateSound Unit soundEffect ID is implicit by currently played emote
$EWT object package state enter 2, exit 3, 4, 5
$FD[1-9] PlayFidgetSound CreatureSoundDataRec::m_soundFidget (only has 5 entries, so don’t use 6-9)
$FDX PlayUnitSound (stand) soundEffect ID is defined by CreatureSoundDataRec::m_soundStandID
$FL[0-3] FootstepAnimEventHit (left) Forward
$FR[0-3] FootstepAnimEventHit (right) Forward
$FSD HandleFootfallAnimEvent Unit Walk, Run (multiple times), ... Plays some sound. Footstep? Also seen at several emotes etc. where feet are moved. CGUnit_C::HandleFootfallAnimEvent
$GC[0-3] GameObject_C_PlayAnimatedSound soundEffect ID is defined by GameObjectDisplayInfoRec::m_Sound[x + 6] ({Custom0, Custom1, Custom2, Custom3})
$GO[0-5] GameObject_C_PlayAnimatedSound soundEffect ID is defined by GameObjectDisplayInfoRec::m_Sound[x] ({Stand, Open, Loop, Close, Destroy, Opened})
$HIT PlayWoundAnimKit Unit Attack*, *Unarmed, ShieldBash, Special* soundEntryId depends on SpellVisualKit
$KVS  ? MapLoad.cpp -- not found in 6.0.1.18179
$RL[0-3] FootstepAnimEventHit (left) Running
$RR[0-3] FootstepAnimEventHit (right) Running
$SCD PlaySoundKit (spellCastDirectedSound) soundEffect ID is defined by CreatureSoundDataRec::m_spellCastDirectedSoundID
$SHK spellEffectCameraShakesID AddShake GO
$SHL ExchangeSheathedWeapon (left) Sheath, HipSheath
$SHR ExchangeSheathedWeapon (right) Sheath, HipSheath
$SL[0-3] FootstepAnimEventHit (left) Stop, (JumpEnd), (Shuffle*) Stop
$SMD PlaySoundKit (submerged) soundEffect ID is defined by CreatureSoundDatarec::m_submergedSoundID
$SMG PlaySoundKit (submerge) soundEffect ID is defined by CreatureSoundDatarec::m_submergeSoundID
$SND soundEntryId PlaySoundKit (custom) GO
$SR[0-3] FootstepAnimEventHit (right) Stop, (JumpEnd), (Shuffle*) Stop
$STx Mounts MountTransitionObject::UpdateCharacterData Not seen in 6.0.1.18179 -- x is {E and B} , sequence time is taken of both, pivot of $STB. (Also, attachment info for attachment 0)
$TRD HandleSpellEventSound Unit EmoteWork*, UseStanding* soundEffect ID is implicit by SpellRec
$VG[0-8] HandleBoneAnimGrabEvent
$VT[0-8] HandleBoneAnimThrowEvent
$WGG PlayUnitSound (wingGlide) soundEffect ID is defined by CreatureSoundDataRec::m_soundWingGlideID
$WL[0-3] FootstepAnimEventHit (left)
$WNG PlayUnitSound (wingFlap) soundEffect ID is defined by CreatureSoundDataRec::m_soundWingFlapID
$WR[0-3] FootstepAnimEventHit (right)
$WTB Weapons Weapon Trail Bottom position, also used for Bow String
$WTT Weapons Weapon Trail Top position
$WWG  ? Calls some function in the Object VMT. -- Not seen in 6.0.1.18179
DEST  ? exploding ballista, that one has a really fucked up block. Oo
POIN Unit not fired Data: ?, seen on multiple models. Basilisk for example. (6801)
WHEE  ? Data: 601+, Used on wheels at vehicles.