コード例 #1
0
        public Chunk_MAIN(WDTFile file) : base(file, "MAIN", Magic)
        {
            map = new bool[64, 64];
            int tileCount = 0;

            for (int y = 0; y < 64; y++)
            {
                for (int x = 0; x < 64; x++)
                {
                    UInt32 flags = file.readUInt32();
                    file.skip(4); // Runtime area

                    if (flags == 1)
                    {
                        map[x, y] = true;
                        tileCount++;
                    }
                    else
                    {
                        map[x, y] = false;
                    }
                }
            }

            LogWrite(string.Format("Loaded ADT map structure with {0} tiles (out of possible 4096).", tileCount));
        }
コード例 #2
0
ファイル: ADTFile.cs プロジェクト: remixod/sharpwow
        public ADTFile(WDTFile wdt, string fileName, uint indexX, uint indexY, bool initial = false)
        {
            bool unkFlag = (wdt.Flags & 0x84) != 0;
            if (unkFlag)
                throw new Exception();

            IndexX = indexX;
            IndexY = indexY;
            FileName = fileName;
            mLoadEvent = new System.Threading.ManualResetEvent(false);
            if (initial == false)
            {
                Game.GameManager.ThreadManager.LaunchThread(
                    () =>
                    {
                        try
                        {
                            AsyncLoadProc();
                        }
                        catch (Exception)
                        {
                        }
                    }
                );
            }
            else
                AsyncLoadProc();

            ADTManager.AddADT(this);
        }
コード例 #3
0
        public Chunk_MODF(WDTFile file) : base(file, "MODF", Magic)
        {
            Stuffer.Stuff(this, file, GetLogPrefix());

            // There are 2 bytes (UInt16) of padding here, but we should
            // be safe to ignore as chunks are parsed by size.
        }
コード例 #4
0
ファイル: HeightMapExtractor.cs プロジェクト: pallmall/WCell
		/// <summary>
		/// Writes all height maps to the default MapDir
		/// </summary>
		public static void ExportHeightMaps(WDTFile wdt)
		{
			if ((wdt.Header.Header1 & WDTFlags.GlobalWMO) != 0)
			{
				// has no terrain
				return;
			}

			var terrainInfo = ExtractRegionHeightMaps(wdt);
			var count = 0;
			var path = Path.Combine(ToolConfig.MapDir, wdt.Entry.Id.ToString());
			if (!Directory.Exists(path))
			{
				Directory.CreateDirectory(path);
			}

			for (var tileY = 0; tileY < TerrainConstants.TilesPerMapSide; tileY++)
			{
				for (var tileX = 0; tileX < TerrainConstants.TilesPerMapSide; tileX++)
				{
					var adtTerrainInfo = terrainInfo[tileX, tileY];
					if (adtTerrainInfo == null) continue;
					if (adtTerrainInfo.HeightMaps == null) continue;

					using (var file = File.Create(Path.Combine(path, TerrainConstants.GetHeightMapFile(tileX, tileY))))
					{
						WriteADTTerrainInfo(file, adtTerrainInfo);
					}
					count++;
				}
			}
			log.Info("Extracted {0} tiles for {1}.", count, (MapId)wdt.Entry.Id);
		}
コード例 #5
0
ファイル: HeightMapExtractor.cs プロジェクト: pallmall/WCell
		public static ADTTerrainInfo[,] ExtractRegionHeightMaps(WDTFile wdt)
		{
			var terrainInfo = new ADTTerrainInfo[TerrainConstants.TilesPerMapSide, TerrainConstants.TilesPerMapSide];

			for (var y = 0; y < TerrainConstants.TilesPerMapSide; y++)
			{
				for (var x = 0; x < TerrainConstants.TilesPerMapSide; x++)
				{
					if (!wdt.TileProfile[x, y]) continue;
					var info = new ADTTerrainInfo();

					var adtName = TerrainConstants.GetADTFile(wdt.Name, x, y);
					var adt = ADTParser.Process(WDTParser.MpqManager, wdt.Path, adtName);
					if (adt == null) continue;

					info.LiquidProfile = ExtractTileLiquidProfile(adt);
					info.LiquidTypes = ExtractTileLiquidTypes(adt);
					info.HeightMaps = ExtractTileHeightMaps(adt);
					info.CheckFlatness();

					terrainInfo[x, y] = info;
				}
			}

			return terrainInfo;
		}
コード例 #6
0
        private static RegionModels ExtractRegionM2s(WDTFile wdt)
        {
            LoadedM2Ids = new List <uint>(250);
            RegionModels models;

            if (wdt == null)
            {
                return(null);
            }
            if ((wdt.Header.Header1 & WDTFlags.GlobalWMO) != 0)
            {
                // No terrain, load the global WMO
                if (wdt.ObjectDefinitions == null)
                {
                    return(null);
                }

                models = new RegionModels
                {
                    HasTiles      = false,
                    ObjectsByTile = new TileModels[1, 1]
                };

                models.ObjectsByTile[0, 0] = ExtractWDTM2s(wdt.ObjectDefinitions);

                LoadedM2Ids.Clear();
                return(models);
            }


            models = new RegionModels
            {
                HasTiles = true
            };

            for (var tileY = 0; tileY < TerrainConstants.TilesPerMapSide; tileY++)
            {
                for (var tileX = 0; tileX < TerrainConstants.TilesPerMapSide; tileX++)
                {
                    //if (tileX != 36) continue;
                    //if (tileY != 49) continue;

                    if (!wdt.TileProfile[tileX, tileY])
                    {
                        continue;
                    }
                    var adtName = string.Format("{0}_{1:00}_{2:00}", wdt.Name, tileX, tileY);
                    var adt     = ADTParser.Process(manager, wdt.Path, adtName);
                    if (adt == null)
                    {
                        continue;
                    }

                    models.ObjectsByTile[tileX, tileY] = ExtractTileM2s(tileX, tileY, adt);
                }
            }

            LoadedM2Ids.Clear();
            return(models);
        }
コード例 #7
0
ファイル: Chunk_MAIN.cs プロジェクト: Kruithne/W3DT
        public Chunk_MAIN(WDTFile file)
            : base(file, "MAIN", Magic)
        {
            map = new bool[64,64];
            int tileCount = 0;

            for (int y = 0; y < 64; y++)
            {
                for (int x = 0; x < 64; x++)
                {
                    UInt32 flags = file.readUInt32();
                    file.skip(4); // Runtime area

                    if (flags == 1)
                    {
                        map[x, y] = true;
                        tileCount++;
                    }
                    else
                    {
                        map[x, y] = false;
                    }
                }
            }

            LogWrite(string.Format("Loaded ADT map structure with {0} tiles (out of possible 4096).", tileCount));
        }
コード例 #8
0
        public ADTFile(WDTFile wdt, string fileName, uint indexX, uint indexY, bool initial = false)
        {
            bool unkFlag = (wdt.Flags & 0x84) != 0;

            if (unkFlag)
            {
                throw new Exception();
            }

            IndexX     = indexX;
            IndexY     = indexY;
            FileName   = fileName;
            mLoadEvent = new System.Threading.ManualResetEvent(false);
            if (initial == false)
            {
                Game.GameManager.ThreadManager.LaunchThread(
                    () =>
                {
                    try
                    {
                        AsyncLoadProc();
                    }
                    catch (Exception)
                    {
                    }
                }
                    );
            }
            else
            {
                AsyncLoadProc();
            }

            ADTManager.AddADT(this);
        }
コード例 #9
0
        public string SetZone(string zone)
        {
            int continentID;

            if (!zoneToMapId.TryGetValue(zone, out continentID))
            {
                int colon = zone.IndexOf(":");
                if (colon == -1)
                {
                    return(null);
                }
                zone = zone.Substring(colon);
                if (!zoneToMapId.TryGetValue(zone, out continentID))
                {
                    return(null);
                }
            }

            archive.ExtractFile("DBFilesClient\\Map.dbc", "Map.dbc");
            DBC     maps = new DBC();
            DBCFile mf   = new DBCFile("Map.dbc", maps);


            for (int i = 0; i < maps.recordCount; i++)
            {
                int mapID = maps.GetInt(i, 0);
                // Console.WriteLine("   ID:" + maps.GetInt(i, 0));
                // Console.WriteLine(" File: " + maps.GetString(i, 1));
                // Console.WriteLine(" Name: " + maps.GetString(i, 4)); // the file!!!

                if (mapID == continentID) // file == continentFile)
                {
                    //  Console.WriteLine(String.Format("{0,4} {1}", mapID, maps.GetString(i, 1)));
                    string file = maps.GetString(i, 1);
                    continentFile = file;



                    wdt = new WDT();

                    wdtf = new WDTFile(archive, continentFile, wdt, wmomanager, modelmanager);
                    if (!wdtf.loaded)
                    {
                        wdt = null; // bad
                    }
                    else
                    {
                        // Console.WriteLine("  global Objects " + wdt.gwmois.Count + " Models " + wdt.gwmois.Count);
                        //global_triangles.color = new float[3] { 0.8f, 0.8f, 1.0f };
                    }
                    return(continentFile);
                }
            }
            if (wdt == null)
            {
                return("Failed to open file files for continent ID" + continentID);
            }
            return(null);
        }
コード例 #10
0
ファイル: Chunk_MODF.cs プロジェクト: Kruithne/W3DT
        public Chunk_MODF(WDTFile file)
            : base(file, "MODF", Magic)
        {
            Stuffer.Stuff(this, file, GetLogPrefix());

            // There are 2 bytes (UInt16) of padding here, but we should
            // be safe to ignore as chunks are parsed by size.
        }
コード例 #11
0
ファイル: Chunk_MPHD.cs プロジェクト: justMaku/W3DT
        public Chunk_MPHD(WDTFile file) : base(file, "MPHD", Magic)
        {
            flags = file.readUInt32();
            unk1  = file.readUInt32();
            // 4 * 6 bytes of unused data here.
            // No need to skip, seek check higher up will take care of it.

            LogValue("flags", flags);
            LogValue("unk1", unk1);
        }
コード例 #12
0
ファイル: MapTileExtractor.cs プロジェクト: Jeroz/WCell
		/// <summary>
		/// Writes all height maps to the default MapDir
		/// </summary>
		public static void ExportMapTiles(WDTFile wdt)
		{
            // Map data should only be stored per map
		    ClearObjectData();
            var path = Path.Combine(ToolConfig.MapDir, wdt.Entry.Id.ToString());

			if (wdt.IsWMOOnly)
			{
				// This Map has no Tiles, but the MODF still needs to be written
                // These maps will be considered to have one tile at {0, 0} with only one MODF written therein
			    var adt = ExtractWMOOnly(wdt);
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                using (var file = File.Create(Path.Combine(path, TerrainConstants.GetMapFilename(0, 0))))
                {
                    WriteTileInfo(file, adt);
                }

                log.Info("Extracted {0}.", (MapId)wdt.Entry.Id);
				return;
			}

            // Read in the ADT data - this includes height and liquid maps, WMO information and M2 information
			TerrainInfo = ExtractMapTiles(wdt);

            // Write the processed data to files
            var count = 0;
			if (!Directory.Exists(path))
			{
				Directory.CreateDirectory(path);
			}

			for (var tileX = 0; tileX < TerrainConstants.TilesPerMapSide; tileX++)
			{
				for (var tileY = 0; tileY < TerrainConstants.TilesPerMapSide; tileY++)
				{
					var adt = TerrainInfo[tileY, tileX];
					if (adt == null) continue;
					
					using (var file = File.Create(Path.Combine(path, TerrainConstants.GetMapFilename(tileX, tileY))))
					{
					    WriteTileInfo(file, adt);
                        file.Close();
					}
					count++;
				}
			}

			log.Info("Extracted {0} tiles for {1}.", count, (MapId)wdt.Entry.Id);
		}
コード例 #13
0
 public override void Close()
 {
     archive.Close();
     wdt          = null;
     wdtf         = null;
     modelmanager = null;
     wmomanager   = null;
     zoneToMapId  = null;
     mapIdToFile  = null;
     areaIdToName = null;
     archive      = null;
     base.Close();
 }
コード例 #14
0
ファイル: ZoneTileSetReader.cs プロジェクト: pallmall/WCell
		public static void GetTileSets(WDTFile wdt)
		{
			if ((wdt.Header.Header1 & WDTFlags.GlobalWMO) == 0)
			{
				var tileSet = new ZoneTileSet();
				Tiles[wdt.Entry.Id] = tileSet;

				ZoneGrid grid;

				// Read in the Tiles
				for (var y = 0; y < 64; y++)
				{
					for (var x = 0; x < 64; x++)
					{
						if (!wdt.TileProfile[x, y]) continue;
						++count;
						var adtName = TerrainConstants.GetADTFile(wdt.Name, x, y);
						var adt = ADTParser.Process(WDTParser.MpqManager, wdt.Path, adtName);
						if (adt == null) continue;

						tileSet.ZoneGrids[x, y] = grid = new ZoneGrid(new uint[TerrainConstants.ChunksPerTileSide, TerrainConstants.ChunksPerTileSide]);
						// Read in the TileChunks 
						for (var j = 0; j < 16; j++)
						{
							for (var i = 0; i < 16; i++)
							{
								var areaId = adt.MapChunks[i, j].AreaId;
								if (Enum.IsDefined(typeof(ZoneId), areaId))
								{
									grid.ZoneIds[i, j] = areaId;
								}
								else
								{
									grid.ZoneIds[i, j] = 0;
								}
							}
						}
						//return tiles;
					}
				}
			}
			else
			{
				log.Info("Could not read Zones from WMO: " + (MapId)wdt.Entry.Id);
			}
		}
コード例 #15
0
        public void SetContinent(string continent)
        {
            continentFile = continent;

            wdt = new WDT();

            wdtf = new WDTFile(archive, continentFile, wdt, wmomanager, modelmanager, this.logger, dataConfig);
            if (!wdtf.loaded)
            {
                wdt = null; // bad
                throw new Exception("Failed to set continent to: " + continent);
            }
            else
            {
                // logger.WriteLine("  global Objects " + wdt.gwmois.Count + " Models " + wdt.gwmois.Count);
                //global_triangles.color = new float[3] { 0.8f, 0.8f, 1.0f };
            }
        }
コード例 #16
0
        public void SetContinent(string continent)
        {
            continentFile = continent;


            wdt = new WDT();

            wdtf = new WDTFile(archive, continentFile, wdt, wmomanager, modelmanager);
            if (!wdtf.loaded)
            {
                wdt = null; // bad
            }
            else
            {
                // PathGraph.Log("  global Objects " + wdt.gwmois.Count + " Models " + wdt.gwmois.Count);
                //global_triangles.color = new float[3] { 0.8f, 0.8f, 1.0f };
            }
        }
コード例 #17
0
ファイル: WDTParser.cs プロジェクト: NVN/WCell
        static void Process(DBCMapEntry entry)
        {
            if (Parsed == null)
            {
                throw new Exception("WDTParser.Parsed must be set before calling WDTParser.Process");
            }

            var dir = entry.MapDirName;
            var wdtDir = Path.Combine(baseDir, dir);
            var wdtName = dir;

            var wdtFilePath = Path.Combine(wdtDir, wdtName + Extension);
            if (!MpqManager.FileExists(wdtFilePath)) return;

            var wdt = new WDTFile
            {
                Manager = MpqManager,
                Entry = entry,
                Name = wdtName,
                Path = wdtDir
            };

            using (var fileReader = new BinaryReader(MpqManager.OpenFile(wdtFilePath)))
            {
                ReadMVER(fileReader, wdt);
                ReadMPHD(fileReader, wdt);
                ReadMAIN(fileReader, wdt);

                if (wdt.Header.IsWMOMap)
                {
                    // No terrain, the map is a "global" wmo
                    // MWMO and MODF chunks follow
                    wdt.IsWMOOnly = true;
                    ReadMWMO(fileReader, wdt);
                    ReadMODF(fileReader, wdt);
                }
            }

            Parsed(wdt);
        }
コード例 #18
0
ファイル: MPQTriangleSupplier.cs プロジェクト: iwaitu/babbot
        public void SetContinent(string continent)
        {
            continentFile = continent;

            wdt = new WDT();

            wdtf = new WDTFile(archive, continentFile, wdt, wmomanager, modelmanager);
            if (!wdtf.loaded)
            {
                wdt = null; // bad
                throw new Exception("Failed to set continent to: " + continent);
            }
            else
            {

                // Console.WriteLine("  global Objects " + wdt.gwmois.Count + " Models " + wdt.gwmois.Count);
                //global_triangles.color = new float[3] { 0.8f, 0.8f, 1.0f };
            }
        }
コード例 #19
0
ファイル: MPQTriangleSupplier.cs プロジェクト: iwaitu/babbot
 public override void Close()
 {
     archive.Close();
     wdt = null;
     wdtf = null;
     modelmanager = null;
     wmomanager = null;
     zoneToMapId = null;
     mapIdToFile = null;
     areaIdToName = null;
     archive = null;
     base.Close();
 }
コード例 #20
0
        public string SetZone(string zone)
        {
            int continentID;

            if (!zoneToMapId.TryGetValue(zone, out continentID))
            {
                int colon = zone.IndexOf(":");
                if(colon == -1)
                    return null;
                zone = zone.Substring(colon);
                if (!zoneToMapId.TryGetValue(zone, out continentID))
                {
                    return null;
                }
            }

            archive.ExtractFile("DBFilesClient\\Map.dbc", "Map.dbc");
            DBC maps = new DBC();
            DBCFile mf = new DBCFile("Map.dbc", maps);

            for (int i = 0; i < maps.recordCount; i++)
            {
                int mapID = maps.GetInt(i, 0);
               // Console.WriteLine("   ID:" + maps.GetInt(i, 0));
               // Console.WriteLine(" File: " + maps.GetString(i, 1));
               // Console.WriteLine(" Name: " + maps.GetString(i, 4)); // the file!!!

                if (mapID == continentID) // file == continentFile)
                {
                    //  Console.WriteLine(String.Format("{0,4} {1}", mapID, maps.GetString(i, 1)));
                    string file = maps.GetString(i, 1);
                    continentFile = file;

                    wdt = new WDT();

                    wdtf = new WDTFile(archive, continentFile, wdt, wmomanager, modelmanager);
                    if (!wdtf.loaded)
                        wdt = null; // bad
                    else
                    {

                       // Console.WriteLine("  global Objects " + wdt.gwmois.Count + " Models " + wdt.gwmois.Count);
                        //global_triangles.color = new float[3] { 0.8f, 0.8f, 1.0f };
                    }
                    return continentFile;
                }
            }
            if (wdt == null)
            {
                return "Failed to open file files for continent ID" + continentID;
            }
            return null;
        }
コード例 #21
0
ファイル: WDTParser.cs プロジェクト: NVN/WCell
        static void ReadMAIN(BinaryReader fileReader, WDTFile wdt)
        {
            var type = fileReader.ReadUInt32();
            var size = fileReader.ReadUInt32();

            // Rows are along the x-axis
            for (var x = 0; x < TerrainConstants.MapTileCount; x++)
            {
                // Columns are along the y-axis
                for (var y = 0; y < TerrainConstants.MapTileCount; y++)
                {
                    //if (x == 48 && y == 30)
                    //{
                    //    wdt.TileProfile[y, x] = true;
                    //}
                    //else
                    //{
                    //    wdt.TileProfile[y, x] = false;
                    //}
                    // Stored as [col, row], that's weird.
                    wdt.TileProfile[y, x] = (fileReader.ReadInt64() != 0);
                }
            }
        }
コード例 #22
0
ファイル: Chunk_MWMO.cs プロジェクト: Kruithne/W3DT
 public Chunk_MWMO(WDTFile file)
     : base(file, "MWMO", Magic)
 {
     fileName = file.readString(); // Zero-terminated, this should be safe.
     LogValue("Map WMO", fileName);
 }
コード例 #23
0
ファイル: MapTileExtractor.cs プロジェクト: ray2006/WCell
		public static ADT[,] ExtractMapTiles(WDTFile wdt)
		{
			var mapTiles = new ADT[TerrainConstants.TilesPerMapSide, TerrainConstants.TilesPerMapSide];

			for (var x = 0; x < TerrainConstants.TilesPerMapSide; x++)
			{
				for (var y = 0; y < TerrainConstants.TilesPerMapSide; y++)
				{
					if (!wdt.TileProfile[y, x]) continue;

                    var adt = ADTParser.Process(WDTParser.MpqManager, wdt.Entry, x, y);
                    if (adt == null) continue;

				    adt.IsWMOOnly = false;
                    
                    // Load in the WMORoots and their DoodadDefinitions
                    // Load in the ADTs referenced M2Models
				    PrepareChunkInfo(wdt.Manager, adt);

					mapTiles[y, x] = adt;
				}
			}

			return mapTiles;
		}
コード例 #24
0
ファイル: WDTParser.cs プロジェクト: NVN/WCell
        static void ReadMODF(BinaryReader fileReader, WDTFile wdt)
        {
            var type = fileReader.ReadUInt32();
            var size = fileReader.ReadUInt32();

            var endPos = fileReader.BaseStream.Position + size;
            while (fileReader.BaseStream.Position < endPos)
            {
                var objectDef = new MapObjectDefinition();
                var nameIndex = fileReader.ReadInt32(); // 4 bytes
                objectDef.FilePath = wdt.WmoFiles[nameIndex];
                objectDef.UniqueId = fileReader.ReadUInt32(); // 4 bytes
                objectDef.Position = fileReader.ReadVector3(); // 12 bytes
                objectDef.OrientationA = fileReader.ReadSingle(); // 4 Bytes
                objectDef.OrientationB = fileReader.ReadSingle(); // 4 Bytes
                objectDef.OrientationC = fileReader.ReadSingle(); // 4 Bytes
                objectDef.Extents = fileReader.ReadBoundingBox(); // 12*2 bytes
                objectDef.Flags = fileReader.ReadUInt16(); // 2 bytes
                objectDef.DoodadSetId = fileReader.ReadUInt16(); // 2 bytes
                objectDef.NameSet = fileReader.ReadUInt16(); // 2 bytes
                fileReader.ReadUInt16(); // padding

                wdt.WmoDefinitions.Add(objectDef);
            }
        }
コード例 #25
0
ファイル: WDTParser.cs プロジェクト: NVN/WCell
        static void ReadMWMO(BinaryReader fileReader, WDTFile wdt)
        {
            var type = fileReader.ReadUInt32();
            var size = fileReader.ReadUInt32();

            var endPos = fileReader.BaseStream.Position + size;
            while (fileReader.BaseStream.Position < endPos)
            {
                if (fileReader.PeekByte() == 0)
                {
                    fileReader.BaseStream.Position++;
                }
                else
                {
                    wdt.WmoFiles.Add(fileReader.ReadCString());
                }
            }
        }
コード例 #26
0
ファイル: Chunk_MVER.cs プロジェクト: Kruithne/W3DT
 public Chunk_MVER(WDTFile file)
     : base(file, "MVER", Magic)
 {
     version = file.readUInt32();
     LogValue("version", version);
 }
コード例 #27
0
ファイル: M2Extractor.cs プロジェクト: KroneckerX/WCell
        private static RegionModels ExtractRegionM2s(WDTFile wdt)
        {
            LoadedM2Ids = new List<uint>(250);
            RegionModels models;

            if (wdt == null) return null;
            if ((wdt.Header.Header1 & WDTFlags.GlobalWMO) != 0)
            {
                // No terrain, load the global WMO
                if (wdt.ObjectDefinitions == null) return null;

                models = new RegionModels
                {
                    HasTiles = false,
                    ObjectsByTile = new TileModels[1, 1]
                };

                models.ObjectsByTile[0, 0] = ExtractWDTM2s(wdt.ObjectDefinitions);

                LoadedM2Ids.Clear();
                return models;
            }

            models = new RegionModels
            {
                HasTiles = true
            };

            for (var tileY = 0; tileY < TerrainConstants.TilesPerMapSide; tileY++)
            {
                for (var tileX = 0; tileX < TerrainConstants.TilesPerMapSide; tileX++)
                {
                    //if (tileX != 36) continue;
                    //if (tileY != 49) continue;

                    if (!wdt.TileProfile[tileX, tileY]) continue;
                    var adtName = string.Format("{0}_{1:00}_{2:00}", wdt.Name, tileX, tileY);
                    var adt = ADTParser.Process(manager, wdt.Path, adtName);
                    if (adt == null) continue;

                    models.ObjectsByTile[tileX, tileY] = ExtractTileM2s(tileX, tileY, adt);
                }
            }

            LoadedM2Ids.Clear();
            return models;
        }
コード例 #28
0
 public Chunk_MWMO(WDTFile file) : base(file, "MWMO", Magic)
 {
     fileName = file.readString(); // Zero-terminated, this should be safe.
     LogValue("Map WMO", fileName);
 }
コード例 #29
0
ファイル: WDTParser.cs プロジェクト: NVN/WCell
 static void ReadMVER(BinaryReader fileReader, WDTFile wdt)
 {
     var type = fileReader.ReadUInt32();
     var size = fileReader.ReadUInt32();
     wdt.Version = fileReader.ReadInt32();
 }
コード例 #30
0
ファイル: WDTParser.cs プロジェクト: NVN/WCell
 static void ReadMPHD(BinaryReader fileReader, WDTFile wdt)
 {
     var type = fileReader.ReadUInt32();
     var size = fileReader.ReadUInt32();
     wdt.Header.Header1 = (WDTFlags)fileReader.ReadInt32();
     wdt.Header.Header2 = fileReader.ReadInt32();
     wdt.Header.Header3 = fileReader.ReadInt32();
     wdt.Header.Header4 = fileReader.ReadInt32();
     wdt.Header.Header5 = fileReader.ReadInt32();
     wdt.Header.Header6 = fileReader.ReadInt32(); 
     wdt.Header.Header7 = fileReader.ReadInt32();
     wdt.Header.Header8 = fileReader.ReadInt32();
 }
コード例 #31
0
ファイル: MapTileExtractor.cs プロジェクト: ray2006/WCell
        public static ADT ExtractWMOOnly(WDTFile wdt)
        {
            var adt = new ADT("");
            adt.ObjectDefinitions.Capacity = wdt.WmoDefinitions.Count;
            foreach (var wmoDefinition in wdt.WmoDefinitions)
            {
                adt.ObjectDefinitions.Add(wmoDefinition);
            }

            adt.ObjectFiles.Capacity = wdt.WmoFiles.Count;
            foreach (var wmoFile in wdt.WmoFiles)
            {
                adt.ObjectFiles.Add(wmoFile);
            }

            foreach (var def in adt.ObjectDefinitions)
            {
                LoadWMO(wdt.Manager, def);
            }
            return adt;
        }
コード例 #32
0
ファイル: Chunk_MVER.cs プロジェクト: justMaku/W3DT
 public Chunk_MVER(WDTFile file) : base(file, "MVER", Magic)
 {
     version = file.readUInt32();
     LogValue("version", version);
 }
コード例 #33
0
ファイル: MapBoundaryUtil.cs プロジェクト: Jeroz/WCell
		public static void GetBoundaries(WDTFile wdt)
		{
			Vector3 min;
			Vector3 max;

			if (wdt.Header.Header1.HasFlag(WDTFlags.GlobalWMO))
			{
				const float maxDim = TerrainConstants.TileSize * (32.0f);

				// No terrain, get the boundaries from the MODF
				min = wdt.WmoDefinitions[0].Extents.Min;
				max = wdt.WmoDefinitions[0].Extents.Max;

				var diff = Math.Max(max.X - min.X, max.Y - min.Y);
				if ((min.X + diff) > maxDim)
				{
					var newdiff = maxDim - diff;
					if ((min.X - newdiff) < -maxDim)
					{
						throw new Exception("Can't square map: " + wdt.Name);
					}
					min.X = min.X - newdiff;
				}

				if ((min.Y + diff) > maxDim)
				{
					var newdiff = maxDim - diff;
					if ((min.Y - newdiff) < -maxDim)
					{
						throw new Exception("Can't square map: " + wdt.Name);
					}
					min.Y = min.Y - newdiff;
				}

				max.X = min.X + diff;
				max.Y = min.Y + diff;
			}
			else
			{
				var minX = TerrainConstants.TilesPerMapSide;
				var maxX = 0;
				var minY = TerrainConstants.TilesPerMapSide;
				var maxY = 0;
				for (var y = 0; y < TerrainConstants.TilesPerMapSide; y++)
				{
					for (var x = 0; x < TerrainConstants.TilesPerMapSide; x++)
					{
						if (!wdt.TileProfile[x, y]) continue;
						minX = Math.Min(minX, x);
						maxX = Math.Max(maxX, x);
						minY = Math.Min(minY, y);
						maxY = Math.Max(maxY, y);
					}
				}

				var diff = Math.Max(maxX - minX, maxY - minY);
				if ((minX + diff) > TerrainConstants.TilesPerMapSide)
				{
					var newdiff = TerrainConstants.TilesPerMapSide - diff;
					if ((minX - newdiff) < 0)
					{
						throw new Exception("Can't square this map.");
					}
					minX = minX - newdiff;
				}

				if ((minY + diff) > TerrainConstants.TilesPerMapSide)
				{
					var newdiff = TerrainConstants.TilesPerMapSide - diff;
					if ((minY - newdiff) < 0)
					{
						throw new Exception("Can't square this map.");
					}
					minY = minY - newdiff;
				}

				maxX = minX + diff;
				maxY = minY + diff;

				var maxXLoc = TerrainConstants.TileSize * (32 - minX);
				var maxYLoc = TerrainConstants.TileSize * (32 - minY);
				var minXLoc = TerrainConstants.TileSize * (31 - maxX);
				var minYLoc = TerrainConstants.TileSize * (31 - maxY);

				//var maxXLoc = TerrainConstants.TileSize * (maxX - 32);
				//var maxYLoc = TerrainConstants.TileSize * (maxY - 32);
				//var minXLoc = TerrainConstants.TileSize * (minX - 32);
				//var minYLoc = TerrainConstants.TileSize * (minY - 32);

				min = new Vector3(minXLoc, minYLoc, -2048.0f);
				max = new Vector3(maxXLoc, maxYLoc, 2048.0f);

				// Convert to in-game coordinates
				var temp = min.X;
				min.X = min.Y;
				min.Y = temp;

				temp = max.X;
				max.X = max.Y;
				max.Y = temp;
			}

			// TODO: Map bounds that in the file are a little off
			min.X -= 200.0f;
			min.Y -= 200.0f;
		    max.X += 200.0f;
			max.Y += 200.0f;

            // Some bounding boxes (specifically the ones for the WMO-only tiles) have incorrect Z spans
            min.Z = -2048.0f;
            max.Z = 2048.0f;

			//text.WriteLine(dbcMapEntry.Id + "\t" + wdtName + "\tMin: " + min + "\tMax: " + max);
			Boundaries[wdt.Entry.Id] = new BoundingBox(min, max);
		}
コード例 #34
0
        public override void Work()
        {
            LogWrite("Beginning export of {0}...", mapName);

            TextureBox      texProvider = new TextureBox();
            WaveFrontWriter ob          = new WaveFrontWriter(fileName, texProvider, true, true, false);

            try
            {
                if (!Program.IsCASCReady)
                {
                    throw new MapExportException("CASC file engine is not loaded.");
                }

                string wdtPath = string.Format(@"World\Maps\{0}\{0}.wdt", mapName);
                Program.CASCEngine.SaveFileTo(wdtPath, Constants.TEMP_DIRECTORY);

                WDTFile headerFile = new WDTFile(Path.Combine(Constants.TEMP_DIRECTORY, wdtPath));
                headerFile.parse();

                if (!headerFile.Chunks.Any(c => c.ChunkID == Formats.WDT.Chunk_MPHD.Magic))
                {
                    throw new MapExportException("Invalid map header (WDT)");
                }

                // ToDo: Check if world WMO exists and load it.

                // Ensure we have a MAIN chunk before trying to process terrain.
                Formats.WDT.Chunk_MAIN mainChunk   = (Formats.WDT.Chunk_MAIN)headerFile.Chunks.Where(c => c.ChunkID == Formats.WDT.Chunk_MAIN.Magic).FirstOrDefault();
                Formats.WDT.Chunk_MPHD headerChunk = (Formats.WDT.Chunk_MPHD)headerFile.Chunks.Where(c => c.ChunkID == Formats.WDT.Chunk_MPHD.Magic).FirstOrDefault();

                if (mainChunk != null && headerChunk != null)
                {
                    // Pre-calculate UV mapping for terrain.
                    UV[,,] uvMaps = new UV[8, 8, 5];
                    for (int x = 0; x < 8; x++)
                    {
                        for (int y = 0; y < 8; y++)
                        {
                            float uTL = 1 - (0.125f * x);
                            float vTL = 1 - (0.125f * y);
                            float uBR = uTL - 0.125f;
                            float vBR = vTL - 0.125f;

                            int aX = 7 - x;
                            uvMaps[aX, y, 0] = new UV(uBR, vTL); // TL
                            uvMaps[aX, y, 1] = new UV(uBR, vBR); // TR
                            uvMaps[aX, y, 2] = new UV(uTL, vTL); // BL
                            uvMaps[aX, y, 3] = new UV(uTL, vBR); // BR
                            uvMaps[aX, y, 4] = new UV(uTL - 0.0625f, vTL - 0.0625f);
                        }
                    }

                    int meshIndex = 1;
                    int wmoIndex  = 1;

                    // Create a directory for map data (alpha maps, etc).
                    string dataDirRaw = string.Format("{0}.data", Path.GetFileNameWithoutExtension(fileName));
                    string dataDir    = Path.Combine(Path.GetDirectoryName(fileName), dataDirRaw);

                    if (!Directory.Exists(dataDir))
                    {
                        Directory.CreateDirectory(dataDir);
                    }

                    Dictionary <byte[], uint> texCache = new Dictionary <byte[], uint>(new ByteArrayComparer());

                    for (int y = 0; y < 64; y++)
                    {
                        for (int x = 0; x < 64; x++)
                        {
                            if (mainChunk.map[x, y] && ShouldInclude(x, y))
                            {
                                string pathBase = string.Format(@"World\Maps\{0}\{0}_{1}_{2}", mapName, x, y);
                                string adtPath  = pathBase + ".adt";
                                string texPath  = pathBase + "_tex0.adt";
                                string objPath  = pathBase + "_obj0.adt";

                                Program.CASCEngine.SaveFileTo(adtPath, Constants.TEMP_DIRECTORY);
                                Program.CASCEngine.SaveFileTo(texPath, Constants.TEMP_DIRECTORY);
                                Program.CASCEngine.SaveFileTo(objPath, Constants.TEMP_DIRECTORY);

                                string adtTempPath = Path.Combine(Constants.TEMP_DIRECTORY, adtPath);
                                string texTempPath = Path.Combine(Constants.TEMP_DIRECTORY, texPath);
                                string objTempPath = Path.Combine(Constants.TEMP_DIRECTORY, objPath);

                                try
                                {
                                    ADTFile adt = new ADTFile(adtTempPath, ADTFileType.ROOT);
                                    adt.parse();

                                    ADTFile tex = new ADTFile(texTempPath, ADTFileType.TEX);
                                    tex.parse();

                                    ADTFile obj = new ADTFile(objTempPath, ADTFileType.OBJ);
                                    obj.parse();

                                    // Textures
                                    Chunk_MTEX texChunk = (Chunk_MTEX)tex.getChunk(Chunk_MTEX.Magic);

                                    uint     texIndex    = 0;
                                    Bitmap[] textureData = new Bitmap[texChunk.textures.count()];
                                    foreach (KeyValuePair <int, string> texture in texChunk.textures.raw())
                                    {
                                        string texFile  = texture.Value;
                                        string tempPath = Path.Combine(Constants.TEMP_DIRECTORY, texFile);

                                        if (!File.Exists(tempPath))
                                        {
                                            Program.CASCEngine.SaveFileTo(texFile, Constants.TEMP_DIRECTORY);
                                        }

                                        using (BlpFile blp = new BlpFile(File.OpenRead(tempPath)))
                                            textureData[texIndex] = blp.GetBitmap(0);

                                        texIndex++;
                                    }

                                    Chunk_MCNK[] soupChunks  = adt.getChunksByID(Chunk_MCNK.Magic).Cast <Chunk_MCNK>().ToArray();
                                    Chunk_MCNK[] layerChunks = tex.getChunksByID(Chunk_MCNK.Magic).Cast <Chunk_MCNK>().ToArray();

                                    // Terrain
                                    for (int i = 0; i < 256; i++)
                                    {
                                        Chunk_MCNK soupChunk  = soupChunks[i];
                                        Chunk_MCNK layerChunk = layerChunks[i];

                                        // Terrain chunks
                                        Chunk_MCVT hmChunk = (Chunk_MCVT)soupChunk.getChunk(Chunk_MCVT.Magic);
                                        Chunk_MCNR nChunk  = (Chunk_MCNR)soupChunk.getChunk(Chunk_MCNR.Magic);

                                        // Texture chunks
                                        Chunk_MCLY layers = (Chunk_MCLY)layerChunk.getChunk(Chunk_MCLY.Magic);

                                        // Alpha mapping
                                        Chunk_MCAL alphaMapChunk = (Chunk_MCAL)layerChunk.getChunk(Chunk_MCAL.Magic, false);

                                        string texFileName = string.Format("baked_{0}_{1}_{2}.png", x, y, i);
                                        string texFilePath = Path.Combine(dataDir, texFileName);

                                        EventManager.Trigger_LoadingPrompt(string.Format("Rendering tile {0} at {1},{2}...", i + 1, x, y));
                                        uint texFaceIndex = 0;

                                        if (!File.Exists(texFilePath))
                                        {
                                            Bitmap bmpBase = new Bitmap(layers.layers.Length > 0 ? textureData[layers.layers[0].textureID] : blank);
                                            using (Graphics baseG = Graphics.FromImage(bmpBase))
                                                using (ImageAttributes att = new ImageAttributes())
                                                {
                                                    att.SetWrapMode(WrapMode.TileFlipXY);
                                                    baseG.CompositingQuality = CompositingQuality.HighQuality;
                                                    baseG.InterpolationMode  = InterpolationMode.HighQualityBicubic;
                                                    baseG.CompositingMode    = CompositingMode.SourceOver;

                                                    for (int mI = 1; mI < layers.layers.Length; mI++) // First layer never has an alpha map
                                                    {
                                                        byte[,] alphaMap;
                                                        MCLYLayer layer         = layers.layers[mI];
                                                        bool      headerFlagSet = ((headerChunk.flags & 0x4) == 0x4) || ((headerChunk.flags & 0x80) == 0x80);
                                                        bool      layerFlagSet  = ((layer.flags & 0x200) == 0x200);
                                                        bool      fixAlphaMap   = !((soupChunk.flags & 0x200) == 0x200);

                                                        if (layerFlagSet)
                                                        {
                                                            alphaMap = alphaMapChunk.parse(Chunk_MCAL.CompressType.COMPRESSED, layer.ofsMCAL, fixAlphaMap);
                                                        }
                                                        else
                                                        {
                                                            alphaMap = alphaMapChunk.parse(headerFlagSet ? Chunk_MCAL.CompressType.UNCOMPRESSED_4096 : Chunk_MCAL.CompressType.UNCOMPRESSED_2048, layer.ofsMCAL, fixAlphaMap);
                                                        }

                                                        Bitmap bmpRawTex   = textureData[layer.textureID];
                                                        Bitmap bmpAlphaMap = new Bitmap(64, 64);
                                                        for (int drawX = 0; drawX < 64; drawX++)
                                                        {
                                                            for (int drawY = 0; drawY < 64; drawY++)
                                                            {
                                                                bmpAlphaMap.SetPixel(drawX, drawY, Color.FromArgb(alphaMap[drawX, drawY], 0, 0, 0));
                                                            }
                                                        }

                                                        Bitmap bmpAlphaMapScaled = new Bitmap(bmpRawTex.Width, bmpRawTex.Height);
                                                        using (Graphics g = Graphics.FromImage(bmpAlphaMapScaled))
                                                        {
                                                            g.CompositingQuality = CompositingQuality.HighQuality;
                                                            g.InterpolationMode  = InterpolationMode.HighQualityBicubic;
                                                            g.CompositingMode    = CompositingMode.SourceCopy;
                                                            g.DrawImage(bmpAlphaMap, new Rectangle(0, 0, bmpAlphaMapScaled.Width, bmpAlphaMapScaled.Height), 0, 0, bmpAlphaMap.Width, bmpAlphaMap.Height, GraphicsUnit.Pixel, att);
                                                        }
                                                        bmpAlphaMap.Dispose();

                                                        Bitmap bmpTex = new Bitmap(bmpRawTex.Width, bmpRawTex.Height);
                                                        for (int drawX = 0; drawX < bmpRawTex.Width; drawX++)
                                                        {
                                                            for (int drawY = 0; drawY < bmpRawTex.Height; drawY++)
                                                            {
                                                                // Hacky fix to flip the texture.
                                                                // Remove this if we fix the terrain read-order.
                                                                int sourceX = (bmpRawTex.Width - 1) - drawX;
                                                                //int sourceY = (bmpRawTex.Height - 1) - drawY;

                                                                bmpTex.SetPixel(sourceX, drawY, Color.FromArgb(
                                                                                    bmpAlphaMapScaled.GetPixel(drawX, drawY).A,
                                                                                    bmpRawTex.GetPixel(drawX, drawY).R,
                                                                                    bmpRawTex.GetPixel(drawX, drawY).G,
                                                                                    bmpRawTex.GetPixel(drawX, drawY).B
                                                                                    ));
                                                            }
                                                        }
                                                        bmpAlphaMapScaled.Dispose();
                                                        baseG.DrawImage(bmpTex, 0, 0, bmpBase.Width, bmpBase.Height);
                                                    }

                                                    using (MemoryStream str = new MemoryStream())
                                                        using (MD5 md5 = MD5.Create())
                                                        {
                                                            bmpBase.Save(str, ImageFormat.Png);
                                                            byte[] raw = md5.ComputeHash(str.ToArray());
                                                            uint   cacheID;

                                                            if (texCache.TryGetValue(raw, out cacheID))
                                                            {
                                                                // Cache found, use that instead.
                                                                texFaceIndex = cacheID;
                                                            }
                                                            else
                                                            {
                                                                // No cache found, store new.
                                                                bmpBase.Save(texFilePath);
                                                                texProvider.addTexture(-1, Path.Combine(dataDirRaw, texFileName));
                                                                cacheID = (uint)texProvider.LastIndex;

                                                                texFaceIndex = cacheID;
                                                                texCache.Add(raw, cacheID);
                                                            }
                                                        }
                                                    bmpBase.Dispose();
                                                }
                                        }

                                        Mesh mesh = new Mesh("Terrain Mesh #" + meshIndex);
                                        meshIndex++;

                                        int v = 0;

                                        float pX = soupChunk.position.X;
                                        float pY = -soupChunk.position.Y;
                                        float pZ = soupChunk.position.Z;

                                        int ofs = 10;
                                        for (int sX = 8; sX > 0; sX--)
                                        {
                                            for (int sY = 1; sY < 9; sY++)
                                            {
                                                int cIndex  = ofs - 1;
                                                int blIndex = cIndex - 9;
                                                int tlIndex = cIndex + 8;

                                                float tr = hmChunk.vertices[tlIndex + 1];
                                                float tl = hmChunk.vertices[tlIndex];
                                                float br = hmChunk.vertices[blIndex + 1];
                                                float bl = hmChunk.vertices[blIndex];
                                                float c  = hmChunk.vertices[cIndex];

                                                float oX = pX + (sX * ADTFile.TILE_SIZE);
                                                float oY = pY + (sY * ADTFile.TILE_SIZE);

                                                // Apply UV mapping
                                                for (int uv = 0; uv < 5; uv++)
                                                {
                                                    mesh.addUV(uvMaps[sX - 1, sY - 1, uv]);
                                                }

                                                // Apply verts
                                                mesh.addVert(new Position(oX, tl + pZ, oY));
                                                mesh.addVert(new Position(oX, tr + pZ, oY + ADTFile.TILE_SIZE));
                                                mesh.addVert(new Position(oX + ADTFile.TILE_SIZE, bl + pZ, oY));
                                                mesh.addVert(new Position(oX + ADTFile.TILE_SIZE, br + pZ, oY + ADTFile.TILE_SIZE));
                                                mesh.addVert(new Position(oX + (ADTFile.TILE_SIZE / 2), c + pZ, oY + (ADTFile.TILE_SIZE / 2)));

                                                // Normals
                                                mesh.addNormal(nChunk.normals[tlIndex]);
                                                mesh.addNormal(nChunk.normals[tlIndex + 1]);
                                                mesh.addNormal(nChunk.normals[blIndex]);
                                                mesh.addNormal(nChunk.normals[blIndex + 1]);
                                                mesh.addNormal(nChunk.normals[cIndex]);

                                                // Faces
                                                mesh.addFace(texFaceIndex, v, v + 2, v + 4);
                                                mesh.addFace(texFaceIndex, v + 1, v + 3, v + 4);
                                                mesh.addFace(texFaceIndex, v, v + 1, v + 4);
                                                mesh.addFace(texFaceIndex, v + 2, v + 3, v + 4);

                                                v   += 5;
                                                ofs += 1;
                                            }
                                            ofs += 9;
                                        }

                                        ob.addMesh(mesh);
                                    }

                                    // Parse WMO objects that appear in the world.
                                    EventManager.Trigger_LoadingPrompt("Constructing buildings...");

                                    Chunk_MWID wmoIndexChunk = (Chunk_MWID)obj.getChunk(Chunk_MWID.Magic, false);
                                    Chunk_MWMO wmoModelChunk = (Chunk_MWMO)obj.getChunk(Chunk_MWMO.Magic, false);
                                    Chunk_MODF wmoRefChunk   = (Chunk_MODF)obj.getChunk(Chunk_MODF.Magic, false);

                                    if (wmoIndexChunk != null && wmoModelChunk != null && wmoRefChunk != null)
                                    {
                                        foreach (Chunk_MODF.MODFEntry entry in wmoRefChunk.entries)
                                        {
                                            string          wmoModel    = wmoModelChunk.objects.get((int)wmoIndexChunk.offsets[entry.entry]);
                                            List <CASCFile> groupSearch = CASCSearch.Search(Path.Combine(Path.GetDirectoryName(wmoModel), Path.GetFileNameWithoutExtension(wmoModel)), CASCSearch.SearchType.STARTS_WITH);

                                            if (groupSearch.Count > 0)
                                            {
                                                // Set-up root/group files for the WMO.
                                                WMOFile        wmo        = null;
                                                List <WMOFile> groupFiles = new List <WMOFile>(groupSearch.Count - 1);
                                                string         rootName   = Path.GetFileName(wmoModel).ToLower();

                                                foreach (CASCFile file in groupSearch)
                                                {
                                                    Program.CASCEngine.SaveFileTo(file.FullName, Constants.TEMP_DIRECTORY);
                                                    string tempPath = Path.Combine(Constants.TEMP_DIRECTORY, file.FullName);

                                                    if (file.FullName.ToLower().EndsWith(rootName))
                                                    {
                                                        wmo = new WMOFile(tempPath, true);
                                                    }
                                                    else
                                                    {
                                                        groupFiles.Add(new WMOFile(tempPath, false));
                                                    }
                                                }

                                                foreach (WMOFile groupFile in groupFiles)
                                                {
                                                    wmo.addGroupFile(groupFile);
                                                }

                                                groupFiles.Clear();
                                                wmo.parse();

                                                // Export/register textures needed for this WMO.
                                                Formats.WMO.Chunk_MOTX wmoTexChunk = (Formats.WMO.Chunk_MOTX)wmo.getChunk(Formats.WMO.Chunk_MOTX.Magic);
                                                Dictionary <int, int>  wmoTexMap   = new Dictionary <int, int>();

                                                foreach (KeyValuePair <int, string> node in wmoTexChunk.textures.raw())
                                                {
                                                    string tempPath = Path.Combine(Constants.TEMP_DIRECTORY, node.Value);
                                                    string dumpPath = Path.Combine(Path.GetDirectoryName(node.Value), Path.GetFileNameWithoutExtension(node.Value) + ".png");

                                                    // Extract
                                                    if (!File.Exists(tempPath))
                                                    {
                                                        Program.CASCEngine.SaveFileTo(node.Value, Constants.TEMP_DIRECTORY);
                                                    }

                                                    // Convert
                                                    using (BlpFile blp = new BlpFile(File.OpenRead(tempPath)))
                                                        using (Bitmap bmp = blp.GetBitmap(0))
                                                        {
                                                            string dumpLoc = Path.Combine(dataDir, dumpPath);
                                                            Directory.CreateDirectory(Path.GetDirectoryName(dumpLoc));
                                                            bmp.Save(dumpLoc);
                                                        }

                                                    // Register
                                                    texProvider.addTexture(-1, Path.Combine(dataDirRaw, dumpPath));
                                                    wmoTexMap.Add(node.Key, texProvider.LastIndex);
                                                }

                                                Formats.WMO.Chunk_MOGN wmoNameChunk = (Formats.WMO.Chunk_MOGN)wmo.getChunk(Formats.WMO.Chunk_MOGN.Magic);
                                                Formats.WMO.Chunk_MOMT wmoMatChunk  = (Formats.WMO.Chunk_MOMT)wmo.getChunk(Formats.WMO.Chunk_MOMT.Magic);

                                                foreach (Chunk_Base rawChunk in wmo.getChunksByID(Formats.WMO.Chunk_MOGP.Magic))
                                                {
                                                    Formats.WMO.Chunk_MOGP chunk = (Formats.WMO.Chunk_MOGP)rawChunk;
                                                    string meshName = wmoNameChunk.data.get((int)chunk.groupNameIndex);

                                                    // Skip antiportals.
                                                    if (meshName.ToLower().Equals("antiportal"))
                                                    {
                                                        continue;
                                                    }

                                                    Mesh mesh = new Mesh(string.Format("WMO{0}_{1}", wmoIndex, meshName));

                                                    // Populate mesh with vertices.
                                                    Formats.WMO.Chunk_MOVT vertChunk = (Formats.WMO.Chunk_MOVT)chunk.getChunk(Formats.WMO.Chunk_MOVT.Magic);
                                                    foreach (Position vertPos in vertChunk.vertices)
                                                    {
                                                        mesh.addVert(new Position(entry.position.X + vertPos.X, entry.position.Y + vertPos.Y, entry.position.Z + vertPos.Z));
                                                    }

                                                    // Populate mesh with UVs.
                                                    Formats.WMO.Chunk_MOTV uvChunk = (Formats.WMO.Chunk_MOTV)chunk.getChunk(Formats.WMO.Chunk_MOTV.Magic);
                                                    foreach (UV uv in uvChunk.uvData)
                                                    {
                                                        mesh.addUV(uv);
                                                    }

                                                    // Populate mesh with normals.
                                                    Formats.WMO.Chunk_MONR normChunk = (Formats.WMO.Chunk_MONR)chunk.getChunk(Formats.WMO.Chunk_MONR.Magic);
                                                    foreach (Position norm in normChunk.normals)
                                                    {
                                                        mesh.addNormal(norm);
                                                    }

                                                    // Populate mesh with triangles (faces).
                                                    Formats.WMO.Chunk_MOVI faceChunk    = (Formats.WMO.Chunk_MOVI)chunk.getChunk(Formats.WMO.Chunk_MOVI.Magic);
                                                    Formats.WMO.Chunk_MOPY faceMatChunk = (Formats.WMO.Chunk_MOPY)chunk.getChunk(Formats.WMO.Chunk_MOPY.Magic);

                                                    for (int i = 0; i < faceChunk.positions.Length; i++)
                                                    {
                                                        Formats.WMO.FacePosition position = faceChunk.positions[i];
                                                        Formats.WMO.FaceInfo     info     = faceMatChunk.faceInfo[i];

                                                        if (info.materialID != 0xFF) // 0xFF (255) identifies a collision face.
                                                        {
                                                            Material mat   = wmoMatChunk.materials[info.materialID];
                                                            uint     texID = (uint)wmoTexMap[(int)mat.texture1.offset];

                                                            mesh.addFace(texID, mat.texture2.colour, position.point1, position.point2, position.point3);
                                                        }
                                                    }

                                                    Log.Write("CreateWMOMesh (ADT): " + mesh.ToAdvancedString());
                                                    ob.addMesh(mesh);
                                                }
                                            }
                                        }
                                    }
                                }
                                catch (ADTException ex)
                                {
                                    LogWrite("Unable to process tile {0},{1} due to exception: {2}", x, y, ex.Message);
                                    LogWrite(ex.StackTrace);
                                }
                            }
                        }
                    }

                    EventManager.Trigger_LoadingPrompt(new Random().Next(100) == 42 ? "Reticulating splines..." : "Writing terrain data to file...");

                    ob.Write();
                    ob.Close();
                }

                // Job's done.
                EventManager.Trigger_MapExportDone(true);
            }
            catch (Exception e)
            {
                ob.Close();
                LogWrite("Unable to complete map extraction due to exception: {0}", e.Message);
                EventManager.Trigger_MapExportDone(false, e.Message + e.StackTrace);
            }
        }