https://wowdev.wiki/api.php?action=feedcontributions&user=Rangorn&feedformat=atomwowdev - User contributions [en]2024-03-28T17:14:46ZUser contributionsMediaWiki 1.39.6https://wowdev.wiki/index.php?title=ADT/v18&diff=23415ADT/v182017-01-28T16:09:47Z<p>Rangorn: /* MLDX (Legion+) */</p>
<hr />
<div>[[ADT]] files contain terrain and object information for map tiles. They have a [[Chunk|chunked]] structure just like the [[WDT]] files.<br />
<br />
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 [[ADT#MCNK_chunk|MCNK]] (mapchunk) chunks :) Each [[ADT#MCNK_chunk|MCNK]] chunk has a small header of its own, and additional chunks within its data block, following the same id-size-data format.<br />
<br />
== An important note about the coordinate system used ==<br />
Wow's main coordinate system is really weird but understanding it is very important in order to correctly interpret the ADT files.<br />
<br />
It's important to remember that:<br />
* Unlike most coordinate systems Z is used for the height, Y is used for the width and X is used for the depth.<br />
* Y is "reversed". The more you go to the right (if north is up), the more it decreases.<br />
* The center of the axis is in the center of the map.<br />
* The top-left corner of the map has X = 17066, Y = 17066<br />
* The bottom-right corner of the map has X = -17066, Y = -17066<br />
* The bottom-left corner of the map has X = -17006, Y = 17066<br />
* The top-right corner of the map has X = 17006, Y = -17066<br />
<br />
Just to be absolutely clear, assuming you playing a character that is not flying or swimming and is facing north:<br />
* Forward = D3DXVECTOR3(1, 0, 0);<br />
* Right = D3DXVECTOR3(0, -1, 0);<br />
* Up = D3DXVECTOR3(0, 0, 1);<br />
<br />
<br />
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).<br />
MODF and MDDF do indeed use a different coordinate system, even for the absolute Position. (Anyone clarify on the reasoning behind this?)<br />
<br />
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!<br />
<br />
== Map size, blocks, chunks ==<br />
=== Introduction ===<br />
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.<br />
<br />
=== Map size ===<br />
Each block is 533.33333 yards (1600 feet) in width and height. The map is divided into 64x64 blocks so the total width and height of the map will be 34133.33312 yards, however the origin of the coordinate system is at the center of the map so the minimum and maximum X and Y coordinates will be ±17066.66656).<br />
<br />
Since each block has 16x16 chunks, the size of a chunk will be 33.3333 yards (100 feet).<br />
<br />
=== ADT files and blocks ===<br />
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>_<BlockX>_<BlockY>.adt'''.<br />
<br />
* '''<InternalMapName>''' - {{Template:DBField|table=Map|column=m_Directory}}<br />
* '''<BlockX>''' - Index of the tile on the X axis<br />
* '''<BlockY>''' - Index of the tile on the Y axis<br />
<br />
Converting ADT co-ords to block X/Y can be done with the following formula (where axis is x or y): floor((32 - (axis / 533.33333)))<br />
<br />
==split files (Cata+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
Beginning with Cataclysm, ADTs are split into multiple files: .adt (root), _tex%d.adt (tex) and _obj%d.adt (obj) with %d being the level of detail (0 or 1). Chunks are distributed over the files. To load a map, the client loads a set of three and treats them as one. While the [[Media:Mjo_adt.png|distribution schema]] appears to be quite fixed, the client does not keep the semantics of which file is which and parses them all the same.<br />
<br />
The main difference content-wise is [[ADT#MCIN_chunk_.28.3CCata.29|MCIN]] being gone, and [[ADT#MCNK_chunk|MCNK]] in tex and obj files not having the header it has in root files.<br />
<br />
{{Template:Sandbox/PrettyVersion|expansionlevel=7}} added _lod.adt (lod) files as another type. They are used for increased draw distance, this time including low quality versions of liquids and geometry as well (in the end, root lod bands).<br />
== MVER chunk ==<br />
* split files: all<br />
struct MVER {<br />
uint32_t version;<br />
};<br />
<br />
== MHDR chunk ==<br />
* split files: root<br />
*'''Contains offsets relative to &MHDR.data in the file for specific chunks.''' WoW only takes this for parsing the ADT file.<br />
struct SMMapHeader {<br />
enum MHDRFlags {<br />
mhdr_MFBO = 1, ''// contains a MFBO chunk.''<br />
mhdr_northrend = 2, ''// is set for some northrend ones.''<br />
};<br />
uint32_t flags;<br />
[[ADT#MCIN_chunk|MCIN]]* mcin; // Cata+: obviously gone. probably all offsets gone.<br />
[[ADT#MTEX_chunk|MTEX]]* mtex;<br />
[[ADT#MMDX_chunk|MMDX]]* mmdx;<br />
[[ADT#MMID_chunk|MMID]]* mmid;<br />
[[ADT#MWMO_chunk|MWMO]]* mwmo;<br />
[[ADT#MWID_chunk|MWID]]* mwid;<br />
[[ADT#MDDF_chunk|MDDF]]* mddf;<br />
[[ADT#MODF_chunk|MODF]]* modf;<br />
[[ADT#MFBO_chunk|MFBO]]* mfbo; ''// this is only set if flags & mhdr_MFBO.''<br />
[[ADT#MH2O_chunk|MH2O]]* mh2o;<br />
[[ADT#MTXF_chunk_.28WotLK.2B.29|MTXF]]* mtxf;<br />
uint8_t mamp_value; // Cata+, explicit MAMP chunk overrides data<br />
uint8_t padding[3];<br />
uint32_t unused[3];<br />
} mhdr;<br />
<br />
== MCIN chunk (<Cata)==<br />
{{Template:SectionBox/VersionRange|max_expansionlevel=3|note=No longer possible due to [[#split_files_.28Cata.2B.29|split files]]}}<br />
*'''Pointers to [[ADT#MCNK_chunk|MCNK]] chunks and their sizes.'''<br />
struct SMChunkInfo {<br />
uint32_t offset; ''// absolute offset.''<br />
uint32_t size; ''// the size of the MCNK chunk, this is refering to.''<br />
uint32_t flags; ''// these two are always 0. only set in the client.'', FLAG_LOADED = 1<br />
uint32_t asyncId; <br />
} mcin[16*16];<br />
<br />
== MTEX chunk ==<br />
* split files: tex<br />
*'''List of textures used for texturing the terrain in this map tile.'''<br />
struct MTEX {<br />
char filenames[0]; ''// zero-terminated strings with complete paths to textures. Referenced in [[ADT/v18#MCLY_sub-chunk|MCLY]].''<br />
};<br />
<br />
== MMDX chunk ==<br />
* split files: obj<br />
*'''List of filenames for [[M2]] models that appear in this map tile.'''<br />
struct MMDX {<br />
char filenames[0]; ''// zero-terminated strings with complete paths to models. Referenced in [[ADT/v18#MMID_chunk|MMID]].''<br />
};<br />
<br />
== MMID chunk ==<br />
* split files: obj<br />
*'''List of offsets of model filenames in the [[ADT#MMDX_chunk|MMDX]] chunk.'''<br />
struct MMID {<br />
uint32_t offsets[0]; ''// filename starting position in [[ADT/v18#MMDX_chunk|MMDX]] chunk. These entries are getting referenced in the [[ADT/v18#MDDF_chunk|MDDF]] chunk.''<br />
};<br />
<br />
== MWMO chunk ==<br />
* split files: obj<br />
*'''List of filenames for [[WMO]]s (world map objects) that appear in this map tile.'''<br />
struct MWMO {<br />
char filenames[0]; ''// zero-terminated strings with complete paths to models. Referenced in [[ADT/v18#MMID_chunk|MWID]].''<br />
};<br />
<br />
== MWID chunk ==<br />
* split files: obj<br />
*'''List of offsets of WMO filenames in the [[ADT#MWMO_chunk|MWMO]] chunk.'''<br />
struct MWID {<br />
uint32_t offsets[0]; ''// filename starting position in [[ADT/v18#MWMO_chunk|MWMO]] chunk. These entries are getting referenced in the [[ADT/v18#MODF_chunk|MODF]] chunk.''<br />
};<br />
<br />
== MDDF chunk ==<br />
* split files: obj<br />
*'''Placement information for doodads ([[M2]] models).''' Additional to this, the models to render are referenced in each [[ADT/v18#MCRF_sub-chunk|MCRF]] chunk.<br />
enum MDDFFlags {<br />
mddf_biodome = 1, ''// this sets internal flags to | 0x800 (WDOODADDEF.var0xC).''<br />
mddf_shrubbery = 2, ''// the actual meaning of these is unknown to me. maybe biodome is for really big M2s. 6.0.1.18179 seems not to check <br />
// for this flag''<br />
};<br />
struct SMDoodadDef {<br />
uint32_t mmidEntry; ''// references an entry in the [[ADT#MMID_chunk|MMID]] chunk, specifying the model to use.''<br />
uint32_t uniqueId; ''// this ID should be unique for all ADTs currently loaded. Best, they are unique for the whole map. Blizzard has <br />
these unique for the whole game.''<br />
float position[3]; ''// This is relative to a corner of the map. Subtract 17066 from the non vertical values and you should start to see <br />
// something that makes sense. You'll then likely have to negate one of the non vertical values in whatever coordinate <br />
// system you're using to finally move it into place.''<br />
float rotation[3]; ''// degrees. This is not the same coordinate system orientation like the ADT itself! (see history.)<br />
uint16_t scale; ''// 1024 is the default size equaling 1.0f.''<br />
uint16_t flags; ''// values from enum MDDFFlags.''<br />
} doodadDefs[0];<br />
<br />
* How to compute a matrix to map M2 to world coordinates<br />
Math is the same as for '''[[ADT#MODF_chunk|MODF]]''', only with scale being added.<br />
<br />
Example in js with gl-matrix:<br />
createPlacementMatrix : function(mddf) {<br />
var TILESIZE = 533.333333333;<br />
<br />
var posx = 32 * TILESIZE - mddf.position[0];<br />
var posy = mddf.position[1];<br />
var posz = 32 * TILESIZE - mddf.position[2];<br />
<br />
var placementMatrix = mat4.create();<br />
mat4.identity(placementMatrix);<br />
<br />
mat4.rotateX(placementMatrix, placementMatrix, glMatrix.toRadian(90));<br />
mat4.rotateY(placementMatrix, placementMatrix, glMatrix.toRadian(90));<br />
<br />
mat4.translate(placementMatrix, placementMatrix, [posx, posy, posz]);<br />
<br />
mat4.rotateY(placementMatrix, placementMatrix, glMatrix.toRadian(mddf.rotation[1] - 270));<br />
mat4.rotateZ(placementMatrix, placementMatrix, glMatrix.toRadian(-mddf.rotation[0]));<br />
mat4.rotateX(placementMatrix, placementMatrix, glMatrix.toRadian(mddf.rotation[2] - 90));<br />
<br />
<br />
mat4.scale(placementMatrix, placementMatrix, [mddf.scale / 1024, mddf.scale / 1024, mddf.scale / 1024]);<br />
<br />
return placementMatrix;<br />
}<br />
<br />
== MODF chunk ==<br />
* split files: obj<br />
*'''Placement information for [[WMO]]s.''' Additional to this, the WMOs to render are referenced in each [[ADT/v18#MCRF_sub-chunk|MCRF]] chunk. ''(?)''<br />
enum MODFFlags {<br />
modf_destroyable = 1, ''// set for destroyable buildings like the tower in DeathknightStart. This makes it a server-controllable game object.''<br />
modf_use_lod = 2, ''// WoD(?)+: also load _LOD1.WMO for use dependent on distance''<br />
modf_unk_4 = 4, ''// Legion(?)+: unknown<br />
};<br />
struct SMMapObjDef {<br />
uint32_t nameId; ''// references an entry in the [[ADT#MWID_chunk|MWID]] chunk, specifying the model to use.''<br />
uint32_t uniqueId; ''// this ID should be unique for all ADTs currently loaded. Best, they are unique for the whole map.''<br />
{{Type|C3Vector}} position;<br />
{{Type|C3Vector}} rotation; ''// same as in [[ADT#MDDF_chunk|MDDF]].''<br />
{{Type|CAaBox}} extents; ''// position plus the transformed wmo bounding box. used for defining if they are rendered as well as collision.''<br />
uint16_t flags; ''// values from enum MODFFlags.''<br />
uint16_t doodadSet; ''// which WMO doodad set is used.''<br />
uint16_t nameSet; ''// which WMO name set is used. Used for renaming goldshire inn to northshire inn while using the same model.''<br />
uint16_t unk; ''// Legion(?)+: has data finally!<br />
} mapObjDefs[0];<br />
<br />
* How to compute a matrix to map WMO to world coordinates<br />
The position field in MODF is in Y-up coordinate system with upper-left corner being (0,0). And when you move to the right or down in this system the values increases.<br><br />
While in WoW world coordinates are in Z-up order with the top-left corner being (17.066, 17,066) and when you move to left or down - the values decreases.<br><br />
So to get a proper positioning you need to translate those values to world coordinate system by substracting them x and z (index 0 and 2 in position array) from 17,066.<br />
<br />
The rotation field is given in degrees. You would need to translate it into radians before passing to rotate function.<br />
<br />
Example implementation in js with gl-matrix library[https://github.com/toji/gl-matrix]:<br />
function createPlacementMatrix(modf){<br />
var TILESIZE = 533.333333333;<br />
<br />
var posx = 32*TILESIZE - modf.position[0];<br />
var posy = modf.position[1];<br />
var posz = 32*TILESIZE - modf.position[2];<br />
<br />
<br />
var placementMatrix = mat4.create();<br />
mat4.identity(placementMatrix);<br />
<br />
//Rotate coordinate system into Z-up<br />
mat4.rotateX(placementMatrix, placementMatrix, glMatrix.toRadian(90));<br />
mat4.rotateY(placementMatrix, placementMatrix, glMatrix.toRadian(90));<br />
<br />
//Translate the center of coordinate system<br />
mat4.translate(placementMatrix, placementMatrix, [posx, posy, posz]);<br />
<br />
// Rotate the coordinates<br />
mat4.rotateY(placementMatrix, placementMatrix, glMatrix.toRadian(modf.rotation[1]-270));<br />
mat4.rotateZ(placementMatrix, placementMatrix, glMatrix.toRadian(-modf.rotation[0]));<br />
mat4.rotateX(placementMatrix, placementMatrix, glMatrix.toRadian(modf.rotation[2]-90));<br />
<br />
<br />
return placementMatrix;<br />
}<br />
<br />
To get WMO vertexes into world position, you would need to multiply this matrix by 4-component vertex vector from left, with index 0-2 being x, y, z and index 3 being 1.<br />
<br />
placementMatrix * (x, y, z, 1)<br />
<br />
Example multiplication in js with gl-matrix:<br />
<br />
function translate (position, positionMatrix) {<br />
var position4 = vec4.fromValues(position[0], position[1], position[2], 1);<br />
vec4.transformMat4(position4 , position4 , positionMatrix);<br />
return position4;<br />
}<br />
<br />
For rendering, it is recommended to make this transformation in shader. So you would not have dublicate vertex data in gpu memory. <br />
Example in glsl:<br />
attribute vec3 aPosition;<br />
uniform mat4 uPlacementMat;<br />
<br />
void main() {<br />
vec4 worldPoint = uPlacementMat * vec4(aPosition, 1);<br />
gl_Position = worldPoint; <br />
}<br />
<br />
== MH2O chunk (WotLK+) ==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=3|note=Replacement for [[#MCLQ_sub-chunk|MCLQ]]}}<br />
* split files: root<br />
Replacement for [[#MCLQ_sub-chunk|MCLQ]], which is still parsed by the client for backwards compatibility.<br />
<br />
The chunk is seperated in three parts: A header (<tt>SMLiquidChunk</tt>), the data-block (<tt>SMLiquidInstance</tt>) and the referenced data from both. Reading it all at once and then using the supplied offsets inside is recommended. All offsets are relative to the data begin of the chunk. The header is the only part with a guaranteed position. All other parts are specified by offsets. <br />
<br />
===header===<br />
The header is a list of 256 entries:<br />
struct SMLiquidChunk {<br />
uint32_t offset_instances; // points to SMLiquidInstance[layer_count]<br />
uint32_t layer_count; // 0 if the chunk has no liquids. If > 1, the offsets will point to arrays.<br />
uint32_t offset_attributes; // points to mh2o_chunk_attributes, can be ommitted for all-0<br />
} chunks[16*16];<br />
<br />
===attributes===<br />
<tt>chunks[].offset_attributes</tt> points to one of<br />
struct mh2o_chunk_attributes {<br />
uint64_t fishable; // seems to be usable as visibility information.<br />
uint64_t deep;<br />
};<br />
<br />
===instances===<br />
<tt>chunks[].offset_instances</tt> points to <tt>chunks[].layer_count</tt> entries of<br />
struct SMLiquidInstance {<br />
{{Template:Type/foreign_key|type=uint16_t|table=LiquidType}} liquid_type;<br />
{{Template:Type/foreign_key|type=uint16_t|table=LiquidObject}} liquid_object;<br />
float min_height_level;<br />
float max_height_level;<br />
uint8_t x_offset; // The X offset of the liquid square (0-7)<br />
uint8_t y_offset; // The Y offset of the liquid square (0-7)<br />
uint8_t width; // The width of the liquid square (1-8)<br />
uint8_t height; // The height of the liquid square (1-8)<br />
uint32_t offset_exists_bitmap; // not all tiles in the instances need to be filled. always 8*8 bits.<br />
// offset can be 0 for all-exist. also see (and extend) [[Talk:ADT/v18#SMLiquidInstance]]<br />
uint32_t offset_vertex_data; // actual data format defined by {{Template:DBField|table=LiquidMaterial|column=m_LVF}} via {{Template:DBField|table=LiquidType|column=m_materialID}}<br />
// note: there is a hardcoded check that ocean (liquid_type = 2) is LVF 2 (depth only)<br />
};<br />
<br />
===instance vertex data===<br />
Regardless of format, the arrays will always have <tt>(width + 1) * (height + 1)</tt> entries.<br />
<br />
* no <tt>heightmap</tt> means that <tt>min/max_height_level</tt> is used for all points.<br />
* <tt>depthmap</tt> values are mapped to <tt>[0.0 1.0]</tt> for the shaders.<br />
<br />
struct uv_map_entry {<br />
uint16_t x; // divided by 8 for shaders<br />
uint16_t y;<br />
};<br />
<br />
====Case 0, Height and Depth data====<br />
This is the go-to layout for pre-WoD (MoP?) data. <br />
struct {<br />
float heightmap[];<br />
char depthmap[];<br />
};<br />
<br />
====Case 1, Height and Texture Coordinate data====<br />
struct {<br />
float heightmap[];<br />
uv_map_entry uvmap[];<br />
}<br />
<br />
I couldn't get the UV coordinates to make sense so I ended up disabling them. -- Rour<br />
<br />
====Case 2, Depth only data====<br />
This format is hard coded for liquid type ocean (2).<br />
struct {<br />
char depthmap[];<br />
}<br />
<br />
====Case 3, Height, Depth and Texture Coordinates====<br />
struct {<br />
float heightmap[];<br />
uv_map_entry uvmap[];<br />
char depthmap[];<br />
}<br />
<br />
===example, notes===<br />
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.<br />
<br />
An example: let's say there's a river crossing a chunk like this ('x' is the river):<br />
<br />
++++++++<br />
++++++++<br />
xxxxxx++<br />
++xxxxxx<br />
++++++++<br />
++++++++<br />
++++++++<br />
++++++++<br />
<br />
This would lead to <tt>x_offset</tt> = 0, <tt>y_offset</tt> = 2, <tt>width</tt> = 8 and <tt>height</tt> = 2. The data at <tt>vertex_data.heightmap</tt> 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).<br />
<br />
The data pointed to by <tt>offset_exists_bitmap</tt> would finally define which of the quads should be rendered. Its length is always <tt>eight</tt> 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).<br />
<br />
Note that it is always possible to omit <tt>offset_exists_bitmap</tt> and/or <tt>offset_vertex_data</tt> to save some bytes in the ADT file! If <tt>offset_attributes</tt> is not given, the whole liquid instance is to be rendered. If <tt>offset_vertex_data</tt> 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).<br />
<br />
== MCNK chunk ==<br />
* split files: header in root, no header in obj and tex<br />
<br />
*'''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.<br />
<br />
Each map chunk has 9x9 vertices, and in between them 8x8 additional vertices, several texture layers, normal vectors, a shadow map, etc.<br />
<br />
The [[ADT#MCNK_chunk|MCNK]] header is 128 bytes large.<br />
<br />
struct SMChunk<br />
{<br />
struct<br />
{<br />
uint32_t has_mcsh : 1;<br />
uint32_t impass : 1;<br />
uint32_t lq_river : 1;<br />
uint32_t lq_ocean : 1;<br />
uint32_t lq_magma : 1;<br />
uint32_t lq_slime : 1;<br />
uint32_t has_mccv : 1;<br />
uint32_t unknown_0x80 : 1;<br />
uint32_t : 7; // not set in 6.2.0.20338<br />
uint32_t do_not_fix_alpha_map : 1; // "fix" alpha maps in MCAL (4 bit alpha maps are 63*63 instead of 64*64).<br />
// Note that this also means that it *has* to be 4 bit alpha maps, otherwise UnpackAlphaShadowBits will assert.<br />
uint32_t high_res_holes : 1; // Since ~5.3 WoW uses full 64-bit to store holes for each tile if this flag is set.<br />
uint32_t : 15; // not set in 6.2.0.20338<br />
} flags;<br />
<br />
/*0x004*/ uint32_t IndexX;<br />
/*0x008*/ uint32_t IndexY;<br />
#if version < ?<br />
float radius;<br />
#endif<br />
/*0x00C*/ uint32_t nLayers; // maximum 4<br />
/*0x010*/ uint32_t nDoodadRefs;<br />
#if version >= ~5.3<br />
uint64_t holes_high_res; // only used with flags.high_res_holes<br />
#else<br />
/*0x014*/ uint32_t [[ADT#MCVT_sub-chunk|ofsHeight]];<br />
/*0x018*/ uint32_t [[ADT#MCNR_sub-chunk|ofsNormal]];<br />
#endif<br />
/*0x01C*/ uint32_t [[ADT#MCLY_sub-chunk|ofsLayer]];<br />
/*0x020*/ uint32_t [[ADT#MCRF_sub-chunk|ofsRefs]];<br />
/*0x024*/ uint32_t [[ADT#MCAL_sub-chunk|ofsAlpha]];<br />
/*0x028*/ uint32_t sizeAlpha;<br />
/*0x02C*/ uint32_t [[ADT#MCSH_sub-chunk|ofsShadow]]; // only with flags.has_mcsh<br />
/*0x030*/ uint32_t sizeShadow;<br />
/*0x034*/ uint32_t areaid; // in alpha: both zone id and sub zone id, as uint16s.<br />
/*0x038*/ uint32_t nMapObjRefs;<br />
/*0x03C*/ uint16_t holes_low_res;<br />
/*0x03E*/ uint16_t unknown_but_used; // in alpha: padding<br />
/*0x040*/ uint2_t[8][8] ReallyLowQualityTextureingMap; // "predTex", It is used to determine which detail doodads to show. Values are an array of two bit <br />
// unsigned integers, naming the layer.<br />
/*0x050*/ uint64_t noEffectDoodad; // WoD: may be an explicit MCDD chunk<br />
/*0x058*/ uint32_t [[ADT#MCSE_sub-chunk|ofsSndEmitters]];<br />
/*0x05C*/ uint32_t nSndEmitters; // will be set to 0 in the client if ofsSndEmitters doesn't point to [[ADT#MCSE_sub-chunk|MCSE]]!<br />
/*0x060*/ uint32_t [[ADT#MCLQ_sub-chunk|ofsLiquid]];<br />
/*0x064*/ uint32_t sizeLiquid; // 8 when not used; only read if >8.<br />
<br />
// in alpha, remainder is padding but unused.<br />
<br />
/*0x068*/ {{Template:Type|C3Vector}} position;<br />
/*0x074*/ uint32_t [[ADT#MCCV_sub-chunk|ofsMCCV]]; // only with flags.has_mccv, had uint32_t textureId; in ObscuR's structure.<br />
/*0x078*/ uint32_t [[ADT#MCLV_sub-chunk|ofsMCLV]]; // introduced in Cataclysm<br />
/*0x07C*/ uint32_t unused; // currently unused<br />
/*0x080*/<br />
};<br />
<br />
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:<br />
0x1 0x2 0x4 0x8<br />
0x10 0x20 0x40 0x80<br />
0x100 0x200 0x400 0x800<br />
0x1000 0x2000 0x4000 0x8000<br />
<br />
Since approx. 5.3, WoW uses a new 64-bit hole map if needed. If so, flag high_res_holes is set in the MCNK header and the 8 bytes at offset chunkBegin+0x14 (ofsHeight and ofsNormal) contain the hole map. Otherwise, the low resolution 16-bit hole map is used. See MapChunk::CreatePointers and/or [http://www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-bots-programs/wow-memory-editing/409718-navmesh-mpq-geometry-parsing-issues.html#post2757020 this post].<br />
Read those 8 bytes as byte array and check for holes like (Holes[row] >> col) & 1. If you interpret it as an uint64_t and shift like you did before on the 16-bit map, you have to invert the rows because of endianness.<br />
<br />
=== MCVT sub-chunk ===<br />
* split files: root<br />
<br />
struct <br />
{<br />
float height[9*9 + 8*8];<br />
} mcvt;<br />
<br />
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.'''<br />
1 2 3 4 5 6 7 8 9<br />
10 11 12 13 14 15 16 17<br />
18 19 20 21 22 23 24 25 26<br />
27 28 29 30 31 32 33 34<br />
35 36 37 38 39 40 41 42 43<br />
44 45 46 47 48 49 50 51<br />
52 53 54 55 56 57 58 59 60<br />
61 62 63 64 65 66 67 68<br />
69 70 71 72 73 74 75 76 77<br />
78 79 80 81 82 83 84 85<br />
86 87 88 89 90 91 92 93 94<br />
95 96 97 98 99 100 101 102<br />
103 104 105 106 107 108 109 110 111<br />
112 113 114 115 116 117 118 119<br />
120 121 122 123 124 125 126 127 128<br />
129 130 131 132 133 134 135 136<br />
137 138 139 140 141 142 143 144 145<br />
<br />
<del>The inner 8 vertices are only rendered in WoW when its using the up-close LoD. Otherwise, it only renders the outer 9.</del> Nonsense? If I only change one of these it looks like: [[Media:WoWScrnShot_022409_204540.jpg]].<br />
<br />
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:<br />
1 2<br />
10<br />
18 19<br />
So to render them in OpenGL you can use something like this:<br />
<br />
gl.glBegin(GL.GL_TRIANGLE_STRIP);<br />
for(int x=0;x<8;x++){<br />
for(int y=0;y<8;y++){<br />
float nL1=mcvt.getValNoLOD(x, y);<br />
float nL2=mcvt.getValNoLOD(x, y+1);<br />
float nL3=mcvt.getValNoLOD(x+1, y);<br />
float nL4=mcvt.getValNoLOD(x+1, y+1);<br />
float L=mcvt.getValLOD(x, y);<br />
<br />
gl.glVertex3f( y, x, nL1);<br />
gl.glVertex3f( y+1, x, nL2);<br />
gl.glVertex3f(y+0.5f, x+0.5f, L);<br />
<br />
gl.glVertex3f( y, x, nL1);<br />
gl.glVertex3f( y, x+1,nL3);<br />
gl.glVertex3f(y+0.5f, x+0.5f,L);<br />
<br />
gl.glVertex3f( y, x+1, nL3);<br />
gl.glVertex3f( y+1, x+1, nL4);<br />
gl.glVertex3f(y+0.5f, x+0.5f,L);<br />
<br />
gl.glVertex3f( y+1, x,nL2);<br />
gl.glVertex3f( y+1, x+1, nL4);<br />
gl.glVertex3f(y+0.5f, x+0.5f, L);<br />
<br />
}<br />
} <br />
gl.glEnd();<br />
Although it seems there is still a mistake :/<br />
--[[user:Tigurius|Tigurius]]<br />
<br />
Old ones:<br />
<br />
To stripify try this one: ( stripsize is now : 16*18 + 7*2 + 8*2 )<br />
<br />
void stripify(V *in, V *out)<br />
{<br />
for (int row=0; row<8; row++) { <br />
V *thisrow = &in[row*9*2];<br />
V *nextrow = &in[row*9*2 + 9];<br />
V *overrow = &in[(row+1)*9*2];<br />
if (row>0) *out++ = thisrow[0];// jump end<br />
for (int col=0; col<8; col++) {<br />
*out++ = thisrow[col];<br />
*out++ = nextrow[col];<br />
}<br />
*out++ = thisrow[8];<br />
*out++ = overrow[8];<br />
*out++ = overrow[8];// jump start<br />
*out++ = thisrow[0];// jump end<br />
*out++ = thisrow[0];<br />
for (int col=0; col<8; col++) {<br />
*out++ = overrow[col];<br />
*out++ = nextrow[col];<br />
}<br />
if (row<8) *out++ = overrow[8];<br />
if (row<7) *out++ = overrow[8];// jump start<br />
}<br />
}<br />
<br />
or try this one (made by tharo)<br />
<br />
// to make it not TOO complicated u get data as 9*9 and 8*9 chain. <br />
// the 9th value is never used but calculation is more easy now ^^<br />
private int stripify(Point3d[] in, Point3d[] out) {<br />
int outc=0;<br />
<br />
for (int row=0; row<8; row++) {<br />
int thisrow = row*9*2;<br />
int nextrow = row*9*2 + 9;<br />
int overrow = (row+1) *9*2;<br />
<br />
for(int col=0; col<8; col++) {<br />
out[outc++] = in[thisrow+col];<br />
out[outc++] = in[nextrow+col]; <br />
}<br />
out[outc++] = in[thisrow+8];<br />
<br />
for(int col=8; col>0; col--) {<br />
out[outc++] = in[overrow+col];<br />
out[outc++] = in[nextrow+col-1]; <br />
}<br />
out[outc++] = in[overrow];<br />
out[outc++] = in[thisrow];<br />
out[outc++] = in[nexttow];<br />
out[outc++] = in[overrow];<br />
}<br />
for(int row=8; row>=0; row--) {<br />
out[outc++] = in[row*9*2];<br />
} <br />
return outc;<br />
}<br />
<br />
These points look like they might be better organized as a triangle fan instead of a strip. This is my untested guess:<br />
float wowData[145];<br />
int off = 9;<br />
float x, y;<br />
<br />
for (y = 0; y < 8; ++y, off += 9)<br />
{<br />
for (x = 0; x < 8; ++x, ++off)<br />
{<br />
glBegin(GL_TRIANGLE_FAN);<br />
glVertex3f(x, y, wowData[off]);<br />
glVertex3f(x - 0.5f, y - 0.5f, wowData[off - 9]);<br />
glVertex3f(x + 0.5f, y - 0.5f, wowData[off - 8]);<br />
glVertex3f(x + 0.5f, y + 0.5f, wowData[off + 9]);<br />
glVertex3f(x - 0.5f, y + 0.5f, wowData[off + 8]);<br />
glVertex3f(x - 0.5f, y - 0.5f, wowData[off - 9]);<br />
glEnd();<br />
}<br />
}<br />
--[[user:Kelmar|Kelmar]]<br />
<br />
=== MCLV sub-chunk (Cata+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: root<br />
struct<br />
{<br />
{{Template:Type|CArgb}} values[9*9 + 8*8]; // or rgba?<br />
} chunk_lighting;<br />
<br />
Alpha is apparently ignored. Heavily used in Deepholm. In contrast to MCCV does not only color but also [[Media:Mjo_mclv.jpg|lightens up the vertices]].<br />
<br />
These are the result of baking level-designer placed omni lights. With {{Template:Sandbox/PrettyVersion|expansionlevel=6}}, they added the actual lights in [[WDT#lgt|_lgt.wdt]]s to do live lighting also influencing the character and shadow.<br />
<br />
=== MCCV sub-chunk (WotLK+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=3}}<br />
* split files: root<br />
*'''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 [http://www.youtube.com/watch?v=3FjuEPnuKtU this video] (see 3:25 to 3:45) from Blizzcon 09. Additionally, there is a [[Media:WoWScrnShot_092409_003328.jpg|screenshot]] showing some of the effects possible. <br />
<br />
struct MCCV {<br />
struct MCCVEntry {<br />
uint8_t blue; ''// these values range from 0x00 to 0xFF with 0x7F being the default.''<br />
uint8_t green; ''// you can interpret the values as 0x7F being 1.0 and these values being multiplicated with the vertex colors.''<br />
uint8_t red; ''// setting all values to 0x00 makes a chunk completely black.''<br />
uint8_t alpha; ''// seems not to have any effect.''<br />
} entries[9*9+8*8];<br />
};<br />
<br />
<strike>Probably argb, not rgba? --[[User:Schlumpf|Schlumpf]] ([[User talk:Schlumpf|talk]]) 17:05, 26 July 2015 (UTC)</strike><br />
<br />
in WotLK the client uses bgra --[[User:Adspartan|Adspartan]] ([[User talk:Adspartan|talk]]) 02:12, 22 May 2016 (CEST)<br />
<br />
=== MCNR sub-chunk ===<br />
* split files: root<br />
*'''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.<br />
<br />
struct SMNormal {<br />
struct MCNREntry {<br />
int8_t normal[3]; ''// normalized. X, Z, Y. 127 == 1.0, -127 == -1.0.''<br />
} entries[9*9+8*8];<br />
uint8_t unknown[3*3+2*2]; ''// this data is not included in the MCNR chunk but additional data which purpose is unknown. 0.5.3.3368 lists this as padding<br />
''// '''always''' 0 112 245 18 0 8 0 0 0 84 245 18 0. Nobody yet found a different pattern. The data is '''not''' derived from the normals.''<br />
''// It also does not seem that the client reads this data. --[[User:Schlumpf|Schlumpf]] ([[User talk:Schlumpf|talk]]) 23:01, 26 July 2015 (UTC)''<br />
''// While stated that this data is not "included in the MCNR chunk", the chunk-size defined for the MCNR chunk '''does''' cover this data. --[[User:Kruithne|Kruithne]] Feb 2016''<br />
''// ... from Cataclysm only (on LK files and before, MCNR defined size is 435 and not 448) [[User:Mjollna|Mjollna]] ([[User talk:Mjollna|talk]])<br />
};<br />
<br />
=== MCLY sub-chunk ===<br />
* split files: tex<br />
''Complete and right as of 19-AUG-09 (3.0.9 or higher)''<br />
*'''Texture layer definitions for this map chunk. 16 bytes per layer, up to 4 layers (thus, layer count = size / 16).''' <br />
<br />
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 [[ADT#MCAL_chunk|MCAL]] chunk. That one is relative to MCAL.<br />
<br />
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.<br />
<br />
The textureId is just the array index of the filename array in the [[ADT#MTEX_chunk|MTEX]] chunk.<br />
<br />
For getting the right feeling when walking, you should set the effectId which links to {{Template:DBField|table=GroundEffectTexture|column=m_ID}}. 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!<br />
<br />
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_.28WotLK.2B.29|MTXF]] chunk in, if you want that. Look at an skybox Blizzard made to see how you should do it.<br />
<br />
struct SMLayer<br />
{<br />
uint32_t textureId; <br />
struct<br />
{<br />
uint32_t animation_rotation : 3; // each tick is 45°<br />
uint32_t animation_speed : 3; <br />
uint32_t animation_enabled : 1;<br />
uint32_t overbright : 1; // This will make the texture way brighter. Used for lava to make it "glow".<br />
uint32_t use_alpha_map : 1; // set for every layer after the first<br />
uint32_t alpha_map_compressed : 1; // see MCAL chunk description<br />
uint32_t use_cube_map_reflection : 1; // This makes the layer behave like its a reflection of the skybox. See below<br />
uint32_t unknown_0x800 : 1; // WoD?+ if either of 0x800 or 0x1000 is set, [[#MTXF_chunk_.28WotLK.2B.29|texture effects]]' texture_scale is applied<br />
uint32_t unknown_0x1000 : 1; // WoD?+ see 0x800<br />
uint32_t : 19;<br />
} flags; <br />
uint32_t offsetInMCAL;<br />
{{Template:Type/foreign_key|table=GroundEffectTexture}} effectId; // 0xFFFFFFFF for none, in alpha: uint16_t + padding<br />
} layers[/* <= 4 */];<br />
<br />
To know how much entries there are, read until you hit the end of the chunk. Or divide it by 16 (4 + 4 + 4 + 4)<br />
<br />
*Explanation for flag '''0x400''' (use_cube_map_reflection):<br />
<br />
First of all you can see the effects in this video: [http://www.youtube.com/watch?v=uOE9OIG_rFM Video] The texture that became the 0x400 flag was the following: [[Media:4b795c7c7f36b_TCB_CrystalSong_B.jpg]] . 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).<br />
<br />
There are some important things you should be aware when using the flag 0x400:<br />
*It doesnt matter for which layer you set the flag 0x400, it will always affect the groundlayer. <br />
*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.<br />
*All of the skyboxtextures blizzard has need to specify a special flag to be decompressed correctly. This is done using the [[#MTXF_chunk_.28WotLK.2B.29|MTXF]]-chunk. If the texture has a 1 in MTXF it will be interpreted correctly, else it will be green.<br />
<br />
--[[User:Cromon|Cromon]]<br />
<br />
=== MCRF sub-chunk (<Cata)===<br />
{{Template:SectionBox/VersionRange|max_expansionlevel=3|note=Now split into [[#MCRD_.28Cata.2B.29|MCRD]] and [[#MCRW_.28Cata.2B.29|MCRW]]}}<br />
<br />
*'''A list of with MCNK.nDoodadRefs + MCNK.nMapObjRefs indices into the file's [[ADT#MDDF_chunk|MDDF]] and [[ADT#MODF_chunk|MODF]] chunks,''' saying which [[ADT#MCNK_chunk|MCNK]] subchunk those particular doodads and objects are drawn within. This [[ADT#MCRF_sub-chunk|MCRF]] list contains duplicates for map doodads that overlap areas. <br />
<br />
uint32_t doodad_refs[header.nDoodadRefs]; // into MDDF<br />
uint32_t object_refs[header.nMapObjRefs]; // into MODF<br />
<br />
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. --[[User:Cromon|Cromon]]<br />
<br />
===MCRD (Cata+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: obj<br />
uint32_t mddf_entry[];<br />
===MCRW (Cata+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: obj<br />
uint32_t modf_entry[];<br />
=== MCSH sub-chunk ===<br />
* split files: tex<br />
<br />
*'''Shadow map for static shadows on the terrain.''' Can be left out with the chunk&1 flag not set.<br />
<br />
struct {<br />
uint1_t shadow_map[64][64];<br />
} mcsh;<br />
<br />
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.<br />
<br />
=== MCAL sub-chunk ===<br />
* split files: tex<br />
*'''Alpha maps for additional texture layers.''' <br />
<br />
There are 3 kinds of alpha maps here: Which one depends on [[ADT#MCLY_sub-chunk|MCLY]] (0x200) and [[WDT#MPHD_chunk|WDT's MPHD]] (0x4 and 0x80) flags.<br />
<br />
{| border="1"<br />
! [[ADT#MCLY_sub-chunk|MCLY]] !! [[WDT#MPHD_chunk|WDT's MPHD]] !! mode<br />
|-<br />
| || || Uncompressed (2048)<br />
|-<br />
| align="center"| 0x200 set || || Compressed<br />
|-<br />
| || align="center"| 0x4 or 0x80 set || Uncompressed (4096)<br />
|-<br />
| align="center"| 0x200 set || align="center"| 0x4 or 0x80 set || Compressed: MPHD is only about bit depth!<br />
|}<br />
<br />
Additionally to this, [[ADT#MCNK_chunk|MCNK]] can have (and mostly does have) the 0x8000 flag. If this flag is set and bit depth is 8 ([[WDT#MPHD_chunk|WDT's MPHD]] has either flag), then modify alpha values so that if there is shadow at the corresponding position, the alpha value is multiplied by 0.7f (178 * alpha >> 8 to be exact).<br />
<br />
==== Uncompressed (4096) ====<br />
uint8_t alpha_map[64][64];<br />
<br />
For 4096 byte chunks, just read the values straight into your alpha channel. This, again, should result in 4096 bytes for a 64px by 64px size in the final alpha map.<br />
<br />
==== Uncompressed (2048) ====<br />
uint4_t alpha_map[64][64]; // note: the client uses a 4bit alpha texture, so does not do any custom normalization. Blit_Argb4444_Abgr8888 does `value & 0xF | 0x10 * value`.<br />
<br />
Contains 2048 bytes of data, but each byte contains two values in LSB first order. This should result in a 4096 alpha map (64px by 64px).<br />
<br />
* Read a byte.<br />
* Split the byte into two 4-bit values. eg: b0101b a0101a<br />
* This results in 16 possible values for each pixel; 15 is full alpha and 0 is no alpha. If you want to normalize, use Blit_Argb4444_Abgr8888 (value & 0xF | 0x10 * value).<br />
* Record each value separately into the alpha channel in the order of a then b.<br />
<br />
Not that depending on MCNK flag FLAG_DO_NOT_FIX_ALPHA_MAP, this is not actually a 64*64 map but rather a 63*63 map with the last row and column being equivalent to the previous one. <br />
<br />
struct { uint4_t alpha_map[63]; uint4_t ignored; }[63]; <br />
uint4_t ignored[64];<br />
<br />
where <br />
<br />
alpha_map[x][63] == alpha_map[x][62]<br />
alpha_map[63][x] == alpha_map[62][x]<br />
alpha_map[63][63] == alpha_map[62][62]<br />
<br />
and all "ignored" values are ignored, while still preserving the 2048 byte footprint.<br />
<br />
* I claim that this shall be handled by saving in fixed4444 only (i.e. always set mcnk.flags FLAG_DO_NOT_FIX_ALPHA_MAP and explicitly save the "fixed" (as in have all values) version). --[[User:Schlumpf|Schlumpf]] ([[User talk:Schlumpf|talk]]) 21:45, 24 October 2015 (UTC)<br />
<br />
==== Compressed ====<br />
struct<br />
{<br />
enum class mode_t<br />
{<br />
copy = 0, // append value[0..count - 1]<br />
fill = 1, // append value[0] count times<br />
};<br />
uint8_t mode : 1;<br />
uint8_t count : 7;<br />
uint8_t value[];<br />
} compressed_alpha_map[]; // size depends on content. will decompress to exactly 64*64 bytes.<br />
// minimum size is 32 times copy-127-x plus one copy-32-x, thus 66 bytes<br />
// maximum size is 32 times fill-127-x[127] plus one fill-32-x[127], thus 4129 bytes<br />
<br />
* read a byte<br />
* in the first bit of that byte (sign bit) check if it's true. When true that means we are in "fill" mode, if false, "copy" mode<br />
* the next 7 bits of the byte determine how many times we "fill" or "copy" (count) (eg, max value 127)<br />
* fill mode: repeat the byte following the one we just read *count* number of times into the alpha map<br />
* copy mode: read *count* number of following bytes into the alpha map<br />
* repeat until the map is complete<br />
Notes:<br />
* this should result in 4096 bytes in the alpha map (64 px by 64 px)<br />
* you should not have any extra compression data left over after completion<br />
* reads left to right, top to bottom<br />
<br />
-- Michael Redig 25-5-2015<br />
<br />
-- [[User:Flow|Flow]] 21-10-2008<br />
<br />
=====Sample C++ code=====<br />
unsigned offI = 0; //offset IN buffer<br />
unsigned offO = 0; //offset OUT buffer<br />
char* buffIn; // pointer to data in adt file<br />
char buffOut[4096]; // the resulting alpha map<br />
<br />
while( offO < 4096 )<br />
{<br />
// fill or copy mode<br />
bool fill = buffIn[offI] & 0x80;<br />
unsigned n = buffIn[offI] & 0x7F;<br />
offI++;<br />
for( unsigned k = 0; k < n; k++ )<br />
{<br />
buffOut[offO] = buffIn[offI];<br />
offO++;<br />
if( !fill )<br />
offI++;<br />
}<br />
if( fill ) offI++;<br />
}<br />
<br />
==== Blend ====<br />
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:<br />
<br />
finalColor = tex0 * (1.0 - (alpha1 + alpha2 + alpha3)) + tex1 * alpha1 + tex2 * alpha2 + tex3 * alpha3<br />
<br />
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.<br />
<br />
-- [[User:Slartibartfast|Slartibartfast]] 01-11-2008<br />
<br />
====How to render====<br />
<br />
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:<br />
<br />
gl_FragColor = texture2D(texture0, vec2(gl_TexCoord[0])) * texture2D(texture3, vec2(gl_TexCoord[3])).r<br />
+ texture2D(texture1, vec2(gl_TexCoord[1])) * texture2D(texture3, vec2(gl_TexCoord[3])).g<br />
+ texture2D(texture2, vec2(gl_TexCoord[2])) * texture2D(texture3, vec2(gl_TexCoord[3])).b;<br />
<br />
(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)<br />
<br />
Also, seems like alpha textures are upscaled using bicubic interpolation. --[[User:Deamon|Deamon]] ([[User talk:Deamon|talk]]) 21:49, 11 October 2015 (UTC)<br />
<br />
=== MCLQ sub-chunk ===<br />
{{Template:SectionBox/VersionRange|max_expansionlevel=2|note=Deprecated, but still parsed. Replaced with [[#MH2O_chunk_.28WotLK.2B.29|MH2O]]}}<br />
* split files: root<br />
<br />
*'''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. <br />
*'''Deprecated with WotLK''': I advise you to implement the MH2O one as its better if you want to write a editor for ADT files. <br />
<br />
The size of the chunk is in the mapchunk header. The type of liquid is given in the mapchunk flags, also in the header. <br />
<br />
This information is old and incomplete as well as maybe wrong. <br />
<br />
struct {<br />
{{Type|CRange}} height;<br />
struct SLVert {<br />
union {<br />
struct SWVert {<br />
char depth;<br />
char flow0Pct;<br />
char flow1Pct;<br />
char filler;<br />
float height;<br />
} waterVert;<br />
struct SOVert {<br />
char depth;<br />
char foam;<br />
char wet;<br />
char filler;<br />
} oceanVert;<br />
struct SMVert {<br />
unsigned __int16 s;<br />
unsigned __int16 t;<br />
float height;<br />
} magmaVert;<br />
};<br />
} verts[9*9];<br />
struct SLTiles {<br />
char tiles[8][8]; // 0x0f or 0x8 mean don't render (?)<br />
} tiles;<br />
uint32_t nFlowvs;<br />
struct SWFlowv {<br />
{{Type|CAaSphere}} sphere;<br />
{{Type|C3Vector}} dir;<br />
float velocity;<br />
float amplitude;<br />
float frequency;<br />
} flowvs[2]; // always 2 in file, independent on nFlowvs.<br />
};<br />
<br />
=== MCSE sub-chunk ===<br />
* split files: root<br />
*'''Sound emitters.'''<br />
<br />
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 {{Template:DBRef|table=SoundEntriesAdvanced}}.<br />
<br />
struct CWSoundEmitter<br />
{<br />
/*000h*/ {{Template:Type/foreign_key|table=SoundEntriesAdvanced}} entry_id;<br />
/*004h*/ {{Template:Type|C3Vector}} position;<br />
/*008h*/ <br />
/*00Ch*/ <br />
/*010h*/ {{Template:Type|C3Vector}} size; // I'm not really sure with this. I'm far too lazy to analyze this. Seems like <br />
noone ever needed these anyway.<br />
/*014h*/ <br />
/*018h*/ <br />
} MCSE[];<br />
<br />
==== old one by ObscuR (for older versions) ====<br />
struct CWSoundEmitter<br />
{<br />
/*000h*/ UINT32 soundPointID; <br />
/*004h*/ UINT32 soundNameID; <br />
/*008h*/ C3Vector pos;<br />
/*014h*/ float minDistance; <br />
/*018h*/ float maxDistance; <br />
/*01Ch*/ float cutoffDistance; <br />
/*020h*/ UINT16 startTime; <br />
/*022h*/ UINT16 endTime;<br />
/*024h*/ UINT16 mode;<br />
/*026h*/ UINT16 groupSilenceMin; <br />
/*028h*/ UINT16 groupSilenceMax; <br />
/*02Ah*/ UINT16 playInstancesMin;<br />
/*02Ch*/ UINT16 playInstancesMax; <br />
/*02Eh*/ BYTE loopCountMin;<br />
/*02Fh*/ BYTE loopCountMax;<br />
/*030h*/ UINT16 interSoundGapMin;<br />
/*032h*/ UINT16 interSoundGapMax;<br />
/*034h*/ <br />
} MCSE[];<br />
<br />
===MCBB (MoP+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: root<br />
struct // blend batches. max 256 per MCNK<br />
{<br />
uint32_t mbmh_index;<br />
uint32_t indexCount; // MBMI<br />
uint32_t indexFirst; // in addition to mbmh.mbnv_base<br />
uint32_t vertexCount; // MBNV <br />
uint32_t vertexFirst; // in addition to mbmh.mbnv_base<br />
} MCBB[];<br />
===MCMT (Cata+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: tex<br />
struct<br />
{<br />
{{Template:Type/foreign_key|type=uint8_t|table=TerrainMaterial}} material_id[4]; // per MCLY<br />
} MCMT;<br />
<br />
===MCDD (Cata?+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: root?<br />
struct // at least seen in WoD<br />
{<br />
// there seems to be a high-res (?) mode which is not taken into account <br />
// in live clients (32 bytes instead of 8) (?). if inlined to MCNK is low-res.<br />
uint1_t disable[8][8]; // disable detail doodads here<br />
// uint1_t disable[16][16];<br />
} MCDD;<br />
<br />
== MFBO chunk (BC+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=2}}<br />
* split files: root<br />
*'''A bounding box for flying.'''<br />
<br />
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.<br />
<br />
Therefore the structure is:<br />
struct plane{<br />
short[3][3] height;<br />
};<br />
struct<br />
{<br />
plane maximum;<br />
plane minimum;<br />
} MFBO;<br />
<br />
==MTXF chunk (WotLK+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=3}}<br />
* split files: tex<br />
Array of flags for entries in MTEX. Always same number of entries as MTEX. <br />
<br />
struct SMTextureFlags<br />
{<br />
uint32_t do_not_load_specular_or_height_texture_but_use_cubemap : 1; // probably just 'disable_all_shading'<br />
uint32_t : 3; // no non-zero values in 20490<br />
#if {{Template:Sandbox/VersionRange|min_expansionlevel=5}}<br />
uint32_t texture_scale : 4;<br />
uint32_t : 24; // no non-zero values in 20490<br />
#else<br />
uint32_t : 28; // no non-zero values in 20490<br />
#endif<br />
} MTXF[];<br />
<br />
In WotLK this is mostly used for layers using terrain cube maps (e.g. crystalsong's "TILESET\\Terrain Cube Maps\\TCB_CrystalSong_A.blp"). Without this flag, it would try to load the _s.blp and fail loading.<br />
<br />
==MTXP chunk (MoP?+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: tex<br />
struct SMTextureParams<br />
{<br />
SMTextureFlags flags; // same as in mtxf (or taken from there if no mtxp present)<br />
float height; // default 0.0 -- the _h texture values are scaled to [0, value) to determine actual "height".<br />
// this determines if textures overlap or not (e.g. roots on top of roads)<br />
float unk2; // default 1.0 -- some kind of weight<br />
uint32_t unk3; // padding? (no default, no non-zero values in 20490)<br />
} MTXP[];<br />
<br />
If unk1 == 0.0 and unk2 == 1.0, it will not load a _h texture.<br />
<br />
==MBMH (MoP+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: root<br />
* there can be multiple entries per map object (as different textures are possible)<br />
struct // blend mesh header<br />
{<br />
uint32_t mapObjectID; // (unique ID)<br />
uint32_t textureId; // of linked WMO<br />
char unknown[0xc];<br />
uint32_t mbmi_base; // first index into MBMI for this mesh<br />
uint32_t mbnv_base; // first index into MBNV for this mesh<br />
} MBMH[];<br />
==MBBB (MoP+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: root<br />
* each one corresponds to a MBMH entry of same index<br />
struct // blend mesh bounding boxes<br />
{<br />
uint32_t mapObjectID; // (unique ID) -- repeated for unknown reason<br />
{{Template:Type|CAaBox}} bounding;<br />
} MBBB[];<br />
<br />
==MBNV (MoP+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: root<br />
struct // blend mesh vertices<br />
{<br />
{{Template:Type|C3Vector}} pos;<br />
{{Template:Type|C3Vector}} normal;<br />
{{Template:Type|C2Vector}} texture_coordinates;<br />
{{Template:Type|CArgb}} color[3]; // used: PN: none; PNC: 0; PNC2: 0, 1; PNC2T: 0, 2<br />
} MBNV[];<br />
<br />
==MBMI (MoP+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: root<br />
struct // blend mesh indices<br />
{<br />
uint16_t index;<br />
} MBMI[];<br />
==MAMP (Cata+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: tex<br />
struct<br />
{<br />
char fred; // texture_size = 64 / (2^mamp_value). either defined here or in MHDR.mamp_value. <br />
} mamp;<br />
==MLHD (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
struct <br />
{<br />
uint32_t unknown;<br />
float some_kind_of_bounding[6];<br />
} ml_header;<br />
==MLVH (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
float ml_v_heightData[129*129 + 128*128 + additional]; // interleaved global height map + additional data of not fixed size <br />
==MLVI (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
uint16_t ml_v_indices[];<br />
==MLLL (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
struct<br />
{<br />
float _0; // some kind of size/lod band/distance/level, 32, 16, 8…<br />
uint32_t _1;<br />
uint32_t _2;<br />
uint32_t _3;<br />
uint32_t _4;<br />
} ml_ll;<br />
==MLND (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
struct<br />
{<br />
uint32_t _0;<br />
uint32_t _1;<br />
uint32_t _2;<br />
uint32_t _3;<br />
uint16_t indices[4];<br />
} ml_nd[];<br />
==MLSI (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
uint16_t ml_skirtIndices[]; // into MLVH<br />
==MLLD (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
struct SMLodLiquidData<br />
{<br />
enum<br />
{<br />
Flag_HasTileData = 1,<br />
_compressed_A = 2,<br />
_compressed_B = 4,<br />
};<br />
uint32_t m_flags;<br />
// … compressed (rle?) or uncompressed data, two blobs (16384 and 2048)<br />
} lodLiquidData[];<br />
==MLLN (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
MLLN, MLLI and MLLV are order-dependent. A MLLN introduces a new liquid, the following MLLI defines the indices, the next MLLV the vertices.<br />
struct MLLN<br />
{<br />
uint32_t _0;<br />
uint32_t num_indices; // MLLI<br />
uint32_t _2;<br />
uint16_t _3a;<br />
uint16_t _3b;<br />
uint32_t _4;<br />
uint32_t _5;<br />
} ml_liquid_n;<br />
==MLLV (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
{{Template:Type|C3Vector}} ml_liquid_vertices[];<br />
==MLLI (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
{{Template:Type|C3sVector}} ml_liquid_indices[]; // 3 shorts into MLLV<br />
<br />
==MLMD (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: obj1<br />
struct { // same as MODF but without bounding box (may be out of sync), better look at both<br />
uint32_t mwidEntry;<br />
uint32_t uniqueId;<br />
{{Template:Type|C3Vector}} position;<br />
{{Template:Type|C3Vector}} rotation;<br />
uint16_t flags;<br />
uint16_t doodadSet;<br />
uint16_t nameSet;<br />
uint16_t unk;<br />
} lod_object_defs[];<br />
==MLMX (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: obj1<br />
struct <br />
{<br />
{{Template:Type|CAaBox}} bounding;<br />
float radius;<br />
} lod_object_extents[]; // same count as MLMD<br />
<br />
Used for seeing objects map in legion from a defined distance based on the radius.<br />
The CaaBox is defined with a max point and a min point, the points coords are servers coordinates, so you should take the object position in MODF (wmo) or MDDF (m2) and convert it to server coords from clients coords<br />
Radius is generally approximatively around 50 (Radius is CaaBox[3] - CaaBox[0]), the visibility object depends from the view distance param too (maybe a factor like radius * viewdistance factor)<br />
Feel free to reformulate this as i'm not familiar with wiki structures --[[User:Rangorn|Rangorn]] ([[User talk:Rangorn|talk]]) 17:05, 28 January 2017 (UTC)<br />
<br />
==MLDD (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: obj1<br />
SMDoodadDef lod_doodad_defs[]; // see MDDF<br />
==MLDX (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: obj1<br />
struct <br />
{<br />
{{Template:Type|CAaBox}} bounding;<br />
float radius;<br />
} lod_doodad_extents[]; // same count as MLDD<br />
<br />
See MLMX for explanations --[[User:Rangorn|Rangorn]] ([[User talk:Rangorn|talk]]) 17:05, 28 January 2017 (UTC)<br />
<br />
==MLDL (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: obj1<br />
uint32_t unk[]; // same count as MLDD<br />
<br />
[[Category:Format]]</div>Rangornhttps://wowdev.wiki/index.php?title=ADT/v18&diff=23414ADT/v182017-01-28T16:09:05Z<p>Rangorn: /* MLMX (Legion+) */</p>
<hr />
<div>[[ADT]] files contain terrain and object information for map tiles. They have a [[Chunk|chunked]] structure just like the [[WDT]] files.<br />
<br />
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 [[ADT#MCNK_chunk|MCNK]] (mapchunk) chunks :) Each [[ADT#MCNK_chunk|MCNK]] chunk has a small header of its own, and additional chunks within its data block, following the same id-size-data format.<br />
<br />
== An important note about the coordinate system used ==<br />
Wow's main coordinate system is really weird but understanding it is very important in order to correctly interpret the ADT files.<br />
<br />
It's important to remember that:<br />
* Unlike most coordinate systems Z is used for the height, Y is used for the width and X is used for the depth.<br />
* Y is "reversed". The more you go to the right (if north is up), the more it decreases.<br />
* The center of the axis is in the center of the map.<br />
* The top-left corner of the map has X = 17066, Y = 17066<br />
* The bottom-right corner of the map has X = -17066, Y = -17066<br />
* The bottom-left corner of the map has X = -17006, Y = 17066<br />
* The top-right corner of the map has X = 17006, Y = -17066<br />
<br />
Just to be absolutely clear, assuming you playing a character that is not flying or swimming and is facing north:<br />
* Forward = D3DXVECTOR3(1, 0, 0);<br />
* Right = D3DXVECTOR3(0, -1, 0);<br />
* Up = D3DXVECTOR3(0, 0, 1);<br />
<br />
<br />
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).<br />
MODF and MDDF do indeed use a different coordinate system, even for the absolute Position. (Anyone clarify on the reasoning behind this?)<br />
<br />
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!<br />
<br />
== Map size, blocks, chunks ==<br />
=== Introduction ===<br />
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.<br />
<br />
=== Map size ===<br />
Each block is 533.33333 yards (1600 feet) in width and height. The map is divided into 64x64 blocks so the total width and height of the map will be 34133.33312 yards, however the origin of the coordinate system is at the center of the map so the minimum and maximum X and Y coordinates will be ±17066.66656).<br />
<br />
Since each block has 16x16 chunks, the size of a chunk will be 33.3333 yards (100 feet).<br />
<br />
=== ADT files and blocks ===<br />
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>_<BlockX>_<BlockY>.adt'''.<br />
<br />
* '''<InternalMapName>''' - {{Template:DBField|table=Map|column=m_Directory}}<br />
* '''<BlockX>''' - Index of the tile on the X axis<br />
* '''<BlockY>''' - Index of the tile on the Y axis<br />
<br />
Converting ADT co-ords to block X/Y can be done with the following formula (where axis is x or y): floor((32 - (axis / 533.33333)))<br />
<br />
==split files (Cata+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
Beginning with Cataclysm, ADTs are split into multiple files: .adt (root), _tex%d.adt (tex) and _obj%d.adt (obj) with %d being the level of detail (0 or 1). Chunks are distributed over the files. To load a map, the client loads a set of three and treats them as one. While the [[Media:Mjo_adt.png|distribution schema]] appears to be quite fixed, the client does not keep the semantics of which file is which and parses them all the same.<br />
<br />
The main difference content-wise is [[ADT#MCIN_chunk_.28.3CCata.29|MCIN]] being gone, and [[ADT#MCNK_chunk|MCNK]] in tex and obj files not having the header it has in root files.<br />
<br />
{{Template:Sandbox/PrettyVersion|expansionlevel=7}} added _lod.adt (lod) files as another type. They are used for increased draw distance, this time including low quality versions of liquids and geometry as well (in the end, root lod bands).<br />
== MVER chunk ==<br />
* split files: all<br />
struct MVER {<br />
uint32_t version;<br />
};<br />
<br />
== MHDR chunk ==<br />
* split files: root<br />
*'''Contains offsets relative to &MHDR.data in the file for specific chunks.''' WoW only takes this for parsing the ADT file.<br />
struct SMMapHeader {<br />
enum MHDRFlags {<br />
mhdr_MFBO = 1, ''// contains a MFBO chunk.''<br />
mhdr_northrend = 2, ''// is set for some northrend ones.''<br />
};<br />
uint32_t flags;<br />
[[ADT#MCIN_chunk|MCIN]]* mcin; // Cata+: obviously gone. probably all offsets gone.<br />
[[ADT#MTEX_chunk|MTEX]]* mtex;<br />
[[ADT#MMDX_chunk|MMDX]]* mmdx;<br />
[[ADT#MMID_chunk|MMID]]* mmid;<br />
[[ADT#MWMO_chunk|MWMO]]* mwmo;<br />
[[ADT#MWID_chunk|MWID]]* mwid;<br />
[[ADT#MDDF_chunk|MDDF]]* mddf;<br />
[[ADT#MODF_chunk|MODF]]* modf;<br />
[[ADT#MFBO_chunk|MFBO]]* mfbo; ''// this is only set if flags & mhdr_MFBO.''<br />
[[ADT#MH2O_chunk|MH2O]]* mh2o;<br />
[[ADT#MTXF_chunk_.28WotLK.2B.29|MTXF]]* mtxf;<br />
uint8_t mamp_value; // Cata+, explicit MAMP chunk overrides data<br />
uint8_t padding[3];<br />
uint32_t unused[3];<br />
} mhdr;<br />
<br />
== MCIN chunk (<Cata)==<br />
{{Template:SectionBox/VersionRange|max_expansionlevel=3|note=No longer possible due to [[#split_files_.28Cata.2B.29|split files]]}}<br />
*'''Pointers to [[ADT#MCNK_chunk|MCNK]] chunks and their sizes.'''<br />
struct SMChunkInfo {<br />
uint32_t offset; ''// absolute offset.''<br />
uint32_t size; ''// the size of the MCNK chunk, this is refering to.''<br />
uint32_t flags; ''// these two are always 0. only set in the client.'', FLAG_LOADED = 1<br />
uint32_t asyncId; <br />
} mcin[16*16];<br />
<br />
== MTEX chunk ==<br />
* split files: tex<br />
*'''List of textures used for texturing the terrain in this map tile.'''<br />
struct MTEX {<br />
char filenames[0]; ''// zero-terminated strings with complete paths to textures. Referenced in [[ADT/v18#MCLY_sub-chunk|MCLY]].''<br />
};<br />
<br />
== MMDX chunk ==<br />
* split files: obj<br />
*'''List of filenames for [[M2]] models that appear in this map tile.'''<br />
struct MMDX {<br />
char filenames[0]; ''// zero-terminated strings with complete paths to models. Referenced in [[ADT/v18#MMID_chunk|MMID]].''<br />
};<br />
<br />
== MMID chunk ==<br />
* split files: obj<br />
*'''List of offsets of model filenames in the [[ADT#MMDX_chunk|MMDX]] chunk.'''<br />
struct MMID {<br />
uint32_t offsets[0]; ''// filename starting position in [[ADT/v18#MMDX_chunk|MMDX]] chunk. These entries are getting referenced in the [[ADT/v18#MDDF_chunk|MDDF]] chunk.''<br />
};<br />
<br />
== MWMO chunk ==<br />
* split files: obj<br />
*'''List of filenames for [[WMO]]s (world map objects) that appear in this map tile.'''<br />
struct MWMO {<br />
char filenames[0]; ''// zero-terminated strings with complete paths to models. Referenced in [[ADT/v18#MMID_chunk|MWID]].''<br />
};<br />
<br />
== MWID chunk ==<br />
* split files: obj<br />
*'''List of offsets of WMO filenames in the [[ADT#MWMO_chunk|MWMO]] chunk.'''<br />
struct MWID {<br />
uint32_t offsets[0]; ''// filename starting position in [[ADT/v18#MWMO_chunk|MWMO]] chunk. These entries are getting referenced in the [[ADT/v18#MODF_chunk|MODF]] chunk.''<br />
};<br />
<br />
== MDDF chunk ==<br />
* split files: obj<br />
*'''Placement information for doodads ([[M2]] models).''' Additional to this, the models to render are referenced in each [[ADT/v18#MCRF_sub-chunk|MCRF]] chunk.<br />
enum MDDFFlags {<br />
mddf_biodome = 1, ''// this sets internal flags to | 0x800 (WDOODADDEF.var0xC).''<br />
mddf_shrubbery = 2, ''// the actual meaning of these is unknown to me. maybe biodome is for really big M2s. 6.0.1.18179 seems not to check <br />
// for this flag''<br />
};<br />
struct SMDoodadDef {<br />
uint32_t mmidEntry; ''// references an entry in the [[ADT#MMID_chunk|MMID]] chunk, specifying the model to use.''<br />
uint32_t uniqueId; ''// this ID should be unique for all ADTs currently loaded. Best, they are unique for the whole map. Blizzard has <br />
these unique for the whole game.''<br />
float position[3]; ''// This is relative to a corner of the map. Subtract 17066 from the non vertical values and you should start to see <br />
// something that makes sense. You'll then likely have to negate one of the non vertical values in whatever coordinate <br />
// system you're using to finally move it into place.''<br />
float rotation[3]; ''// degrees. This is not the same coordinate system orientation like the ADT itself! (see history.)<br />
uint16_t scale; ''// 1024 is the default size equaling 1.0f.''<br />
uint16_t flags; ''// values from enum MDDFFlags.''<br />
} doodadDefs[0];<br />
<br />
* How to compute a matrix to map M2 to world coordinates<br />
Math is the same as for '''[[ADT#MODF_chunk|MODF]]''', only with scale being added.<br />
<br />
Example in js with gl-matrix:<br />
createPlacementMatrix : function(mddf) {<br />
var TILESIZE = 533.333333333;<br />
<br />
var posx = 32 * TILESIZE - mddf.position[0];<br />
var posy = mddf.position[1];<br />
var posz = 32 * TILESIZE - mddf.position[2];<br />
<br />
var placementMatrix = mat4.create();<br />
mat4.identity(placementMatrix);<br />
<br />
mat4.rotateX(placementMatrix, placementMatrix, glMatrix.toRadian(90));<br />
mat4.rotateY(placementMatrix, placementMatrix, glMatrix.toRadian(90));<br />
<br />
mat4.translate(placementMatrix, placementMatrix, [posx, posy, posz]);<br />
<br />
mat4.rotateY(placementMatrix, placementMatrix, glMatrix.toRadian(mddf.rotation[1] - 270));<br />
mat4.rotateZ(placementMatrix, placementMatrix, glMatrix.toRadian(-mddf.rotation[0]));<br />
mat4.rotateX(placementMatrix, placementMatrix, glMatrix.toRadian(mddf.rotation[2] - 90));<br />
<br />
<br />
mat4.scale(placementMatrix, placementMatrix, [mddf.scale / 1024, mddf.scale / 1024, mddf.scale / 1024]);<br />
<br />
return placementMatrix;<br />
}<br />
<br />
== MODF chunk ==<br />
* split files: obj<br />
*'''Placement information for [[WMO]]s.''' Additional to this, the WMOs to render are referenced in each [[ADT/v18#MCRF_sub-chunk|MCRF]] chunk. ''(?)''<br />
enum MODFFlags {<br />
modf_destroyable = 1, ''// set for destroyable buildings like the tower in DeathknightStart. This makes it a server-controllable game object.''<br />
modf_use_lod = 2, ''// WoD(?)+: also load _LOD1.WMO for use dependent on distance''<br />
modf_unk_4 = 4, ''// Legion(?)+: unknown<br />
};<br />
struct SMMapObjDef {<br />
uint32_t nameId; ''// references an entry in the [[ADT#MWID_chunk|MWID]] chunk, specifying the model to use.''<br />
uint32_t uniqueId; ''// this ID should be unique for all ADTs currently loaded. Best, they are unique for the whole map.''<br />
{{Type|C3Vector}} position;<br />
{{Type|C3Vector}} rotation; ''// same as in [[ADT#MDDF_chunk|MDDF]].''<br />
{{Type|CAaBox}} extents; ''// position plus the transformed wmo bounding box. used for defining if they are rendered as well as collision.''<br />
uint16_t flags; ''// values from enum MODFFlags.''<br />
uint16_t doodadSet; ''// which WMO doodad set is used.''<br />
uint16_t nameSet; ''// which WMO name set is used. Used for renaming goldshire inn to northshire inn while using the same model.''<br />
uint16_t unk; ''// Legion(?)+: has data finally!<br />
} mapObjDefs[0];<br />
<br />
* How to compute a matrix to map WMO to world coordinates<br />
The position field in MODF is in Y-up coordinate system with upper-left corner being (0,0). And when you move to the right or down in this system the values increases.<br><br />
While in WoW world coordinates are in Z-up order with the top-left corner being (17.066, 17,066) and when you move to left or down - the values decreases.<br><br />
So to get a proper positioning you need to translate those values to world coordinate system by substracting them x and z (index 0 and 2 in position array) from 17,066.<br />
<br />
The rotation field is given in degrees. You would need to translate it into radians before passing to rotate function.<br />
<br />
Example implementation in js with gl-matrix library[https://github.com/toji/gl-matrix]:<br />
function createPlacementMatrix(modf){<br />
var TILESIZE = 533.333333333;<br />
<br />
var posx = 32*TILESIZE - modf.position[0];<br />
var posy = modf.position[1];<br />
var posz = 32*TILESIZE - modf.position[2];<br />
<br />
<br />
var placementMatrix = mat4.create();<br />
mat4.identity(placementMatrix);<br />
<br />
//Rotate coordinate system into Z-up<br />
mat4.rotateX(placementMatrix, placementMatrix, glMatrix.toRadian(90));<br />
mat4.rotateY(placementMatrix, placementMatrix, glMatrix.toRadian(90));<br />
<br />
//Translate the center of coordinate system<br />
mat4.translate(placementMatrix, placementMatrix, [posx, posy, posz]);<br />
<br />
// Rotate the coordinates<br />
mat4.rotateY(placementMatrix, placementMatrix, glMatrix.toRadian(modf.rotation[1]-270));<br />
mat4.rotateZ(placementMatrix, placementMatrix, glMatrix.toRadian(-modf.rotation[0]));<br />
mat4.rotateX(placementMatrix, placementMatrix, glMatrix.toRadian(modf.rotation[2]-90));<br />
<br />
<br />
return placementMatrix;<br />
}<br />
<br />
To get WMO vertexes into world position, you would need to multiply this matrix by 4-component vertex vector from left, with index 0-2 being x, y, z and index 3 being 1.<br />
<br />
placementMatrix * (x, y, z, 1)<br />
<br />
Example multiplication in js with gl-matrix:<br />
<br />
function translate (position, positionMatrix) {<br />
var position4 = vec4.fromValues(position[0], position[1], position[2], 1);<br />
vec4.transformMat4(position4 , position4 , positionMatrix);<br />
return position4;<br />
}<br />
<br />
For rendering, it is recommended to make this transformation in shader. So you would not have dublicate vertex data in gpu memory. <br />
Example in glsl:<br />
attribute vec3 aPosition;<br />
uniform mat4 uPlacementMat;<br />
<br />
void main() {<br />
vec4 worldPoint = uPlacementMat * vec4(aPosition, 1);<br />
gl_Position = worldPoint; <br />
}<br />
<br />
== MH2O chunk (WotLK+) ==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=3|note=Replacement for [[#MCLQ_sub-chunk|MCLQ]]}}<br />
* split files: root<br />
Replacement for [[#MCLQ_sub-chunk|MCLQ]], which is still parsed by the client for backwards compatibility.<br />
<br />
The chunk is seperated in three parts: A header (<tt>SMLiquidChunk</tt>), the data-block (<tt>SMLiquidInstance</tt>) and the referenced data from both. Reading it all at once and then using the supplied offsets inside is recommended. All offsets are relative to the data begin of the chunk. The header is the only part with a guaranteed position. All other parts are specified by offsets. <br />
<br />
===header===<br />
The header is a list of 256 entries:<br />
struct SMLiquidChunk {<br />
uint32_t offset_instances; // points to SMLiquidInstance[layer_count]<br />
uint32_t layer_count; // 0 if the chunk has no liquids. If > 1, the offsets will point to arrays.<br />
uint32_t offset_attributes; // points to mh2o_chunk_attributes, can be ommitted for all-0<br />
} chunks[16*16];<br />
<br />
===attributes===<br />
<tt>chunks[].offset_attributes</tt> points to one of<br />
struct mh2o_chunk_attributes {<br />
uint64_t fishable; // seems to be usable as visibility information.<br />
uint64_t deep;<br />
};<br />
<br />
===instances===<br />
<tt>chunks[].offset_instances</tt> points to <tt>chunks[].layer_count</tt> entries of<br />
struct SMLiquidInstance {<br />
{{Template:Type/foreign_key|type=uint16_t|table=LiquidType}} liquid_type;<br />
{{Template:Type/foreign_key|type=uint16_t|table=LiquidObject}} liquid_object;<br />
float min_height_level;<br />
float max_height_level;<br />
uint8_t x_offset; // The X offset of the liquid square (0-7)<br />
uint8_t y_offset; // The Y offset of the liquid square (0-7)<br />
uint8_t width; // The width of the liquid square (1-8)<br />
uint8_t height; // The height of the liquid square (1-8)<br />
uint32_t offset_exists_bitmap; // not all tiles in the instances need to be filled. always 8*8 bits.<br />
// offset can be 0 for all-exist. also see (and extend) [[Talk:ADT/v18#SMLiquidInstance]]<br />
uint32_t offset_vertex_data; // actual data format defined by {{Template:DBField|table=LiquidMaterial|column=m_LVF}} via {{Template:DBField|table=LiquidType|column=m_materialID}}<br />
// note: there is a hardcoded check that ocean (liquid_type = 2) is LVF 2 (depth only)<br />
};<br />
<br />
===instance vertex data===<br />
Regardless of format, the arrays will always have <tt>(width + 1) * (height + 1)</tt> entries.<br />
<br />
* no <tt>heightmap</tt> means that <tt>min/max_height_level</tt> is used for all points.<br />
* <tt>depthmap</tt> values are mapped to <tt>[0.0 1.0]</tt> for the shaders.<br />
<br />
struct uv_map_entry {<br />
uint16_t x; // divided by 8 for shaders<br />
uint16_t y;<br />
};<br />
<br />
====Case 0, Height and Depth data====<br />
This is the go-to layout for pre-WoD (MoP?) data. <br />
struct {<br />
float heightmap[];<br />
char depthmap[];<br />
};<br />
<br />
====Case 1, Height and Texture Coordinate data====<br />
struct {<br />
float heightmap[];<br />
uv_map_entry uvmap[];<br />
}<br />
<br />
I couldn't get the UV coordinates to make sense so I ended up disabling them. -- Rour<br />
<br />
====Case 2, Depth only data====<br />
This format is hard coded for liquid type ocean (2).<br />
struct {<br />
char depthmap[];<br />
}<br />
<br />
====Case 3, Height, Depth and Texture Coordinates====<br />
struct {<br />
float heightmap[];<br />
uv_map_entry uvmap[];<br />
char depthmap[];<br />
}<br />
<br />
===example, notes===<br />
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.<br />
<br />
An example: let's say there's a river crossing a chunk like this ('x' is the river):<br />
<br />
++++++++<br />
++++++++<br />
xxxxxx++<br />
++xxxxxx<br />
++++++++<br />
++++++++<br />
++++++++<br />
++++++++<br />
<br />
This would lead to <tt>x_offset</tt> = 0, <tt>y_offset</tt> = 2, <tt>width</tt> = 8 and <tt>height</tt> = 2. The data at <tt>vertex_data.heightmap</tt> 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).<br />
<br />
The data pointed to by <tt>offset_exists_bitmap</tt> would finally define which of the quads should be rendered. Its length is always <tt>eight</tt> 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).<br />
<br />
Note that it is always possible to omit <tt>offset_exists_bitmap</tt> and/or <tt>offset_vertex_data</tt> to save some bytes in the ADT file! If <tt>offset_attributes</tt> is not given, the whole liquid instance is to be rendered. If <tt>offset_vertex_data</tt> 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).<br />
<br />
== MCNK chunk ==<br />
* split files: header in root, no header in obj and tex<br />
<br />
*'''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.<br />
<br />
Each map chunk has 9x9 vertices, and in between them 8x8 additional vertices, several texture layers, normal vectors, a shadow map, etc.<br />
<br />
The [[ADT#MCNK_chunk|MCNK]] header is 128 bytes large.<br />
<br />
struct SMChunk<br />
{<br />
struct<br />
{<br />
uint32_t has_mcsh : 1;<br />
uint32_t impass : 1;<br />
uint32_t lq_river : 1;<br />
uint32_t lq_ocean : 1;<br />
uint32_t lq_magma : 1;<br />
uint32_t lq_slime : 1;<br />
uint32_t has_mccv : 1;<br />
uint32_t unknown_0x80 : 1;<br />
uint32_t : 7; // not set in 6.2.0.20338<br />
uint32_t do_not_fix_alpha_map : 1; // "fix" alpha maps in MCAL (4 bit alpha maps are 63*63 instead of 64*64).<br />
// Note that this also means that it *has* to be 4 bit alpha maps, otherwise UnpackAlphaShadowBits will assert.<br />
uint32_t high_res_holes : 1; // Since ~5.3 WoW uses full 64-bit to store holes for each tile if this flag is set.<br />
uint32_t : 15; // not set in 6.2.0.20338<br />
} flags;<br />
<br />
/*0x004*/ uint32_t IndexX;<br />
/*0x008*/ uint32_t IndexY;<br />
#if version < ?<br />
float radius;<br />
#endif<br />
/*0x00C*/ uint32_t nLayers; // maximum 4<br />
/*0x010*/ uint32_t nDoodadRefs;<br />
#if version >= ~5.3<br />
uint64_t holes_high_res; // only used with flags.high_res_holes<br />
#else<br />
/*0x014*/ uint32_t [[ADT#MCVT_sub-chunk|ofsHeight]];<br />
/*0x018*/ uint32_t [[ADT#MCNR_sub-chunk|ofsNormal]];<br />
#endif<br />
/*0x01C*/ uint32_t [[ADT#MCLY_sub-chunk|ofsLayer]];<br />
/*0x020*/ uint32_t [[ADT#MCRF_sub-chunk|ofsRefs]];<br />
/*0x024*/ uint32_t [[ADT#MCAL_sub-chunk|ofsAlpha]];<br />
/*0x028*/ uint32_t sizeAlpha;<br />
/*0x02C*/ uint32_t [[ADT#MCSH_sub-chunk|ofsShadow]]; // only with flags.has_mcsh<br />
/*0x030*/ uint32_t sizeShadow;<br />
/*0x034*/ uint32_t areaid; // in alpha: both zone id and sub zone id, as uint16s.<br />
/*0x038*/ uint32_t nMapObjRefs;<br />
/*0x03C*/ uint16_t holes_low_res;<br />
/*0x03E*/ uint16_t unknown_but_used; // in alpha: padding<br />
/*0x040*/ uint2_t[8][8] ReallyLowQualityTextureingMap; // "predTex", It is used to determine which detail doodads to show. Values are an array of two bit <br />
// unsigned integers, naming the layer.<br />
/*0x050*/ uint64_t noEffectDoodad; // WoD: may be an explicit MCDD chunk<br />
/*0x058*/ uint32_t [[ADT#MCSE_sub-chunk|ofsSndEmitters]];<br />
/*0x05C*/ uint32_t nSndEmitters; // will be set to 0 in the client if ofsSndEmitters doesn't point to [[ADT#MCSE_sub-chunk|MCSE]]!<br />
/*0x060*/ uint32_t [[ADT#MCLQ_sub-chunk|ofsLiquid]];<br />
/*0x064*/ uint32_t sizeLiquid; // 8 when not used; only read if >8.<br />
<br />
// in alpha, remainder is padding but unused.<br />
<br />
/*0x068*/ {{Template:Type|C3Vector}} position;<br />
/*0x074*/ uint32_t [[ADT#MCCV_sub-chunk|ofsMCCV]]; // only with flags.has_mccv, had uint32_t textureId; in ObscuR's structure.<br />
/*0x078*/ uint32_t [[ADT#MCLV_sub-chunk|ofsMCLV]]; // introduced in Cataclysm<br />
/*0x07C*/ uint32_t unused; // currently unused<br />
/*0x080*/<br />
};<br />
<br />
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:<br />
0x1 0x2 0x4 0x8<br />
0x10 0x20 0x40 0x80<br />
0x100 0x200 0x400 0x800<br />
0x1000 0x2000 0x4000 0x8000<br />
<br />
Since approx. 5.3, WoW uses a new 64-bit hole map if needed. If so, flag high_res_holes is set in the MCNK header and the 8 bytes at offset chunkBegin+0x14 (ofsHeight and ofsNormal) contain the hole map. Otherwise, the low resolution 16-bit hole map is used. See MapChunk::CreatePointers and/or [http://www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-bots-programs/wow-memory-editing/409718-navmesh-mpq-geometry-parsing-issues.html#post2757020 this post].<br />
Read those 8 bytes as byte array and check for holes like (Holes[row] >> col) & 1. If you interpret it as an uint64_t and shift like you did before on the 16-bit map, you have to invert the rows because of endianness.<br />
<br />
=== MCVT sub-chunk ===<br />
* split files: root<br />
<br />
struct <br />
{<br />
float height[9*9 + 8*8];<br />
} mcvt;<br />
<br />
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.'''<br />
1 2 3 4 5 6 7 8 9<br />
10 11 12 13 14 15 16 17<br />
18 19 20 21 22 23 24 25 26<br />
27 28 29 30 31 32 33 34<br />
35 36 37 38 39 40 41 42 43<br />
44 45 46 47 48 49 50 51<br />
52 53 54 55 56 57 58 59 60<br />
61 62 63 64 65 66 67 68<br />
69 70 71 72 73 74 75 76 77<br />
78 79 80 81 82 83 84 85<br />
86 87 88 89 90 91 92 93 94<br />
95 96 97 98 99 100 101 102<br />
103 104 105 106 107 108 109 110 111<br />
112 113 114 115 116 117 118 119<br />
120 121 122 123 124 125 126 127 128<br />
129 130 131 132 133 134 135 136<br />
137 138 139 140 141 142 143 144 145<br />
<br />
<del>The inner 8 vertices are only rendered in WoW when its using the up-close LoD. Otherwise, it only renders the outer 9.</del> Nonsense? If I only change one of these it looks like: [[Media:WoWScrnShot_022409_204540.jpg]].<br />
<br />
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:<br />
1 2<br />
10<br />
18 19<br />
So to render them in OpenGL you can use something like this:<br />
<br />
gl.glBegin(GL.GL_TRIANGLE_STRIP);<br />
for(int x=0;x<8;x++){<br />
for(int y=0;y<8;y++){<br />
float nL1=mcvt.getValNoLOD(x, y);<br />
float nL2=mcvt.getValNoLOD(x, y+1);<br />
float nL3=mcvt.getValNoLOD(x+1, y);<br />
float nL4=mcvt.getValNoLOD(x+1, y+1);<br />
float L=mcvt.getValLOD(x, y);<br />
<br />
gl.glVertex3f( y, x, nL1);<br />
gl.glVertex3f( y+1, x, nL2);<br />
gl.glVertex3f(y+0.5f, x+0.5f, L);<br />
<br />
gl.glVertex3f( y, x, nL1);<br />
gl.glVertex3f( y, x+1,nL3);<br />
gl.glVertex3f(y+0.5f, x+0.5f,L);<br />
<br />
gl.glVertex3f( y, x+1, nL3);<br />
gl.glVertex3f( y+1, x+1, nL4);<br />
gl.glVertex3f(y+0.5f, x+0.5f,L);<br />
<br />
gl.glVertex3f( y+1, x,nL2);<br />
gl.glVertex3f( y+1, x+1, nL4);<br />
gl.glVertex3f(y+0.5f, x+0.5f, L);<br />
<br />
}<br />
} <br />
gl.glEnd();<br />
Although it seems there is still a mistake :/<br />
--[[user:Tigurius|Tigurius]]<br />
<br />
Old ones:<br />
<br />
To stripify try this one: ( stripsize is now : 16*18 + 7*2 + 8*2 )<br />
<br />
void stripify(V *in, V *out)<br />
{<br />
for (int row=0; row<8; row++) { <br />
V *thisrow = &in[row*9*2];<br />
V *nextrow = &in[row*9*2 + 9];<br />
V *overrow = &in[(row+1)*9*2];<br />
if (row>0) *out++ = thisrow[0];// jump end<br />
for (int col=0; col<8; col++) {<br />
*out++ = thisrow[col];<br />
*out++ = nextrow[col];<br />
}<br />
*out++ = thisrow[8];<br />
*out++ = overrow[8];<br />
*out++ = overrow[8];// jump start<br />
*out++ = thisrow[0];// jump end<br />
*out++ = thisrow[0];<br />
for (int col=0; col<8; col++) {<br />
*out++ = overrow[col];<br />
*out++ = nextrow[col];<br />
}<br />
if (row<8) *out++ = overrow[8];<br />
if (row<7) *out++ = overrow[8];// jump start<br />
}<br />
}<br />
<br />
or try this one (made by tharo)<br />
<br />
// to make it not TOO complicated u get data as 9*9 and 8*9 chain. <br />
// the 9th value is never used but calculation is more easy now ^^<br />
private int stripify(Point3d[] in, Point3d[] out) {<br />
int outc=0;<br />
<br />
for (int row=0; row<8; row++) {<br />
int thisrow = row*9*2;<br />
int nextrow = row*9*2 + 9;<br />
int overrow = (row+1) *9*2;<br />
<br />
for(int col=0; col<8; col++) {<br />
out[outc++] = in[thisrow+col];<br />
out[outc++] = in[nextrow+col]; <br />
}<br />
out[outc++] = in[thisrow+8];<br />
<br />
for(int col=8; col>0; col--) {<br />
out[outc++] = in[overrow+col];<br />
out[outc++] = in[nextrow+col-1]; <br />
}<br />
out[outc++] = in[overrow];<br />
out[outc++] = in[thisrow];<br />
out[outc++] = in[nexttow];<br />
out[outc++] = in[overrow];<br />
}<br />
for(int row=8; row>=0; row--) {<br />
out[outc++] = in[row*9*2];<br />
} <br />
return outc;<br />
}<br />
<br />
These points look like they might be better organized as a triangle fan instead of a strip. This is my untested guess:<br />
float wowData[145];<br />
int off = 9;<br />
float x, y;<br />
<br />
for (y = 0; y < 8; ++y, off += 9)<br />
{<br />
for (x = 0; x < 8; ++x, ++off)<br />
{<br />
glBegin(GL_TRIANGLE_FAN);<br />
glVertex3f(x, y, wowData[off]);<br />
glVertex3f(x - 0.5f, y - 0.5f, wowData[off - 9]);<br />
glVertex3f(x + 0.5f, y - 0.5f, wowData[off - 8]);<br />
glVertex3f(x + 0.5f, y + 0.5f, wowData[off + 9]);<br />
glVertex3f(x - 0.5f, y + 0.5f, wowData[off + 8]);<br />
glVertex3f(x - 0.5f, y - 0.5f, wowData[off - 9]);<br />
glEnd();<br />
}<br />
}<br />
--[[user:Kelmar|Kelmar]]<br />
<br />
=== MCLV sub-chunk (Cata+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: root<br />
struct<br />
{<br />
{{Template:Type|CArgb}} values[9*9 + 8*8]; // or rgba?<br />
} chunk_lighting;<br />
<br />
Alpha is apparently ignored. Heavily used in Deepholm. In contrast to MCCV does not only color but also [[Media:Mjo_mclv.jpg|lightens up the vertices]].<br />
<br />
These are the result of baking level-designer placed omni lights. With {{Template:Sandbox/PrettyVersion|expansionlevel=6}}, they added the actual lights in [[WDT#lgt|_lgt.wdt]]s to do live lighting also influencing the character and shadow.<br />
<br />
=== MCCV sub-chunk (WotLK+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=3}}<br />
* split files: root<br />
*'''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 [http://www.youtube.com/watch?v=3FjuEPnuKtU this video] (see 3:25 to 3:45) from Blizzcon 09. Additionally, there is a [[Media:WoWScrnShot_092409_003328.jpg|screenshot]] showing some of the effects possible. <br />
<br />
struct MCCV {<br />
struct MCCVEntry {<br />
uint8_t blue; ''// these values range from 0x00 to 0xFF with 0x7F being the default.''<br />
uint8_t green; ''// you can interpret the values as 0x7F being 1.0 and these values being multiplicated with the vertex colors.''<br />
uint8_t red; ''// setting all values to 0x00 makes a chunk completely black.''<br />
uint8_t alpha; ''// seems not to have any effect.''<br />
} entries[9*9+8*8];<br />
};<br />
<br />
<strike>Probably argb, not rgba? --[[User:Schlumpf|Schlumpf]] ([[User talk:Schlumpf|talk]]) 17:05, 26 July 2015 (UTC)</strike><br />
<br />
in WotLK the client uses bgra --[[User:Adspartan|Adspartan]] ([[User talk:Adspartan|talk]]) 02:12, 22 May 2016 (CEST)<br />
<br />
=== MCNR sub-chunk ===<br />
* split files: root<br />
*'''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.<br />
<br />
struct SMNormal {<br />
struct MCNREntry {<br />
int8_t normal[3]; ''// normalized. X, Z, Y. 127 == 1.0, -127 == -1.0.''<br />
} entries[9*9+8*8];<br />
uint8_t unknown[3*3+2*2]; ''// this data is not included in the MCNR chunk but additional data which purpose is unknown. 0.5.3.3368 lists this as padding<br />
''// '''always''' 0 112 245 18 0 8 0 0 0 84 245 18 0. Nobody yet found a different pattern. The data is '''not''' derived from the normals.''<br />
''// It also does not seem that the client reads this data. --[[User:Schlumpf|Schlumpf]] ([[User talk:Schlumpf|talk]]) 23:01, 26 July 2015 (UTC)''<br />
''// While stated that this data is not "included in the MCNR chunk", the chunk-size defined for the MCNR chunk '''does''' cover this data. --[[User:Kruithne|Kruithne]] Feb 2016''<br />
''// ... from Cataclysm only (on LK files and before, MCNR defined size is 435 and not 448) [[User:Mjollna|Mjollna]] ([[User talk:Mjollna|talk]])<br />
};<br />
<br />
=== MCLY sub-chunk ===<br />
* split files: tex<br />
''Complete and right as of 19-AUG-09 (3.0.9 or higher)''<br />
*'''Texture layer definitions for this map chunk. 16 bytes per layer, up to 4 layers (thus, layer count = size / 16).''' <br />
<br />
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 [[ADT#MCAL_chunk|MCAL]] chunk. That one is relative to MCAL.<br />
<br />
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.<br />
<br />
The textureId is just the array index of the filename array in the [[ADT#MTEX_chunk|MTEX]] chunk.<br />
<br />
For getting the right feeling when walking, you should set the effectId which links to {{Template:DBField|table=GroundEffectTexture|column=m_ID}}. 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!<br />
<br />
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_.28WotLK.2B.29|MTXF]] chunk in, if you want that. Look at an skybox Blizzard made to see how you should do it.<br />
<br />
struct SMLayer<br />
{<br />
uint32_t textureId; <br />
struct<br />
{<br />
uint32_t animation_rotation : 3; // each tick is 45°<br />
uint32_t animation_speed : 3; <br />
uint32_t animation_enabled : 1;<br />
uint32_t overbright : 1; // This will make the texture way brighter. Used for lava to make it "glow".<br />
uint32_t use_alpha_map : 1; // set for every layer after the first<br />
uint32_t alpha_map_compressed : 1; // see MCAL chunk description<br />
uint32_t use_cube_map_reflection : 1; // This makes the layer behave like its a reflection of the skybox. See below<br />
uint32_t unknown_0x800 : 1; // WoD?+ if either of 0x800 or 0x1000 is set, [[#MTXF_chunk_.28WotLK.2B.29|texture effects]]' texture_scale is applied<br />
uint32_t unknown_0x1000 : 1; // WoD?+ see 0x800<br />
uint32_t : 19;<br />
} flags; <br />
uint32_t offsetInMCAL;<br />
{{Template:Type/foreign_key|table=GroundEffectTexture}} effectId; // 0xFFFFFFFF for none, in alpha: uint16_t + padding<br />
} layers[/* <= 4 */];<br />
<br />
To know how much entries there are, read until you hit the end of the chunk. Or divide it by 16 (4 + 4 + 4 + 4)<br />
<br />
*Explanation for flag '''0x400''' (use_cube_map_reflection):<br />
<br />
First of all you can see the effects in this video: [http://www.youtube.com/watch?v=uOE9OIG_rFM Video] The texture that became the 0x400 flag was the following: [[Media:4b795c7c7f36b_TCB_CrystalSong_B.jpg]] . 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).<br />
<br />
There are some important things you should be aware when using the flag 0x400:<br />
*It doesnt matter for which layer you set the flag 0x400, it will always affect the groundlayer. <br />
*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.<br />
*All of the skyboxtextures blizzard has need to specify a special flag to be decompressed correctly. This is done using the [[#MTXF_chunk_.28WotLK.2B.29|MTXF]]-chunk. If the texture has a 1 in MTXF it will be interpreted correctly, else it will be green.<br />
<br />
--[[User:Cromon|Cromon]]<br />
<br />
=== MCRF sub-chunk (<Cata)===<br />
{{Template:SectionBox/VersionRange|max_expansionlevel=3|note=Now split into [[#MCRD_.28Cata.2B.29|MCRD]] and [[#MCRW_.28Cata.2B.29|MCRW]]}}<br />
<br />
*'''A list of with MCNK.nDoodadRefs + MCNK.nMapObjRefs indices into the file's [[ADT#MDDF_chunk|MDDF]] and [[ADT#MODF_chunk|MODF]] chunks,''' saying which [[ADT#MCNK_chunk|MCNK]] subchunk those particular doodads and objects are drawn within. This [[ADT#MCRF_sub-chunk|MCRF]] list contains duplicates for map doodads that overlap areas. <br />
<br />
uint32_t doodad_refs[header.nDoodadRefs]; // into MDDF<br />
uint32_t object_refs[header.nMapObjRefs]; // into MODF<br />
<br />
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. --[[User:Cromon|Cromon]]<br />
<br />
===MCRD (Cata+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: obj<br />
uint32_t mddf_entry[];<br />
===MCRW (Cata+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: obj<br />
uint32_t modf_entry[];<br />
=== MCSH sub-chunk ===<br />
* split files: tex<br />
<br />
*'''Shadow map for static shadows on the terrain.''' Can be left out with the chunk&1 flag not set.<br />
<br />
struct {<br />
uint1_t shadow_map[64][64];<br />
} mcsh;<br />
<br />
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.<br />
<br />
=== MCAL sub-chunk ===<br />
* split files: tex<br />
*'''Alpha maps for additional texture layers.''' <br />
<br />
There are 3 kinds of alpha maps here: Which one depends on [[ADT#MCLY_sub-chunk|MCLY]] (0x200) and [[WDT#MPHD_chunk|WDT's MPHD]] (0x4 and 0x80) flags.<br />
<br />
{| border="1"<br />
! [[ADT#MCLY_sub-chunk|MCLY]] !! [[WDT#MPHD_chunk|WDT's MPHD]] !! mode<br />
|-<br />
| || || Uncompressed (2048)<br />
|-<br />
| align="center"| 0x200 set || || Compressed<br />
|-<br />
| || align="center"| 0x4 or 0x80 set || Uncompressed (4096)<br />
|-<br />
| align="center"| 0x200 set || align="center"| 0x4 or 0x80 set || Compressed: MPHD is only about bit depth!<br />
|}<br />
<br />
Additionally to this, [[ADT#MCNK_chunk|MCNK]] can have (and mostly does have) the 0x8000 flag. If this flag is set and bit depth is 8 ([[WDT#MPHD_chunk|WDT's MPHD]] has either flag), then modify alpha values so that if there is shadow at the corresponding position, the alpha value is multiplied by 0.7f (178 * alpha >> 8 to be exact).<br />
<br />
==== Uncompressed (4096) ====<br />
uint8_t alpha_map[64][64];<br />
<br />
For 4096 byte chunks, just read the values straight into your alpha channel. This, again, should result in 4096 bytes for a 64px by 64px size in the final alpha map.<br />
<br />
==== Uncompressed (2048) ====<br />
uint4_t alpha_map[64][64]; // note: the client uses a 4bit alpha texture, so does not do any custom normalization. Blit_Argb4444_Abgr8888 does `value & 0xF | 0x10 * value`.<br />
<br />
Contains 2048 bytes of data, but each byte contains two values in LSB first order. This should result in a 4096 alpha map (64px by 64px).<br />
<br />
* Read a byte.<br />
* Split the byte into two 4-bit values. eg: b0101b a0101a<br />
* This results in 16 possible values for each pixel; 15 is full alpha and 0 is no alpha. If you want to normalize, use Blit_Argb4444_Abgr8888 (value & 0xF | 0x10 * value).<br />
* Record each value separately into the alpha channel in the order of a then b.<br />
<br />
Not that depending on MCNK flag FLAG_DO_NOT_FIX_ALPHA_MAP, this is not actually a 64*64 map but rather a 63*63 map with the last row and column being equivalent to the previous one. <br />
<br />
struct { uint4_t alpha_map[63]; uint4_t ignored; }[63]; <br />
uint4_t ignored[64];<br />
<br />
where <br />
<br />
alpha_map[x][63] == alpha_map[x][62]<br />
alpha_map[63][x] == alpha_map[62][x]<br />
alpha_map[63][63] == alpha_map[62][62]<br />
<br />
and all "ignored" values are ignored, while still preserving the 2048 byte footprint.<br />
<br />
* I claim that this shall be handled by saving in fixed4444 only (i.e. always set mcnk.flags FLAG_DO_NOT_FIX_ALPHA_MAP and explicitly save the "fixed" (as in have all values) version). --[[User:Schlumpf|Schlumpf]] ([[User talk:Schlumpf|talk]]) 21:45, 24 October 2015 (UTC)<br />
<br />
==== Compressed ====<br />
struct<br />
{<br />
enum class mode_t<br />
{<br />
copy = 0, // append value[0..count - 1]<br />
fill = 1, // append value[0] count times<br />
};<br />
uint8_t mode : 1;<br />
uint8_t count : 7;<br />
uint8_t value[];<br />
} compressed_alpha_map[]; // size depends on content. will decompress to exactly 64*64 bytes.<br />
// minimum size is 32 times copy-127-x plus one copy-32-x, thus 66 bytes<br />
// maximum size is 32 times fill-127-x[127] plus one fill-32-x[127], thus 4129 bytes<br />
<br />
* read a byte<br />
* in the first bit of that byte (sign bit) check if it's true. When true that means we are in "fill" mode, if false, "copy" mode<br />
* the next 7 bits of the byte determine how many times we "fill" or "copy" (count) (eg, max value 127)<br />
* fill mode: repeat the byte following the one we just read *count* number of times into the alpha map<br />
* copy mode: read *count* number of following bytes into the alpha map<br />
* repeat until the map is complete<br />
Notes:<br />
* this should result in 4096 bytes in the alpha map (64 px by 64 px)<br />
* you should not have any extra compression data left over after completion<br />
* reads left to right, top to bottom<br />
<br />
-- Michael Redig 25-5-2015<br />
<br />
-- [[User:Flow|Flow]] 21-10-2008<br />
<br />
=====Sample C++ code=====<br />
unsigned offI = 0; //offset IN buffer<br />
unsigned offO = 0; //offset OUT buffer<br />
char* buffIn; // pointer to data in adt file<br />
char buffOut[4096]; // the resulting alpha map<br />
<br />
while( offO < 4096 )<br />
{<br />
// fill or copy mode<br />
bool fill = buffIn[offI] & 0x80;<br />
unsigned n = buffIn[offI] & 0x7F;<br />
offI++;<br />
for( unsigned k = 0; k < n; k++ )<br />
{<br />
buffOut[offO] = buffIn[offI];<br />
offO++;<br />
if( !fill )<br />
offI++;<br />
}<br />
if( fill ) offI++;<br />
}<br />
<br />
==== Blend ====<br />
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:<br />
<br />
finalColor = tex0 * (1.0 - (alpha1 + alpha2 + alpha3)) + tex1 * alpha1 + tex2 * alpha2 + tex3 * alpha3<br />
<br />
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.<br />
<br />
-- [[User:Slartibartfast|Slartibartfast]] 01-11-2008<br />
<br />
====How to render====<br />
<br />
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:<br />
<br />
gl_FragColor = texture2D(texture0, vec2(gl_TexCoord[0])) * texture2D(texture3, vec2(gl_TexCoord[3])).r<br />
+ texture2D(texture1, vec2(gl_TexCoord[1])) * texture2D(texture3, vec2(gl_TexCoord[3])).g<br />
+ texture2D(texture2, vec2(gl_TexCoord[2])) * texture2D(texture3, vec2(gl_TexCoord[3])).b;<br />
<br />
(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)<br />
<br />
Also, seems like alpha textures are upscaled using bicubic interpolation. --[[User:Deamon|Deamon]] ([[User talk:Deamon|talk]]) 21:49, 11 October 2015 (UTC)<br />
<br />
=== MCLQ sub-chunk ===<br />
{{Template:SectionBox/VersionRange|max_expansionlevel=2|note=Deprecated, but still parsed. Replaced with [[#MH2O_chunk_.28WotLK.2B.29|MH2O]]}}<br />
* split files: root<br />
<br />
*'''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. <br />
*'''Deprecated with WotLK''': I advise you to implement the MH2O one as its better if you want to write a editor for ADT files. <br />
<br />
The size of the chunk is in the mapchunk header. The type of liquid is given in the mapchunk flags, also in the header. <br />
<br />
This information is old and incomplete as well as maybe wrong. <br />
<br />
struct {<br />
{{Type|CRange}} height;<br />
struct SLVert {<br />
union {<br />
struct SWVert {<br />
char depth;<br />
char flow0Pct;<br />
char flow1Pct;<br />
char filler;<br />
float height;<br />
} waterVert;<br />
struct SOVert {<br />
char depth;<br />
char foam;<br />
char wet;<br />
char filler;<br />
} oceanVert;<br />
struct SMVert {<br />
unsigned __int16 s;<br />
unsigned __int16 t;<br />
float height;<br />
} magmaVert;<br />
};<br />
} verts[9*9];<br />
struct SLTiles {<br />
char tiles[8][8]; // 0x0f or 0x8 mean don't render (?)<br />
} tiles;<br />
uint32_t nFlowvs;<br />
struct SWFlowv {<br />
{{Type|CAaSphere}} sphere;<br />
{{Type|C3Vector}} dir;<br />
float velocity;<br />
float amplitude;<br />
float frequency;<br />
} flowvs[2]; // always 2 in file, independent on nFlowvs.<br />
};<br />
<br />
=== MCSE sub-chunk ===<br />
* split files: root<br />
*'''Sound emitters.'''<br />
<br />
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 {{Template:DBRef|table=SoundEntriesAdvanced}}.<br />
<br />
struct CWSoundEmitter<br />
{<br />
/*000h*/ {{Template:Type/foreign_key|table=SoundEntriesAdvanced}} entry_id;<br />
/*004h*/ {{Template:Type|C3Vector}} position;<br />
/*008h*/ <br />
/*00Ch*/ <br />
/*010h*/ {{Template:Type|C3Vector}} size; // I'm not really sure with this. I'm far too lazy to analyze this. Seems like <br />
noone ever needed these anyway.<br />
/*014h*/ <br />
/*018h*/ <br />
} MCSE[];<br />
<br />
==== old one by ObscuR (for older versions) ====<br />
struct CWSoundEmitter<br />
{<br />
/*000h*/ UINT32 soundPointID; <br />
/*004h*/ UINT32 soundNameID; <br />
/*008h*/ C3Vector pos;<br />
/*014h*/ float minDistance; <br />
/*018h*/ float maxDistance; <br />
/*01Ch*/ float cutoffDistance; <br />
/*020h*/ UINT16 startTime; <br />
/*022h*/ UINT16 endTime;<br />
/*024h*/ UINT16 mode;<br />
/*026h*/ UINT16 groupSilenceMin; <br />
/*028h*/ UINT16 groupSilenceMax; <br />
/*02Ah*/ UINT16 playInstancesMin;<br />
/*02Ch*/ UINT16 playInstancesMax; <br />
/*02Eh*/ BYTE loopCountMin;<br />
/*02Fh*/ BYTE loopCountMax;<br />
/*030h*/ UINT16 interSoundGapMin;<br />
/*032h*/ UINT16 interSoundGapMax;<br />
/*034h*/ <br />
} MCSE[];<br />
<br />
===MCBB (MoP+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: root<br />
struct // blend batches. max 256 per MCNK<br />
{<br />
uint32_t mbmh_index;<br />
uint32_t indexCount; // MBMI<br />
uint32_t indexFirst; // in addition to mbmh.mbnv_base<br />
uint32_t vertexCount; // MBNV <br />
uint32_t vertexFirst; // in addition to mbmh.mbnv_base<br />
} MCBB[];<br />
===MCMT (Cata+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: tex<br />
struct<br />
{<br />
{{Template:Type/foreign_key|type=uint8_t|table=TerrainMaterial}} material_id[4]; // per MCLY<br />
} MCMT;<br />
<br />
===MCDD (Cata?+)===<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: root?<br />
struct // at least seen in WoD<br />
{<br />
// there seems to be a high-res (?) mode which is not taken into account <br />
// in live clients (32 bytes instead of 8) (?). if inlined to MCNK is low-res.<br />
uint1_t disable[8][8]; // disable detail doodads here<br />
// uint1_t disable[16][16];<br />
} MCDD;<br />
<br />
== MFBO chunk (BC+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=2}}<br />
* split files: root<br />
*'''A bounding box for flying.'''<br />
<br />
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.<br />
<br />
Therefore the structure is:<br />
struct plane{<br />
short[3][3] height;<br />
};<br />
struct<br />
{<br />
plane maximum;<br />
plane minimum;<br />
} MFBO;<br />
<br />
==MTXF chunk (WotLK+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=3}}<br />
* split files: tex<br />
Array of flags for entries in MTEX. Always same number of entries as MTEX. <br />
<br />
struct SMTextureFlags<br />
{<br />
uint32_t do_not_load_specular_or_height_texture_but_use_cubemap : 1; // probably just 'disable_all_shading'<br />
uint32_t : 3; // no non-zero values in 20490<br />
#if {{Template:Sandbox/VersionRange|min_expansionlevel=5}}<br />
uint32_t texture_scale : 4;<br />
uint32_t : 24; // no non-zero values in 20490<br />
#else<br />
uint32_t : 28; // no non-zero values in 20490<br />
#endif<br />
} MTXF[];<br />
<br />
In WotLK this is mostly used for layers using terrain cube maps (e.g. crystalsong's "TILESET\\Terrain Cube Maps\\TCB_CrystalSong_A.blp"). Without this flag, it would try to load the _s.blp and fail loading.<br />
<br />
==MTXP chunk (MoP?+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: tex<br />
struct SMTextureParams<br />
{<br />
SMTextureFlags flags; // same as in mtxf (or taken from there if no mtxp present)<br />
float height; // default 0.0 -- the _h texture values are scaled to [0, value) to determine actual "height".<br />
// this determines if textures overlap or not (e.g. roots on top of roads)<br />
float unk2; // default 1.0 -- some kind of weight<br />
uint32_t unk3; // padding? (no default, no non-zero values in 20490)<br />
} MTXP[];<br />
<br />
If unk1 == 0.0 and unk2 == 1.0, it will not load a _h texture.<br />
<br />
==MBMH (MoP+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: root<br />
* there can be multiple entries per map object (as different textures are possible)<br />
struct // blend mesh header<br />
{<br />
uint32_t mapObjectID; // (unique ID)<br />
uint32_t textureId; // of linked WMO<br />
char unknown[0xc];<br />
uint32_t mbmi_base; // first index into MBMI for this mesh<br />
uint32_t mbnv_base; // first index into MBNV for this mesh<br />
} MBMH[];<br />
==MBBB (MoP+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: root<br />
* each one corresponds to a MBMH entry of same index<br />
struct // blend mesh bounding boxes<br />
{<br />
uint32_t mapObjectID; // (unique ID) -- repeated for unknown reason<br />
{{Template:Type|CAaBox}} bounding;<br />
} MBBB[];<br />
<br />
==MBNV (MoP+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: root<br />
struct // blend mesh vertices<br />
{<br />
{{Template:Type|C3Vector}} pos;<br />
{{Template:Type|C3Vector}} normal;<br />
{{Template:Type|C2Vector}} texture_coordinates;<br />
{{Template:Type|CArgb}} color[3]; // used: PN: none; PNC: 0; PNC2: 0, 1; PNC2T: 0, 2<br />
} MBNV[];<br />
<br />
==MBMI (MoP+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=5}}<br />
* split files: root<br />
struct // blend mesh indices<br />
{<br />
uint16_t index;<br />
} MBMI[];<br />
==MAMP (Cata+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=4}}<br />
* split files: tex<br />
struct<br />
{<br />
char fred; // texture_size = 64 / (2^mamp_value). either defined here or in MHDR.mamp_value. <br />
} mamp;<br />
==MLHD (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
struct <br />
{<br />
uint32_t unknown;<br />
float some_kind_of_bounding[6];<br />
} ml_header;<br />
==MLVH (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
float ml_v_heightData[129*129 + 128*128 + additional]; // interleaved global height map + additional data of not fixed size <br />
==MLVI (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
uint16_t ml_v_indices[];<br />
==MLLL (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
struct<br />
{<br />
float _0; // some kind of size/lod band/distance/level, 32, 16, 8…<br />
uint32_t _1;<br />
uint32_t _2;<br />
uint32_t _3;<br />
uint32_t _4;<br />
} ml_ll;<br />
==MLND (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
struct<br />
{<br />
uint32_t _0;<br />
uint32_t _1;<br />
uint32_t _2;<br />
uint32_t _3;<br />
uint16_t indices[4];<br />
} ml_nd[];<br />
==MLSI (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
uint16_t ml_skirtIndices[]; // into MLVH<br />
==MLLD (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
struct SMLodLiquidData<br />
{<br />
enum<br />
{<br />
Flag_HasTileData = 1,<br />
_compressed_A = 2,<br />
_compressed_B = 4,<br />
};<br />
uint32_t m_flags;<br />
// … compressed (rle?) or uncompressed data, two blobs (16384 and 2048)<br />
} lodLiquidData[];<br />
==MLLN (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
MLLN, MLLI and MLLV are order-dependent. A MLLN introduces a new liquid, the following MLLI defines the indices, the next MLLV the vertices.<br />
struct MLLN<br />
{<br />
uint32_t _0;<br />
uint32_t num_indices; // MLLI<br />
uint32_t _2;<br />
uint16_t _3a;<br />
uint16_t _3b;<br />
uint32_t _4;<br />
uint32_t _5;<br />
} ml_liquid_n;<br />
==MLLV (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
{{Template:Type|C3Vector}} ml_liquid_vertices[];<br />
==MLLI (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: lod<br />
{{Template:Type|C3sVector}} ml_liquid_indices[]; // 3 shorts into MLLV<br />
<br />
==MLMD (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: obj1<br />
struct { // same as MODF but without bounding box (may be out of sync), better look at both<br />
uint32_t mwidEntry;<br />
uint32_t uniqueId;<br />
{{Template:Type|C3Vector}} position;<br />
{{Template:Type|C3Vector}} rotation;<br />
uint16_t flags;<br />
uint16_t doodadSet;<br />
uint16_t nameSet;<br />
uint16_t unk;<br />
} lod_object_defs[];<br />
==MLMX (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: obj1<br />
struct <br />
{<br />
{{Template:Type|CAaBox}} bounding;<br />
float radius;<br />
} lod_object_extents[]; // same count as MLMD<br />
<br />
Used for seeing objects map in legion from a defined distance based on the radius.<br />
The CaaBox is defined with a max point and a min point, the points coords are servers coordinates, so you should take the object position in MODF (wmo) or MDDF (m2) and convert it to server coords from clients coords<br />
Radius is generally approximatively around 50 (Radius is CaaBox[3] - CaaBox[0]), the visibility object depends from the view distance param too (maybe a factor like radius * viewdistance factor)<br />
Feel free to reformulate this as i'm not familiar with wiki structures --[[User:Rangorn|Rangorn]] ([[User talk:Rangorn|talk]]) 17:05, 28 January 2017 (UTC)<br />
<br />
==MLDD (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: obj1<br />
SMDoodadDef lod_doodad_defs[]; // see MDDF<br />
==MLDX (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: obj1<br />
struct <br />
{<br />
{{Template:Type|CAaBox}} bounding;<br />
float radius;<br />
} lod_doodad_extents[]; // same count as MLDD<br />
<br />
==MLDL (Legion+)==<br />
{{Template:SectionBox/VersionRange|min_expansionlevel=7}}<br />
* split files: obj1<br />
uint32_t unk[]; // same count as MLDD<br />
<br />
[[Category:Format]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/GlobalStrings&diff=23096DB/GlobalStrings2016-10-11T01:15:48Z<p>Rangorn: </p>
<hr />
<div>Contains all interface String values, successor of FrameXML/GlobalString.lua {{Template:Sandbox/VersionRange|min_expansionlevel=7}}<br />
<br />
==7.0.3.22594==<br />
===Table===<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180 " | Field <br />
! width="80" | Type <br />
! width="600" | Notes<br />
|- <br />
| 1 || ID || Integer || <br />
|- <br />
| 2 || Stringname || String || <br />
|- <br />
| 3 || Stringvalue || String ||<br />
|- <br />
| 4 || unk || Integer ||<br />
|}<br />
<br />
<br />
==Structure==<br />
===7.0.3.22594===<br />
struct GlobalStrings {<br />
uint32_t m_ID;<br />
{{Template:Type|stringref}} stringname;<br />
{{Template:Type|langstringref}} stringvalue;<br />
uint8_t unk;<br />
};<br />
<br />
<br />
[[Category:DBC]]<br />
[[Category:DBC_Legion]][[Category:7.0.3.22594]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/GlobalStrings&diff=23080DB/GlobalStrings2016-09-29T23:20:18Z<p>Rangorn: </p>
<hr />
<div>Contains all interface String values, successor of FrameXML/GlobalString.lua since ???<br />
<br />
==7.0.3.22594==<br />
===Table===<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180 " | Field <br />
! width="80" | Type <br />
! width="600" | Notes<br />
|- <br />
| 1 || ID || Integer || <br />
|- <br />
| 2 || Stringname || String || <br />
|- <br />
| 3 || Stringvalue || String ||<br />
|- <br />
| 4 || unk || Integer ||<br />
|}<br />
<br />
<br />
==Structure==<br />
===7.0.3.22594===<br />
struct CameraShakesRec {<br />
uint32_t m_ID;<br />
{{Template:Type|stringref}} m_stringname;<br />
{{Template:Type|stringref}} m_stringvalue;<br />
uint8 m_unk;<br />
};<br />
<br />
{{Template:Sandbox/VersionRange|min_expansionlevel=7}}<br />
[[Category:DBC]]<br />
[[Category:DBC_Legion]][[Category:7.0.1.21737]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/SpellxSpellVisual&diff=22023DB/SpellxSpellVisual2016-07-16T16:46:46Z<p>Rangorn: </p>
<hr />
<div>New in WoD (6.2.X)<br />
<br />
==6.2.3.20886==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180" | Field <br />
! width="80" | Type <br />
! width="500" | Notes<br />
|- style="background:#E0E0E0;"<br />
| 1 || ID || Integer || <br />
|-<br />
| 2 || [[DB/Spell|SpellID]] || IRefID ||<br />
|-<br />
| 3 || Unknown || Float || Almost Always 0<br />
|- style="background:#E0E0E0;"<br />
| 4 || [[DB/SpellVisual|SpellVisual1]] || iRefID || <br />
|-<br />
| 5 || [[DB/SpellVisual|SpellVisual2]] || iRefID || <br />
|- style="background:#E0E0E0;"<br />
| 6 || Unknown || Integer || Almost Always 1<br />
|-<br />
| 7 || Unknown || Integer || Almost Always 0<br />
|- style="background:#E0E0E0;"<br />
| 8 || Unknown || Integer || Almost Always 0<br />
|}<br />
<br />
==7.0.1.21737==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180" | Field <br />
! width="80" | Type <br />
! width="500" | Notes<br />
|- style="background:#E0E0E0;"<br />
| 1 || [[DB/Spell|SpellID]] || IRefID ||<br />
|-<br />
| 2 || Unknown || Float || Almost Always 1<br />
|- style="background:#E0E0E0;"<br />
| 3 || [[DB/SpellVisual|SpellVisual1]] || iRefID || <br />
|-<br />
| 4 || [[DB/SpellVisual|SpellVisual2]] || iRefID || <br />
|- style="background:#E0E0E0;"<br />
| 5 || Unknown || Integer || Wasn't able to match the value<br />
|-<br />
| 6 || Unknown || Integer || Wasn't able to match the value<br />
|- style="background:#E0E0E0;"<br />
| 7 || Unknown || Integer || Wasn't able to match the value<br />
|-<br />
| 8 || ID || Integer || <br />
|}<br />
[[User:Synric|Synric]] 12th July 2016<br />
<br />
[[Category:DBC]]<br />
[[Category:DBC_WoD]]<br />
[[Category:DBC_Legion]]<br />
[[Category:SpellData]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/SpellxSpellVisual&diff=22022DB/SpellxSpellVisual2016-07-16T16:38:45Z<p>Rangorn: /* 6.2.3.20886 */</p>
<hr />
<div>New in WoD (6.2.X)<br />
<br />
==6.2.3.20886==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180" | Field <br />
! width="80" | Type <br />
! width="500" | Notes<br />
|- style="background:#E0E0E0;"<br />
| 1 || ID || Integer || <br />
|-<br />
| 2 || [[DB/Spell|SpellID]] || IRefID ||<br />
|-<br />
| 3 || Unknown || Float || Almost Always 0<br />
|- style="background:#E0E0E0;"<br />
| 4 || [[DB/SpellVisual|SpellVisual1]] || iRefID || <br />
|-<br />
| 5 || [[DB/SpellVisual|SpellVisual2]] || iRefID || <br />
|- style="background:#E0E0E0;"<br />
| 6 || Unknown || Integer || Almost Always 1<br />
|-<br />
| 7 || Unknown || Integer || Almost Always 0<br />
|- style="background:#E0E0E0;"<br />
| 8 || Unknown || Integer || Almost Always 0<br />
|}<br />
<br />
==7.0.1.21737==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180" | Field <br />
! width="80" | Type <br />
! width="500" | Notes<br />
|- style="background:#E0E0E0;"<br />
| 1 || [[DB/Spell|SpellID]] || IRefID ||<br />
|-<br />
| 2 || Unknown || Float || Almost Always 1<br />
|- style="background:#E0E0E0;"<br />
| 3 || [[DB/SpellVisual|SpellVisual1]] || iRefID || <br />
|-<br />
| 4 || [[DB/SpellVisual|SpellVisual2]] || iRefID || <br />
|- style="background:#E0E0E0;"<br />
| 5 || Unknown || Integer || Wasn't able to match the value<br />
|-<br />
| 6 || Unknown || Integer || Wasn't able to match the value<br />
|- style="background:#E0E0E0;"<br />
| 7 || Unknown || Integer || Wasn't able to match the value<br />
|-<br />
| 8 || ID || Integer || <br />
|}<br />
[[User:Synric|Synric]] 12th July 2016<br />
<br />
[[Category:DBC]]<br />
[[Category:DBC_Wod]]<br />
[[Category:DBC_Legion]]<br />
[[Category:SpellData]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/SpellxSpellVisual&diff=22021DB/SpellxSpellVisual2016-07-16T16:37:34Z<p>Rangorn: </p>
<hr />
<div>New in WoD (6.2.X)<br />
<br />
==6.2.3.20886==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180" | Field <br />
! width="80" | Type <br />
! width="500" | Notes<br />
|- style="background:#E0E0E0;"<br />
| 1 || ID || Integer || <br />
|-<br />
| 2 || [[DB/Spell|SpellID]] || IRefID ||<br />
|-<br />
| 3 || Unknown || Float || Almost Always 1<br />
|- style="background:#E0E0E0;"<br />
| 4 || [[DB/SpellVisual|SpellVisual1]] || iRefID || <br />
|-<br />
| 5 || [[DB/SpellVisual|SpellVisual2]] || iRefID || <br />
|- style="background:#E0E0E0;"<br />
| 6 || Unknown || Integer || Wasn't able to match the value<br />
|-<br />
| 7 || Unknown || Integer || Wasn't able to match the value<br />
|- style="background:#E0E0E0;"<br />
| 8 || Unknown || Integer || Wasn't able to match the value<br />
|}<br />
<br />
<br />
==7.0.1.21737==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180" | Field <br />
! width="80" | Type <br />
! width="500" | Notes<br />
|- style="background:#E0E0E0;"<br />
| 1 || [[DB/Spell|SpellID]] || IRefID ||<br />
|-<br />
| 2 || Unknown || Float || Almost Always 1<br />
|- style="background:#E0E0E0;"<br />
| 3 || [[DB/SpellVisual|SpellVisual1]] || iRefID || <br />
|-<br />
| 4 || [[DB/SpellVisual|SpellVisual2]] || iRefID || <br />
|- style="background:#E0E0E0;"<br />
| 5 || Unknown || Integer || Wasn't able to match the value<br />
|-<br />
| 6 || Unknown || Integer || Wasn't able to match the value<br />
|- style="background:#E0E0E0;"<br />
| 7 || Unknown || Integer || Wasn't able to match the value<br />
|-<br />
| 8 || ID || Integer || <br />
|}<br />
[[User:Synric|Synric]] 12th July 2016<br />
<br />
[[Category:DBC]]<br />
[[Category:DBC_Wod]]<br />
[[Category:DBC_Legion]]<br />
[[Category:SpellData]]</div>Rangornhttps://wowdev.wiki/index.php?title=WMO&diff=21367WMO2016-05-17T18:53:02Z<p>Rangorn: </p>
<hr />
<div>[[WMO/v17|WMO]] files contain world map objects. They, too, have a [[Chunk|chunked]] structure just like the [[WDT]] files.<br />
<br />
There are two types of [[WMO/v17]] files, actually:<br />
<br />
*[[WMO/v17#WMO_root_file|WMO root file]] - lists textures ([[BLP]] Files), doodads ([[M2]] or [[MDX]] Files), etc., and orientation for the [[WMO/v17]] groups<br />
*[[WMO/v17#WMO_group_file|WMO group file]] - 3d model data for one unit in the world map object <br />
<br />
The root file and the groups are stored with the following filenames:<br />
<br />
*World\wmo\path\WMOName.wmo<br />
*World\wmo\path\WMOName_NNN.wmo<br />
<br />
There is a hardcoded maximum of 512 group files per root object.<br />
<br />
= WMO root file =<br />
<br />
The root file lists the following:<br />
<br />
* textures ([[BLP]] File references)<br />
* materials<br />
* models ([[M2|MDX / M2]] File references)<br />
* groups<br />
* visibility information<br />
* more data<br />
<br />
== MOHD chunk ==<br />
<br />
*'''Header for the map object. 64 bytes.'''<br />
<br />
struct SMOHeader<br />
{<br />
/*000h*/ uint32_t nTextures; <br />
/*004h*/ uint32_t nGroups; <br />
/*008h*/ uint32_t nPortals; <br />
/*00Ch*/ uint32_t nLights; <br />
/*010h*/ uint32_t nDoodadNames; <br />
/*014h*/ uint32_t nDoodadDefs; // *<br />
/*018h*/ uint32_t nDoodadSets; <br />
/*01Ch*/ {{Template:Type|CArgb}} color; // Color settings for base (ambient) color. See the flag at /*03Ch*/. <br />
/*020h*/ {{Template:Type/foreign_key|table=WMOAreaTable|column=m_WMOID}} wmoID;<br />
/*024h*/ {{Template:Type|CAaBox}} bounding_box;<br />
/*03Ch*/ uint32_t flag_0x1 : 1; // sets CMapObjGroup::field_6C | 1<br />
/*03Ch*/ uint32_t flag_add_base_color : 1; // add base (ambient) color (of MOHD) to MOCV. apparently does more, e.g. required for multiple MOCVs<br />
/*03Ch*/ uint32_t flag_liquid_related : 1; // fills the whole WMO with water (used for underwater WMOs). (possibly - LiquidType related, see below in the MLIQ).<br />
/*03Ch*/ uint32_t flag_has_some_outdoor_group : 1; // possibly - has some group that is outdoors<br />
/*03Ch*/ uint32_t Flag_Lod : 1; // (Legion+)<br />
/*03Ch*/ uint32_t : 27; // unused as of 7.0.1.20994<br />
} header;<br />
<br />
== MOTX chunk ==<br />
<br />
*'''List of textures ([[BLP]] Files) used in this map object. <del>There are nTextures entries in this chunk.</del>''' <br />
<br />
A block of <del>zero-padded, zero-terminated strings,</del> 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.<br />
<br />
char texture_filenames[];<br />
<br />
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.<br />
Sometimes there also empty aligtments for no (it seems like no) real reason.<br />
<br />
== MOMT chunk ==<br />
<br />
*'''Materials used in this map object, 64 bytes per texture ([[BLP]] file), nMaterials entries.'''<br />
<br />
struct SMOMaterial<br />
{<br />
uint32_t flag_0x1 : 1; // ? (I'm not sure atm I tend to use lightmap or something like this)<br />
uint32_t flag_0x2 : 1;<br />
uint32_t flag_no_backface_culling : 1; // two-sided<br />
uint32_t flag_darkened : 1; // ?, the intern face of windows are flagged 0x08<br />
uint32_t flag_bright_at_night : 1; // (unshaded) (used on windows and lamps in Stormwind, for example)<br />
uint32_t flag_0x20 : 1;<br />
uint32_t flag_clamp : 1; // ?, looks like GL_CLAMP<br />
uint32_t flag_repeat : 1; // ?, looks like GL_REPEAT<br />
uint32_t flag_0x100 : 1;<br />
uint32_t : 23; // unused as of 7.0.1.20994<br />
/*004h*/ uint32_t shader; // Index into CMapObj::s_wmoShaderMetaData. See below (shader types).<br />
/*008h*/ uint32_t blendMode; // Blending: 0 for opaque, 1 for transparent<br />
/*00Ch*/ uint32_t texture_0; // offset into MOTX<br />
/*010h*/ uint32_t color_0; // rgba8 (four uint8s)<br />
/*014h*/ uint32_t flags_0;<br />
/*018h*/ uint32_t texture_1;<br />
/*01Ch*/ uint32_t color_1;<br />
/*020h*/ {{Template:Type/foreign_key|table=TerrainType}} ground_type; // according to CMapObjDef::GetGroundType<br />
/*024h*/ uint32_t texture_2;<br />
/*028h*/ uint32_t color_2;<br />
/*02Ch*/ uint32_t flags_2;<br />
/*030h*/ uint32_t runTimeData[4]; // This data is explicitly nulled upon loading. Contains textures or similar stuff.<br />
/*034h*/<br />
/*038h*/<br />
/*03Ch*/<br />
/*040h*/<br />
} materials[];<br />
<br />
texture_1, 2 and 3 are start positions for texture filenames in the [[WMO/v17#MOTX_chunk|MOTX]] data block ; texture_1 for the first texture, texture_2 for the second (see shaders), etc. texture_1 defaults to "createcrappygreentexture.blp".<br />
<br />
color_2 is diffuse color : <tt>CWorldView::GatherMapObjDefGroupLiquids(): geomFactory->SetDiffuseColor((CImVector*)(smo+7));</tt><br />
<br />
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.<br />
<br />
===Shader types===<br />
<br />
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.<br />
<br />
Data is from 15464.<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! value <br />
! name<br />
! textures without shader<br />
! textures with shader <br />
! texcoord count<br />
! color count<br />
|-<br />
| 0 || Diffuse || 1 || 1 || 1 || 1<br />
|-style="background:#F0F8FF;"<br />
| 1 || Specular || 1 || 1 || 1 || 1<br />
|-<br />
| 2 || Metal || 1 || 1 || 1 || 1<br />
|-style="background:#F0F8FF;"<br />
| 3 || Env || 1 || 2 || 1 || 1<br />
|-<br />
| 4 || Opaque || 1 || 1 || 1 || 1<br />
|-style="background:#F0F8FF;"<br />
| 5 || EnvMetal || 1 || 2 || 1 || 1<br />
|-<br />
| 6 || TwoLayerDiffuse || 1 || 2 || 2 || 2<br />
|-style="background:#F0F8FF;"<br />
| 7 || TwoLayerEnvMetal || 1 || 3 || 2 || 2<br />
|-<br />
| 8 || TwoLayerTerrain || 1 || 2 || 1 || 2 || automatically adds _s in the filename of the second texture<br />
|-style="background:#F0F8FF;"<br />
| 9 || DiffuseEmissive || 1 || 2 || 2 || 2<br />
|-<br />
| 10 || || 1 || 1 || 1 || 1 || Seems to be invalid. Does something with MOTA (tangents).<br />
|-style="background:#F0F8FF;"<br />
| 11 || MaskedEnvMetal || 1 || 3 || 2 || 2<br />
|-<br />
| 12 || EnvMetalEmissive || 1 || 3 || 2 || 2<br />
|-style="background:#F0F8FF;"<br />
| 13 || TwoLayerDiffuseOpaque || 1 || 2 || 2 || 2<br />
|-<br />
| 14 || TwoLayerDiffuseEmissive || 1 || 1 || 1 || 1 || Seems to be invalid. Does something with MOTA (tangents).<br />
|-style="background:#F0F8FF;"<br />
| 15 || || 1 || 2 || 2 || 2<br />
|-<br />
| 16 || Diffuse || 1 || 1 || 1 || 1 || SMOMaterial::SH_DIFFUSE_TERRAIN -- "Blend Material": used for blending WMO with terrain (dynamic blend batches)<br />
|}<br />
<br />
tex coord and color count decide vertex buffer format: EGxVertexBufferFormat_PNC''2''T''2''<br />
<br />
===Shader types (18179)===<br />
{| style="background:#FCFCFC; color:black"<br />
|-<br />
! value<br />
! #textures without shader<br />
! #textures with shader<br />
! texcoord count<br />
! color count<br />
|-<br />
| 0 - Diffuse || 1 || 1 || 1 || 1<br />
|-style="background:#F0F8FF;"<br />
| 1 - Specular || 1 || 1 || 1 || 1<br />
|-<br />
| 2 - Metal || 1 || 1 || 1 || 1<br />
|-style="background:#F0F8FF;"<br />
| 3 - Env || 1 || 2 || 1 || 1<br />
|-<br />
| 4 - Opaque || 1 || 1 || 1 || 1<br />
|-style="background:#F0F8FF;"<br />
| 5 - EnvMetal || 1 || 2 || 1 || 1<br />
|-<br />
| 6 - TwoLayerDiffuse || 1 || 2 || 2 || 2<br />
|-style="background:#F0F8FF;"<br />
| 7 - TwoLayerEnvMetal || 1 || 3 || 2 || 2<br />
|-<br />
| 8 - TwoLayerTerrain || 1 || 2 || 1 || 2 || automatically adds _s in the filename of the second texture<br />
|-style="background:#F0F8FF;"<br />
| 9 - DiffuseEmissive || 1 || 2 || 2 || 2<br />
|-<br />
| 10 - waterWindow || 1 || 1 || 1 || 1 || automatically generates MOTA<br />
|-style="background:#F0F8FF;"<br />
| 11 - MaskedEnvMetal || 1 || 3 || 2 || 2<br />
|-<br />
| 12 - EnvMetalEmissive || 1 || 3 || 2 || 2<br />
|-style="background:#F0F8FF;"<br />
| 13 - TwoLayerDiffuseOpaque || 1 || 2 || 2 || 2<br />
|-<br />
| 14 - submarineWindow || 1 || 1 || 1 || 1 || automatically generates MOTA<br />
|-style="background:#F0F8FF;"<br />
| 15 - TwoLayerDiffuseEmissive || 1 || 2 || 2 || 2<br />
|-<br />
| 16 - DiffuseTerrain || 1 || 1 || 1 || 1 || SMOMaterial::SH_DIFFUSE_TERRAIN -- "Blend Material": used for blending WMO with terrain (dynamic blend batches)<br />
|-style="background:#F0F8FF;"<br />
| 17 - AdditiveMaskedEnvMetal || 1 || 3 || 2 || 2<br />
|}<br />
<br />
=== void CMapObj::CreateMaterial (unsigned int materialId) ===<br />
<br />
void CMapObj::CreateMaterial (unsigned int materialId)<br />
{<br />
assert (m_materialCount);<br />
assert (m_materialTexturesList);<br />
assert (materialId < m_materialCount);<br />
<br />
if (++m_materialTexturesList[materialId].refcount <= 1)<br />
{<br />
SMOMaterial* material = &m_smoMaterials[materialId];<br />
<br />
const char* texNames[3];<br />
texNames[0] = &m_textureFilenamesRaw[material->firstTextureOffset];<br />
texNames[1] = &m_textureFilenamesRaw[material->secondTextureOffset];<br />
texNames[2] = &m_textureFilenamesRaw[material->thirdTextureOffset];<br />
if ( *texNames[0] )<br />
texNames[0] = "createcrappygreentexture.blp";<br />
<br />
assert (material->shader < SMOMaterial::SH_COUNT);<br />
<br />
int const textureCount<br />
( CShaderEffect::s_enableShaders<br />
? s_wmoShaderMetaData[material->shader].texturesWithShader<br />
: s_wmoShaderMetaData[material->shader].texturesWithoutShader<br />
);<br />
<br />
int textures_set (0);<br />
<br />
for (; textures_set < textureCount; ++textures_set)<br />
{<br />
if (!texNames[textures_set])<br />
{<br />
material->shader = MapObjOpaque;<br />
textures_set = 1;<br />
break;<br />
}<br />
}<br />
<br />
for (; textures_set < 3; ++textures_set)<br />
{<br />
texNames[textures_set] = nullptr;<br />
}<br />
<br />
if (material->shader == MapObjTwoLayerTerrain && texNames[1])<br />
{<br />
texNames[1] = insert_specular_suffix (texNames[1]);<br />
}<br />
<br />
int flags (std::max (m_field_2C, 12));<br />
<br />
const char* parent_name (m_field_9E8 & 1 ? m_filename : nullptr);<br />
<br />
m_materialTexturesList[materialId]->textures[0] = texNames[0] ? CMap::CreateTexture (texNames[0], parent_name, flags) : nullptr;<br />
m_materialTexturesList[materialId]->textures[1] = texNames[1] ? CMap::CreateTexture (texNames[1], parent_name, flags) : nullptr;<br />
m_materialTexturesList[materialId]->textures[2] = texNames[2] ? CMap::CreateTexture (texNames[2], parent_name, flags) : nullptr;<br />
}<br />
}<br />
<br />
== MOGN chunk ==<br />
<br />
*'''List of group names for the groups in this map object.'''<br />
<br />
char group_names[];<br />
<br />
A contiguous block of zero-terminated strings. The names are purely informational except for "antiportal". The names are referenced from MOGI and MOGP.<br />
<br />
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).<br />
<br />
== MOGI chunk ==<br />
<br />
*'''Group information for WMO groups, 32 bytes per group, nGroups entries.'''<br />
<br />
struct WMOGroup<br />
{<br />
/*000h*/ uint32_t flags; // see [[WMO/v17#group_flags|information in in MOGP]], they are equivalent<br />
/*004h*/ {{Template:Type|CAaBox}} bounding_box;<br />
/*01Ch*/ int32_t nameoffset; // name in [[WMO/v17#MOGN_chunk|MOGN]] chunk (-1 for no name)<br />
} groups[];<br />
<br />
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.<br />
<br />
The name offsets point to the position in the file relative to the MOGN header.<br />
<br />
== MOSB chunk ==<br />
<br />
*'''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.<br />
<br />
char skybox_filename[];<br />
<br />
== MOPV chunk ==<br />
<br />
*'''Portal vertices, one entry is a float[3], '''usually''' 4 * 3 * float per portal''' (actual number of vertices given in portal entry)<br />
<br />
{{Template:Type|C3Vector}} portal_vertices[];<br />
<br />
Portals are (always?) rectangles that specify where doors or entrances are in a [[WMO/v17]]. They could be used for visibility, but I currently have no idea what relations they have to each other or how they work.<br />
<br />
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.<br />
<br />
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. <br />
<br />
It's fun, you can actually map out the topology of the [[WMO/v17]] using this and the [[WMO/v17#MOPR_chunk|MOPR]] chunk. This could be used to speed up the rendering once/if I figure out how.<br />
<br />
It looks like there are nPortals entries, but in some models like CavernsofTime.wmo, |MOPV| = 1896 bytes = 474 floats = 158 vertices = 39.5 rectangles ???<br />
<br />
== MOPT chunk ==<br />
<br />
*'''Portal information. 20 bytes per portal, nPortals entries.''' 128 portals max.<br />
<br />
struct SMOPortal<br />
{<br />
uint16_t base_index;<br />
uint16_t index_count;<br />
{{Template:Type|C4Plane}} plane;<br />
} portals[];<br />
<br />
== MOPR chunk ==<br />
<br />
* Map Object Portal References from groups. Mostly twice the number of portals. Actual count defined by sum (MOGP.portals_used).<br />
<br />
''It's not correct that there's 2*nPortals of 8 bytes in all cases. For example in WOTLK data, Stormwind.wmo has 319 portals but only 627 portal relationships before MOVV chunk starts. Maybe some portals are unused or something, I haven't analyzed further - but there are many models where this is the case. ---[[User:Relaxok|Relaxok]], 09-06-2012<br />
''<br />
<br />
struct SMOPortalRef'' // 04-29-2005 By ObscuR''<br />
{<br />
uint16_t portal_index; // into MOPR<br />
uint16_t group_index; // the other one<br />
uint16_t side; // positive or negative.<br />
uint16_t unk;<br />
} portal_references[];<br />
<br />
== MOVV chunk ==<br />
<br />
*'''Visible block vertices''', 0xC byte per entry.<br />
<br />
Just a list of vertices that corresponds to the visible block list.<br />
<br />
{{Template:Type|C3Vector}} visible_block_vertices[];<br />
<br />
== MOVB chunk ==<br />
<br />
*'''Visible block list'''<br />
<br />
struct<br />
{<br />
uint16_t firstVertex;<br />
uint16_t count;<br />
) visible_blocks[];<br />
<br />
== MOLT chunk ==<br />
<br />
*'''Lighting information. 48 bytes per light, nLights entries'''<br />
<br />
enum LightType<br />
{<br />
OMNI_LGT,<br />
SPOT_LGT,<br />
DIRECT_LGT,<br />
AMBIENT_LGT<br />
};<br />
<br />
struct SMOLight ''// 04-29-2005 By ObscuR''<br />
{<br />
/*000h*/ uint8_t lightType;<br />
/*001h*/ uint8_t type;<br />
/*002h*/ uint8_t useAtten;<br />
/*003h*/ uint8_t pad;<br />
/*004h*/ uint8_t color[4]; // Color (B,G,R,A)<br />
/*008h*/ float position[3]; // Position (X,Z,-Y)<br />
/*014h*/ float intensity;<br />
/*018h*/ float attenStart;<br />
/*01Ch*/ float attenEnd;<br />
/*020h*/ float unk1;<br />
/*024h*/ float unk2;<br />
/*028h*/ float unk3;<br />
/*02Ch*/ float unk4;<br />
/*030h*/ <br />
} lights[];<br />
<br />
First 4 uint8_t are probably flags, mostly with the values (0,1,1,1).<br />
<br />
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.<br />
<br />
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... :)<br />
<br />
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. --- [[User:Skarn|Skarn]] ([[User talk:Skarn|talk]])<br />
<br />
== MODS chunk ==<br />
<br />
*'''This chunk defines doodad sets.''' <br />
<br />
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.<br />
<br />
The doodad set number for every WMO instance is specified in the [[ADT]] files.<br />
<br />
struct SMODoodadSet<br />
{<br />
/*000h*/ char name[20]; // set name<br />
/*014h*/ uint32_t firstinstanceindex; // index of first doodad instance in this set<br />
/*018h*/ uint32_t numDoodads; // number of doodad instances in this set<br />
/*01Ch*/ uint32_t unused;<br />
} doodad_sets[];<br />
<br />
firstinstanceindex is not the name index, but the actual order the doodads come in the MODD chunk in the WMO -MaiN<br />
<br />
== MODN chunk ==<br />
<br />
*'''List of filenames for [[M2]] ([[MDX|mdx]]) models that appear in this [[WMO/v17]].''' <br />
A block of zero-padded, zero-terminated strings. There are nModels file names in this list. They have to be .[[MDX]]!<br />
<br />
char doodad_filenames[];<br />
<br />
== MODD chunk ==<br />
<br />
*'''Information for doodad instances. 40 bytes per doodad instance, nDoodads entries.''' <br />
<br />
-- There are not nDoodads entries here! Divide the chunk length by 40 to get the correct amount.<br />
<br />
While [[WMO/v17]]s and models ([[M2]]s) in a map tile are rotated along the axes, doodads within a [[WMO/v17]] are oriented using quaternions! Hooray for consistency!<br />
<br />
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/v17]]s 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.<br />
<br />
struct SMODoodadDef<br />
{<br />
/*000h*/ uint32_t name_offset : 24; // reference offset into [[WMO/v17#MODN_chunk|MODN]]<br />
/*003h*/ uint32_t flag_AcceptProjTex : 1;<br />
/*003h*/ uint32_t flag_0x2 : 1; // MapStaticEntity::field_34 |= 1<br />
/*003h*/ uint32_t flag_0x4 : 1;<br />
/*003h*/ uint32_t flag_0x8 : 1;<br />
/*003h*/ uint32_t : 4; // unused as of 7.0.1.20994<br />
/*004h*/ {{Template:Type|C3Vector}} position; // (X,Z,-Y)<br />
/*010h*/ {{Template:Type|C4Quaternion}} orientation; // (X, Y, Z, W)<br />
/*020h*/ float scale; // scale factor<br />
/*024h*/ uint8_t color[4]; // (B,G,R,A) lightning color<br />
} doodad_definitions[];<br />
<br />
* How to compute a matrix to map WMO's M2 to world coordinates<br />
<br />
The coordinate system here is WMO's local coordinate system. It's Z-up already, that differs it from Y-up in '''[[ADT#MODF_chunk|MODF(ADT)]]''', '''[[WDT#MODF_chunk|MODF(WDT)]]''' and '''[[ADT#MDDF_chunk|MDDF]]''' chunks.<br />
To compute the whole placement matrix for doodad you would need take positionMatrix of WMO from '''[[ADT#MODF_chunk|MODF(ADT)]]''' or '''[[WDT#MODF_chunk|MODF(WDT)]]''' and multiply it by positionMatrix calculated here.<br />
<br />
Example implementation in js with gl-matrix library:<br />
function createPlacementMatrix(modd, wmoPlacementMatrix){<br />
var placementMatrix = mat4.create();<br />
mat4.identity(placementMatrix);<br />
mat4.multiply(placementMatrix, placementMatrix, wmoPlacementMatrix);<br />
<br />
mat4.translate(placementMatrix, placementMatrix, [modd.pos[0],modd.pos[1], modd.pos[2]]);<br />
<br />
var orientMatrix = mat4.create();<br />
mat4.fromQuat(orientMatrix,<br />
[modd.rotation[0], //imag.x<br />
modd.rotation[1], //imag.y,<br />
modd.rotation[2], //imag.z,<br />
modd.rotation[3] //real<br />
]<br />
);<br />
mat4.multiply(placementMatrix, placementMatrix, orientMatrix);<br />
<br />
mat4.scale(placementMatrix, placementMatrix, [modd.scale, modd.scale, modd.scale]);<br />
return placementMatrix;<br />
}<br />
<br />
== MFOG chunk ==<br />
<br />
*'''Fog information. Made up of blocks of 48 bytes.'''<br />
<br />
struct SMOFog<br />
{<br />
/*000h*/ uint32_t flag_infinite_radius : 1; // Ignore radius in CWorldView::QueryCameraFog<br />
/*000h*/ uint32_t : 3; // unused as of 7.0.1.20994<br />
/*000h*/ uint32_t flag_0x10 : 1;<br />
/*000h*/ uint32_t : 27; // unused as of 7.0.1.20994<br />
/*004h*/ {{Template:Type|C3Vector}} pos;<br />
/*010h*/ float smaller_radius;<br />
/*014h*/ float larger_radius;<br />
/*018h*/ float fog_end;<br />
/*01Ch*/ float fog_start_multiplier; // (0..1)<br />
/*020h*/ uint8_t color[4]; // The back buffer is also cleared to this colour<br />
/*024h*/ float unk1; // almost always 222.222<br />
/*028h*/ float unk2;<br />
/*02Ch*/ uint8_t color2[4];<br />
} fogs[];<br />
<br />
*Fog end: This is the distance at which all visibility ceases, and you see no objects or terrain except for the fog color.<br />
*Fog start: This is where the fog starts. Obtained by multiplying the fog end value by the fog start multiplier.<br />
<br />
== MCVP chunk (optional) ==<br />
<br />
*'''Convex Volume Planes. Contains blocks of floating-point numbers.''' 0x10 bytes (4 floats) per entry.<br />
<br />
{{Template:Type|C4Plane}} convex_volume_planes[]; // normal points out<br />
<br />
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.<br />
<br />
==GFID (Legion+)==<br />
* required when WMO is load from fileID (e.g. game objects)<br />
struct<br />
{<br />
uint32_t ids[header.flags & SMOHeader::Flag_Lod ? 3 : 1];<br />
} group_file_ids[header.nGroups];<br />
<br />
= WMO group file =<br />
<br />
WMO group files contain the actual polygon soup for a particular section of the entire [[WMO/v17]].<br />
<br />
Every group file has one top-level [[WMO/v17#MOGP_chunk|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. <br />
<br />
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.<br />
<br />
The following chunks are always present in the following order:<br />
*[[WMO/v17#MOGP_chunk|MOGP]]<br />
*[[WMO/v17#MOPY_chunk|MOPY]]<br />
*[[WMO/v17#MOVI_chunk|MOVI]]<br />
*[[WMO/v17#MOVT_chunk|MOVT]]<br />
*[[WMO/v17#MONR_chunk|MONR]]<br />
*[[WMO/v17#MOTV_chunk|MOTV]]<br />
*[[WMO/v17#MOBA_chunk|MOBA]]<br />
<br />
These chunks are only present if a flag in the header is set. See the list below for the flags.<br />
*Cataclysm introduced a new optional MOBS chunk, I guess it's related to [[WMO/v17#MOBA_chunk|MOBA]]. ---[[User:Bananenbrot|Bananenbrot]], 12-18-2010<br />
*[[WMO/v17#MOLR_chunk|MOLR]]<br />
*[[WMO/v17#MODR_chunk|MODR]]<br />
*[[WMO/v17#MOBN_chunk|MOBN]]<br />
*[[WMO/v17#MOBR_chunk|MOBR]]<br />
*MPBV<br />
*MPBP<br />
*MPBI<br />
*MPBG<br />
*[[WMO/v17#MOCV_chunk|MOCV]]<br />
*[[WMO/v17#MLIQ_chunk|MLIQ]]<br />
*MORI<br />
*MORB<br />
* [[WMO/v17#MOTV_chunk|MOTV]] 2<br />
* [[WMO/v17#MOCV_chunk|MOCV]] 2<br />
<br />
== MOGP chunk ==<br />
<br />
Note: In its header is given a wrong size. Just use 0x44. -eLaps<br />
*Actually, the size is correct, the other chunks are just subchunks of MOGP :) ---[[User:Tigurius|Tigurius]]<br />
<br />
'''Offset Type Description'''<br />
0x00 uint32 Group name (offset into [[WMO/v17#MOGN_chunk|MOGN]] chunk)<br />
0x04 uint32 Descriptive group name (offset into [[WMO/v17#MOGN_chunk|MOGN]] chunk)<br />
0x08 uint32 Flags<br />
0x0C float[3] Bounding box corner 1 (same as in [[WMO/v17#MOGI_chunk|MOGI]])<br />
0x18 float[3] Bounding box corner 2<br />
0x24 uint16 Index into the [[WMO/v17#MOPR_chunk|MOPR]] chunk<br />
0x26 uint16 Number of items used from the [[WMO/v17#MOPR_chunk|MOPR]] chunk<br />
0x28 uint16 Number of batches A<br />
0x2A uint16 Number of batches interior<br />
0x2C uint32 Number of batches exterior<br />
0x30 uint8[4] Up to four indices into the WMO fog list<br />
0x34 uint32 LiquidType related, see below in the MLIQ chunk.<br />
0x38 {{Template:Type/foreign_key|table=WMOAreaTable|column=m_WMOGroupID}} WMO group ID<br />
0x3C uint32 &1: WoD(?)+ CanCutTerrain (by MOPL planes), others (UNUSED: 20740)<br />
0x40 uint32 (UNUSED: 20740)<br />
<br />
The fields referenced from the [[WMO/v17#MOPR_chunk|MOPR]] chunk indicate portals leading out of the [[WMO/v17]] group in question.<br />
<br />
For the "Number of batches" fields, A + batches_interior + batches_exterior == the total number of batches in the [[WMO/v17]] group (in the [[WMO/v17#MOBA_chunk|MOBA]] chunk). This might be some kind of LOD thing, or just separating the batches into different types/groups...?<br />
<br />
Flags: always contain more information than flags in [[WMO/v17#MOGI_chunk|MOGI]]. I suppose [[WMO/v17#MOGI_chunk|MOGI]] only deals with topology/culling, while flags here also include rendering info.<br />
<br />
===group flags===<br />
'''Flag Meaning'''<br />
0x1 Has [[WMO/v17#MOBN_chunk|MOBN]] and [[WMO/v17#MOBR_chunk|MOBR]] chunk.<br />
0x2 (UNUSED: 20740) possibly: subtract mohd.color in mocv fixing <br />
0x4 Has vertex colors ([[WMO/v17#MOCV_chunk|MOCV]] chunk).<br />
0x8 SMOGroup::EXTERIOR -- Outdoor<br />
0x10 (UNUSED: 20740)<br />
0x20 (UNUSED: 20740)<br />
0x40<br />
0x80 SMOGroup::UNREACHABLE<br />
0x100<br />
0x200 Has lights ([[WMO/v17#MOLR_chunk|MOLR]] chunk)<br />
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)<br />
0x800 Has doodads ([[WMO/v17#MODR_chunk|MODR]] chunk)<br />
0x1000 SMOGroup::LIQUIDSURFACE -- Has water ([[WMO/v17#MLIQ_chunk|MLIQ]] chunk)<br />
0x2000 SMOGroup::INTERIOR -- Indoor<br />
0x4000 (UNUSED: 20740)<br />
0x8000<br />
0x10000 SMOGroup::ALWAYSDRAW -- clear 0x8 after CMapObjGroup::Create() in MOGP and MOGI<br />
0x20000 (UNUSED: 20740) Has MORI and MORB chunks.<br />
0x40000 Show skybox -- automatically unset if MOSB not present.<br />
0x80000 is_not_water_but_ocean, LiquidType related, see below in the MLIQ chunk.<br />
0x100000<br />
0x200000<br />
0x400000 (UNUSED: 20740)<br />
0x800000<br />
0x1000000 SMOGroup::CVERTS2: Has two MOCV chunks: Just add two or don't set 0x4 to only use cverts2.<br />
0x2000000 SMOGroup::TVERTS2: Has two MOTV chunks: Just add two.<br />
0x4000000 Just call CMapObjGroup::CreateOccluders() independent of groupname being "antiportal". requires intBatchCount == 0, extBatchCount == 0, UNREACHABLE.<br />
0x8000000 unk. requires intBatchCount == 0, extBatchCount == 0, UNREACHABLE.<br />
0x10000000 (UNUSED: 20740)<br />
0x20000000 (UNUSED: 20740)<br />
0x40000000 SMOGroup::TVERTS3: Has three MOTV chunks, eg. for MOMT with shader 18.<br />
0x80000000<br />
<br />
=== "antiportal" ===<br />
<br />
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.<br />
<br />
void CMapObjGroup::CreateOccluders()<br />
{<br />
for ( unsigned int mopy_index (0), movi_index (0)<br />
; mopy_index < this->mopy_count<br />
; ++mopy_index, ++movi_index<br />
) <br />
{<br />
{{Template:Type|C3Vector}}* points[3] = <br />
{ &this->m_vertices[this->movi[movi_index].points[0]]<br />
, &this->m_vertices[this->movi[movi_index].points[1]]<br />
, &this->m_vertices[this->movi[movi_index].points[2]]<br />
};<br />
<br />
float avg ((points[0]->z + points[1]->z + points[2]->z) / 3.0); <br />
<br />
unsigned int two_points[2];<br />
unsigned int two_points_index (0);<br />
<br />
for (unsigned int i (0); i < 3; ++i)<br />
{<br />
if (points[i]->z > avg)<br />
{<br />
two_points[two_points_index++] = i;<br />
}<br />
}<br />
<br />
if (two_points_index > 1)<br />
{<br />
CMapObjOccluder* occluder (CMapObj::AllocOccluder());<br />
occluder->p1 = points[two_points[0]];<br />
occluder->p2 = points[two_points[1]];<br />
<br />
append (this->occluders, occluder);<br />
}<br />
}<br />
}<br />
<br />
== MOPY chunk ==<br />
<br />
*'''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.'''<br />
<br />
enum SMOPolyFlags ''// 03-29-2005 By ObscuR''<br />
{<br />
F_NOCAMCOLLIDE,<br />
F_DETAIL,<br />
F_COLLISION,<br />
F_HINT,<br />
F_RENDER,<br />
F_COLLIDE_HIT,<br />
};<br />
<br />
struct SMOPoly<br />
{<br />
/*000h*/ uint8 flags;<br />
/*001h*/ uint8 material_id;<br />
};<br />
<br />
'''Flag Description'''<br />
0x00 ?<br />
0x01 ?<br />
0x02 NOCAMCOLLIDE<br />
0x04 no collision<br />
0x08 ?<br />
0x20 ?<br />
0x40 ?<br />
<br />
Frequently used flags are 0x20 and 0x40, but I have no idea what they do.<br />
<br />
Be quiet peasants! Glorious Ohai of Modcraft Von Superparanoid discovered '''disabling collision for a triangle''' in the following structure; ''MOPYs triangle_material_info - struct MOPY materialInfoForTriangles - struct MOPY_FLAGS flags - ubyte MOPY_FLAG_NO_CAM_COLLIDE : 1'' setting its value to 1 with ''ubyte MOPY_FLAG_UNK_0x20 : 1'' value to 0, will disable collision for the selected triangle. <br />
<br />
From 15464:<br />
bool isNoCamCollide (uint8 flags) { return flags & 2; }<br />
bool isDetailFace (uint8 flags) { return flags & 4; }<br />
bool isCollisionFace (uint8 flags) { return flags & 8; }<br />
bool isColor (uint8 flags) { return !(flags & 8); }<br />
bool isRenderFace (uint8 flags) { return (flags & 0x24) == 0x20; }<br />
bool isTransFace (uint8 flags) { return (flags & 1) && (flags & 0x24); }<br />
bool isCollidable (uint8 flags) { return isCollisionFace (flags) || isRenderFace (flags); }<br />
<br />
Material ID specifies an index into the material table in the root [[WMO/v17]] file's [[WMO/v17#MOMT_chunk|MOMT]] chunk. Some of the triangles have 0xFF for the material ID, I skip these. (but there might very well be a use for them?)<br />
<br />
The triangles with 0xFF Material ID seem to be a simplified mesh. Like for collision detection or something like that. At least stairs are flattened to ramps if you only display these polys. --[[User:shlainn|shlainn]] 7 Jun 2009<br />
<br />
0xFF representing -1 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. --[[User:Schlumpf|schlumpf_]] 20:40, 7 June 2009 (CEST)<br />
<br />
Triangles stored here are more-or-less pre-sorted by texture, so it's ok to draw them sequentially.<br />
<br />
== MOVI chunk ==<br />
<br />
*'''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.<br />
<br />
== MOVT chunk ==<br />
<br />
*'''Vertices chunk.''', count = size / (sizeof(float) * 3). 3 floats per vertex, the coordinates are in (X,Z,-Y) order. It's likely that [[WMO/v17]]s and models ([[M2]]s) 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.<br />
<br />
== MONR chunk ==<br />
<br />
*'''Normals.''' count = size / (sizeof(float) * 3). 3 floats per vertex normal, in (X,Z,-Y) order.<br />
<br />
== MOTV chunk ==<br />
<br />
*'''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 [[WMO/v17#.22antiportal.22|antiportal]] WMO groups.<br />
<br />
== MOBA chunk ==<br />
<br />
*'''Render batches. Records of 24 bytes.'''<br />
<br />
struct Batch<br />
{<br />
/*0x00*/ int16_t a[2*3]; // indices? a box? (-2,-2,-1,2,2,3 in cameron) -> seems to be a bounding box for rendering<br />
/*0x0C*/ uint32_t first_index; // index of the first face index used in MOVI<br />
/*0x10*/ uint16_t num_indices; // number of indices used<br />
/*0x12*/ uint16_t first_vertex; // index of the first vertex used in MOVT<br />
/*0x14*/ uint16_t last_vertex; // index of the last vertex used (batch includes this one)<br />
/*0x16*/ uint8_t flags;<br />
enum<br />
{<br />
use_uint16_t_material = 2, // Legion+. instead of materialId use a uint16_t at 0xA (a[5])<br />
};<br />
/*0x17*/ uint8_t materialId; // index in MOMT<br />
};<br />
<br />
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. <del>Concerning the byte at 0x16, as a material ID is coded on a uint8, I guess it is completely unused.</del><br />
--[[User:Gamhea|Gamhea]] 12:23, 29 July 2013 (UTC)<br />
<br />
<br />
a[2*3] seems to be a bounding box used for rendering.<br />
<br />
'''Legion :'''<br />
When flag use_uint16_t_material is set to 2, values in a[2*3] seem to be 0 except a[5] which is the materialId, this lead to rendering issues on previous expansion as parts of the wmo are disappearing when moving around the wmo.<br />
To fix this, set materialId with the value of a[5].<br />
Now take the first_vertex value, and the last_vertex value in this chunk, they point to the MOVT chunk consisted of 3 float bytes array ( X Y Z values in this order.)<br />
Between the first and the last vertex, they are many vertex values, you have to get for all theses vertex, the minX, minY, minZ and maxX, maxY, maxZ values, then convert it to int16 (8.02656 became 8)<br />
When you have the 6 needed values, insert theses 6 values in the a[2*3] bounding box in this order : minX, minY, minZ,maxX, maxY, maxZ.<br />
Everything should be fine now !<br />
. --[[User:Rangorn|Rangorn]] ([[User talk:Rangorn|talk]]) 20:52, 17 May 2016 (CEST)<br />
<br />
== MOLR chunk ==<br />
<br />
*'''Light references, one 16-bit integer per light reference.'''<br />
<br />
This is basically a list of lights used in this [[WMO/v17]] group, the numbers are indices into the [[WMO/v17]] root file's [[WMO/v17#MOLT_chunk|MOLT]] table.<br />
<br />
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. :(<br />
<br />
== MODR chunk ==<br />
<br />
*'''Doodad references, one 16-bit integer per doodad.'''<br />
The numbers are indices into the doodad instance table ([[WMO/v17#MODD_chunk|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.<br />
<br />
== MOBN chunk ==<br />
<br />
*'''Nodes of the BSP tree, used for collision (along with bounding boxes ?). Array of t_BSP_NODE. / CAaBspNode.''' 0x10 bytes.<br />
<br />
struct t_BSP_NODE<br />
{ <br />
uint16_t planeType; // 4: leaf, 0 for YZ-plane, 1 for XZ-plane, 2 for XY-plane<br />
int16_t children[2]; // index of bsp child node (right in this array)<br />
uint16_t numFaces; // num of triangle faces in [[WMO/v17#MOBR_chunk|MOBR]]<br />
uint32_t firstFace; // index of the first triangle index(in [[WMO/v17#MOBR_chunk|MOBR]])<br />
float fDist;<br />
};<br />
<br />
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|mobius]].<br />
<br />
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 --[[User:Schlumpf|Schlumpf]] ([[User talk:Schlumpf|talk]]) 11:18, 3 January 2016 (UTC))<br />
<br />
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. --[[User:Deamon|Deamon]] ([[User talk:Deamon|talk]]) 10:01, 15 January 2016 (UTC)<br />
<br />
<br />
#define epsilon 0.01F<br />
void MergeBox(CVect3 (&result)[2], float *box1, float *box2)'''<br />
{<br />
result[0][0] = box1[0];<br />
result[0][1] = box1[1];<br />
result[0][2] = box1[2];<br />
result[1][0] = box2[0];<br />
result[1][1] = box2[1];<br />
result[1][2] = box2[2];<br />
}<br />
void AjustDelta(CVect3 (&src)[2], float *dst, float coef)'''<br />
{<br />
float d1 = (src[1][0]- src[0][0]) * coef;// delta x<br />
float d2 = (src[1][1]- src[0][1]) * coef;// delta y<br />
float d3 = (src[1][2]- src[0][2]) * coef;// delta z<br />
dst[1] = d1 + src[0][1];<br />
dst[0] = d2 + src[0][0];<br />
dst[2] = d3 + src[0][2];<br />
}<br />
void TraverseBsp(int iNode, CVect3 (&pEyes)[2] , CVect3 (&pBox)[2],void *(pAction)(T_BSP_NODE *,void *param),void *param)'''<br />
{<br />
int plane;<br />
float eyesmin_boxmin;<br />
float boxmax_eyesmax;<br />
float eyesmin_fdist;<br />
float eyes_max_fdist;<br />
float eyesmin_div_deltadist;<br />
CVect3 tBox1[2];<br />
CVect3 tBox2[2];<br />
CVect3 newEyes[2];<br />
CVect3 ajusted;<br />
T_BSP_NODE *pNode = &m_tNode[iNode];<br />
if ( pNode)<br />
{<br />
if (pNode->planetype & 4 )<br />
{<br />
if(pAction == 0)<br />
{<br />
RenderGeometry(GetEngine3DInstance(),pNode);<br />
return;<br />
}<br />
else<br />
{<br />
pAction(pNode,param);<br />
}<br />
}<br />
plane =pNode->planetype & 3;<br />
eyesmin_boxmin = pEyes[0][plane] - pBox[0][plane];<br />
if ( ( -epsilon < eyesmin_boxmin) | (-epsilon == eyesmin_boxmin) || (pEyes[1][plane]- pBox[0][plane]) >= -epsilon )<br />
{<br />
boxmax_eyesmax = pBox[1][plane] - pEyes[1][plane];<br />
if ( (epsilon < boxmax_eyesmax) | (epsilon == boxmax_eyesmax) || (pBox[1][plane] - pEyes[0][plane]) >= epsilon )<br />
{<br />
memmove(tBox1,pBox,sizeof(pBox));<br />
tBox1[0][plane] = pNode->fDist;<br />
memmove(tBox2,pBox,sizeof(pBox));<br />
tBox2[1][plane] = pNode->fDist;<br />
eyesmin_fdist = pEyes[0][plane] - pNode->fDist;<br />
eyes_max_fdist = (pEyes[1][plane]) - pNode->fDist;<br />
if ( eyesmin_fdist >= -epsilon && eyesmin_fdist <= epsilon|| (eyes_max_fdist >= -epsilon) && eyes_max_fdist <= epsilon )<br />
{<br />
if ( pNode->children[1] != (short)-1 ) TraverseBsp(pNode->children[1], pEyes, tBox1,pAction,param);<br />
if ( pNode->children[0] != (short)-1 ) TraverseBsp(pNode->children[0] , pEyes, tBox2,pAction,param);<br />
return;<br />
}<br />
if ( eyesmin_fdist > epsilon && eyes_max_fdist < epsilon)<br />
{<br />
if ( pNode->children[1] != (short)-1 ) TraverseBsp(pNode->children[1], pEyes, tBox1,pAction,param);<br />
return;<br />
}<br />
if ( eyesmin_fdist < -epsilon && eyes_max_fdist < -epsilon)<br />
{<br />
if ( pNode->children[0] != (short)-1 ) TraverseBsp(pNode->children[0] , pEyes, tBox2,pAction,param);<br />
return;<br />
}<br />
eyesmin_div_deltadist = (float)(eyesmin_fdist / (eyesmin_fdist - eyes_max_fdist));<br />
AjustDelta(pEyes, ajusted, eyesmin_div_deltadist);<br />
if ( eyesmin_fdist <= 0.0 )<br />
{<br />
if ( pNode->children[0] != (short)-1 )<br />
{<br />
MergeBox(newEyes, &pEyes[0][0], ajusted);<br />
TraverseBsp(pNode->children[0] , newEyes, tBox2,pAction,param);<br />
}<br />
if (pNode->children[1] != (short)-1 )<br />
{<br />
MergeBox(newEyes, ajusted, &pEyes[1][0]);<br />
TraverseBsp(pNode->children[1] , newEyes, tBox1,pAction,param);<br />
}<br />
}<br />
else<br />
{<br />
if ( pNode->children[1] != (short)-1 )<br />
{<br />
MergeBox(newEyes, &pEyes[0][0], ajusted);<br />
TraverseBsp(pNode->children[1] , newEyes, tBox1,pAction,param);<br />
}<br />
if (pNode->children[0] != (short)-1 )<br />
{<br />
MergeBox(newEyes, ajusted, &pEyes[1][0]);<br />
TraverseBsp(pNode->children[0] , newEyes, tBox2,pAction,param);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
CheckFromEyes(CVect3 (&pEyes)[2],void *(pAction)(T_BSP_NODE *,void *param),void *param )<br />
{<br />
/*CVect3 eyes[2];<br />
instance_mat.invert();<br />
eyes[0] = _fixCoordSystemInv((instance_mat*p->m_pCameraViewport->GetCameraTarget())+CVect3(0,-10,0) );<br />
eyes[1] = _fixCoordSystemInv((instance_mat*p->m_pCameraViewport->GetCameraTarget())+CVect3(0,60,0) ); <br />
// make vector down<br />
*/<br />
/* eyes[0] = CVect3(-1.474797e+001F, -1.195053e+001F, 5.416779e+000F); // Debug absolute position from WP Azaroth 1164,58,-10645.83<br />
eyes[1] = CVect3(-1.474797e+001F, -1.195053e+001F, -1.754583e+003F);<br />
*/<br />
TraverseBsp(0,pEyes,m_bbox,pAction);<br />
}<br />
<br />
This BSP seems to be used for collision purpose only. <br />
<br />
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.<br />
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. <br />
<br />
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.<br />
Probably there is somewhere a flag telling us with which method we should use for the object.<br />
<br />
The code attached seems to work fine for BSP method--[[peter-pan|peter-pan]].<br />
<br />
== MOBR chunk ==<br />
<br />
*'''Face indices''' for CAaBsp (MOBN). Unsigned shorts.<br />
*'''Triangle indices (in [[WMO/v17#MOVI_chunk|MOVI]] which define triangles) to describe polygon planes defined by [[WMO/v17#MOBN_chunk|MOBN]] BSP nodes.'''<br />
<br />
Example code required to get an actual indicies array from MOBR array:<br />
var bpsIndicies = new Array(mobr.length*3);<br />
for (var i = 0; i < mobr.length; i++) {<br />
bpsIndicies[i*3 + 0] = movi[3*mobr[i]+0];<br />
bpsIndicies[i*3 + 1] = movi[3*mobr[i]+1];<br />
bpsIndicies[i*3 + 2] = movi[3*mobr[i]+2];<br />
}<br />
<br />
Example code to get indicies into MOVT for triangles, referenced from BSP node definition:<br />
for (var triangleInd = node.firstFace; triangleInd<node.firstFace+node.numFaces; triangleInd++) {<br />
//3 vertices per triangle<br />
movt[bpsIndicies[3*triangleInd + 0]]<br />
movt[bpsIndicies[3*triangleInd + 1]]<br />
movt[bpsIndicies[3*triangleInd + 2]]<br />
}<br />
<br />
== MOCV chunk ==<br />
<br />
*'''Vertex colors, 4 bytes per vertex (BGRA), for [[WMO/v17]] groups using indoor lighting.''' <br />
I don't know if this is supposed to work together with, or replace, the lights referenced in [[WMO/v17#MOLR_chunk|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?)<br />
<br />
- I'm pretty sure WoW does not use lightmaps in it's [[WMO/v17]]s...<br />
<br />
After further inspection, this is it, actual pre-lit vertex colors for [[WMO/v17]]s - vertex lighting is turned off. This is used if flag 0x2000 in the [[WMO/v17#MOGI_chunk|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!!!<br />
<br />
''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. [[User:Relaxok|Relaxok]] 18:29, 20 March 2013 (UTC)''<br />
<br />
== MLIQ chunk ==<br />
<br />
*'''Specifies liquids inside WMOs.''' <br />
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)<br />
<br />
Chunk header:<br />
'''Offset Type Description'''<br />
0x00 uint32 number of X vertices (xverts)<br />
0x04 uint32 number of Y vertices (yverts)<br />
0x08 uint32 number of X tiles (xtiles = xverts-1)<br />
0x0C uint32 number of Y tiles (ytiles = yverts-1)<br />
0x10 float[3] base coordinates for X and Y<br />
0x1C uint16 material ID<br />
<br />
We then have <code>[xverts*yverts]</code> of the following:<br />
'''Offset Type Description'''<br />
0x00 uint16[2] Unknown Data, first value is usually 86. Second value is (so far) always 0.<br />
0x04 float height data<br />
<br />
Followed by <code>[xtiles*ytiles]</code> of:<br />
'''Offset Type Description'''<br />
0x00 uint8 types? // Unsure, if really types. (0 - 20)<br />
<br />
The liquid data contains the vertex height map (xverts * yverts * 8 bytes) and the tile flags (xtiles * ytiles bytes) as descripbed in [[ADT]] files ([[ADT#MCLQ_chunk|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).<br />
<br />
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. --[[User:Bananenbrot|Bananenbrot]] 22:02, 1 August 2012 (UTC)<br />
<br />
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. --[[User:Kjasi|Kjasi]] 14 February 2016<br />
<br />
=== how to determine {{DBRef|table=LiquidType}} to use ===<br />
<br />
enum liquid_basic_types<br />
{<br />
liquid_basic_types_water = 0,<br />
liquid_basic_types_ocean = 1,<br />
liquid_basic_types_magma = 2,<br />
liquid_basic_types_slime = 3,<br />
<br />
liquid_basic_types_MASK = 3,<br />
};<br />
enum liquid_types<br />
{<br />
// ...<br />
LIQUID_WMO_Water = 13,<br />
LIQUID_WMO_Ocean = 14,<br />
LIQUID_Green_Lava = 15,<br />
LIQUID_WMO_Magma = 19,<br />
LIQUID_WMO_Slime = 20,<br />
<br />
LIQUID_END_BASIC_LIQUIDS = 20,<br />
LIQUID_FIRST_NONBASIC_LIQUID_TYPE = 21,<br />
<br />
LIQUID_NAXX_SLIME = 21,<br />
// ...<br />
};<br />
<br />
enum SMOGroup::flags<br />
{<br />
LIQUIDSURFACE = 0x1000,<br />
is_not_water_but_ocean = 0x80000,<br />
};<br />
<br />
liquid_types to_wmo_liquid (int x)<br />
{<br />
liquid_basic_types const basic (x & liquid_basic_types_MASK);<br />
switch (basic)<br />
{<br />
case liquid_basic_types_water:<br />
return (smoGroup->flags & is_not_water_but_ocean) ? LIQUID_WMO_Ocean : LIQUID_WMO_Water;<br />
case liquid_basic_types_ocean:<br />
return LIQUID_WMO_Ocean;<br />
case liquid_basic_types_magma:<br />
return LIQUID_WMO_Magma;<br />
case liquid_basic_types_slime:<br />
return LIQUID_WMO_Slime;<br />
}<br />
}<br />
<br />
<br />
if ( mapObj->mohd_data->field_3C & 4 )<br />
{<br />
if ( smoGroup->field_34 < LIQUID_FIRST_NONBASIC_LIQUID_TYPE )<br />
{<br />
this->liquid_type = to_wmo_liquid (smoGroup->field_34 - 1);<br />
}<br />
else<br />
{<br />
this->liquid_type = smoGroup->field_34;<br />
}<br />
}<br />
else<br />
{<br />
if ( smoGroup->field_34 == LIQUID_Green_Lava )<br />
{<br />
this->liquid_type = 0;<br />
}<br />
else<br />
{<br />
int const liquidType (smoGroup->field_34 + 1);<br />
int const tmp (smoGroup->field_34);<br />
if ( smoGroup->field_34 < LIQUID_END_BASIC_LIQUIDS )<br />
{<br />
this->liquid_type = to_wmo_liquid (smoGroup->field_34);<br />
}<br />
else<br />
{<br />
this->liquid_type = smoGroup->field_34 + 1;<br />
}<br />
assert (!liquidType || !(smoGroup->flags & SMOGroup::LIQUIDSURFACE));<br />
}<br />
}<br />
<br />
== MORI ==<br />
uint16_t triangle_strip_indices[];<br />
<br />
== MORB ==<br />
<br />
* ignored if !CMap::enableTriangleStrips<br />
* modifies MOBA, therefore has same count.<br />
* size is not checked, but 2 * sizeof(int), even though it is only (int, short).<br />
struct MORB_entry<br />
{<br />
uint32_t start_index;<br />
uint16_t index_count;<br />
uint16_t padding;<br />
}<br />
* overwrites 0xC and 0x10 of MOBA (start, count).<br />
<br />
== MOTA ==<br />
<br />
* Map Object Tangent Array<br />
<br />
struct MOTA<br />
{<br />
unsigned short first_index[moba_count]; // either -1 or first index of batch.count indices into tangents[]. <br />
// if auto-generated, only has entries for batches with <br />
// material[batch.material].shader == 10 or 14.<br />
{{Template:Type|C4Vector}} tangents[accumulated_num_indices]; // sum (batches[i].count | material[batches[i].material].shader == 10 or 14)<br />
};<br />
<br />
Is auto generated, if there are batches with shaders 10 or 14, but no tangents. (And maybe some additional condition.) See CMapObjGroup::Create().<br />
<br />
== MOBS ==<br />
<br />
* size = 0x18<br />
struct MOBS_entry<br />
{<br />
char unk[0x18];<br />
};<br />
== MDAL ==<br />
<br />
* likely new in WoD, unknown contents.<br />
<br />
struct<br />
{<br />
{{Template:Type|CArgb}} replacement_for_header_color; // if -1 or not present, take color from header<br />
} mdal;<br />
<br />
==MOPL (WoD(?)+)==<br />
* requires MOGP.canCutTerrain<br />
{{Template:Type|C4Plane}} terrain_cutting_planes[<=32];<br />
<br />
[[Category:Format]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/CharTitles&diff=21361DB/CharTitles2016-05-17T12:49:58Z<p>Rangorn: </p>
<hr />
<div>PVP rank and other titles. Added in Burning Crusade Alpha 2.0.0.5665 <br />
<br />
==Structure==<br />
'''Column Field Type Notes''' <br />
1 ID Integer <br />
2 condition Integer This is never used by the client. Still looks like pointing somewhere. Serverside?<br />
3 Male String Title name <br />
4 Female String Title name <br />
5 titleMaskID Integer Used ingame in the drop down menu.<br />
6 unknown Integer All 0 except for Cataclysm titles which are 1.<br />
<br />
==3.3.5a.12340==<br />
'''Column Field Type Notes''' <br />
1 ID Integer <br />
2 Unknown Integer This is never used by the client. Still looks like pointing somewhere. Serverside?<br />
3-19 Male [[Loc]] Title name <br />
20-36 Female [[Loc]] Title name <br />
37 titleMaskID Integer Used ingame in the drop down menu. (186 is max value, check characters.characters structure for more info)<br />
--[[User:Amaroth|Amaroth]] ([[User talk:Amaroth|talk]]) 07:34, 5 May 2015 (UTC)<br />
<br />
==6.0.1.18179==<br />
struct CharTitlesRec {<br />
uint32_t m_ID;<br />
uint32_t m_Condition_ID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_name1_lang;<br />
uint32_t m_mask_ID;<br />
uint32_t m_flags;<br />
};<br />
<br />
[[Category:DBC]][[Category:DBC_WotLK]]<br />
[[Category:DBC_WoD]][[Category:6.0.1.18179]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/Mount&diff=21360DB/Mount2016-05-17T12:48:56Z<p>Rangorn: Replaced content with "According to Schlumpf, i can't contribute, so i should remove this page like he just said."</p>
<hr />
<div>According to Schlumpf, i can't contribute, so i should remove this page like he just said.</div>Rangornhttps://wowdev.wiki/index.php?title=DB/CurrencyTypes&diff=21359DB/CurrencyTypes2016-05-17T12:48:15Z<p>Rangorn: /* 7.0.3.21384 */</p>
<hr />
<div>This file lists currencies that are now listed out of the inventory but on a tab in the character frame.<br />
<br />
==Structure==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180 " | Field <br />
! width="80" | Type <br />
! width="600" | Notes<br />
|- <br />
|1 || ID || Integer || They include some kind of groups too. See below. Real id in column 4.<br />
|- style="background:#F0F8FF;"<br />
|2 || [[Item.dbc|Item]] || iRefID || The itemd displayed.<br />
|- style="background:#F0F8FF;"<br />
|3 || [[CurrencyCategory.dbc|Category]] || iRefID || Yes, there are non-existant categories in here.<br />
|- <br />
|4 || bitIndex || Integer || These are getting shifted and used as bitmasks for "currencyTokensBackpack" resulting in a maximum of 32 * 2 types.<br />
|}<br />
==6.0.1.18179==<br />
struct CurrencyTypesRec {<br />
uint32_t m_ID;<br />
uint32_t m_categoryID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_spellWeight;<br />
uint32_t m_spellCategory;<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
uint32_t m_quality;<br />
{{Template:Type|stringref}} m_description_lang;<br />
};<br />
<br />
==ID==<br />
The id is always seperatable into three digits. The least significant is some kind of real id for that category. The other two define the category. They always increase in steps of 20.<br />
<br />
===Example===<br />
12* are Marks of Honor.<br />
*121 (14) - {{Template:Data/Item|id=20560|name=Alterac Valley Mark of Honor}}<br />
*122 (15) - {{Template:Data/Item|id=20559|name=Arathi Basin Mark of Honor}}<br />
*123 (16) - {{Template:Data/Item|id=29024|name=Eye of the Storm Mark of Honor}}<br />
*124 (17) - {{Template:Data/Item|id=42425|name=Strand of the Ancients Mark of Honor}}<br />
*125 (18) - {{Template:Data/Item|id=20558|name=Warsong Gulch Mark of Honor}}<br />
*126 (19) - {{Template:Data/Item|id=43589|name=Wintergrasp Mark of Honor}}<br />
10* are WotLK marks and basic PvP currencies.<br />
*101 (10) - {{Template:Data/Item|id=40752|name=Emblem of Heroism}}<br />
*102 (11) - {{Template:Data/Item|id=40753|name=Emblem of Valor}}<br />
*103 (12) - {{Template:Data/Item|id=43307|name=Arena Points}}<br />
*104 (13) - {{Template:Data/Item|id=43308|name=Honor Points}}<br />
<br />
<br />
[[Category:DBC]]<br />
[[Category:3.0.2.8905]][[Category:DBC_WotLK]]<br />
[[Category:DBC_WoD]][[Category:6.0.1.18179]]</div>Rangornhttps://wowdev.wiki/index.php?title=Hotfix.tbl&diff=21358Hotfix.tbl2016-05-17T12:29:50Z<p>Rangorn: </p>
<hr />
<div>According to Schlumpf, i can't contribute, so i should remove this page like he just said.</div>Rangornhttps://wowdev.wiki/index.php?title=Hotfix.tbl&diff=21357Hotfix.tbl2016-05-17T12:29:36Z<p>Rangorn: </p>
<hr />
<div>Accordong to Schlumpf, i can't contribute, so i should remove this page like he just said.</div>Rangornhttps://wowdev.wiki/index.php?title=DB/CharTitles&diff=21236DB/CharTitles2016-04-18T16:45:38Z<p>Rangorn: Add legion structure 7.0.3 21491</p>
<hr />
<div>PVP rank and other titles. Added in Burning Crusade Alpha 2.0.0.5665 <br />
<br />
==Structure==<br />
'''Column Field Type Notes''' <br />
1 ID Integer <br />
2 condition Integer This is never used by the client. Still looks like pointing somewhere. Serverside?<br />
3 Male String Title name <br />
4 Female String Title name <br />
5 titleMaskID Integer Used ingame in the drop down menu.<br />
6 unknown Integer All 0 except for Cataclysm titles which are 1.<br />
<br />
==3.3.5a.12340==<br />
'''Column Field Type Notes''' <br />
1 ID Integer <br />
2 Unknown Integer This is never used by the client. Still looks like pointing somewhere. Serverside?<br />
3-19 Male [[Loc]] Title name <br />
20-36 Female [[Loc]] Title name <br />
37 titleMaskID Integer Used ingame in the drop down menu. (186 is max value, check characters.characters structure for more info)<br />
--[[User:Amaroth|Amaroth]] ([[User talk:Amaroth|talk]]) 07:34, 5 May 2015 (UTC)<br />
<br />
==6.0.1.18179==<br />
struct CharTitlesRec {<br />
uint32_t m_ID;<br />
uint32_t m_Condition_ID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_name1_lang;<br />
uint32_t m_mask_ID;<br />
uint32_t m_flags;<br />
};<br />
<br />
==7.0.3.21491==<br />
struct CharTitlesRec {<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_name1_lang;<br />
uint16_t m_Condition_ID;<br />
uint16_t m_mask_ID;<br />
uint32_t m_flags;<br />
};<br />
<br />
[[Category:DBC]][[Category:DBC_WotLK]]<br />
[[Category:DBC_WoD]][[Category:6.0.1.18179]]<br />
[[Category:DBC_Legion]][[Category:7.0.3.21491]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/Mount&diff=21234DB/Mount2016-04-17T19:48:23Z<p>Rangorn: Fix uint size</p>
<hr />
<div>New in Wod, List of mounts<br />
<br />
==Structure==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180 " | Field <br />
! width="80" | Type <br />
! width="600" | Notes<br />
|- <br />
| 1 || ID || Integer || <br />
|- style="background:#F0F8FF;"<br />
| 2 || [[Spell.dbc|SpellID]] || iRefID || The spell id for casting the mount<br />
|- style="background:#F0F8FF;"<br />
| 3 || [[MountType.dbc|MountTypeID]] || iRefID || <br />
|- style="background:#F0F8FF;"<br />
| 4 || [[CreatureDisplayInfo.dbc|DisplayId]] || iRefID || DisplayId of the mount<br />
|- style="background:#F0F8FF;"<br />
| 5 || Flags || Integer || <br />
|- style="background:#F0F8FF;"<br />
| 6 || Name ||[[Loc]]|| Name of the mount<br />
|- style="background:#F0F8FF;"<br />
| 7 || Description ||[[Loc]]|| Description of the mount<br />
|- style="background:#F0F8FF;"<br />
| 8 || SourceDescription ||[[Loc]]|| Where you can get the mount<br />
|- style="background:#F0F8FF;"<br />
| 9 || Source || Integer || <br />
|- style="background:#F0F8FF;"<br />
| 10 || PlayerConditionId || Integer || <br />
|- style="background:#F0F8FF;"<br />
|}<br />
<br />
==7.0.3.21491==<br />
struct MountRec { <br />
uint32_t m_SpellID;<br />
uint32_t m_DisplayID;<br />
stringrefⁱ m_Name;<br />
stringrefⁱ m_Description;<br />
stringrefⁱ m_SourceDescription;<br />
uint16_t m_MountTypeID;<br />
uint16_t m_Flags;<br />
uint16_t m_PlayerConditionID;<br />
uint8_t m_Source;<br />
uint40_t m_ID;<br />
};<br />
[[Category:DBC]]<br />
[[Category:DBC_WoD]]<br />
[[Category:DBC_Legion]][[Category:7.0.3.21491]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/Mount&diff=21233DB/Mount2016-04-17T19:42:37Z<p>Rangorn: Created page with "New in Wod, List of mounts ==Structure== {| style="background:#FCFCFC; color:black" |- ! width="80" | Column ! width="180 " | Field ! width="80" | Type ! width="600" | No..."</p>
<hr />
<div>New in Wod, List of mounts<br />
<br />
==Structure==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180 " | Field <br />
! width="80" | Type <br />
! width="600" | Notes<br />
|- <br />
| 1 || ID || Integer || <br />
|- style="background:#F0F8FF;"<br />
| 2 || [[Spell.dbc|SpellID]] || iRefID || The spell id for casting the mount<br />
|- style="background:#F0F8FF;"<br />
| 3 || [[MountType.dbc|MountTypeID]] || iRefID || <br />
|- style="background:#F0F8FF;"<br />
| 4 || [[CreatureDisplayInfo.dbc|DisplayId]] || iRefID || DisplayId of the mount<br />
|- style="background:#F0F8FF;"<br />
| 5 || Flags || Integer || <br />
|- style="background:#F0F8FF;"<br />
| 6 || Name ||[[Loc]]|| Name of the mount<br />
|- style="background:#F0F8FF;"<br />
| 7 || Description ||[[Loc]]|| Description of the mount<br />
|- style="background:#F0F8FF;"<br />
| 8 || SourceDescription ||[[Loc]]|| Where you can get the mount<br />
|- style="background:#F0F8FF;"<br />
| 9 || Source || Integer || <br />
|- style="background:#F0F8FF;"<br />
| 10 || PlayerConditionId || Integer || <br />
|- style="background:#F0F8FF;"<br />
|}<br />
<br />
==7.0.3.21491==<br />
struct MountRec { <br />
uint32_t m_SpellID;<br />
uint32_t m_DisplayID;<br />
stringrefⁱ m_Name;<br />
stringrefⁱ m_Description;<br />
stringrefⁱ m_SourceDescription;<br />
uint32_t m_MountTypeID;<br />
uint32_t m_Flags;<br />
uint32_t m_PlayerConditionID;<br />
uint32_t m_Source;<br />
uint32_t m_ID;<br />
};<br />
[[Category:DBC]]<br />
[[Category:DBC_WoD]]<br />
[[Category:DBC_Legion]][[Category:7.0.3.21491]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/CurrencyTypes&diff=21225DB/CurrencyTypes2016-04-13T15:42:56Z<p>Rangorn: finally..</p>
<hr />
<div>This file lists currencies that are now listed out of the inventory but on a tab in the character frame.<br />
<br />
==Structure==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180 " | Field <br />
! width="80" | Type <br />
! width="600" | Notes<br />
|- <br />
|1 || ID || Integer || They include some kind of groups too. See below. Real id in column 4.<br />
|- style="background:#F0F8FF;"<br />
|2 || [[Item.dbc|Item]] || iRefID || The itemd displayed.<br />
|- style="background:#F0F8FF;"<br />
|3 || [[CurrencyCategory.dbc|Category]] || iRefID || Yes, there are non-existant categories in here.<br />
|- <br />
|4 || bitIndex || Integer || These are getting shifted and used as bitmasks for "currencyTokensBackpack" resulting in a maximum of 32 * 2 types.<br />
|}<br />
==6.0.1.18179==<br />
struct CurrencyTypesRec {<br />
uint32_t m_ID;<br />
uint32_t m_categoryID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_spellWeight;<br />
uint32_t m_spellCategory;<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
uint32_t m_quality;<br />
{{Template:Type|stringref}} m_description_lang;<br />
};<br />
<br />
==7.0.3.21384== <br />
struct CurrencyTypesRec {<br />
uint32_t m_ID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
{{Template:Type|stringref}} m_description_lang;<br />
uint8_t m_categoryID;<br />
uint8_t m_spellWeight;<br />
uint8_t m_spellCategory;<br />
uint8_t m_quality;<br />
};<br />
<br />
<br />
==ID==<br />
The id is always seperatable into three digits. The least significant is some kind of real id for that category. The other two define the category. They always increase in steps of 20.<br />
<br />
===Example===<br />
12* are Marks of Honor.<br />
*121 (14) - {{Template:Data/Item|id=20560|name=Alterac Valley Mark of Honor}}<br />
*122 (15) - {{Template:Data/Item|id=20559|name=Arathi Basin Mark of Honor}}<br />
*123 (16) - {{Template:Data/Item|id=29024|name=Eye of the Storm Mark of Honor}}<br />
*124 (17) - {{Template:Data/Item|id=42425|name=Strand of the Ancients Mark of Honor}}<br />
*125 (18) - {{Template:Data/Item|id=20558|name=Warsong Gulch Mark of Honor}}<br />
*126 (19) - {{Template:Data/Item|id=43589|name=Wintergrasp Mark of Honor}}<br />
10* are WotLK marks and basic PvP currencies.<br />
*101 (10) - {{Template:Data/Item|id=40752|name=Emblem of Heroism}}<br />
*102 (11) - {{Template:Data/Item|id=40753|name=Emblem of Valor}}<br />
*103 (12) - {{Template:Data/Item|id=43307|name=Arena Points}}<br />
*104 (13) - {{Template:Data/Item|id=43308|name=Honor Points}}<br />
<br />
<br />
[[Category:DBC]]<br />
[[Category:3.0.2.8905]][[Category:DBC_WotLK]]<br />
[[Category:DBC_WoD]][[Category:6.0.1.18179]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/CurrencyTypes&diff=21224DB/CurrencyTypes2016-04-13T15:42:30Z<p>Rangorn: Undo revision 21223 by Rangorn (talk)</p>
<hr />
<div>This file lists currencies that are now listed out of the inventory but on a tab in the character frame.<br />
<br />
==Structure==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180 " | Field <br />
! width="80" | Type <br />
! width="600" | Notes<br />
|- <br />
|1 || ID || Integer || They include some kind of groups too. See below. Real id in column 4.<br />
|- style="background:#F0F8FF;"<br />
|2 || [[Item.dbc|Item]] || iRefID || The itemd displayed.<br />
|- style="background:#F0F8FF;"<br />
|3 || [[CurrencyCategory.dbc|Category]] || iRefID || Yes, there are non-existant categories in here.<br />
|- <br />
|4 || bitIndex || Integer || These are getting shifted and used as bitmasks for "currencyTokensBackpack" resulting in a maximum of 32 * 2 types.<br />
|}<br />
==6.0.1.18179==<br />
struct CurrencyTypesRec {<br />
uint32_t m_ID;<br />
uint32_t m_categoryID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_spellWeight;<br />
uint32_t m_spellCategory;<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
uint32_t m_quality;<br />
{{Template:Type|stringref}} m_description_lang;<br />
};<br />
<br />
==7.0.3.21384== <br />
struct ChrClassesRec {<br />
uint32_t m_ID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
{{Template:Type|stringref}} m_description_lang;<br />
uint8_t m_categoryID;<br />
uint8_t m_spellWeight;<br />
uint8_t m_spellCategory;<br />
uint8_t m_quality;<br />
};<br />
<br />
<br />
==ID==<br />
The id is always seperatable into three digits. The least significant is some kind of real id for that category. The other two define the category. They always increase in steps of 20.<br />
<br />
===Example===<br />
12* are Marks of Honor.<br />
*121 (14) - {{Template:Data/Item|id=20560|name=Alterac Valley Mark of Honor}}<br />
*122 (15) - {{Template:Data/Item|id=20559|name=Arathi Basin Mark of Honor}}<br />
*123 (16) - {{Template:Data/Item|id=29024|name=Eye of the Storm Mark of Honor}}<br />
*124 (17) - {{Template:Data/Item|id=42425|name=Strand of the Ancients Mark of Honor}}<br />
*125 (18) - {{Template:Data/Item|id=20558|name=Warsong Gulch Mark of Honor}}<br />
*126 (19) - {{Template:Data/Item|id=43589|name=Wintergrasp Mark of Honor}}<br />
10* are WotLK marks and basic PvP currencies.<br />
*101 (10) - {{Template:Data/Item|id=40752|name=Emblem of Heroism}}<br />
*102 (11) - {{Template:Data/Item|id=40753|name=Emblem of Valor}}<br />
*103 (12) - {{Template:Data/Item|id=43307|name=Arena Points}}<br />
*104 (13) - {{Template:Data/Item|id=43308|name=Honor Points}}<br />
<br />
<br />
[[Category:DBC]]<br />
[[Category:3.0.2.8905]][[Category:DBC_WotLK]]<br />
[[Category:DBC_WoD]][[Category:6.0.1.18179]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/CurrencyTypes&diff=21223DB/CurrencyTypes2016-04-13T15:41:49Z<p>Rangorn: Undo revision 21222 by Rangorn (talk)</p>
<hr />
<div>This file lists currencies that are now listed out of the inventory but on a tab in the character frame.<br />
<br />
==Structure==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180 " | Field <br />
! width="80" | Type <br />
! width="600" | Notes<br />
|- <br />
|1 || ID || Integer || They include some kind of groups too. See below. Real id in column 4.<br />
|- style="background:#F0F8FF;"<br />
|2 || [[Item.dbc|Item]] || iRefID || The itemd displayed.<br />
|- style="background:#F0F8FF;"<br />
|3 || [[CurrencyCategory.dbc|Category]] || iRefID || Yes, there are non-existant categories in here.<br />
|- <br />
|4 || bitIndex || Integer || These are getting shifted and used as bitmasks for "currencyTokensBackpack" resulting in a maximum of 32 * 2 types.<br />
|}<br />
==6.0.1.18179==<br />
struct CurrencyTypesRec {<br />
uint32_t m_ID;<br />
uint32_t m_categoryID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_spellWeight;<br />
uint32_t m_spellCategory;<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
uint32_t m_quality;<br />
{{Template:Type|stringref}} m_description_lang;<br />
};<br />
<br />
==7.0.3.21384== <br />
struct ChrClassesRec {<br />
uint32_t m_ID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
{{Template:Type|stringref}} m_description_lang;<br />
uint8_t m_categoryID;<br />
uint8_t m_spellWeight;<br />
uint8_t m_spellCategory;<br />
uint8_t m_quality;<br />
};<br />
<br />
==ID==<br />
The id is always seperatable into three digits. The least significant is some kind of real id for that category. The other two define the category. They always increase in steps of 20.<br />
<br />
===Example===<br />
12* are Marks of Honor.<br />
*121 (14) - {{Template:Data/Item|id=20560|name=Alterac Valley Mark of Honor}}<br />
*122 (15) - {{Template:Data/Item|id=20559|name=Arathi Basin Mark of Honor}}<br />
*123 (16) - {{Template:Data/Item|id=29024|name=Eye of the Storm Mark of Honor}}<br />
*124 (17) - {{Template:Data/Item|id=42425|name=Strand of the Ancients Mark of Honor}}<br />
*125 (18) - {{Template:Data/Item|id=20558|name=Warsong Gulch Mark of Honor}}<br />
*126 (19) - {{Template:Data/Item|id=43589|name=Wintergrasp Mark of Honor}}<br />
10* are WotLK marks and basic PvP currencies.<br />
*101 (10) - {{Template:Data/Item|id=40752|name=Emblem of Heroism}}<br />
*102 (11) - {{Template:Data/Item|id=40753|name=Emblem of Valor}}<br />
*103 (12) - {{Template:Data/Item|id=43307|name=Arena Points}}<br />
*104 (13) - {{Template:Data/Item|id=43308|name=Honor Points}}<br />
<br />
<br />
[[Category:DBC]]<br />
[[Category:3.0.2.8905]][[Category:DBC_WotLK]]<br />
[[Category:DBC_WoD]][[Category:6.0.1.18179]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/CurrencyTypes&diff=21222DB/CurrencyTypes2016-04-13T15:40:50Z<p>Rangorn: </p>
<hr />
<div>This file lists currencies that are now listed out of the inventory but on a tab in the character frame.<br />
<br />
==Structure==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180 " | Field <br />
! width="80" | Type <br />
! width="600" | Notes<br />
|- <br />
|1 || ID || Integer || They include some kind of groups too. See below. Real id in column 4.<br />
|- style="background:#F0F8FF;"<br />
|2 || [[Item.dbc|Item]] || iRefID || The itemd displayed.<br />
|- style="background:#F0F8FF;"<br />
|3 || [[CurrencyCategory.dbc|Category]] || iRefID || Yes, there are non-existant categories in here.<br />
|- <br />
|4 || bitIndex || Integer || These are getting shifted and used as bitmasks for "currencyTokensBackpack" resulting in a maximum of 32 * 2 types.<br />
|}<br />
==6.0.1.18179==<br />
struct CurrencyTypesRec {<br />
uint32_t m_ID;<br />
uint32_t m_categoryID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_spellWeight;<br />
uint32_t m_spellCategory;<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
uint32_t m_quality;<br />
{{Template:Type|stringref}} m_description_lang;<br />
};<br />
<br />
==7.0.3.21384== <br />
struct ChrClassesRec {<br />
uint32_t m_ID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
{{Template:Type|stringref}} m_description_lang;<br />
uint8_t m_categoryID;<br />
uint8_t m_spellWeight;<br />
uint8_t m_spellCategory;<br />
uint8_t m_quality;<br />
};<br />
<br />
<br />
==ID==<br />
The id is always seperatable into three digits. The least significant is some kind of real id for that category. The other two define the category. They always increase in steps of 20.<br />
<br />
===Example===<br />
12* are Marks of Honor.<br />
*121 (14) - {{Template:Data/Item|id=20560|name=Alterac Valley Mark of Honor}}<br />
*122 (15) - {{Template:Data/Item|id=20559|name=Arathi Basin Mark of Honor}}<br />
*123 (16) - {{Template:Data/Item|id=29024|name=Eye of the Storm Mark of Honor}}<br />
*124 (17) - {{Template:Data/Item|id=42425|name=Strand of the Ancients Mark of Honor}}<br />
*125 (18) - {{Template:Data/Item|id=20558|name=Warsong Gulch Mark of Honor}}<br />
*126 (19) - {{Template:Data/Item|id=43589|name=Wintergrasp Mark of Honor}}<br />
10* are WotLK marks and basic PvP currencies.<br />
*101 (10) - {{Template:Data/Item|id=40752|name=Emblem of Heroism}}<br />
*102 (11) - {{Template:Data/Item|id=40753|name=Emblem of Valor}}<br />
*103 (12) - {{Template:Data/Item|id=43307|name=Arena Points}}<br />
*104 (13) - {{Template:Data/Item|id=43308|name=Honor Points}}<br />
<br />
<br />
[[Category:DBC]]<br />
[[Category:3.0.2.8905]][[Category:DBC_WotLK]]<br />
[[Category:DBC_WoD]][[Category:6.0.1.18179]]</div>Rangornhttps://wowdev.wiki/index.php?title=DB/CurrencyTypes&diff=21221DB/CurrencyTypes2016-04-13T15:37:59Z<p>Rangorn: Add Legion wdb4 structure</p>
<hr />
<div>This file lists currencies that are now listed out of the inventory but on a tab in the character frame.<br />
<br />
==Structure==<br />
{| style="background:#FCFCFC; color:black"<br />
|- <br />
! width="80" | Column <br />
! width="180 " | Field <br />
! width="80" | Type <br />
! width="600" | Notes<br />
|- <br />
|1 || ID || Integer || They include some kind of groups too. See below. Real id in column 4.<br />
|- style="background:#F0F8FF;"<br />
|2 || [[Item.dbc|Item]] || iRefID || The itemd displayed.<br />
|- style="background:#F0F8FF;"<br />
|3 || [[CurrencyCategory.dbc|Category]] || iRefID || Yes, there are non-existant categories in here.<br />
|- <br />
|4 || bitIndex || Integer || These are getting shifted and used as bitmasks for "currencyTokensBackpack" resulting in a maximum of 32 * 2 types.<br />
|}<br />
==6.0.1.18179==<br />
struct CurrencyTypesRec {<br />
uint32_t m_ID;<br />
uint32_t m_categoryID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_spellWeight;<br />
uint32_t m_spellCategory;<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
uint32_t m_quality;<br />
{{Template:Type|stringref}} m_description_lang;<br />
};<br />
<br />
==7.0.3.21384== <br />
struct ChrClassesRec {<br />
uint32_t m_ID;<br />
{{Template:Type|stringref}} m_name_lang;<br />
{{Template:Type|stringref}} m_inventoryIcon[2];<br />
uint32_t m_maxQty;<br />
uint32_t m_maxEarnablePerWeek;<br />
uint32_t m_flags;<br />
{{Template:Type|stringref}} m_description_lang;<br />
uint8_t m_categoryID;<br />
uint8_t m_spellWeight;<br />
uint8_t m_spellCategory;<br />
uint8_t m_quality;<br />
};<br />
<br />
==ID==<br />
The id is always seperatable into three digits. The least significant is some kind of real id for that category. The other two define the category. They always increase in steps of 20.<br />
<br />
===Example===<br />
12* are Marks of Honor.<br />
*121 (14) - {{Template:Data/Item|id=20560|name=Alterac Valley Mark of Honor}}<br />
*122 (15) - {{Template:Data/Item|id=20559|name=Arathi Basin Mark of Honor}}<br />
*123 (16) - {{Template:Data/Item|id=29024|name=Eye of the Storm Mark of Honor}}<br />
*124 (17) - {{Template:Data/Item|id=42425|name=Strand of the Ancients Mark of Honor}}<br />
*125 (18) - {{Template:Data/Item|id=20558|name=Warsong Gulch Mark of Honor}}<br />
*126 (19) - {{Template:Data/Item|id=43589|name=Wintergrasp Mark of Honor}}<br />
10* are WotLK marks and basic PvP currencies.<br />
*101 (10) - {{Template:Data/Item|id=40752|name=Emblem of Heroism}}<br />
*102 (11) - {{Template:Data/Item|id=40753|name=Emblem of Valor}}<br />
*103 (12) - {{Template:Data/Item|id=43307|name=Arena Points}}<br />
*104 (13) - {{Template:Data/Item|id=43308|name=Honor Points}}<br />
<br />
<br />
[[Category:DBC]]<br />
[[Category:3.0.2.8905]][[Category:DBC_WotLK]]<br />
[[Category:DBC_WoD]][[Category:6.0.1.18179]]</div>Rangornhttps://wowdev.wiki/index.php?title=Hotfix.tbl&diff=21166Hotfix.tbl2016-03-04T23:10:01Z<p>Rangorn: Created page with " = Hotfix.tbl = The hotfix.tbl file was introduced in wod, it's used for sending hotfixes to the client, when you log in game. It's located in the Cache/ADB folder. == Struc..."</p>
<hr />
<div><br />
= Hotfix.tbl =<br />
<br />
The hotfix.tbl file was introduced in wod, it's used for sending hotfixes to the client, when you log in game.<br />
It's located in the Cache/ADB folder.<br />
<br />
== Structure ==<br />
<br />
=== Header ===<br />
<br />
'''Offset Type Description'''<br />
0x00 char[4] Identifier, XFTH for this file<br />
0x04 uint32 Unknown, always 1<br />
0x08 uint32 Timestamp in unix format<br />
0x0C uint32 Build version<br />
<br />
=== Block ===<br />
<br />
'''Column Field Type Notes''' <br />
1 Table_Hash Integer 4-byte integer identifying the hash (see https://github.com/TrinityCore/WowPacketParser/blob/master/WowPacketParser/Enums/DB2Hash.cs)<br />
2 Record_ID Integer 4-byte integer identifying the ID from the hotfixes/DB2<br />
3 Timestamp Integer 4-byte integer identifying the timestamp in unix</div>Rangorn