Jump to content

madmax

Community Manager
  • Posts

    2052
  • Joined

  • Last visited

  • Days Won

    74
  • Donations

    0.00 GBP 

Everything posted by madmax

  1. madmax

    BLP File

    Introduction BLP files are used as texture storage. The textures can be stored with a 256 color palette or full 24bit RGB colors. The format supports 1, 4 and 8-bit alpha transparency and DXT compression. The file format is NOT chunked. Wikipedia has a nice overview over the format: http://en.wikipedia.org/wiki/.BLP Header From http://www.pxr.dk/wowdev/wiki/index.php?title=BLP Offset Type Description ------------------------------------------------------------------------------------------ 0x00 char[4] always 'BLP2' 0x04 uint32 Type, always 1 0x08 uint8 Compression: 1 for uncompressed, 2 for DXTC 0x09 uint8 Alpha channel bit depth: 0, 1 or 8 0x0A uint8 Alpha encoding 0x0B uint8 Has MipMaps? 0x0C uint32 X resolution (power of 2) 0x10 uint32 Y resolution (power of 2) 0x14 uint32[16] offsets for every mipmap level (or 0 when there is no more mipmap level) 0x54 uint32[16] sizes for every mipmap level (or 0 when there is no more mipmap level) 0x94 uint32[256] palette of 256 BGRA Values If HasMipMaps is 0, there is only 1 mipmap level. The palette is always present, even if it is not used. In that case all values are 0. Encoding Schemes Type 1 Compression 1 AlphaDepth 0 (uncompressed paletted image with no alpha) Each byte of the image data is an index into Palette which contains the actual RGB value for the pixel. Although the palette entries are 32-bits, the alpha value of each Palette entry may contain garbage and should be discarded. Type 1 Compression 1 AlphaDepth 1 (uncompressed paletted image with 1-bit alpha) This is the same as Type 1 Encoding 1 AlphaDepth 0 except that immediately following the index array is a second image array containing 1-bit alpha values for each pixel. The first byte of the array is for pixels 0 through 7, the second byte for pixels 8 through 15 and so on. Bit 0 of each byte corresponds to the first pixel (leftmost) in the group, bit 7 to the rightmost. A set bit indicates the pixel is opaque while a zero bit indicates a transparent pixel. Type 1 Compression 1 AlphaDepth 8 (uncompressed paletted image with 8-bit alpha) This is the same as Type 1 Encoding 1 AlphaDepth 0 except that immediately following the index array is a second image array containing the actual 8-bit alpha values for each pixel. This second array starts at BLP2Header.Offset[0] + BLP2Header.Width * BLP2Header.Height. Type 1 Compression 2 AlphaDepth 0 (DXT1 no alpha) The image data are formatted using DXT1 compression with no alpha channel. Type 1 Compression 2 AlphaDepth 1 (DXT1 one bit alpha) The image data are formatted using DXT1 compression with a one-bit alpha channel. Type 1 Compression 2 AlphaDepth 4 AlphaEncoding 1 (DXT3 four bits alpha) The image data are formatted using DXT3 compression. Type 1 Compression 2 AlphaDepth 8 AlphaEncoding 1 (DXT3 eight bits alpha) The image data are formatted using DXT3 compression. Type 1 Compression 2 AlphaDepth 8 AlphaEncoding 7 (DXT5) The image data are formatted using DXT5 compression. DXT Compression BLP only uses DXT 1,3 and 5. From: http://en.wikipedia.org/wiki/DXTn DXT1 DXT1 (also known as Block Compression 1 or BC1) is the smallest variation of S3TC, storing 16 input pixels in 64 bits of output, consisting of two 16-bit RGB 5:6:5 colour values and a 4x4 two bit lookup table. If the first colour value (c0) is numerically greater than the second colour value (c1), then two other colours are calculated, such that c2 = 2/3 c0 + 1/3 c1 and c3 = 1/3 c0 + 2/3 c1 Otherwise, if c0 <= c1, then c2 = 1/2 c0 + 1/2 c1 and c3 is transparent black corresponding to a pre-multiplied alpha format. The lookup table is then consulted to determine the colour value for each pixel, with a value of 0 corresponding to c0 and a value of 3 corresponding to c3 . DXT1 does not store alpha data enabling higher compression ratios. DXT3 DXT3 (also known as Block Compression 2 or BC2) converts 16 input pixels (corresponding to a 4x4 pixel block) into 128 bits of output, consisting of 64 bits of alpha channel data (4 bits for each pixel) followed by 64 bits of colour data, encoded the same way as DXT1 (with the exception that the 4 colour version of the DXT1 algorithm is always used instead of deciding which version to use based on the relative values of c0 and c1 ). In DXT3, the colour data is interpreted as not having been pre-multiplied by alpha. Typically DXT2/3 are well suited to images with sharp alpha transitions, between translucent and opaque areas. DXT5 DXT5 (also known as Block Compression 3 or BC3) converts 16 input pixels into 128 bits of output, consisting of 64 bits of alpha channel data (two 8 bit alpha values and a 4x4 3 bit lookup table) followed by 64 bits of colour data (encoded the same way as DXT2 and DXT3). If α0 > α1, then six other alpha values are calculated, such that α2 = (6α0 + 1α1) / 7, α3 = (5α0 + 2α1) / 7, α4 = (4α0 + 3α1) / 7, α5 = (3α0 + 4α1) / 7, α6 = (2α0 + 5α1) / 7, α7 = (1α0 + 6α1) / 7 Otherwise, if α0 <= α1, four other alpha values are calculated such that α2 = (4α0 + 1α1) / 5, α3 = (3α0 + 2α1) / 5, α4 = (2α0 + 3α1) / 5, α5 = (1α0 + 4α1) / 5, α6 = 0, α7 = 255 The lookup table is then consulted to determine the alpha value for each pixel, with a value of 0 corresponding to α0 and a value of 7 corresponding to α7. DXT5's colour data is not pre-multiplied by alpha. Because DXT4/5 use an interpolated alpha scheme, they generally produce superior results for alpha (transparency) gradients than DXT2/3.
  2. madmax

    BLS

    BLS specify specific instructions to the video card as to how to render parts of the world and how to do certain effects. There are two major types of shaders: fragment shaders (also known as pixel shaders) and vertex shaders. Fragment shaders are executed on a per-pixel basis, thus can influence texture fetching and combining operations, whereas vertex shaders are executed on a per-vertex basis. These can change vertex positions to achieve mesh animation, particle systems, and texture animation. BLS files can be found under Shaders\Pixel as well as Shaders\Vertex. They are refernced from [[WFX]] files as well as directly from WoW.exe, so there is no client database pointing to them. There are different types of shaders. *Vertex shaders: **arbvp1 **arbvp1_cg12 **vs_1_1 **vs_2_0 **vs_3_0 *Pixel shaders: **arbfp1 **nvrc **nvts **ps_1_1 **ps_1_4 **ps_2_0 **ps_3_0 They are sorted in folders as of 3.*. Previously, there were no different folders but an additional header in the files defining the type. Header *Main header (0xC bytes) This header is in all files - pixel and vertex shaders in all profiles. struct BLSHeader { ''/*0x00*/'' char[4] magix; // in reverse character order: "SVXG" in case of a vertex shader, "SPXG" in case of a fragment shader ''/*0x04*/'' uint32 version; // Always 0x10003 - version 1.3 of format ''/*0x08*/'' uint32 permutationCount; ''/*0x0C*/'' }; Blocks There are permutationCount blocks of the following structure. They are padded to 0x*0, 0x*4, 0x*8 and 0x*C. struct BLSBlock { ''/*0x00*/'' DWORD flags0; // seen: 0x3FE80 in pixel shaders; 0x1A0F in vertex shaders. there may be more .. ''/*0x04*/'' DWORD flags4; // seen: 0x200 in pixel shaders; 0x3FEC1 in vertex shaders (there may be more ..) ''/*0x08*/'' DWORD unk8; // Never seen anything in here. ''/*0x0C*/'' uint32 size; // Tells you how large the block actually is. ''/*0x10*/'' char data[size]; // In whatever format defined. ''/*----*/'' };
  3. madmax

    Dnc.db File

    Introduction dnc.db specifies the day-night cycle. It hasn't changed in any way from 1.0 or even 0.*. This looks like info for outdoor lighting with respect to the day-night cycle. The colors for the different light types are self-explanatory. The XYZ coordinates specify a directional light source. Header 8 bytes at the beginning of the file specify the number of rows (including head row) and columns 00h uint32 Number of Rows 04h uint32 Number of Columns Data Each data field has exactly 8 bytes. 00h uint32 Field Type (0x53=S for String, 0x46=F for Float) 04h uint32 Field Value String value types are offsets into a Block of Zero-Terminated strings at the end of the file. File contents Extracted and Formatted for a better overview. Hour Minute DayIntensity DayR DayG DayB DayX DayY DayZ 0.0 0.0 0.0 0.0 0.0 0.0 0.7 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.7 -0.3 1.0 2.0 0.0 0.0 0.0 0.0 0.0 0.7 -0.5 0.9 3.0 0.0 0.0 0.0 0.0 0.0 0.7 -0.7 0.7 4.0 0.0 0.0 0.0 0.0 0.0 0.7 -0.9 0.5 5.0 0.0 0.0 0.0 0.0 0.0 0.7 -1.0 0.3 6.0 0.0 0.8 0.5 0.5 0.5 0.7 -1.0 0.0 7.0 0.0 0.8 0.5 0.5 0.5 0.7 -1.0 -0.3 8.0 0.0 0.8 0.6 0.6 0.6 0.7 -0.9 -0.5 9.0 0.0 0.8 0.6 0.6 0.6 0.7 -0.7 -0.7 10.0 0.0 0.8 0.7 0.7 0.7 0.7 -0.5 -0.9 11.0 0.0 0.8 0.7 0.7 0.7 0.7 -0.3 -1.0 12.0 0.0 0.8 0.7 0.7 0.7 0.7 0.0 -1.0 13.0 0.0 0.8 0.7 0.7 0.7 0.7 0.3 -1.0 14.0 0.0 0.8 0.7 0.7 0.7 0.7 0.5 -0.9 15.0 0.0 0.8 0.7 0.7 0.7 0.7 0.7 -0.7 16.0 0.0 0.8 0.8 0.7 0.7 0.7 0.9 -0.5 17.0 0.0 0.8 0.8 0.5 0.5 0.7 1.0 -0.3 18.0 0.0 0.8 0.6 0.3 0.3 0.7 1.0 0.0 19.0 0.0 0.8 0.4 0.1 0.1 0.7 1.0 0.3 20.0 0.0 0.0 0.2 0.0 0.0 0.7 0.9 0.5 21.0 0.0 0.0 0.0 0.0 0.0 0.7 0.7 0.7 22.0 0.0 0.0 0.0 0.0 0.0 0.7 0.5 0.9 23.0 0.0 0.0 0.0 0.0 0.0 0.7 0.3 1.0 Hour Minute NightIntensity NightR NightG NightB NightX NightY NightZ 0.0 0.0 1.0 0.0 0.0 0.5 0.7 0.0 -1.0 1.0 0.0 1.0 0.0 0.0 0.5 0.7 0.3 -1.0 2.0 0.0 1.0 0.0 0.0 0.5 0.7 0.5 -0.9 3.0 0.0 1.0 0.0 0.0 0.5 0.7 0.7 -0.7 4.0 0.0 1.0 0.0 0.0 0.5 0.7 0.9 -0.5 5.0 0.0 1.0 0.0 0.0 0.5 0.7 1.0 -0.3 6.0 0.0 0.0 0.0 0.0 0.0 0.7 1.0 0.0 7.0 0.0 0.0 0.0 0.0 0.0 0.7 1.0 0.3 8.0 0.0 0.0 0.0 0.0 0.0 0.7 0.9 0.5 9.0 0.0 0.0 0.0 0.0 0.0 0.7 0.7 0.7 10.0 0.0 0.0 0.0 0.0 0.0 0.7 0.5 0.9 11.0 0.0 0.0 0.0 0.0 0.0 0.7 0.3 1.0 12.0 0.0 0.0 0.0 0.0 0.0 0.7 0.0 1.0 13.0 0.0 0.0 0.0 0.0 0.0 0.7 -0.3 1.0 14.0 0.0 0.0 0.0 0.0 0.0 0.7 -0.5 0.9 15.0 0.0 0.0 0.0 0.0 0.0 0.7 -0.7 0.7 16.0 0.0 0.0 0.0 0.0 0.0 0.7 -0.9 0.5 17.0 0.0 0.0 0.0 0.0 0.0 0.7 -1.0 0.3 18.0 0.0 1.0 0.0 0.0 0.0 0.7 -1.0 0.0 19.0 0.0 1.0 0.0 0.0 0.0 0.7 -1.0 -0.3 20.0 0.0 1.0 0.0 0.0 0.0 0.7 -0.9 -0.5 21.0 0.0 1.0 0.0 0.0 0.5 0.7 -0.7 -0.7 22.0 0.0 1.0 0.0 0.0 0.5 0.7 -0.5 -0.9 23.0 0.0 1.0 0.0 0.0 0.5 0.7 -0.3 -1.0 Hour Minute Ambient Intensity AmbientR AmbientG AmbientB FogDepth FogIntensity FogR FogG FogB 0.0 0.0 0.8 0.3 0.3 0.6 3700.0 0.8 0.0 0.0 0.1 1.0 0.0 0.8 0.3 0.3 0.6 3700.0 0.8 0.0 0.0 0.1 2.0 0.0 0.8 0.3 0.3 0.6 3700.0 0.8 0.0 0.0 0.1 3.0 0.0 0.8 0.3 0.4 0.6 3700.0 0.8 0.0 0.0 0.1 4.0 0.0 0.8 0.4 0.4 0.7 3700.0 0.7 0.0 0.0 0.1 5.0 0.0 0.8 0.4 0.4 0.7 3700.0 0.7 0.0 0.0 0.1 6.0 0.0 0.8 0.6 0.6 0.8 3700.0 0.7 0.1 0.1 0.1 7.0 0.0 0.8 0.6 0.6 0.8 3700.0 0.5 0.2 0.2 0.2 8.0 0.0 0.8 0.6 0.6 0.8 3700.0 0.3 0.2 0.2 0.2 9.0 0.0 0.8 0.7 0.7 0.9 3700.0 0.3 0.2 0.2 0.2 10.0 0.0 0.8 0.8 0.8 0.9 3700.0 0.3 0.2 0.2 0.2 11.0 0.0 0.8 0.9 0.9 0.9 3700.0 0.3 0.2 0.2 0.2 12.0 0.0 0.8 1.0 1.0 1.0 3700.0 0.3 0.2 0.2 0.2 13.0 0.0 0.8 0.9 0.9 0.9 3700.0 0.3 0.2 0.2 0.2 14.0 0.0 0.8 0.8 0.8 0.9 3700.0 0.3 0.2 0.2 0.2 15.0 0.0 0.8 0.7 0.7 0.9 3700.0 0.3 0.1 0.1 0.1 16.0 0.0 0.8 0.7 0.7 0.9 3700.0 0.3 0.1 0.1 0.1 17.0 0.0 0.8 0.6 0.6 0.8 3700.0 0.3 0.1 0.1 0.1 18.0 0.0 0.8 0.4 0.4 0.8 3700.0 0.5 0.1 0.1 0.1 19.0 0.0 0.8 0.4 0.4 0.7 3700.0 0.7 0.0 0.0 0.1 20.0 0.0 0.8 0.4 0.4 0.7 3700.0 0.7 0.0 0.0 0.1 21.0 0.0 0.8 0.4 0.3 0.7 3700.0 0.7 0.0 0.0 0.1 22.0 0.0 0.8 0.3 0.3 0.7 3700.0 0.8 0.0 0.0 0.1 23.0 0.0 0.8 0.3 0.3 0.6 3700.0 0.8 0.0 0.0 0.1
  4. madmax

    LIT Files

    These files are obsolete! LIT files have stored lighting-information until some patch. Today, lightning is stored in the following DBC files: Light.dbc LightFloatBand.dbc LightIntBand.dbc LightParams.dbc LightSkybox.dbc For worlds that have terrain data, a corresponding LIT file includes information about the sky color, and possibly lighting conditions. They are stored in World\name\lights.lit Header Offset Type Description 0x00 uint32 Always 05 00 00 80 0x04 uint32 nSkies - number of skies defined in this file 64 bytes per sky: Offset Type Description 0x00 3 * int32 (-1,-1,-1) for the 'default' first record, (0,0,0) otherwise 0x0C 3 * float Coordinates (X,Y,Z) 0x18 float Smaller radius for area (?) 0x1C float Larger radius 0x20 char[32] Sky name The float values seem to be multiplied by 36. Dividing by 36 gives back the original scale (I think) In the case of "I think", I think that the game client uses the floats to perform some kind of Cube-Mapped LightMapping (hence the X, Y, Z, -X, -Y, -Z values). -DG Sky data 4 * 0x15F0 bytes per sky. The first block of the four seems to have the sky colors, the second and fourth are usually all black, the third might be lighting colors or something else entirely. Offset Type Description 0x0000 18 * int32 Lengths 0x0048 18 * 64 * int32 Color + time records 0x1248 32 * float Float values A 0x12C8 32 * float Float values B 0x1348 uint32 Int value I 0x134C 32 * float Float values C 0x13CC 32 * float Float values D 0x144C 32 * float Float values E 0x14CC 32 * float Float values F 0x154C uint32 Int value J 0x1550 32 * float Float values G 0x15D0 8 * uint32 Padding (all 0) The color and time records are in the following format: Each row of 64 integers contains 32 pairs of integers: the first value is the time in half-minutes (on a scale of 0 to 2880 from midnight to midnight), the second value is a BGRX color. The i-th row contains Lengths[ i ] records like that. I think the color values for intermediate times are interpolated based on the times given in this list. So there are 18 time-based color rows described here, for the first block these are always the sky colors (well, the first 8 at least). WoWmapview is currently only drawing a very crude, fake sky globe - the colors may or may not match up The 7 sets of floating-point values have to describe the arrangement of the sky colors somehow, but they're pretty difficult to interpret. They usually contain at most 8 values, the rest being 0. So today I experimented with a custom .LIT file (red and blue skies are hilarious), so here are the meanings for the various color tracks: Number Description 0 Global diffuse light 1 Global ambient light 2 Sky color 0 (top) 3 Sky color 1 (middle) 4 Sky color 2 (middle to horizon) 5 Sky color 3 (above horizon) 6 Sky color 4 (horizon) 7 Fog color / background mountains color 8 ? 9 Sun color + sun halo color 10 Sun larger halo color 11 ? 12 Cloud color 13 ? 14 ? 15 Ground shadow color 16 Water color [light] 17 Water color [dark] The different skies are interpolated based on distance. The four sets of data are completely different. Number Description 0 The default look. 1 and 3 are usually all black. 2 might be the 'ghost view' lighting for when you're dead.
  5. madmax

    M2 MDX Files

    Introduction M2 or MDX files are used to store polygon models along with their animations and other information. The file format is plain binary - no chunks. Information is mainly from http://www.pxr.dk/wowdev/wiki/index.php?title=M2 as well as from PseuWoW source. A note on coordinates Blizzard uses an left-handed (?) Z-up coordinate system for the models. When loading, coordinate conversions must be applied for other coordinate systems. This also must be done for all animation data. File Structure M2 files start with a header block which contains an index of number-offset pairs for all other data blocks. All other blocks follow after that one. Blocks are not delimited, but seem to be aligned to 16 byte boundaries. Header Position 0 Length 324 bytes The file format is identified by the magic string "MD20". Pairs of uint32 are given for every data block. nXXX describes the number of elements (not necessarily bytes) in this block while ofsXXX gives the offset to the beginning of the block. If a block does not exists, n and ofs are 0. Offset Type Description -------------------------------------------------------------------- 0x000 char[4] Magic Bytes => "MD20" 0x004 uint32 Version (0x100 before Burning Crusade, 0x104 after BC) 0x008 uint32 nName - model name length (including \0) 0x00C uint32 ofsName - model name offset 0x010 uint32 GlobalModelFlags (0,1,3 seen) 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 nAnimationLookup 0x028 uint32 ofsAnimationLookup 0x02C uint32 nD - always 201 or 203 depending on client version 0x030 uint32 ofsD 0x034 uint32 nBones - number of bones 0x038 uint32 ofsBones - offset to bones 0x03C uint32 nSkelBoneLookup - skeletal bone lookup table 0x040 uint32 ofsSkelBoneLookup 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 nTexReplace 0x080 uint32 ofsTexReplace 0x084 uint32 nRenderFlags - number of blending mode definitions 0x088 uint32 ofsRenderFlags - offset to blending mode definitions 0x08C uint32 nBoneLookupTable - bone lookup table 0x090 uint32 ofsBoneLookupTable 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 float[14] 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 nAttachments 0x108 uint32 ofsAttachments 0x10C uint32 nAttachLookup 0x110 uint32 ofsAttachLookup 0x114 uint32 nAttachments_2 0x118 uint32 ofsAttachments_2 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 Vertices Position header.ofsVertices Element size 48 bytes Vertices are global for all submeshes and views. Offset Type Description -------------------------------------------------------------------- 0x00 float[3] Position (X,Y,Z) 0x0C uint8[4] Bone weights (0 to 255) 0x10 uint8[4] Bone indices (0 to nBones-1) 0x14 float[3] Normal vector (nX, nY, nZ) 0x20 float[2] Texture coordinates (U, V) 0x28 float[2] unknown, mostly 0.0f Views Position header.ofsViews Element size 44 bytes It is not clear what Views are for. But there are always 4 of them. Offset Type Description -------------------------------------------------------------------- 0x00 uint32 nIndex - number of elements in the index list 0x04 uint32 ofsIndex - offset to the index list 0x08 uint32 nTriangle - number of elements in the triangle list (this is 3* the number of triangles to be drawn) 0x0C uint32 ofsTriangle - offset to the triangle list 0x10 uint32 nProps - number of elements in the vertex property list 0x14 uint32 ofsProps - offset to the vertex property list 0x18 uint32 nSubmesh - number of elements in the submesh list 0x1C uint32 ofsSubmesh - offset to the submesh list 0x20 uint32 nTexture - number of elements in the texture list 0x24 uint32 ofsTexture - offset to the texture list 0x28 uint32 LOD distance or something? Indices nIndex uint16 values - referencing vertices from the global Vertex list. Triangles 3 uint16 values per triangle - referencing the Index list Properties 4 bytes per Vertex. Those are indices into the BoneLookupTable for each Vertex. Submeshes 32 bytes per Submesh definition. Offset Type Description -------------------------------------------------------------------- 0x00 uint32 Mesh part ID 0x04 uint16 ofsVertex - Starting vertex number, offset into the Vertex array 0x06 uint16 nVertex - Number of vertices 0x08 uint16 ofsTriangle - Starting triangle index 0x0A uint16 nTriangle - Number of triangle indices 0x0C uint16 nBoneLookup - Number of elements in the bone lookup table 0x0E uint16 ofsBoneLookup - Starting index in the bone lookup table 0x10 uint16 unknown 0x12 uint16 unsure - maybe root bone? 0x14 float[3] Vector (3d) - mass center? Mesh part ID These IDs are referenced for Geosets and such. 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 omitted based on this ID.The submeshes are sorted into groups. Groups are like this for character models. They can be different for other models. 00**: Hairstyles 01**: Facial1 02**: Facial2 03**: Facial3 04**: Braces 05**: Boots 06**: Unknown 07**: Ears 08**: Wristbands 09**: Kneepads? 10**: 11**: Related to pants 12**: Tabard 13**: Trousers / kilts 14**: 15**: Cape 16**: 17**: Eyeglows (including the deathknight ones) 18**: Belt / bellypack These are referenced in CreatureDisplayInfo.dbc->creatureGeosetData.
  6. madmax

    MPQ File

    MPQ File Format Introduction All of the game data for WoW are stored in MPQ Archives. The format's capabilities include compression, encryption, file segmentation, extensible file metadata, cryptographic signature and the ability to store multiple versions of the same file for internationalization and platform-specific differences. MPQ archives can use a variety of compression algorithms which may also be combined. The definitive source for information on MPQ Files is http://wiki.devklog.net/index.php?title=The_MoPaQ_Archive_Format. The following summary only includes facts relevant for 1.12.X Client versions Technical overview All numbers in the MPQ format are in little endian byte order; signed numbers use the two's complement system. Data types are listed either as int (integer, the number of bits specified), byte (8 bits), or char (bytes which contain ASCII characters). All sizes and offsets are in bytes, unless specified otherwise. Structure members are listed in the following general form: offset from the beginning of the structure: data type(array size) member name : member description General Archive Layout The physical layout of the files looks like this Archive HeaderFile DataHash TableBlock Table In the following the components are discussed in the logical order of processing required to read and extract files from MPQ Archives Archive Header Header size is 32 bytes, maximum archive size is 4 GB. 00h: char(4) Magic Indicates that the file is a MPQ archive. Must be ASCII "MPQ" 1Ah. 04h: int32 HeaderSize Size of the archive header. 08h: int32 ArchiveSize Size of the whole archive, including the header. 0Ch: int16 FormatVersion MPQ format version. 0000h for Classic WoW. 0Eh: int8 SectorSizeShift Power of two exponent specifying the number of 512-byte disk sectors in each logical sector in the archive. The size of each logical sector in the archive is 512 * 2^SectorSizeShift. Bugs in the Storm library dictate that this should always be 3 (4096 byte sectors). 10h: int32 HashTableOffset Offset to the beginning of the hash table, relative to the beginning of the archive. 14h: int32 BlockTableOffset Offset to the beginning of the block table, relative to the beginning of the archive. 18h: int32 HashTableEntries Number of entries in the hash table. Must be a power of two, and must be less than 2^16 1Ch: int32 BlockTableEntries Number of entries in the block table. Hash Table The Hash Table serves as a quick means of filename lookup without having to go through string comparisons. For each file in the archive, the full path is hashed using a proprietary algorithm (for source see http://wiki.devklog.net/index.php?title=The_MoPaQ_Archive_Format#Algorithm_Source_Code) resulting in three 32bit integers. The first of those hashes serves as primary lookup key for the Hash Table, the others are used for verification in case of a hash collision. In the case that two files have the same lookup key (hash collision), the first file is stored under that hash, and the second file is stored under the next free hash. So during lookup, if the second and third hash don't match, the algorithm goes down the list until it either finds a match of an empty entry. Each entry in the Hash Table looks like this: 00h: int32 FilePathHashA The hash of the file path, using method A. 04h: int32 FilePathHashB The hash of the file path, using method B. 08h: int16 Language The language of the file. This is a Windows LANGID data type, and uses the same values. 0 indicates the default language (American English), or that the file is language-neutral. 0Ah: int8 Platform The platform the file is used for. 0 indicates the default platform. No other values have been observed. 0Ch: int32 FileBlockIndex If the hash table entry is valid, this is the index into the block table of the file. Otherwise, one of the following two values: FFFFFFFFh Hash table entry is empty, and has always been empty. Terminates searches for a given file. FFFFFFFEh Hash table entry is empty, but was valid at some point (in other words, the file was deleted). Does not terminate searches for a given file. The Hash Table is encrypted using a proprietary encryption algorithm using "(hash table)" as key. The encryption algorithm is also documented at http://wiki.devklog.net/index.php?title=The_MoPaQ_Archive_Format#Algorithm_Source_Code Block Table The Block Table contains offsets into the File Data block for each File in the Archive. It also stores file attributes like compression or encryption. Hash Table FileBlockIndex points to entries in the Block Table. Like the Hash Table it is encrypted, using "(block table)" as key. Each Block Table entry looks like this: 00h: int32 BlockOffset Offset of the beginning of the block, relative to the beginning of the archive. 04h: int32 BlockSize Size of the block in the archive. 08h: int32 FileSize Size of the file data stored in the block. If the file is compressed, this is the size of the uncompressed file data. 0Ch: int32 Flags Bit mask of the flags for the block. Known flags are: Flag name Value Meaning ----------------------------------------------------------------------------------------------------- MPQ_FILE_IMPLODE 0x00000100 File is compressed using PKWARE Data compression library MPQ_FILE_COMPRESS 0x00000200 File is compressed using combination of compression methods MPQ_FILE_ENCRYPTED 0x00010000 The file is encrypted MPQ_FILE_FIX_KEY 0x00020000 The decryption key for the file is altered according to the position of the file in the archive MPQ_FILE_PATCH_FILE 0x00100000 The file contains incremental patch for an existing file in base MPQ MPQ_FILE_SINGLE_UNIT 0x01000000 Instead of being divided to 0x1000-bytes blocks, the file is stored as single unit MPQ_FILE_DELETE_MARKER 0x02000000 File is a deletion marker, indicating that the file no longer exists. This is used to allow patch archives to delete files present in lower- priority archives in the search chain. The file usually has length of 0 or 1 byte and its name is a hash MPQ_FILE_SECTOR_CRC 0x04000000 File has checksums for each sector (explained in the File Data section). Ignored if file is not compressed or imploded. MPQ_FILE_EXISTS 0x80000000 Set if file exists, reset when the file was deleted File Data Block Table BlockOffset points to the beginning of a file data block. Each file data block has a header of nSectors+1 int32 values, indicating offsets to each sector start (relative to the beginning of the file data block). The final value of this list is the total (compressed) file size, including the header. The size of each block can easily be calculated from the difference between two offsets. If the block is compressed, the first byte of every sector indicates the compression method used Extracting a file This is a step-by-step instruction. Read the File Header, find the offsets to the Hash Table and Block Table Read and Decrypt Hash Table and Block Table Compute Hashes for the file (Full Path, all Slashes converted to Backslashes) => Hash0, Hash1, Hash2 HashTableOffset = Hash0 & (Header.HashTableEntries -1) Starting from HashTableOffset, go through the Hash Table and compare Hash1 and Hash2 to HashTable.FilePathHashA and HashTable.FilePathHashB respectively until either a match or an empty entry is found. In the latter case the file you are looking for does not exist. Find the Block Table entry which corresponds to HashTable.FileBlockIndex Go to the file offset specified in BlockTable.BlockOffset Read int32 values until you reach a value that is equal to BlockTable.BlockSize => SectorOffset[0] ... SectorOffset[n] For every entry of SectorOffset[0]...SectorOffset[n-1], seek to BlockTable.BlockOffset+SectorOffset[x] Read SectorOffset[x+1]-SectorOffset[x] bytes of data. If BlockTable.Flags has a compressed flag set, the first byte of each sector indicates the compression method applied. Decrypt and or decompress each sector as necessary, stitch them together, et voila, there is your file The Listfile Each MPQ in WoW 1.12.X contains a "(listfile)". This file lists the full archive contents, one file path per line, in clear text. Hashing the file paths provides lookup keys into Hash Table. The file is provided for convenience, as it seems. It is not used by the client
  7. madmax

    SBT File

    Introduction For each cinematic there is one SBT (SuBTitle) file found in Interface\Cinematics (interface.MPQ). It contains localized subtitles for movies. File Structure At the beginning are 3 bytes 0xEF BB BF. Some kind of ID/Magic bytes/unknown stuff. Each entry is made of a timestamp and a text. The entries are divided by an empty line. 00:00:04:06 - 00:00:13:01 Four years have passed since the mortal races banded together and stood united against the might of the Burning Legion. 00:00:14:00 - 00:00:22:05 Though Azeroth was saved, the tenuous pact between the Horde and the Alliance has all but evaporated. 00:00:24:06 - 00:00:28:21 The drums of war thunder once again. The timestamp is hr:min:sec:msec.
  8. madmax

    TRS File

    Introduction The filenames of Minimap textures stored in MPQ Archives are hashed using MD5. The TRS file provides an easy lookup from real pathnames like Azeroth\map00_00.blp to the corresponding hashed filename f16354735dad4e22e175a398a01992ba.blp File Structure TRS is a plain human-readable text file. Every directory is started by a line that looks like dir: Azeroth after which a table with two columns (tab-separated) lists the corresponding clear-text filenames and hash filenames like this: Azeroth\map00_00.blp f16354735dad4e22e175a398a01992ba.blp Azeroth\map00_01.blp 3f6b258da2fe615b620d34eaa26bb0cc.blp Azeroth\map00_02.blp 6e2e4a333dc88a8581c727d3207d04f8.blp Azeroth\map01_00.blp 1c415ef6183e799c137c959596e34c40.blp Azeroth\map01_01.blp 8abbe680628429c4c6e6abf286bfed68.blp Azeroth\map01_02.blp 018be401f83b315df7abc887324cc1e2.blp ...
  9. madmax

    WDB File

    Header The header is of 16 bytes for pre-1.6 files, and 20 bytes for post-BC files: FieldName Length Description Signature 4-bytes char File Signature Build 4-bytes int Build Identifier Locale 4-bytes char Locale Identifier, reversed (ie. SUne for enUS) Unknown 4-bytes int Added in 1.6 (May be first compatible client build for this db version. not verified) Unknown 4-bytes int Added in 1.6 (Region code. not verified) Unknown 4-bytes int Added in 3.0.8.9464, always null Signatures Signatures, just like locales, are reversed in the header. Signature Filename Description BDIW Itemcache.wdb Warcraft Item Database BDNW Itemnamecache.wdb Warcraft Item Name Database BOGW Gameobjectcache.wdb Warcraft Game Object Database BOMW Creaturecache.wdb Warcraft MOB Database CPNW Npccache.wdb Warcraft NPC Database NDRW Wowcache.wdb Warden Cache TSQW Questcache.wdb Warcraft Quest Database XTIW Itemtextcache.wdb Warcraft Item Text Database BDPW Pagetextcache.wdb Warcraft Page Text Database EntryLength The EntryLength is the length in bytes of the cached row. It is always found in the second column and helps the game determinate corrupted cache entries as well as optimizing the reading process. Rows with an incorrect EntryLength are cleared on log in. EOF (End Of File) All WDB files end with 8 times \x00
  10. madmax

    WDL File

    WDL files contain a low-resolution heightmap for a world. This is probably what the WoW client uses to draw the solid-colored mountain ranges in the background ('in front of' the sky, but 'behind' the fog and the rest of the scenery). It can also be conveniently used to construct a minimap - however, since no water level information is present, the best guess is 0 (sea level) - this results in some lower-than-sea-level areas being blue on the WoWmapview minimap. Oh well. ''Someone told me that the use of WDL files was actually to determine pathing (especially for NPCs). We still need a confirmation on what they're for though. -DG'' *Chunked structure. MWMO, MWID and MODF chunk These chunks seem to have been added to every WDL and contain information about low resolution [[WMO]] used to create a silhouette. MWMO *'''Filenames for [[WMO]] that appear in the low resolution map.''' Zero terminated strings. MWID *'''List of indexes into the MWMO chunk.''' MODF *'''Placement information for the [[WMO]].''' Appears to be the same 64 byte structure used in the [[WDT]] and [[ADT]] MODF chunks. MAOF chunk *'''Map Area Offset.''' Contains 64*64 = 4096 unsigned 32-bit integers, these are absolute offsets in the file to each map tile's MapAreaLow-array-entry. For unused tiles the value is 0. /*000h*/ UINT32 areaLowOffsets[4096]; ''or'' /*000h*/ UINT32 areaLowOffsets[64][64]; MapAreaLow array MARE chunks *'''Map Area''' Heightmap for one map tile. Contains 1717 + 1616 = 545 signed 16-bit integers. So a 17 by 17 grid of height values is given, with additional height values in between grid points. Here, the "outer" 17x17 points are listed (in the usual row major order), followed by 16x16 "inner" points. The height values are on the same scale as those used in the regular height maps. MAHO chunks After each MARE chunk there follows a MAHO (MapAreaHOles) chunk. It may be left out if the data is supposed to be 0 all the time. It's an array of 16 shorts. Each short is a bitmask. If the bit is not set, there is a hole at this position.
  11. madmax

    WMO File

    WMO files contain world map objects. They, too, have a chunked structure just like the WDT files. There are two types of WMO/v17 files, actually: WMO root file - lists textures (BLP Files), doodads (M2 or MDX Files), etc., and orientation for the WMO/v17 groups WMO group file - 3d model data for one unit in the world map object The root file and the groups are stored with the following filenames: World\wmo\path\WMOName.wmo World\wmo\path\WMOName_NNN.wmo There is a hardcoded maximum of 512 group files per root object. WMO root file The root file lists the following: textures (BLP File references) materials models (MDX / M2 File references) groups visibility information more data MOHD chunk Header for the map object. 64 bytes. struct SMOHeader{ /*000h*/ uint32_t nTextures; /*004h*/ uint32_t nGroups; /*008h*/ uint32_t nPortals; /*00Ch*/ uint32_t nLights; /*010h*/ uint32_t nDoodadNames; /*014h*/ uint32_t nDoodadDefs; /*018h*/ uint32_t nDoodadSets; /*01Ch*/ CArgb color; // Color settings for base (ambient) color. See the flag at /*03Ch*/. /*020h*/ foreign_key<uint32_t, &::m_WMOID> wmoID; /*024h*/ CAaBox bounding_box; /*03Ch*/ uint16_t flag_attenuate_vertices_based_on_distance_to_portal : 1; /*03Ch*/ uint16_t flag_skip_base_color : 1; // do not add base (ambient) color (of MOHD) to MOCVs. // apparently does more, e.g. required for multiple MOCVs /*03Ch*/ uint16_t flag_liquid_related : 1; // fills the whole WMO with water (used for underwater WMOs). // (possibly - LiquidType related, see below in the MLIQ). /*03Ch*/ uint16_t flag_has_some_outdoor_group : 1; // possibly - has some group that is outdoors /*03Ch*/ uint16_t Flag_Lod : 1; // ≥ (20740) /*03Ch*/ uint16_t : 11; // unused as of (20994) /*03Eh*/ uint16_t numLod; // ≥ (21108) includes base lod header; // (→ numLod = 3 means '.wmo', 'lod0.wmo' and 'lod1.wmo')} MOTX chunk List of textures (BLP Files) used in this map object. There are nTextures entries in this chunk. A block of zero-padded, zero-terminated strings, that are complete filenames with paths. There will be further material information for each texture in the next chunk. The gaps between the filenames are padded with extra zeroes, but the material chunk does have some positional information for these strings. char texture_filenames[]; The beginning of a string is always aligned to a 4Byte Adress. (0, 4, 8, C). The end of the string is Zero terminated and filled with zeros until the next aligment. Sometimes there also empty aligtments for no (it seems like no) real reason. MOMT chunk Materials used in this map object, 64 bytes per texture (BLP file), nMaterials entries. struct SMOMaterial { uint32_t flag_0x1 : 1; // ? (I'm not sure atm I tend to use lightmap or something like this) uint32_t flag_0x2 : 1; uint32_t flag_no_backface_culling : 1; // two-sided uint32_t flag_darkened : 1; // ?, the intern face of windows are flagged 0x08 uint32_t flag_bright_at_night : 1; // (unshaded) (used on windows and lamps in Stormwind, for example) uint32_t flag_0x20 : 1; uint32_t flag_clamp : 1; // ?, looks like GL_CLAMP uint32_t flag_repeat : 1; // ?, looks like GL_REPEAT uint32_t flag_0x100 : 1; uint32_t : 23; // unused as of 7.0.1.20994 /*004h*/ uint32_t shader; // Index into CMapObj::s_wmoShaderMetaData. See below (shader types). /*008h*/ uint32_t blendMode; // Blending: 0 for opaque, 1 for transparent /*00Ch*/ uint32_t texture_0; // offset into MOTX /*010h*/ uint32_t color_0; // rgba8 (four uint8s) /*014h*/ uint32_t flags_0; /*018h*/ uint32_t texture_1; /*01Ch*/ uint32_t color_1; /*020h*/ foreign_key<uint32_t, &::m_ID> ground_type; // according to CMapObjDef::GetGroundType /*024h*/ uint32_t texture_2; /*028h*/ uint32_t color_2; /*02Ch*/ uint32_t flags_2; /*030h*/ uint32_t runTimeData[4]; // This data is explicitly nulled upon loading. Contains textures or similar stuff. /*034h*/ /*038h*/ /*03Ch*/ /*040h*/ } materials[]; texture_1, 2 and 3 are start positions for texture filenames in the MOTX data block ; texture_1 for the first texture, texture_2 for the second (see shaders), etc. texture_1 defaults to "createcrappygreentexture.blp". color_2 is diffuse color : CWorldView::GatherMapObjDefGroupLiquids(): geomFactory->SetDiffuseColor((CImVectorⁱ*)(smo+7)); The flags might used to tweak alpha testing values, I'm not sure about it, but some grates and flags in IF seem to require an alpha testing threshold of 0, at other places this is greater than 0. Shader types Depending on the shader, a different amount of textures is required. If there aren't enough filenames given, it defaults to Opaque (with one filename). More filenames than required are just ignored. Data is from 15464. value name textures without shader textures with shader texcoord count color count 0 Diffuse 1 1 1 1 1 Specular 1 1 1 1 2 Metal 1 1 1 1 3 Env 1 2 1 1 4 Opaque 1 1 1 1 5 EnvMetal 1 2 1 1 6 TwoLayerDiffuse 1 2 2 2 7 TwoLayerEnvMetal 1 3 2 2 8 TwoLayerTerrain 1 2 1 2 automatically adds _s in the filename of the second texture 9 DiffuseEmissive 1 2 2 2 10 1 1 1 1 Seems to be invalid. Does something with MOTA (tangents). 11 MaskedEnvMetal 1 3 2 2 12 EnvMetalEmissive 1 3 2 2 13 TwoLayerDiffuseOpaque 1 2 2 2 14 TwoLayerDiffuseEmissive 1 1 1 1 Seems to be invalid. Does something with MOTA (tangents). 15 1 2 2 2 16 Diffuse 1 1 1 1 SMOMaterial::SH_DIFFUSE_TERRAIN -- "Blend Material": used for blending WMO with terrain (dynamic blend batches) tex coord and color count decide vertex buffer format: EGxVertexBufferFormat_PNC2T2 Shader types (18179) value #textures without shader #textures with shader texcoord count color count 0 - Diffuse 1 1 1 1 1 - Specular 1 1 1 1 2 - Metal 1 1 1 1 3 - Env 1 2 1 1 4 - Opaque 1 1 1 1 5 - EnvMetal 1 2 1 1 6 - TwoLayerDiffuse 1 2 2 2 7 - TwoLayerEnvMetal 1 3 2 2 8 - TwoLayerTerrain 1 2 1 2 automatically adds _s in the filename of the second texture 9 - DiffuseEmissive 1 2 2 2 10 - waterWindow 1 1 1 1 automatically generates MOTA 11 - MaskedEnvMetal 1 3 2 2 12 - EnvMetalEmissive 1 3 2 2 13 - TwoLayerDiffuseOpaque 1 2 2 2 14 - submarineWindow 1 1 1 1 automatically generates MOTA 15 - TwoLayerDiffuseEmissive 1 2 2 2 16 - DiffuseTerrain 1 1 1 1 SMOMaterial::SH_DIFFUSE_TERRAIN -- "Blend Material": used for blending WMO with terrain (dynamic blend batches) 17 - AdditiveMaskedEnvMetal 1 3 2 2 void CMapObj::CreateMaterial (unsigned int materialId) void CMapObj::CreateMaterial (unsigned int materialId) { assert (m_materialCount); assert (m_materialTexturesList); assert (materialId < m_materialCount); if (++m_materialTexturesList[materialId].refcount <= 1) { SMOMaterial* material = &m_smoMaterials[materialId]; const char* texNames[3]; texNames[0] = &m_textureFilenamesRaw[material->firstTextureOffset]; texNames[1] = &m_textureFilenamesRaw[material->secondTextureOffset]; texNames[2] = &m_textureFilenamesRaw[material->thirdTextureOffset]; if ( *texNames[0] ) texNames[0] = "createcrappygreentexture.blp"; assert (material->shader < SMOMaterial::SH_COUNT); int const textureCount ( CShaderEffect::s_enableShaders ? s_wmoShaderMetaData[material->shader].texturesWithShader : s_wmoShaderMetaData[material->shader].texturesWithoutShader ); int textures_set (0); for (; textures_set < textureCount; ++textures_set) { if (!texNames[textures_set]) { material->shader = MapObjOpaque; textures_set = 1; break; } } for (; textures_set < 3; ++textures_set) { texNames[textures_set] = nullptr; } if (material->shader == MapObjTwoLayerTerrain && texNames[1]) { texNames[1] = insert_specular_suffix (texNames[1]); } int flags (std::max (m_field_2C, 12)); const char* parent_name (m_field_9E8 & 1 ? m_filename : nullptr); m_materialTexturesList[materialId]->textures[0] = texNames[0] ? CMap::CreateTexture (texNames[0], parent_name, flags) : nullptr; m_materialTexturesList[materialId]->textures[1] = texNames[1] ? CMap::CreateTexture (texNames[1], parent_name, flags) : nullptr; m_materialTexturesList[materialId]->textures[2] = texNames[2] ? CMap::CreateTexture (texNames[2], parent_name, flags) : nullptr; } } MOGN chunk List of group names for the groups in this map object. char group_names[]; A contiguous block of zero-terminated strings. The names are purely informational except for "antiportal". The names are referenced from MOGI and MOGP. There are not always nGroups entries in this chunk as it contains extra empty strings and descriptive names. It (always ?) begins with two empty strings, so 0x00 0x00, and is 4-byte padded at the end of the chunk only. The names are indeed referenced in MOGI, and both the name and a descriptive name are referenced in the group file header (2 firsts uint16 of MOGP). MOGI chunk Group information for WMO groups, 32 bytes per group, nGroups entries. struct SMOGroupInfo { #if version < ? uint32_t offset; uint32_t size; #endif /*000h*/ uint32_t flags; // see , they are equivalent /*004h*/ CAaBox bounding_box; /*01Ch*/ int32_t nameoffset; // name in chunk (-1 for no name) } groups[]; Groups don't have placement or orientation information, because the coordinates for the vertices in the additional .WMO/v17 files are already correctly transformed relative to (0,0,0) which is the entire WMO/v17's base position in model space. The name offsets point to the position in the file relative to the MOGN header. MOSB chunk Skybox. Contains an zero-terminated filename for a skybox. (padded to 4 byte alignment if "empty"). If the first byte is 0, the skybox flag in all MOGI entries are cleared and there is no skybox. char skybox_filename[]; MOPV chunk Portal vertices, one entry is a float[3], usually 4 * 3 * float per portal (actual number of vertices given in portal entry) C3Vector portal_vertices[]; Portals are polygon planes (usually quads, but they can have more complex shapes) that specify where separation points between groups in a WMO/v17 are - these are usually doors or entrances, but can be placed elsewhere. Portals are used for occlusion culling, and is a known rendering technique used in many games (among them Unreal Tournament 2004 and Descent. See Portal Rendering on Wikipeda and Antiportal on Wikipedia for more information. Since when "playing" WoW, you're confined to the ground, checking for passing through these portals would be enough to toggle visibility for indoors or outdoors areas, however, when randomly flying around, this is not necessarily the case. So.... What happens when you're flying around on a gryphon, and you fly into that arch-shaped portal into Ironforge? How is that portal calculated? It's all cool as long as you're inside "legal" areas, I suppose. It's fun, you can actually map out the topology of the WMO/v17 using this and the MOPR chunk. This could be used to speed up the rendering once/if I figure out how. MOPT chunk Portal information. 20 bytes per portal, nPortals entries. There is a hardcoded maximum of 128 portals in a single WMO. struct SMOPortal { uint16_t base_index; uint16_t index_count; C4Plane plane; } portals[]; This structure describes one portal separating two WMO groups. A single portal is usually made up of four vertices in a quad (starting at base_index and going to base_index + index_count). However, portals support more complex shapes, and can fully encompass holes such as the archway leading into Ironforge and parts of the Caverns of Time. It is likely that portals are drawn as GL_TRIANGLE_STRIP in WoW's occlusion pipeline, since some portals have a vertex count that is not evenly divisible by four. One example of this is portal #21 in CavernsOfTime.wmo from Build #5875 (WoW 1.12.1), which has 10 vertices. MOPR chunk Map Object Portal References from groups. Mostly twice the number of portals. Actual count defined by sum (MOGP.portals_used). struct SMOPortalRef // 04-29-2005 By ObscuR { uint16_t portal_index; // into MOPR uint16_t group_index; // the other one int16_t side; // positive or negative. uint16_t unk; } portal_references[]; MOVV chunk Visible block vertices, 0xC byte per entry. Just a list of vertices that corresponds to the visible block list. C3Vector visible_block_vertices[]; MOVB chunk Visible block list struct { uint16_t firstVertex; uint16_t count; ) visible_blocks[]; MOLT chunk Lighting information. 48 bytes per light, nLights entries struct SMOLight { enum LightType { OMNI_LGT = 0, SPOT_LGT = 1, DIRECT_LGT = 2, AMBIENT_LGT = 3, }; /*000h*/ uint8_t type; /*001h*/ uint8_t useAtten; /*002h*/ uint8_t pad[2]; /*004h*/ CImVector color; /*008h*/ C3Vector position; /*014h*/ float intensity; /*018h*/ float attenStart; /*01Ch*/ float attenEnd; /*020h*/ float unk[4]; } lights[]; First 4 uint8_t are probably flags, mostly with the values (0,1,1,1). I haven't quite figured out how WoW actually does lighting, as it seems much smoother than the regular vertex lighting in my screenshots. The light paramters might be range or attenuation information, or something else entirely. Some WMO/v17 groups reference a lot of lights at once. The WoW client (at least on my system) uses only one light, which is always directional. Attenuation is always (0, 0.7, 0.03). So I suppose for models/doodads (both are M2 files anyway) it selects an appropriate light to turn on. Global light is handled similarly. Some WMO/v17 textures (BLP files) have specular maps in the alpha channel, the pixel shader renderpath uses these. Still don't know how to determine direction/color for either the outdoor light or WMO/v17 local lights... :) The entire MOLT and related chunks seem to be unused at least in 3.3.5a. Changing light colors and other settings on original WMOs leads to no effect. Removing the light leads to no effect either. I assume that MOLT rendering is disabled somewhere in the WoW.exe, as it might use the same principle as the M2 light emitters which are not properly supported up to WoD. However, when you explore the WMOs in 3D editors you can clearly see that MOCV layer is different under those lamps. So, I assume they are used for baking MOCV colors and also written to the actual file in case the renderer will ever get updated, or just because you can easily import the WMO back and rebake the colors. --- Skarn (talk) MODS chunk This chunk defines doodad sets. Doodads in WoW are M2 model files. There are 32 bytes per doodad set, and nSets entries. Doodad sets specify several versions of "interior decoration" for a WMO/v17. Like, a small house might have tables and a bed laid out neatly in one set called "Set_$DefaultGlobal", and have a horrible mess of abandoned broken things in another set called "Set_Abandoned01". The names are only informative. The doodad set number for every WMO instance is specified in the ADT files. struct SMODoodadSet { /*000h*/ char name[20]; // set name /*014h*/ uint32_t firstinstanceindex; // index of first doodad instance in this set /*018h*/ uint32_t numDoodads; // number of doodad instances in this set /*01Ch*/ uint32_t unused; } doodad_sets[]; firstinstanceindex is not the name index, but the actual order the doodads come in the MODD chunk in the WMO -MaiN MODN chunk List of filenames for M2 (mdx) models that appear in this WMO/v17. A block of zero-padded, zero-terminated strings. There are nModels file names in this list. They have to be .MDX! char doodad_filenames[]; MODD chunk Information for doodad instances. 40 bytes per doodad instance, nDoodads entries. -- There are not nDoodads entries here! Divide the chunk length by 40 to get the correct amount. While WMO/v17s and models (M2s) in a map tile are rotated along the axes, doodads within a WMO/v17 are oriented using quaternions! Hooray for consistency! I had to do some tinkering and mirroring to orient the doodads correctly using the quaternion, see model.cpp in the WoWmapview source code for the exact transform matrix. It's probably because I'm using another coordinate system, as a lot of other coordinates in WMO/v17s and models also have to be read as (X,Z,-Y) to work in my system. But then again, the ADT files have the "correct" order of coordinates. Weird. struct SMODoodadDef { /*000h*/ uint32_t name_offset : 24; // reference offset into /*003h*/ uint32_t flag_AcceptProjTex : 1; /*003h*/ uint32_t flag_0x2 : 1; // MapStaticEntity::field_34 |= 1 /*003h*/ uint32_t flag_0x4 : 1; /*003h*/ uint32_t flag_0x8 : 1; /*003h*/ uint32_t : 4; // unused as of 7.0.1.20994 /*004h*/ C3Vector position; // (X,Z,-Y) /*010h*/ C4Quaternion orientation; // (X, Y, Z, W) /*020h*/ float scale; // scale factor /*024h*/ CImVector color; // (B,G,R,A) lightning color } doodad_definitions[]; It looks like in order to get correct picture the color from SMODoodadDef should be applied only to opaque submeshes of M2. Deamon (talk) How to compute a matrix to map WMO's M2 to world coordinates The coordinate system here is WMO's local coordinate system. It's Z-up already, that differs it from Y-up in MODF(ADT), MODF(WDT) and MDDF chunks. To compute the whole placement matrix for doodad you would need take positionMatrix of WMO from MODF(ADT) or MODF(WDT) and multiply it by positionMatrix calculated here. Example implementation in js with gl-matrix library: function createPlacementMatrix(modd, wmoPlacementMatrix){ var placementMatrix = mat4.create(); mat4.identity(placementMatrix); mat4.multiply(placementMatrix, placementMatrix, wmoPlacementMatrix); mat4.translate(placementMatrix, placementMatrix, [modd.pos[0],modd.pos[1], modd.pos[2]]); var orientMatrix = mat4.create(); mat4.fromQuat(orientMatrix, [modd.rotation[0], //imag.x modd.rotation[1], //imag.y, modd.rotation[2], //imag.z, modd.rotation[3] //real ] ); mat4.multiply(placementMatrix, placementMatrix, orientMatrix); mat4.scale(placementMatrix, placementMatrix, [modd.scale, modd.scale, modd.scale]); return placementMatrix; } MFOG chunk Fog information. Made up of blocks of 48 bytes. struct SMOFog { /*000h*/ uint32_t flag_infinite_radius : 1; // F_IEBLEND: Ignore radius in CWorldView::QueryCameraFog /*000h*/ uint32_t : 3; // unused as of 7.0.1.20994 /*000h*/ uint32_t flag_0x10 : 1; /*000h*/ uint32_t : 27; // unused as of 7.0.1.20994 /*004h*/ C3Vector pos; /*010h*/ float smaller_radius; // start /*014h*/ float larger_radius; // end struct { /*018h*/ float end; /*01Ch*/ float start_scalar; // (0..1) /*020h*/ CImVector color; // The back buffer is also cleared to this colour } fog; struct { /*024h*/ float end; /*028h*/ float start_scalar; // (0..1) /*02Ch*/ CImVector color; } underwater_fog; } fogs[]; Fog end: This is the distance at which all visibility ceases, and you see no objects or terrain except for the fog color. Fog start: This is where the fog starts. Obtained by multiplying the fog end value by the fog start multiplier. MCVP chunk (optional) Convex Volume Planes. Contains blocks of floating-point numbers. 0x10 bytes (4 floats) per entry. C4Plane convex_volume_planes[]; // normal points out These are used to define the volume of when you are inside this WMO. Important for transports. If a point is behind all planes (i.e. point-plane distance is negative for all planes), it is inside. GFID (Legion+) required when WMO is load from fileID (e.g. game objects) struct { uint32 id[MOHD.nGroups]; } groupFileDataIDs[ !MOHD.Flag_Lod ? 1 : MOHD.numLod ? MOHD.numLod : 3 // fallback for missing numLod: assume numLod=2+1base ]; WMO group file WMO group files contain the actual polygon soup for a particular section of the entire WMO/v17. Every group file has one top-level MOGP chunk, that has a 68-byte header followed by more subchunks. So it can be effectively treated as a file with a header at 0x14 and chunks starting at 0x58. The subchunks are not always present. Some are fixed and needed while others are only checked for if some flags in the header are set. The chunks need to be in the right order if you want WoW to read it. The following chunks are always present in the following order: MOGP MOPY MOVI MOVT MONR MOTV MOBA These chunks are only present if a flag in the header is set. See the list below for the flags. Cataclysm introduced a new optional MOBS chunk, I guess it's related to MOBA. ---Bananenbrot, 12-18-2010 MOLR MODR MOBN MOBR MPBV MPBP MPBI MPBG MOCV MLIQ MORI MORB MOTV 2 MOCV 2 MOGP chunk Note: In its header is given a wrong size. Just use 0x44. -eLaps Actually, the size is correct, the other chunks are just subchunks of MOGP :) ---Tigurius Offset Type Description 0x00 uint32 Group name (offset into chunk) 0x04 uint32 Descriptive group name (offset into chunk) 0x08 uint32 Flags 0x0C float[3] Bounding box corner 1 (same as in ) 0x18 float[3] Bounding box corner 2 0x24 uint16 Index into the chunk 0x26 uint16 Number of items used from the chunk 0x28 uint16 Number of batches A 0x2A uint16 Number of batches interior 0x2C uint16 Number of batches exterior 0x2E uint16 unk(padding?) 0x30 uint8[4] Up to four indices into the WMO fog list 0x34 uint32 LiquidType, not always directly used: see below in the MLIQ chunk. 0x38 foreign_key<uint32_t, &::m_WMOGroupID> WMO group ID 0x3C uint32 &1: WoD(?)+ CanCutTerrain (by MOPL planes), others (UNUSED: 20740) 0x40 uint32 (UNUSED: 20740) The fields referenced from the MOPR chunk indicate portals leading out of the WMO/v17 group in question. For the "Number of batches" fields, A + batches_interior + batches_exterior == the total number of batches in the WMO/v17 group (in the MOBA chunk). This might be some kind of LOD thing, or just separating the batches into different types/groups...? Flags: always contain more information than flags in MOGI. I suppose MOGI only deals with topology/culling, while flags here also include rendering info. group flags Flag Meaning 0x1 Has and chunk. 0x2 (UNUSED: 20740) possibly: subtract mohd.color in mocv fixing 0x4 Has vertex colors ( chunk). 0x8 SMOGroup::EXTERIOR -- Outdoor 0x10 (UNUSED: 20740) 0x20 (UNUSED: 20740) 0x40 "Do not use local diffuse lightning". Applicable for both doodads from this wmo group(color from MODD) and water(CWorldView::GatherMapObjDefGroupLiquids). 0x80 SMOGroup::UNREACHABLE 0x100 0x200 Has lights ( chunk) 0x400 <= Cataclysm: Has MPBV, MPBP, MPBI, MPBG chunks, neither 3.3.5a nor Cataclysm alpha actually use them though, but just skips them. Legion+(?): Also load for LoD != 0 (_lod* groups) 0x800 Has doodads ( chunk) 0x1000 SMOGroup::LIQUIDSURFACE -- Has water ( chunk) 0x2000 SMOGroup::INTERIOR -- Indoor 0x4000 (UNUSED: 20740) 0x8000 0x10000 SMOGroup::ALWAYSDRAW -- clear 0x8 after CMapObjGroup::Create() in MOGP and MOGI 0x20000 (UNUSED: 20740) Has MORI and MORB chunks. 0x40000 Show skybox -- automatically unset if MOSB not present. 0x80000 is_not_water_but_ocean, LiquidType related, see below in the MLIQ chunk. 0x100000 0x200000 0x400000 (UNUSED: 20740) 0x800000 0x1000000 SMOGroup::CVERTS2: Has two MOCV chunks: Just add two or don't set 0x4 to only use cverts2. 0x2000000 SMOGroup::TVERTS2: Has two MOTV chunks: Just add two. 0x4000000 Just call CMapObjGroup::CreateOccluders() independent of groupname being "antiportal". requires intBatchCount == 0, extBatchCount == 0, UNREACHABLE. 0x8000000 unk. requires intBatchCount == 0, extBatchCount == 0, UNREACHABLE. 0x10000000 (UNUSED: 20740) 0x20000000 (UNUSED: 20740) 0x40000000 SMOGroup::TVERTS3: Has three MOTV chunks, eg. for MOMT with shader 18. 0x80000000 "antiportal" If a group wmo is named "antiportal", CMapObjGroup::CreateOccluders() is called and group flags 0x4000000 and 0x80 are set automatically in both, MOGP and MOGI. Also, the BSP tree is cleared and batch_count[interior] and [exterior] is set to 0. If flags & 0x4000000 is set, just CMapObjGroup::CreateOccluders() is called, without setting flags or clearing bsp. void CMapObjGroup::CreateOccluders() { for ( unsigned int mopy_index (0), movi_index (0) ; mopy_index < this->mopy_count ; ++mopy_index, ++movi_index ) { C3Vector* points[3] = { &this->m_vertices[this->movi[movi_index].points[0]] , &this->m_vertices[this->movi[movi_index].points[1]] , &this->m_vertices[this->movi[movi_index].points[2]] }; float avg ((points[0]->z + points[1]->z + points[2]->z) / 3.0); unsigned int two_points[2]; unsigned int two_points_index (0); for (unsigned int i (0); i < 3; ++i) { if (points[i]->z > avg) { two_points[two_points_index++] = i; } } if (two_points_index > 1) { CMapObjOccluder* occluder (CMapObj::AllocOccluder()); occluder->p1 = points[two_points[0]]; occluder->p2 = points[two_points[1]]; append (this->occluders, occluder); } } } MOPY chunk Material info for triangles, two bytes per triangle. So size of this chunk in bytes is twice the number of triangles in the WMO group. struct SMOPoly { struct { uint8_t F_UNK_0x01: 1; uint8_t F_NOCAMCOLLIDE : 1; uint8_t F_DETAIL : 1; uint8_t F_COLLISION : 1; uint8_t F_HINT : 1; uint8_t F_RENDER : 1; uint8_t F_UNK_0x40 : 1; uint8_t F_COLLIDE_HIT : 1; bool isTransFace() { return F_UNK_0x01 && (F_DETAIL || F_RENDER); } bool isColor() { return !F_COLLISION; } bool isRenderFace() { return F_RENDER && !F_DETAIL; } bool isCollidable() { return F_COLLISION || isRenderFace(); } } flags; #if version < ? uint8_t lightmapTex; #endif uint8_t material_id; // index into , 0xff for collision faces #if version < ? uint8_t padding; #endif }; 0xFF is used for collision-only triangles. They aren't rendered but have collision. Problem with it: WoW seems to cast and reflect light on them. Its a bug in the engine. --schlumpf_ 20:40, 7 June 2009 (CEST) Triangles stored here are more-or-less pre-sorted by texture, so it's ok to draw them sequentially. MOVI chunk Vertex indices for triangles., count = size / sizeof(unsigned short). Three 16-bit integers per triangle, that are indices into the vertex list. The numbers specify the 3 vertices for each triangle, their order makes it possible to do backface culling. MOVT chunk Vertices chunk., count = size / (sizeof(float) * 3). 3 floats per vertex, the coordinates are in (X,Z,-Y) order. It's likely that WMO/v17s and models (M2s) were created in a coordinate system with the Z axis pointing up and the Y axis into the screen, whereas in OpenGL, the coordinate system used in WoWmapview the Z axis points toward the viewer and the Y axis points up. Hence the juggling around with coordinates. MONR chunk Normals. count = size / (sizeof(float) * 3). 3 floats per vertex normal, in (X,Z,-Y) order. MOTV chunk Texture coordinates, 2 floats per vertex in (X,Y) order. The values usually range from 0.0 to 1.0, but it's ok to have coordinates out of that range. Vertices, normals and texture coordinates are in corresponding order, of course. Not present in antiportal WMO groups. MOBA chunk Render batches. Records of 24 bytes. struct SMOBatch { #if version < ? uint8_t lightMap; uint8_t texture; #endif #if < /*0x00*/ int16_t unknown_box_min[3]; // -2,-2,-1, 2,2,3 in cameron -> seems to be a bounding box for culling /*0x06*/ int16_t unknown_box_max[3]; #else /*0x00*/ uint8_t unknown[0xA]; /*0x0A*/ uint16_t material_id_large; // used if flag_use_uint16_t_material is set. #endif #if version < ? uint16_t first_index; // index of the first face index used in #else /*0x0C*/ uint32_t first_index; // index of the first face index used in #endif /*0x10*/ uint16_t num_indices; // number of indices used /*0x12*/ uint16_t first_vertex; // index of the first vertex used in /*0x14*/ uint16_t last_vertex; // index of the last vertex used (batch includes this one) /*0x16*/ uint8_t flag_unknown_1 : 1; #if ≥ /*0x16*/ uint8_t flag_use_material_id_large : 1; // instead of material_id use material_id_large #endif #if version >= ? /*0x17*/ uint8_t material_id; // index in #else uint8_t padding; #endif }; Batches are groups of faces with the same material ID in root's MOMT, and they're used to accelerate rendering. Note that the client doesn't use them in the same way while rendering in D3D or OpenGL (only D3D uses all batches information). The vertex buffer containing vertices from first_vertex to last_vertex can contain vertices that aren't used by the batch. On the other hand, if one of the faces used need a vertex, it has to be in the buffer. Concerning the byte at 0x16, as a material ID is coded on a uint8, I guess it is completely unused. --Gamhea 12:23, 29 July 2013 (UTC) unknown_box This seems to be a low resolution bounding box of the contained vertices. The client appears to be using them to do batch-level culling, so if they are set incorrectly, the batch may be randomly disappearing. According to Adspartan (talk), the box can be calculated by just iterating over all vertices contained (by following first_vertex and last_vertex to MOVT and taking the minimum/maximum of those. They should probably be rounded away from zero instead of being truncated on conversion to int16_t. This section only applies to versions ≥ . unknown_box seems no longer used (and nulled). Instead, flag_use_material_id_large can be set to use material_id_large which was the last of unknown_box's fields. This means that when "retroporting" files, unknown_box's values need to be calculated (by building minimum and maximum from the corresponding vertices) and material_id should be set, if it can fit a uint8_t. --based on Rangorn (talk) MOLR chunk Light references, one 16-bit integer per light reference. This is basically a list of lights used in this WMO/v17 group, the numbers are indices into the WMO/v17 root file's MOLT table. For some WMO/v17 groups there is a large number of lights specified here, more than what a typical video card will handle at once. I wonder how they do lighting properly. Currently, I just turn on the first GL_MAX_LIGHTS and hope for the best. :( MODR chunk Doodad references, one 16-bit integer per doodad. The numbers are indices into the doodad instance table (MODD chunk) of the WMO/v17 root file. These have to be filtered to the doodad set being used in any given WMO/v17 instance. MOBN chunk Nodes of the BSP tree, used for collision (along with bounding boxes ?). Array of t_BSP_NODE. / CAaBspNode. 0x10 bytes. struct t_BSP_NODE { uint16_t planeType; // 4: leaf, 0 for YZ-plane, 1 for XZ-plane, 2 for XY-plane int16_t children[2]; // index of bsp child node (right in this array) uint16_t numFaces; // num of triangle faces in uint32_t firstFace; // index of the first triangle index(in ) float fDist; }; planetype might be 0 for YZ-plane, 1 for XZ-plane, 2 for XY-plane, 4 for BSP leaf. fDist is where split plane locates based on planetype, ex, you have a planetype 0 and fDist 15, so the split plane is located at offset ( 15, 0, 0 ) with Normal as ( 1, 0, 0 ), I think the offset is relative to current node's bounding box center. The BSP root ( ie. node 0 )'s bounding box is the WMO's boundingbox, then you subdivide it with plane and fdist, then you got two children with two bounding box, and so on. you got the whole BSP tree. As the bsp leaf might overlapping the dividing plane, i think you might have two same face exist on two different bsp leaf. I'll make further tests to prove this. --mobius. The biggest leaf in terms of number of faces in 3.3.5 contains more than 2100 faces (some ice giant in the Storm Peaks), so it's not advised to use more. (While I haven't investigated properly, there might be a limit at 8192 in 6.0.1.18179 --Schlumpf (talk) 11:18, 3 January 2016 (UTC)) fDist is relative to point (0,0,0) of whole WMO. children[0] is child on negative side of dividing plane, children[1] is on positive side. --Deamon (talk) 10:01, 15 January 2016 (UTC) #define epsilon 0.01F void MergeBox(CVect3 (&result)[2], float *box1, float *box2) { result[0][0] = box1[0]; result[0][1] = box1[1]; result[0][2] = box1[2]; result[1][0] = box2[0]; result[1][1] = box2[1]; result[1][2] = box2[2]; } void AjustDelta(CVect3 (&src)[2], float *dst, float coef) { float d1 = (src[1][0]- src[0][0]) * coef;// delta x float d2 = (src[1][1]- src[0][1]) * coef;// delta y float d3 = (src[1][2]- src[0][2]) * coef;// delta z dst[1] = d1 + src[0][1]; dst[0] = d2 + src[0][0]; dst[2] = d3 + src[0][2]; } void TraverseBsp(int iNode, CVect3 (&pEyes)[2] , CVect3 (&pBox)[2],void *(pAction)(T_BSP_NODE *,void *param),void *param) { int plane; float eyesmin_boxmin; float boxmax_eyesmax; float eyesmin_fdist; float eyes_max_fdist; float eyesmin_div_deltadist; CVect3 tBox1[2]; CVect3 tBox2[2]; CVect3 newEyes[2]; CVect3 ajusted; T_BSP_NODE *pNode = &m_tNode[iNode]; if ( pNode) { if (pNode->planetype & 4 ) { if(pAction == 0) { RenderGeometry(GetEngine3DInstance(),pNode); return; } else { pAction(pNode,param); } } plane =pNode->planetype & 3; eyesmin_boxmin = pEyes[0][plane] - pBox[0][plane]; if ( ( -epsilon < eyesmin_boxmin) | (-epsilon == eyesmin_boxmin) || (pEyes[1][plane]- pBox[0][plane]) >= -epsilon ) { boxmax_eyesmax = pBox[1][plane] - pEyes[1][plane]; if ( (epsilon < boxmax_eyesmax) | (epsilon == boxmax_eyesmax) || (pBox[1][plane] - pEyes[0][plane]) >= epsilon ) { memmove(tBox1,pBox,sizeof(pBox)); tBox1[0][plane] = pNode->fDist; memmove(tBox2,pBox,sizeof(pBox)); tBox2[1][plane] = pNode->fDist; eyesmin_fdist = pEyes[0][plane] - pNode->fDist; eyes_max_fdist = (pEyes[1][plane]) - pNode->fDist; if ( eyesmin_fdist >= -epsilon && eyesmin_fdist <= epsilon|| (eyes_max_fdist >= -epsilon) && eyes_max_fdist <= epsilon ) { if ( pNode->children[1] != (short)-1 ) TraverseBsp(pNode->children[1], pEyes, tBox1,pAction,param); if ( pNode->children[0] != (short)-1 ) TraverseBsp(pNode->children[0] , pEyes, tBox2,pAction,param); return; } if ( eyesmin_fdist > epsilon && eyes_max_fdist < epsilon) { if ( pNode->children[1] != (short)-1 ) TraverseBsp(pNode->children[1], pEyes, tBox1,pAction,param); return; } if ( eyesmin_fdist < -epsilon && eyes_max_fdist < -epsilon) { if ( pNode->children[0] != (short)-1 ) TraverseBsp(pNode->children[0] , pEyes, tBox2,pAction,param); return; } eyesmin_div_deltadist = (float)(eyesmin_fdist / (eyesmin_fdist - eyes_max_fdist)); AjustDelta(pEyes, ajusted, eyesmin_div_deltadist); if ( eyesmin_fdist <= 0.0 ) { if ( pNode->children[0] != (short)-1 ) { MergeBox(newEyes, &pEyes[0][0], ajusted); TraverseBsp(pNode->children[0] , newEyes, tBox2,pAction,param); } if (pNode->children[1] != (short)-1 ) { MergeBox(newEyes, ajusted, &pEyes[1][0]); TraverseBsp(pNode->children[1] , newEyes, tBox1,pAction,param); } } else { if ( pNode->children[1] != (short)-1 ) { MergeBox(newEyes, &pEyes[0][0], ajusted); TraverseBsp(pNode->children[1] , newEyes, tBox1,pAction,param); } if (pNode->children[0] != (short)-1 ) { MergeBox(newEyes, ajusted, &pEyes[1][0]); TraverseBsp(pNode->children[0] , newEyes, tBox2,pAction,param); } } } } } } CheckFromEyes(CVect3 (&pEyes)[2],void *(pAction)(T_BSP_NODE *,void *param),void *param ) { /*CVect3 eyes[2]; instance_mat.invert(); eyes[0] = _fixCoordSystemInv((instance_mat*p->m_pCameraViewport->GetCameraTarget())+CVect3(0,-10,0) ); eyes[1] = _fixCoordSystemInv((instance_mat*p->m_pCameraViewport->GetCameraTarget())+CVect3(0,60,0) ); // make vector down */ /* eyes[0] = CVect3(-1.474797e+001F, -1.195053e+001F, 5.416779e+000F); // Debug absolute position from WP Azaroth 1164,58,-10645.83 eyes[1] = CVect3(-1.474797e+001F, -1.195053e+001F, -1.754583e+003F); */ TraverseBsp(0,pEyes,m_bbox,pAction); } This BSP seems to be used for collision purpose only. An object could have has 2 collision system. The first one is encoded in a simplified Geometry (when MOPY. MaterialID=0xFF) the second one is encoded in T_BSP_NODE. Some object has collision method 1 only, some other uses method 2 only. Some object have both collision systems (some polygons are missing in the BSP but are present in the simplified geometry). how to use these 2 system remains unclear. For the time being, I check first the simplified geometry, and then if there is no collision, I apply a second pass using the BSP. It is sub-optimum, but it seems to work. Probably there is somewhere a flag telling us with which method we should use for the object. The code attached seems to work fine for BSP method--peter-pan. MOBR chunk Face indices for CAaBsp (MOBN). Unsigned shorts. Triangle indices (in MOVI which define triangles) to describe polygon planes defined by MOBN BSP nodes. Example code required to get an actual indicies array from MOBR array: var bpsIndicies = new Array(mobr.length*3); for (var i = 0; i < mobr.length; i++) { bpsIndicies[i*3 + 0] = movi[3*mobr[i]+0]; bpsIndicies[i*3 + 1] = movi[3*mobr[i]+1]; bpsIndicies[i*3 + 2] = movi[3*mobr[i]+2]; } Example code to get indicies into MOVT for triangles, referenced from BSP node definition: for (var triangleInd = node.firstFace; triangleInd<node.firstFace+node.numFaces; triangleInd++) { //3 vertices per triangle movt[bpsIndicies[3*triangleInd + 0]] movt[bpsIndicies[3*triangleInd + 1]] movt[bpsIndicies[3*triangleInd + 2]] } MOCV chunk Vertex colors, 4 bytes per vertex (BGRA), for WMO/v17 groups using indoor lighting. I don't know if this is supposed to work together with, or replace, the lights referenced in MOLR. But it sure is the only way for the ground around the goblin smelting pot to turn red in the Deadmines. (but some corridors are, in turn, too dark - how the hell does lighting work anyway, are there lightmaps hidden somewhere?) - I'm pretty sure WoW does not use lightmaps in it's WMO/v17s... After further inspection, this is it, actual pre-lit vertex colors for WMO/v17s - vertex lighting is turned off. This is used if flag 0x2000 in the MOGI chunk is on for this group. This pretty much fixes indoor lighting in Ironforge and Undercity. The "light" lights are used only for M2 models (doodads and characters). (The "too dark" corridors seemed like that because I was looking at it in a window - in full screen it looks pretty much the same as in the game) Now THAT's progress!!! Yes, 0x2000 (INDOOR) flagged WMO groups use _only_ MOCV for lighting, however this chunk is also used to light outdoor groups as well like lantern glow on buildings, etc. If 0x8 (OUTDOOR) flag is set, you start out with normal world lighting (like with light db params) and then you multiply these vertex colors by the texture color and add it to the world lighting. This makes many models look much better. See the Forsaken buildings in Howling Fjord for an example of some that make use of this a lot for glowing windows and lamps. Relaxok 18:29, 20 March 2013 (UTC) CMapObjGroup::FixColorVertexAlpha Prior to being passed to the shaders, MOCV values are manipulated by the CMapObj::FixColorVertexAlpha function in the client. This function performs different manipulations depending on the relationship between the vertex and the MOBA it appears in. It's possible that FixColorVertexAlpha did not always exist, or does not exist in later versions of WoW. It appears to have existed in WotLK, Cata, MoP, and WoD. In client versions that use FixColorVertexAlpha, without applying the function, certain parts of WMOs are noticeably wrong: fireplaces lack a glowing effect; the red light cast from bellows in blacksmith WMOs is undersaturated; etc. WMOs with MOHD->flags & 0x08 Only one manipulation takes place: MOCVs matching vertices in MOGP->batchCounts[1] and MOGP->batchCounts[2] are modified like so: 1. If MOGP.flags & 0x08, replace MOCV->color[a] with 255; else replace MOCV->color[a] with 0 All other WMOs The following manipulations take place: MOCVs matching vertices in MOGP->batchCounts[0] (aka unkBatchCount) are modified like so: 1. Subtract MOHD->color[r|g|b] 2. Subtract MOCV->color[r|g|b] * MOCV->color[a] 3. Divide new MOCV->color[r|g|b] values by 2.0 MOCVs matching vertices in MOGP->batchCounts[1] and MOGP->batchCounts[2] are modified like so: 1. Subtract MOHD->color 2. Add (MOCV->color[r|g|b] * MOCV->color[a]) >> 6 3. Divide MOCV->color[r|g|b] values by 2.0 4. If values are >= 0 and <= 255, keep value as is; else clamp new value to 0, 255. 5. If MOGP.flags & 0x08, replace MOCV->color[a] with 255; else replace MOCV->color[a] with 0 Decompiled code From build 18179, courtesy of schlumpf void CMapObjGroup::FixColorVertexAlpha(CMapObjGroup *mapObjGroup) { int begin_second_fixup = 0; if ( mapObjGroup->unkBatchCount ) { begin_second_fixup = *((unsigned __int16 *)&mapObjGroup->moba[(unsigned __int16)mapObjGroup->unkBatchCount] - 2) + 1; } if ( mapObjGroup->m_mapObj->mohd->flags & flag_has_some_outdoor_group ) { for (int i (begin_second_fixup); i < mapObjGroup->mocv_count; ++i) { mapObjGroup->mocv[i].w = mapObjGroup->m_groupFlags & SMOGroup::EXTERIOR ? 0xFF : 0x00; } } else { if ( mapObjGroup->m_mapObj->mohd->flags & flag_skip_base_color ) { v35 = 0; v36 = 0; v37 = 0; } else { v35 = (mapObjGroup->m_mapObj->mohd.color >> 0) & 0xff; v37 = (mapObjGroup->m_mapObj->mohd.color >> 8) & 0xff; v36 = (mapObjGroup->m_mapObj->mohd.color >> 16) & 0xff; } for (int mocv_index (0); mocv_index < begin_second_fixup; ++mocv_index) { mapObjGroup->mocv[mocv_index].x -= v36; mapObjGroup->mocv[mocv_index].y -= v37; mapObjGroup->mocv[mocv_index].z -= v35; v38 = mapObjGroup->mocv[mocv_index].w / 255.0f; v11 = mapObjGroup->mocv[mocv_index].x - v38 * mapObjGroup->mocv[mocv_index].x; assert (v11 > -0.5f); assert (v11 < 255.5f); mapObjGroup->mocv[mocv_index].x = v11 / 2; v13 = mapObjGroup->mocv[mocv_index].y - v38 * mapObjGroup->mocv[mocv_index].y; assert (v13 > -0.5f); assert (v13 < 255.5f); mapObjGroup->mocv[mocv_index].y = v13 / 2; v14 = mapObjGroup->mocv[mocv_index].z - v38 * mapObjGroup->mocv[mocv_index].z; assert (v14 > -0.5f); assert (v14 < 255.5f); mapObjGroup->mocv[mocv_index++].z = v14 / 2; } for (int i (begin_second_fixup); i < mapObjGroup->mocv_count; ++i) { v19 = (mapObjGroup->mocv[i].x * mapObjGroup->mocv[i].w) / 64 + mapObjGroup->mocv[i].x - v36; mapObjGroup->mocv[i].x = std::min (255, std::max (v19 / 2, 0)); v30 = (mapObjGroup->mocv[i].y * mapObjGroup->mocv[i].w) / 64 + mapObjGroup->mocv[i].y - v37; mapObjGroup->mocv[i].y = std::min (255, std::max (v30 / 2, 0)); v33 = (mapObjGroup->mocv[i].w * mapObjGroup->mocv[i].z) / 64 + mapObjGroup->mocv[i].z - v35; mapObjGroup->mocv[i].z = std::min (255, std::max (v33 / 2, 0)); mapObjGroup->mocv[i].w = mapObjGroup->m_groupFlags & SMOGroup::EXTERIOR ? 0xFF : 0x00; } } } CMapObj::AttenTransVerts Similar to FixColorVertexAlpha above, the client will also run MOCV values through the CMapObj::AttenTransVerts function prior to rendering. In MoP and WoD, it appears that the client only runs AttenTransVerts in cases where flag 0x01 is NOT set on MOHD.flags. AttenTransVerts only modifies MOCV values for vertices in MOGP.batchCounts[0] (aka unkBatchCount) batches. The function iterates over all vertices in MOGP.batchCounts[0], and checks all portals for the group: If no portals are found that lead to a group with MOGI.flags & (0x08 | 0x40), all MOCV alpha values are set to 0.0. If a portal is found leading to a group with MOGI.flags & (0x08 | 0x40), each MOCV alpha is manipulated to be a range of 0.0 to 1.0 based on the distance of the corresponding vertex to the portal. Additionally, the RGB values for each MOCV are bumped by: (0.0 to 1.0) * (127 - existingRGB) Decompiled code void CMapObj::AttenTransVerts (CMapObj *mapObj, CMapObjGroup *mapObjGroup) { mapObjGroup->field_98 |= 1u; if (!mapObjGroup->unkBatchCount) { return; } for ( std::size_t vertex_index (0) ; vertex_index < (*((unsigned __int16 *)&mapObjGroup->moba[(unsigned __int16)mapObjGroup->unkBatchCount] - 2) + 1) ; ++vertex_index ) { float opacity_accum (0.0); for ( std::size_t portal_ref_index (mapObjGroup->mogp->mopr_index) ; portal_ref_index < (mapObjGroup->mogp->mopr_index + mapObjGroup->mogp->mopr_count) ; ++portal_ref_index ) { SMOPortalRef const& portalRef (mapObj->mopr[portal_ref_index]); SMOPortal const& portal (mapObj->mopt[portalRef.portalIndex]); C3Vector const& vertex (&mapObjGroup->movt[vertex_index]); float const portal_to_vertex (distance (portal.plane, vertex)); C3Vector vertex_to_use (vertex); if (portal_to_vertex > 0.001 || portal_to_vertex < -0.001) { C3Ray ray ( C3Ray::FromStartEnd ( vertex , vertex + (portal_to_vertex > 0 ? -1 : 1) * portal.plane.normal , 0 ) ); NTempest::Intersect (ray, &portal.plane, 0LL, &vertex_to_use, 0.0099999998); } float distance_to_use; if ( NTempest::Intersect ( vertex_to_use , &mapObj->mopv[portal.base_index] , portal.index_count , C3Vector::MajorAxis (portal.plane.normal) ) ) { distance_to_use = portalRef.side * distance (portal.plane, vertex); } else { distance_to_use = NTempest::DistanceFromPolygonEdge (vertex, &mapObj->mopv[portal.base_index], portal.index_count); } if (mapObj->mogi[portalRef.group_index].flags & 0x48) { float v25 (distance_to_use >= 0.0 ? distance_to_use / 6.0f : 0.0f); if ((1.0 - v25) > 0.001) { opacity_accum += 1.0 - v25; } } else if (distance_to_use > -1.0) { opacity_accum = 0.0; if (distance_to_use < 1.0) { break; } } } float const opacity ( opacity_accum > 0.001 ? std::min (1.0f, opacity_accum) : 0.0f ); //! \note all assignments asserted to be > -0.5 && < 255.5f CArgb& color (mapObjGroup->mocv[vertex_index]); color.r = ((127.0f - color.r) * opacity) + color.r; color.g = ((127.0f - color.g) * opacity) + color.g; color.b = ((127.0f - color.b) * opacity) + color.b; color.a = opacity * 255.0; } } MLIQ chunk Specifies liquids inside WMOs. This is where the water from Stormwind and BFD etc. is hidden. (slime in Undercity, pool water in the Darnassus temple, some lava in IF) Chunk header: Offset Type Description 0x00 uint32 number of X vertices (xverts) 0x04 uint32 number of Y vertices (yverts) 0x08 uint32 number of X tiles (xtiles = xverts-1) 0x0C uint32 number of Y tiles (ytiles = yverts-1) 0x10 float[3] base coordinates for X and Y 0x1C uint16 material ID After the header, verts and tiles follow: struct SMOLVert { union { struct SMOWVert { uint8_t flow1; uint8_t flow2; uint8_t flow1Pct; uint8_t filler; float height; } waterVert; struct SMOMVert { int16_t s; int16_t t; float height; } magmaVert; }; } verts[xverts*yverts]; struct SMOLTile { uint8_t liquid : 6; uint8_t fishable : 1; uint8_t shared : 1; } tiles[xtiles*ytiles]; The liquid data contains the vertex height map (xverts * yverts * 8 bytes) and the tile flags (xtiles * ytiles bytes) as descripbed in ADT files (MCLQ chunk). The length and width of a liquid tile is the same as on the map, that is, 1/8th of the length of a map chunk. (which is in turn 1/16th the length of a map tile). Note that although I could read Mh2o's heightmap and existstable in row major order (like reading a book), I had to read this one in column major order to compensate for a 90° misrotation. --Bananenbrot 22:02, 1 August 2012 (UTC) Either the unknown data or the "types" must somehow control how the points at the edges work. In looking at 3D mesh screen captures, something is changed to create a flat edge where it meets other MLIQ chunks. The first Unknown data is always 0 when a point isn't used. Other seen values: 1, 4, 12, 22, 27, 31, 105, & 124. Not yet sure what they mean/how to use them, I suspect they become the modifier for the edge placement points. --Kjasi 14 February 2016 how to determine LiquidTypeRec to use enum liquid_basic_types { liquid_basic_types_water = 0, liquid_basic_types_ocean = 1, liquid_basic_types_magma = 2, liquid_basic_types_slime = 3, liquid_basic_types_MASK = 3, }; enum liquid_types { // ... LIQUID_WMO_Water = 13, LIQUID_WMO_Ocean = 14, LIQUID_Green_Lava = 15, LIQUID_WMO_Magma = 19, LIQUID_WMO_Slime = 20, LIQUID_END_BASIC_LIQUIDS = 20, LIQUID_FIRST_NONBASIC_LIQUID_TYPE = 21, LIQUID_NAXX_SLIME = 21, // ... }; enum SMOGroup::flags { LIQUIDSURFACE = 0x1000, is_not_water_but_ocean = 0x80000, }; liquid_types to_wmo_liquid (int x) { liquid_basic_types const basic (x & liquid_basic_types_MASK); switch (basic) { case liquid_basic_types_water: return (smoGroup->flags & is_not_water_but_ocean) ? LIQUID_WMO_Ocean : LIQUID_WMO_Water; case liquid_basic_types_ocean: return LIQUID_WMO_Ocean; case liquid_basic_types_magma: return LIQUID_WMO_Magma; case liquid_basic_types_slime: return LIQUID_WMO_Slime; } } if ( mapObj->mohd_data->field_3C & 4 ) { if ( smoGroup->field_34 < LIQUID_FIRST_NONBASIC_LIQUID_TYPE ) { this->liquid_type = to_wmo_liquid (smoGroup->field_34 - 1); } else { this->liquid_type = smoGroup->field_34; } } else { if ( smoGroup->field_34 == LIQUID_Green_Lava ) { this->liquid_type = 0; } else { int const liquidType (smoGroup->field_34 + 1); int const tmp (smoGroup->field_34); if ( smoGroup->field_34 < LIQUID_END_BASIC_LIQUIDS ) { this->liquid_type = to_wmo_liquid (smoGroup->field_34); } else { this->liquid_type = smoGroup->field_34 + 1; } assert (!liquidType || !(smoGroup->flags & SMOGroup::LIQUIDSURFACE)); } } MORI uint16_t triangle_strip_indices[]; MORB ignored if !CMap::enableTriangleStrips modifies MOBA, therefore has same count. size is not checked, but 2 * sizeof(int), even though it is only (int, short). struct MORB_entry { uint32_t start_index; uint16_t index_count; uint16_t padding; } overwrites 0xC and 0x10 of MOBA (start, count). MOTA Map Object Tangent Array struct MOTA { unsigned short first_index[moba_count]; // either -1 or first index of batch.count indices into tangents[]. // if auto-generated, only has entries for batches with // material[batch.material].shader == 10 or 14. C4Vector tangents[accumulated_num_indices]; // sum (batches[i].count | material[batches[i].material].shader == 10 or 14) }; Is auto generated, if there are batches with shaders 10 or 14, but no tangents. (And maybe some additional condition.) See CMapObjGroup::Create(). MOBS size = 0x18 struct MOBS_entry { char unk[0x18]; }; MDAL likely new in WoD, unknown contents. struct { CArgb replacement_for_header_color; // if -1 or not present, take color from header } mdal; MOPL (WoD(?)+) requires MOGP.canCutTerrain C4Plane terrain_cutting_planes[<=32];
  12. Changed Version to 21.5 Changed Implemented Version to Unset Changed Milestone to 22 Changed Priority to Normal
  13. Changed Status to Confirmed Changed Version to 21.5 Changed Implemented Version to Unset Changed Milestone to 22 Changed Priority to Normal Changed Type to Core
  14. Changed Status to Confirmed Changed Version to 21.5 Changed Implemented Version to Unset Changed Milestone to 22 Changed Priority to Normal
  15. Changed Status to Confirmed Changed Version to 22.1 Changed Milestone to 22 Changed Priority to Normal
  16. NPC: Sprite Darter These do not leave the cage, I expect you are supposed to escort them away from the camp to safety for the quest to be marked complete. Found a couple of videos on this.
  17. Changed Status to Confirmed Changed Implemented Version to 21.5 Changed Milestone to 22 Changed Priority to Normal
  18. Changed Status to Completed Changed Version to 22.1 Changed Implemented Version to 21.0 Changed Milestone to 21 Changed Priority to Normal Changed Type to Core
  19. Changed Status to Confirmed Changed Version to 22.1 Changed Implemented Version to Unset Changed Milestone to 22 Changed Priority to Normal
  20. Version 0.21.GitHub

    5375 downloads

    Mangos Two Server - 0.21 For: World of Warcraft: Wrath of The Lich King Wiki: https://www.getmangos.eu/wiki/documentation/installation-guides/ Status: Playable Support client versions: 3.3.5a (12340) MangosTwo-Serverx86.zip (32 Bit) MangosTwo-Serverx64.zip (64 Bit) Builtin Scripts (SD3 & Eluna) Map Extractors Realm, World & Character databases Authentication Realm-Daemon server (realmd) Mangos-Daemon world server (Mangosd) Requirements: C++ Redistributable for Visual Studio 2015 (Included in .zip)
  21. Version 0.21.GitHub

    3977 downloads

    Mangos One Server - 0.21 For: World of Warcraft: The Burning Crusade Wiki: https://www.getmangos.eu/wiki/documentation/installation-guides/ Status: Playable Support client versions: 2.3.4 (8606) Mangosone-Serverx86.zip (32 Bit) MangosOne-Serverx64.zip (64 Bit) Builtin Scripts (SD3 & Eluna) Map Extractors Realm, World & Character databases Authentication Realm-Daemon server (realmd) Mangos-Daemon world server (Mangosd) Requirements: C++ Redistributable for Visual Studio 2015 (Included in .zip)
  22. Changed Version to 21.0
  23. Thats fine thats what it is intended for
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. Privacy Policy Terms of Use