ADT/v18: Difference between revisions

From wowdev
Jump to navigation Jump to search
(Corrected chunk name : MTFX -> MTXF)
Line 65: Line 65:
   [[ADT#MFBO_chunk|MFBO]]* mfbo;                    ''// this is only set if flags & mhdr_MFBO.''
   [[ADT#MFBO_chunk|MFBO]]* mfbo;                    ''// this is only set if flags & mhdr_MFBO.''
   [[ADT#MH2O_chunk|MH2O]]* mh2o;
   [[ADT#MH2O_chunk|MH2O]]* mh2o;
   [[ADT#MTFX_chunk|MTFX]]* mtfx;
   [[ADT#MTXF_chunk|MTXF]]* mtxf;
   uint32_t unused[4];
   uint32_t unused[4];
  };
  };
Line 498: Line 498:
For getting the right feeling when walking, you should set the effectId which links to [[GroundEffectTexture.dbc]]. It defines the little detail doodads as well as the footstep sounds and if footprints are visible. You can set the id to -1 (int16!) to have no detail doodads and footsteps at all. Also, you need to define the currently on-top layer in the MCNK structure for the correct detail doodads to show up!
For getting the right feeling when walking, you should set the effectId which links to [[GroundEffectTexture.dbc]]. It defines the little detail doodads as well as the footstep sounds and if footprints are visible. You can set the id to -1 (int16!) to have no detail doodads and footsteps at all. Also, you need to define the currently on-top layer in the MCNK structure for the correct detail doodads to show up!


Introduced in Wrath of the Lich King, terrain can now reflect a skybox. This is used for icecubes made out of ADTs to reflect something. You need to have the [[ADT#MTFX_chunk|MTFX]] chunk in, if you want that. Look at an skybox Blizzard made to see how you should do it.
Introduced in Wrath of the Lich King, terrain can now reflect a skybox. This is used for icecubes made out of ADTs to reflect something. You need to have the [[ADT#MTXF_chunk|MTXF]] chunk in, if you want that. Look at an skybox Blizzard made to see how you should do it.


  struct SMLayer ''// 03-29-2005 By ObscuR, --[[User:Schlumpf|schlumpf_]] 19:47, 19 August 2009 (CEST)''
  struct SMLayer ''// 03-29-2005 By ObscuR, --[[User:Schlumpf|schlumpf_]] 19:47, 19 August 2009 (CEST)''
Line 545: Line 545:
*It doesnt matter for which layer you set the flag 0x400, it will always affect the groundlayer.  
*It doesnt matter for which layer you set the flag 0x400, it will always affect the groundlayer.  
*The common skyboxtextures need to have the same ration from width and height as the one posted above. If this isnt the case, it looks like that: [http://www.youtube.com/watch?v=YJDB6OEyxoc Video]  You see that it doesnt really fit the shape.
*The common skyboxtextures need to have the same ration from width and height as the one posted above. If this isnt the case, it looks like that: [http://www.youtube.com/watch?v=YJDB6OEyxoc Video]  You see that it doesnt really fit the shape.
*All of the skyboxtextures blizzard has need to specify a special flag to be decompressed correctly. This is done using the [[ADT#MTFX_chunk|MTFX]]-chunk. If the texture has a 1 in MTFX it will be interpreted correctly, else it will be green.
*All of the skyboxtextures blizzard has need to specify a special flag to be decompressed correctly. This is done using the [[ADT#MTXF_chunk|MTXF]]-chunk. If the texture has a 1 in MTXF it will be interpreted correctly, else it will be green.


--[[User:Cromon|Cromon]]
--[[User:Cromon|Cromon]]
Line 740: Line 740:
  };
  };


==MTFX chunk==
==MTXF chunk==


This chunk is an array of integers that are 1 or 0. 1 means that the texture at the same position in the MTEX array has to be handled differentely. The size of this chunk is always the same as there are entries in the MTEX chunk.  
This chunk is an array of integers that are 1 or 0. 1 means that the texture at the same position in the MTEX array has to be handled differentely. The size of this chunk is always the same as there are entries in the MTEX chunk.  


Simple as it is:
Simple as it is:
  struct MTFX
  struct MTXF
  {
  {
     uint32 mode[nMTEX];
     uint32 mode[nMTEX];
Line 752: Line 752:
The textures with this extended rendering mode are no normal ones, but skyboxes. These skyboxes are getting added as a reflection layer on the terrain. This is used for icecubes reflecting clouds etc. The layer being the reflection one needs to have the 0x400 flag in the MCLY chunk.
The textures with this extended rendering mode are no normal ones, but skyboxes. These skyboxes are getting added as a reflection layer on the terrain. This is used for icecubes reflecting clouds etc. The layer being the reflection one needs to have the 0x400 flag in the MCLY chunk.


'''Textures that are listed here need to be parsed otherwise. This specifies some specifications about the BLP. If you use one of the Terrain Cube Maps in a layer it will be displayed green as the client cannot decompress it in the normal way. If you set an entry in MTFX the texture will be loaded.'''
'''Textures that are listed here need to be parsed otherwise. This specifies some specifications about the BLP. If you use one of the Terrain Cube Maps in a layer it will be displayed green as the client cannot decompress it in the normal way. If you set an entry in MTXF the texture will be loaded.'''




An example ("TILESET\\Terrain Cube Maps\\TCB_CrystalSong_A.blp"):
An example ("TILESET\\Terrain Cube Maps\\TCB_CrystalSong_A.blp"):


'''Without''' MTFX set to 1: http://www.imagr.eu/up/4b794f10abbc1_WoWScrnShot_021510_144015.jpg
'''Without''' MTXF set to 1: http://www.imagr.eu/up/4b794f10abbc1_WoWScrnShot_021510_144015.jpg


'''With''' MTFX set 1: http://www.imagr.eu/up/4b794f3894877_WoWScrnShot_021510_143827.jpg
'''With''' MTXF set 1: http://www.imagr.eu/up/4b794f3894877_WoWScrnShot_021510_143827.jpg


--[[User:Cromon|Cromon]]
--[[User:Cromon|Cromon]]

Revision as of 18:19, 28 March 2012

ADT files contain terrain and object information for map tiles. They have a chunked structure just like the WDT files.

A map tile is split up into 16x16 = 256 map chunks. (not the same as file chunks, although each map chunk will have its own file chunk :) ) So there will be a few initial data chunks to specify textures, objects, models, etc. followed by 256 MCNK (mapchunk) chunks :) Each MCNK chunk has a small header of its own, and additional chunks within its data block, following the same id-size-data format.

An important note about the coordinate system used

Wow's main coordinate system is really weird but understanding it is very important in order to correctly interpret the ADT files. The best way to explain it is with screenshots: [[1]], [[2]].

It's important to remember that:

  • Unlike most coordinate systems Z is used for the height, Y is used for the width and X is used for the depth.
  • Y is "reversed". The more you go to the right (if north is up), the more it decreases.
  • The center of the axis is in the center of the map.
  • The top-left corner of the map has X = 17066, Y = 17066
  • The bottom-right corner of the map has X = -17066, Y = -17066
  • The bottom-left corner of the map has X = -17006, Y = 17066
  • The top-right corner of the map has X = 17006, Y = -17066

Just to be absolutely clear, assuming you playing a character that is not flying or swimming and is facing north:

  • Forward = D3DXVECTOR3(1, 0, 0);
  • Right = D3DXVECTOR3(0, -1, 0);
  • Up = D3DXVECTOR3(0, 0, 1);


This is the coordinate system used internally in all of the network packets and on most chunks in ADT files. I believe (but I'm not sure) that some chunks in this file use a different coordinate system (in particular for objects like M2's or WMO's that don't have an absolute position but only a relative one). MODF and MDDF do indeed use a different coordinate system, even for the absolute Position. (Anyone clarify on the reasoning behind this?)

Unfortunately though whoever wrote this article decided to use yet another coordinate system to explain things, different from Wow's.. So it's kind of a mess!

Map size, blocks, chunks

Introduction

All maps are divided into 64x64 blocks for a total of 4096 (some of which may be unused). Each block are divided into 16x16 chunks (not to be confused with for example the file chunks, such as the "MHDR" chunk.. Completely different thing!). While like I said blocks can be unused, each block will always use all of its 16x16 chunks.

Map size

Each block is 533.33333 yards long (yes, strange number, but that's the way it is). Since like we said above the map is divided into 64x64 blocks the total X and Y of the map will be 34133.33312 yards (but remember that since the center of the axis is at the center of the map, the maximum X and Y will be 17066.66656).

Since each block has 16x16 chunks, the size of a chunk will be 33.3333 yards.

ADT files and blocks

There is an .adt file for each existing block. If a block is unused it won't have an .adt file. The file will be: World/Maps/<InternalMapName>/<InternalMapName>_<BlockY>_<BlockX>.adt.

  • <InternalMapName> - The second field in Map.dbc
  • <BlockY> - obtained with the following formula: floor((32 - (y / 533.33333)))
  • <BlockX> - obtained with the following formula: floor((32 - (x / 533.33333)))

MVER chunk

struct MVER {
  uint32_t version;
};

MHDR chunk

  • Contains offsets relative to &MHDR.data in the file for specific chunks. WoW only takes this for parsing the ADT file.
struct MHDR {
  enum MHDRFlags {
    mhdr_MFBO = 1,                // contains a MFBO chunk.
    mhdr_northrend = 2,           // is set for some northrend ones.
  };
  uint32_t flags;
  MCIN* mcin;
  MTEX* mtex;
  MMDX* mmdx;
  MMID* mmid;
  MWMO* mwmo;
  MWID* mwid;
  MDDF* mddf;
  MODF* modf;
  MFBO* mfbo;                     // this is only set if flags & mhdr_MFBO.
  MH2O* mh2o;
  MTXF* mtxf;
  uint32_t unused[4];
};

MCIN chunk

  • Pointers to MCNK chunks and their sizes.
struct MCIN {
  struct MCINEntry {
    MCNK* mcnk;                   // absolute offset.
    uint32_t size;                // the size of the MCNK chunk, this is refering to.
    uint32_t flags;               // these two are always 0. only set in the client.
    uint32_t asyncId;	
  } entries[16*16];
};

MTEX chunk

  • List of textures used for texturing the terrain in this map tile.
struct MTEX {
  char filenames[0];              // zero-terminated strings with complete paths to textures. Referenced in MCLY.
};

MMDX chunk

  • List of filenames for M2 models that appear in this map tile.
struct MMDX {
  char filenames[0];              // zero-terminated strings with complete paths to models. Referenced in MMID.
};

MMID chunk

  • List of offsets of model filenames in the MMDX chunk.
struct MMID {
  uint32_t offsets[0];            // filename starting position in MMDX chunk. These entries are getting referenced in the MDDF chunk.
};

MWMO chunk

  • List of filenames for WMOs (world map objects) that appear in this map tile.
struct MWMO {
  char filenames[0];              // zero-terminated strings with complete paths to models. Referenced in MWID.
};

MWID chunk

  • List of offsets of WMO filenames in the MWMO chunk.
struct MWID {
  uint32_t offsets[0];            // filename starting position in MWMO chunk. These entries are getting referenced in the MODF chunk.
};

MDDF chunk

  • Placement information for doodads (M2 models). Additional to this, the models to render are referenced in each MCRF chunk.
struct MDDF {
  enum MDDFFlags {
    mddf_biodome = 1,             // this sets internal flags to | 0x800 (WDOODADDEF.var0xC).
    mddf_shrubbery = 2,           // the actual meaning of these is unknown to me. maybe biodome is for really big M2s.
  };
  struct MDDFEntry {
    uint32_t mmidEntry;           // references an entry in the MMID chunk, specifying the model to use.
    uint32_t uniqueId;            // this ID should be unique for all ADTs currently loaded. Best, they are unique for the whole map. Blizzard has these unique for the whole game.
    float position[3];
    float rotation[3];            // degrees. This is not the same coordinate system orientation like the ADT itself! (see history.)
    uint16_t scale;               // 1024 is the default size equaling 1.0f.
    uint16_t flags;               // values from enum MDDFFlags.
  } entries[0];
};

MODF chunk

  • Placement information for WMOs. Additional to this, the WMOs to render are referenced in each MCRF chunk. (?)
struct MODF {
  enum MODFFlags {
    modf_destroyable = 1,         // set for destroyable buildings like the tower in DeathknightStart. This makes it a server-controllable game object.
  };
  struct MODFEntry {
    uint32_t mwidEntry;           // references an entry in the MWID chunk, specifying the model to use.
    uint32_t uniqueId;            // this ID should be unique for all ADTs currently loaded. Best, they are unique for the whole map.
    float position[3];
    float rotation[3];            // same as in MDDF.
    float lowerBounds[3];         // these two are position plus the wmo bounding box.
    float upperBounds[3];         // they are used for defining when if they are rendered as well as collision.
    uint16_t flags;               // values from enum MODFFlags.
    uint16_t doodadSet;           // which WMO doodad set is used.
    uint16_t nameSet;             // which WMO name set is used. Used for renaming goldshire inn to northshire inn while using the same model.
    uint16_t padding;             // it reads only a WORD into the WMAPOBJDEF structure for name. I don't know about the rest.
  } entries[0];
};

MH2O chunk

This is a new chunk in WotLK. The old MCLQ subchunk died for it (still, its parsed by the client for backwards compatibility). Its now holding all values for the adt in one chunk. The chunk itself is seperated in three parts. A header, the data-block and the other referenced data.

Note: all offsets here refer to a position in the ADT file relative to the end of the MH2O header. So if you find 'O2HM' at 0x1000 then you add 0x1008 (the extra eight bytes being for the two four byte parameters to the chunk header) to all of these offsets to get their absolute position in the file.

Structure

Header

The header is a list of 256 entries of this structure:

0x00	int32	ofsInformation		An offset to the MH2O_Information struct(s) for this chunk.
0x04	int32	layerCount		0 if the chunk has no liquids. If > 1, the offsets will point to arrays.
0x08	int32	ofsRender		An offset to the 64 bit render mask.
struct MH2O_Header
{
	MH2O_Information *Information;		// points to an array with layers entries.
	uint32 layerCount;
	uint64 *Render;				// the blocks to render. 8*8 bits (=8 bytes). regardless of the information on width and height.
}

Information

This block is made of 0x18 bytes and is at ofsInformation for all layers.

0x00	int16	LiquidType		Points to LiquidType.dbc
0x02	int16	flags			
0x04	float	heightLevel1		The global liquid-height of this chunk. Which is always in there twice. Blizzard knows why.
0x08	float	heightLevel2		(Actually these values are not always identical, I think they define the highest and lowest points in the heightmap)
0x0C	byte	xOffset			The X offset of the liquid square (0-7)
0x0D	byte	yOffset			The Y offset of the liquid square (0-7)
0x0E	byte	width			The width of the liquid square (1-8)
0x0F	byte	height			The height of the liquid square (1-8)
0x10	int32	ofsMask2		Offset to some data.
0x14	int32	ofsHeightmap		Offset to MH2O_HeightmapData structure for this chunk.
struct MH2O_Information
{
	uint16 LiquidType;
	uint16 flags;
	float heightLevel1;
	float heightLevel2;
	byte xOffset;
	byte yOffset;
	byte width;
	byte height;
	uint8* Mask2;				// points to an array of bits with information about the mask. w*h bits (=h bytes).
	MH2O_HeightmapData *HeightmapData;	// note that if flags & 1 != 1, this chunk is "ocean" and the information stored
						// at HeightmapData does not correspond to the MH2O_HeightmapData structure.
						// Use heightLevel1 (or 2) for height in this case.
}

Data

This block holds w*h float values defining the actual water heights. After them, there are the transparency bytes. These bytes define, if the ocean is full blue or if the ground can be seen.

struct MH2O_HeightmapData
{
	// if type & 1 != 1, this chunk is "ocean".  in this case, do not use this structure.

	float heightMap[]; 	// w*h
	char transparency[]; 	// w*h
}

Explanation: MH2O_Header.Render

Indepth and with example. Thanks Slartibartfast.

The full heightmap that covers a whole chunk would be created from 9x9 float values, effectively creating 8x8 quadratic pieces. But since WotLK and the introduction of the MH2O chunk there is no more need to define the full heightmap if only part of a chunk is actually covered with water (such as with a thin river). Instead, MH2O_Information.x, .y, .width and .height define the size and location of a "liquid rectangle" which can be smaller than a full chunk.

An example: let's say there's a river crossing a chunk like this ('x' is the river):

++++++++
++++++++
xxxxxx++
++xxxxxx
++++++++
++++++++
++++++++
++++++++

This would lead to xOffset = 0, yOffset = 2, width = 8 and height = 2. The data at MH2o_HeightmapData.heightMap would then list 27 float values for the height map (a 9x3 height map which results in 8x2 quads, as shown in the picture above).

The data pointed to by MH2O_Header.Render would finally define which of the quads should be rendered. Its length is always eight bytes, and each byte is a bitmap which contains a 1 for each quad to be rendered and a 0 for each invisible quad. In our example this would be the following bytes: 0x00 (binary 00000000), 0x00 (binary 00000000), 0xFC (binary 11111100), 0x3F (binary 00111111), 0x00 (binary 00000000), 0x00 (binary 00000000), 0x00 (binary 00000000), 0x00 (binary 00000000).

Note that it is always possible to omit MH2O_Header.Render and/or MH2O_Information.HeightmapData to save some bytes in the ADT file! If MH2O_Render.Render is not given, the whole "liquid rectangle" is to be rendered. If MH2O_Information.HeightmapData is not given, then the height map consists only of values equal to heightLevel1 (I am not 100% sure of this one, but this approach seems to work fine for me).

--Namreeb 15:55, 26 October 2009 (HST) --Slartibartfast 00:41, 30 October 2008 (CEST)

MCNK chunk

  • After the above mentioned chunks come 256 individual MCNK chunks, row by row, starting from top-left (northwest). The MCNK chunks have a large block of data that starts with a header, and then has sub-chunks of its own.

Each map chunk has 9x9 vertices, and in between them 8x8 additional vertices, several texture layers, normal vectors, a shadow map, etc.

The MCNK header is 128 bytes large.

struct SMChunk // --schlumpf_ 17:01, 10 August 2009 (CEST), based on: 03-29-2005 By ObscuR, 11-08-2008 by Tharo
{
/*0x000*/  UINT32 flags;
/*0x004*/  UINT32 IndexX;
/*0x008*/  UINT32 IndexY;
/*0x00C*/  UINT32 nLayers;				// maximum 4
/*0x010*/  UINT32 nDoodadRefs;
/*0x014*/  MCVT* ofsHeight;
/*0x018*/  MCNR* ofsNormal;
/*0x01C*/  MCLY* ofsLayer;
/*0x020*/  MCRF* ofsRefs;
/*0x024*/  MCAL* ofsAlpha;
/*0x028*/  UINT32 sizeAlpha;
/*0x02C*/  MCSH* ofsShadow;				// only with flags&0x1
/*0x030*/  UINT32 sizeShadow;
/*0x034*/  UINT32 areaid;
/*0x038*/  UINT32 nMapObjRefs;
/*0x03C*/  UINT32 holes;
/*0x040*/  UINT2[8][8] ReallyLowQualityTextureingMap;	// It is used to determine which detail doodads to show. Values are an array of two bit unsigned integers, naming the layer.
/*0x044*/
/*0x048*/
/*0x04C*/
/*0x050*/  UINT32 predTex;				// 03-29-2005 By ObscuR; TODO: Investigate
/*0x054*/  UINT32 noEffectDoodad;				// 03-29-2005 By ObscuR; TODO: Investigate
/*0x058*/  MCSE* ofsSndEmitters;
/*0x05C*/  UINT32 nSndEmitters;				//will be set to 0 in the client if ofsSndEmitters doesn't point to MCSE!
/*0x060*/  MCLQ* ofsLiquid;
/*0x064*/  UINT32 sizeLiquid;  			// 8 when not used; only read if >8.
/*0x068*/  Vec3f position;
/*0x06C*/  
/*0x070*/ 
/*0x074*/  MCCV* ofsMCCV; 				// only with flags&0x40, had UINT32 textureId; in ObscuR's structure.
/*0x078*/  MCLV* ofsMCLV; 				// introduced in Cataclysm
/*0x07C*/  UINT32 unused; 			                                // currently unused
/*0x080*/
};

enum // 03-29-2005 By ObscuR
{
	FLAG_MCSH,
	FLAG_IMPASS,
	FLAG_LQ_RIVER,
	FLAG_LQ_OCEAN,
	FLAG_LQ_MAGMA,
	FLAG_MCCV,
};

The X' and Z' coordinates specify the top left corner of the map chunk, but they are in an alternate coordinate system! (yay for consistency!) This one has its 0,0 point in the middle of the world, and has the axes' directions reversed.

X = 32*533.3333 - X'
Z = 32*533.3333 - Z'

The Y base coordinate acts as a 'zero point' for the height values in the upcoming height map - that is, all height values are added to this Y height.

The X, Y, Z coordinates are in wow's main coordinates system as explained above. The struck explanation converts the coordinate system to one used by the author but that is never used in wow.

About the holes in the terrain: This is a bitmapped field, the least significant 16 bits are used row-wise in the following arrangement with a 1 bit meaning that the map chunk has a hole in that part of its area:

	0x1	0x2	0x4	0x8
	0x10	0x20	0x40	0x80
	0x100	0x200	0x400	0x800
	0x1000	0x2000	0x4000	0x8000

With this I've been able to fix some holes, the most obvious one being the Gates of Ironforge but there are some glitches for some other holes like the Lakeshire Inn - maybe this would require the more detailed height maps or for me to use a less cheap way to omit the hole triangles. :)

flags:

Flag		Meaning
0x1 		MCSH chunk available
0x2 		Impassable?
0x4 		River
0x8 		Ocean 
0x10		Magma
0x20		Slime?
0x40		MCCV chunk available
0x8000		Unknown, but heavily used in TBC.

MCVT sub-chunk

These are the actual height values for the 9x9+8x8 vertices. 145 floats in the following order/arrangement:. The values in here are only relative to the position given in the corresponding MCNK chunk.

  1    2    3    4    5    6    7    8    9
   10   11   12   13   14   15   16   17
 18   19   20   21   22   23   24   25   26
   27   28   29   30   31   32   33   34
 35   36   37   38   39   40   41   42   43
   44   45   46   47   48   49   50   51
 52   53   54   55   56   57   58   59   60
   61   62   63   64   65   66   67   68
 69   70   71   72   73   74   75   76   77
   78   79   80   81   82   83   84   85
 86   87   88   89   90   91   92   93   94
   95   96   97   98  99  100  101  102
103  104  105  106  107  108  109  110  111
  112  113  114  115  116  117  118  119
120  121  122  123  124  125  126  127  128
  129  130  131  132  133  134  135  136
137  138  139  140  141  142  143  144  145

The inner 8 vertices are only rendered in WoW when its using the up-close LoD. Otherwise, it only renders the outer 9. Nonsense? If I only change one of these it looks like: [3].

Ok, after a further look into it, WoW uses Squares out of 4 of the Outer(called NoLoD)-Vertices with one of the Inner(called LoD)-Vertices in the Center:

  1    2
   10
 18   19

So to render them in OpenGL you can use something like this:

  gl.glBegin(GL.GL_TRIANGLE_STRIP);
  for(int x=0;x<8;x++){
    for(int y=0;y<8;y++){
  	float nL1=mcvt.getValNoLOD(x, y);
  	float nL2=mcvt.getValNoLOD(x, y+1);
  	float nL3=mcvt.getValNoLOD(x+1, y);
  	float nL4=mcvt.getValNoLOD(x+1, y+1);
  	float L=mcvt.getValLOD(x, y);
  
  	gl.glVertex3f( y, x, nL1);
  	gl.glVertex3f( y+1, x, nL2);
  	gl.glVertex3f(y+0.5f, x+0.5f, L);
  
  	gl.glVertex3f( y, x, nL1);
  	gl.glVertex3f( y, x+1,nL3);
  	gl.glVertex3f(y+0.5f, x+0.5f,L);
  
  	gl.glVertex3f( y, x+1, nL3);
  	gl.glVertex3f( y+1, x+1, nL4);
  	gl.glVertex3f(y+0.5f, x+0.5f,L);
  
  	gl.glVertex3f( y+1, x,nL2);
  	gl.glVertex3f( y+1, x+1, nL4);
  	gl.glVertex3f(y+0.5f, x+0.5f, L);
  
    }
  }		
  gl.glEnd();

Although it seems there is still a mistake :/ --Tigurius

Old ones:

To stripify try this one: ( stripsize is now : 16*18 + 7*2 + 8*2 )

void stripify(V *in, V *out)
{
	for (int row=0; row<8; row++) { 
		V *thisrow = &in[row*9*2];
		V *nextrow = &in[row*9*2 + 9];
		V *overrow = &in[(row+1)*9*2];
		if (row>0) *out++ = thisrow[0];// jump end
		for (int col=0; col<8; col++) {
			*out++ = thisrow[col];
			*out++ = nextrow[col];
		}
		*out++ = thisrow[8];
		*out++ = overrow[8];
		*out++ = overrow[8];// jump start
		*out++ = thisrow[0];// jump end
		*out++ = thisrow[0];
		for (int col=0; col<8; col++) {
			*out++ = overrow[col];
 			*out++ = nextrow[col];
		}
		if (row<8) *out++ = overrow[8];
		if (row<7) *out++ = overrow[8];// jump start
	}
}

or try this one (made by tharo)

// to make it not TOO complicated u get data as 9*9 and 8*9 chain. 
// the 9th value is never used but calculation is more easy now ^^
private int stripify(Point3d[] in, Point3d[] out) {
     int outc=0;
     		
     for (int row=0; row<8; row++) {
           int thisrow = row*9*2;
           int nextrow = row*9*2 + 9;
           int overrow = (row+1) *9*2;
     			
           for(int col=0; col<8; col++) {
                 out[outc++] = in[thisrow+col];
                 out[outc++] = in[nextrow+col]; 
           }
           out[outc++] = in[thisrow+8];
     			
           for(int col=8; col>0; col--) {
                 out[outc++] = in[overrow+col];
                 out[outc++] = in[nextrow+col-1]; 
           }
           out[outc++] = in[overrow];
           out[outc++] = in[thisrow];
           out[outc++] = in[nexttow];
           out[outc++] = in[overrow];
     }
     for(int row=8; row>=0; row--) {
           out[outc++] = in[row*9*2];
     }		
     return outc;
}

These points look like they might be better organized as a triangle fan instead of a strip. This is my untested guess:

float wowData[145];
int off = 9;
float x, y;

for (y = 0; y < 8; ++y, off += 9)
{
    for (x = 0; x < 8; ++x, ++off)
    {
        glBegin(GL_TRIANGLE_FAN);
            glVertex3f(x, y, wowData[off]);
            glVertex3f(x - 0.5f, y - 0.5f, wowData[off - 9]);
            glVertex3f(x + 0.5f, y - 0.5f, wowData[off - 8]);
            glVertex3f(x + 0.5f, y + 0.5f, wowData[off + 9]);
            glVertex3f(x - 0.5f, y + 0.5f, wowData[off + 8]);
            glVertex3f(x - 0.5f, y - 0.5f, wowData[off - 9]);
        glEnd();
    }
}

--Kelmar

MCLV sub-chunk

It looks like an array of Colors, one per heightmap vertex (i.e. a sequence of 145 x unsigned int). Most of them are 0xFF000000, but some differ - e.g. 0xFF060102. In almost all cases RGB components are very low (0x00 - 0x10).

MCCV sub-chunk

  • This is used for vertex shading. You can manipulate the color of the vertices by adding this layer of colors blended onto the terrain. You can see the effects of this in this video (see 3:25 to 3:45) from Blizzcon 09. Additionally, there is a screenshot showing some of the effects possible.
struct MCCV {
  struct MCCVEntry {
    uint8_t red;                  // these values range from 0x00 to 0xFF with 0x7F being the default.
    uint8_t green;                // you can interpret the values as 0x7F being 1.0 and these values being multiplicated with the vertex colors.
    uint8_t blue;                 // setting all values to 0x00 makes a chunk completely black.
    uint8_t alpha;                // seems not to have any effect.
  } entries[9*9+8*8];
};

MCNR sub-chunk

  • Normal vectors for each corresponding vector above. Its followed by some weird unknown data which is not included in the chunk itself and might be some edge flag bitmaps.
struct MCNR {
  struct MCNREntry {
    int8_t normal[3];             // normalized. X, Z, Y. 127 == 1.0, -127 == -1.0.
  } entries[9*9+8*8];
};
uint8_t unknown[13];              // this data is not included in the MCNR chunk but additional data which purpose is unknown.

MCLY sub-chunk

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

  • Texture layer definitions for this map chunk. 16 bytes per layer, up to 4 layers.

Every texture layer other than the first will have an alpha map to specify blending amounts. The first layer is rendered with full opacity. To know which alphamap is used, there is an offset into the MCAL chunk. That one is relative to MCAL.

You can animate these by setting the flags. Only simple linear animations are possible. You can specify the direction in 45° steps and the speed.

The textureId is just the array index of the filename array in the MTEX chunk.

For getting the right feeling when walking, you should set the effectId which links to GroundEffectTexture.dbc. It defines the little detail doodads as well as the footstep sounds and if footprints are visible. You can set the id to -1 (int16!) to have no detail doodads and footsteps at all. Also, you need to define the currently on-top layer in the MCNK structure for the correct detail doodads to show up!

Introduced in Wrath of the Lich King, terrain can now reflect a skybox. This is used for icecubes made out of ADTs to reflect something. You need to have the MTXF chunk in, if you want that. Look at an skybox Blizzard made to see how you should do it.

struct SMLayer // 03-29-2005 By ObscuR, --schlumpf_ 19:47, 19 August 2009 (CEST)
{
/*000h*/  UINT32 textureId; 
/*004h*/  UINT32 flags;		
/*008h*/  UINT32 offsetInMCAL;
/*00Ch*/  INT32 effectId;			// (actually int16 and padding)
/*010h*/  		
};
Flag Description
0x001 Animation: Rotate 45° clockwise.
0x002 Animation: Rotate 90° clockwise.
0x004 Animation: Rotate 180° clockwise.
0x008 Animation: Make this faster.
0x010 Animation: Faster!!
0x020 Animation: Faster!!!!
0x040 Animation: Animate this texture as told in the other bits.
0x080 This will make the texture way brighter. Used for lava to make it "glow".
0x100 Use alpha map - set for every layer after the first
0x200 Alpha map is compressed (see MCAL chunk description)
0x400 This makes the layer behave like its a reflection of the skybox. See below


  • Explanation for flag 0x400:

First of all you can see the effects in this video: Video The texture that became the 0x400 flag was the following: Image . Have a look at the bright points that wander with the toon as it moves. This should imitate the stars from the sky (that you can find in the texture).

There are some important things you should be aware when using the flag 0x400:

  • It doesnt matter for which layer you set the flag 0x400, it will always affect the groundlayer.
  • The common skyboxtextures need to have the same ration from width and height as the one posted above. If this isnt the case, it looks like that: Video You see that it doesnt really fit the shape.
  • All of the skyboxtextures blizzard has need to specify a special flag to be decompressed correctly. This is done using the MTXF-chunk. If the texture has a 1 in MTXF it will be interpreted correctly, else it will be green.

--Cromon

MCRF sub-chunk

  • A list of with MCNK.nDoodadRefs + MCNK.nMapObjRefs indices into the file's MDDF and MODF chunks, saying which MCNK subchunk those particular doodads and objects are drawn within. This MCRF list contains duplicates for map doodads that overlap areas.

As both, WMOs and M2s are referenced here, they get doodad indices first, then WMOs. If you have a doodad and a WMO in the ADT as well as the MCNK, you will have a {0,0} in MCRF with nDoodadRefs and MCNK.nMapObjRefs being 1.

The client uses those MCRF-entries to calculate collision. Only objects which are referenced in the current chunk of the toon get checked against collision (this is only for MDX, WMO seem to have different collision). If a doodad entry from MDDF or MODF gets never referenced in a chunks MCRF it wont be drawn at all, WoW doesnt take the MDDF and MODF to draw the objects.

--Cromon

MCSH sub-chunk

  • Shadow map for static shadows on the terrain. Can be left out with the chunk&1 flag not set.

Thanks to Sylvain, the shadow maps work as follows: the shadows are stored per bit, not byte as 0 or 1 (off or on) so we have 8 bytes (which equates to 64 values) X 64 bytes (64 values in this case) which ends up as a square 64x64 shadowmap with either white or black. Note that the shadow values come LSB first.

MCAL sub-chunk

  • Alpha maps for additional texture layers.

There are 3 kinds of alpha map here. The number of alpha map can reference MCLY's flag with 0x100.

  • Uncompress mode size: 4096
  • Uncompress mode size: 2048
  • Self compress mode size: reference MCLY's offsetInMCAL can known the size

In order to know whether you deal with an uncompressed alphamap of 2048 bytes (preWotLK) or 4096 bytes (postWotLK), you should check the flags of the layer in the MCLY chunk.

Uncompress mode1

For every layer, a 32x64 array of alpha values. Can be used as a secondary texture with the modulation op to control the blending of the texture layers. For video cards with 2 texture units, this requires one pass per layer. (For 4 or more texture units, maybe this could be done faster using register combiners? Pixel shaders maybe?)

The size field of this chunk might be wrong for map chunks with zero texture layers. There are a couple of these in some of the development maps.

Similar to the shadow maps, the 32x64 bytes is really a 64x64 alpha map. There are 2 alpha values per byte, first 4 bits and second 4 bits. -Eric

Funny how this happened to work for the wrong sizes, too, because the upper 4 bits became the most significant in the alpha map, and the lower 4 appeared as noise. I sure didn't notice the difference :) It's fixed now of course. - Z.

Self compress mode

Flow - 21-10-2008:

In Wotlk these chunks are compressed.

I think it's a self made compression. Really simple. The size field of this chunk doesn't matter anymore.

How the decompression works

  • read a byte
  • check for sign bit
  • if set we are in fill mode else we are in copy mode
  • take the 7 lesser bits of the first byte as a count indicator
    • fill mode: read the next byte an fill it by count in resulting alpha map
    • copy mode: read the next count bytes and copy them in the resulting alpha map
  • if the alpha map is complete we are done otherwise start at 1. again

Sample C++ code

// 21-10-2008 by Flow
unsigned offI = 0; //offset IN buffer
unsigned offO = 0; //offset OUT buffer
char* buffIn; // pointer to data in adt file
char buffOut[4096]; // the resulting alpha map

while( offO < 4096 )
{
  // fill or copy mode
  bool fill = buffIn[offI] & 0x80;
  unsigned n = buffIn[offI] & 0x7F;
  offI++;
  for( unsigned k = 0; k < n; k++ )
  {
    if (offO == 4096) break;
    buffOut[offO] = buffIn[offI];
    offO++;
    if( !fill )
      offI++;
  }
  if( fill ) offI++;
}

Tested with Wotlk Beta build 8770

Uncompress mode2

Note: not all alpha maps are compressed; some of them are just stored as 4096 plain bytes. If an alpha map is compressed can be determined by checking the compression flag in the corresponding MCLY chunk entry. - Slartibartfast

Note: In order to know which compression you need to use you will need the WDT and the header of the Wdt.

if(layer[i].flags & 0x200)

--> Selfcompress mode

else if(wdtHeader.flags & 0x04)

--> 4096 bytes of plain data

else

--> 2048 compressed bytes like compress mode 1

Blend

Slartibartfast - 1 November 2008:

Blizzard has changed the way how the additional textures are blended onto the ground texture in Northrend (old continents still seem to be blended the old way; they also don't use the new alpha map format). They have gone from a "one-layer-per-step" approach to blending all the 4 textures in a single step according to the following formula:

finalColor = tex0 * (1.0 - (alpha1 + alpha2 + alpha3)) + tex1 * alpha1 + tex2 * alpha2 + tex3 * alpha3

So all the alpha values for the different layers including the ground layer add up to 1.0; the ground layer's alpha value is calculated to match this constraint.

How to render

It is of course possible to devise different ways to render such terrain; one way I use and of which I know that it's working is a 2-pass-approach: first render all ground textures without blending, then use a fragment shader program to mix the 1-3 additional layer textures and render them with a glBlendFunc setting of (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) on top of the ground texture already present in the framebuffer. The fragment program that mixes the textures would have to work like this short GLSL example:

gl_FragColor =   texture2D(texture0, vec2(gl_TexCoord[0])) * texture2D(texture3, vec2(gl_TexCoord[3])).r
               + texture2D(texture1, vec2(gl_TexCoord[1])) * texture2D(texture3, vec2(gl_TexCoord[3])).g
               + texture2D(texture2, vec2(gl_TexCoord[2])) * texture2D(texture3, vec2(gl_TexCoord[3])).b;

(this example uses 4 texture units: texture0 - texture3; the first 3 of them contain the actual textures, while the fourth unit contains the alpha maps combined in one RGB texture)

MCLQ sub-chunk

  • Water levels for this map chunk. This chunk is old and not really used anymore. Still, there is backwards compatibility in the client as old ADTs are not updated as it would be much data to patch it. I guess, it will be done in some expansion. You can fully use this chunk, even to have multiple water. You can have a lot of stacked water with this and the MH2O one. I advise you to implement the MH2O one as its better if you want to write a editor for ADT files.

The size of the chunk is in the mapchunk header. The type of liquid is given in the mapchunk flags, also in the header.

This information is old and incomplete as well as maybe wrong.

The first two floats specify the minimum and maximum liquid height level. After them comes a 9x9 height map for the water with the following format per vertex:

Offset 	Type 	Description
0x00 	int16 	?
0x02 	int16 	?
0x04 	float 	height value

The unknown int values might be color or transparency info, or something entirely different... Most frequently they are 0.

Followed by 8x8 bytes of flags for every liquid "tile" between the 9x9 vertex grid. The value 0x0F means do not render. (the specific flag for this seems to be 8 but I'm not sure - but it fixes some places where there was extra "water" sticking into the rest of the scenery)

Finally, 0x54 bytes of additional data, no idea what it's used for.

MCSE sub-chunk

  • Sound emitters.

This seems to be a bit different to that structure, ObscuR posted back then. From what I can see, WoW takes only 0x1C bytes per entry. Quite a big difference. This change might have happened, when they introduced the SoundEntriesAdvanced.dbc.

struct CWSoundEmitter // --schlumpf_ 21:44, 8 August 2009 (CEST)
{
/*000h*/  uint32 SoundEntriesAdvancedId;
/*004h*/  Vec3F position;
/*008h*/  
/*00Ch*/  
/*010h*/  Vec3F size; 					// I'm not really sure with this. I'm far too lazy to analyze this. Seems like noone ever needed these anyway.
/*014h*/  
/*018h*/  
};

old one by ObscuR (for older versions)

struct CWSoundEmitter // 04-29-2005 By ObscuR (Maybe not accurate but should look like this :p )
{
/*000h*/  UINT32 soundPointID;		
/*004h*/  UINT32 soundNameID;		
/*008h*/  float  pos[3];		
/*00Ch*/   		
/*010h*/  		
/*014h*/  float minDistance;	 		
/*018h*/  float maxDistance;	
/*01Ch*/  float cutoffDistance;		
/*020h*/  UINT16 startTime;	
/*022h*/  UINT16 endTime;
/*024h*/  UINT16 groupSilenceMin;		
/*026h*/  UINT16 groupSilenceMax; 	
/*028h*/  UINT16 playInstancesMin;
/*02Ah*/  UINT16 playInstancesMax;	
/*02Ch*/  UINT16 loopCountMin;
/*02Eh*/  UINT16 loopCountMax;
/*030h*/  UINT16 interSoundGapMin;
/*032h*/  UINT16 interSoundGapMax;
/*034h*/  	
};

MFBO chunk

  • A bounding box for flying.

This chunk is a "box" defining, where you can fly and where you can't. It also defines the height at which one you will fall into nowhere while your camera remains at the same position. Its actually two planes with 3*3 coordinates per plane.

Therefore the structure is:

struct plane{
   short[3][3] height;
};
struct MFBO
{
   plane maximum;
   plane minimum;
};

MTXF chunk

This chunk is an array of integers that are 1 or 0. 1 means that the texture at the same position in the MTEX array has to be handled differentely. The size of this chunk is always the same as there are entries in the MTEX chunk.

Simple as it is:

struct MTXF 
{
   uint32 mode[nMTEX];
}

The textures with this extended rendering mode are no normal ones, but skyboxes. These skyboxes are getting added as a reflection layer on the terrain. This is used for icecubes reflecting clouds etc. The layer being the reflection one needs to have the 0x400 flag in the MCLY chunk.

Textures that are listed here need to be parsed otherwise. This specifies some specifications about the BLP. If you use one of the Terrain Cube Maps in a layer it will be displayed green as the client cannot decompress it in the normal way. If you set an entry in MTXF the texture will be loaded.


An example ("TILESET\\Terrain Cube Maps\\TCB_CrystalSong_A.blp"):

Without MTXF set to 1: http://www.imagr.eu/up/4b794f10abbc1_WoWScrnShot_021510_144015.jpg

With MTXF set 1: http://www.imagr.eu/up/4b794f3894877_WoWScrnShot_021510_143827.jpg

--Cromon


Partitially retrieved from "http://wowdev.org/wiki/index.php/ADT"