public Chunk_MCLV(ADTFile file) : base(file, "MCLV", Magic) { colours = new Colour4[145]; for (int i = 0; i < 145; i++) colours[i] = Colour4.Read(file); }
public Chunk_MODF(ADTFile file) : base(file, "MODF", Magic) { int entryCount = (int)ChunkSize / 64; entries = new MODFEntry[entryCount]; for (int i = 0; i < entryCount; i++) { MODFEntry entry = new MODFEntry(); entry.entry = file.readUInt32(); entry.uniqueID = file.readUInt32(); float x = Constants.WOW_ADT_MAP_MAX - file.readFloat(); float z = file.readFloat() - Constants.WOW_ADT_MAP_MAX; entry.position = new Position(z, file.readFloat(), x); entry.rotation = Position.Read(file); entry.lowerBound = Position.Read(file); entry.upperBound = Position.Read(file); entry.flags = file.readUInt16(); entry.doodadSet = file.readUInt16(); entry.nameSet = file.readUInt16(); entry.unk = file.readUInt16(); entries[i] = entry; } LogWrite("Loaded " + entryCount + " WMO spawns"); }
static void ReadMVER(BinaryReader fileReader, ADTFile adt) { var type = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); adt.Version = fileReader.ReadInt32(); }
public Chunk_MCVT(ADTFile file) : base(file, "MCVT", Magic) { vertices = new float[145]; for (int i = 0; i < 145; i++) vertices[i] = file.readFloat(); }
static void ReadMHDR(BinaryReader fileReader, ADTFile adt) { var type = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); adt.Header.Base = (uint)fileReader.BaseStream.Position; var pad = fileReader.ReadUInt32(); adt.Header.offsInfo = fileReader.ReadUInt32(); adt.Header.offsTex = fileReader.ReadUInt32(); adt.Header.offsModels = fileReader.ReadUInt32(); adt.Header.offsModelIds = fileReader.ReadUInt32(); adt.Header.offsMapObjects = fileReader.ReadUInt32(); adt.Header.offsMapObjectIds = fileReader.ReadUInt32(); adt.Header.offsDoodadDefinitions = fileReader.ReadUInt32(); adt.Header.offsObjectDefinitions = fileReader.ReadUInt32(); adt.Header.offsFlightBoundary = fileReader.ReadUInt32(); adt.Header.offsMH2O = fileReader.ReadUInt32(); var pad3 = fileReader.ReadUInt32(); var pad4 = fileReader.ReadUInt32(); var pad5 = fileReader.ReadUInt32(); var pad6 = fileReader.ReadUInt32(); var pad7 = fileReader.ReadUInt32(); }
public Chunk_MCCV(ADTFile file) : base(file, "MCCV", Magic) { colours = new Colour4[145]; for (int i = 0; i < 145; i++) { colours[i] = Colour4.Read(file); } }
public Chunk_MCVT(ADTFile file) : base(file, "MCVT", Magic) { vertices = new float[145]; for (int i = 0; i < 145; i++) { vertices[i] = file.readFloat(); } }
public Chunk_MMID(ADTFile file) : base(file, "MMID", Magic) { int offsetCount = (int)ChunkSize / 4; offsets = new UInt32[offsetCount]; for (int i = 0; i < offsetCount; i++) offsets[i] = file.readUInt32(); }
public Chunk_MCLY(ADTFile file) : base(file, "MCLY", Magic) { int nLayers = (int)ChunkSize / 16; layers = new MCLYLayer[nLayers]; for (int i = 0; i < nLayers; i++) layers[i] = new MCLYLayer(file.readUInt32(), file.readUInt32(), file.readUInt32(), file.readUInt32()); }
public Chunk_MCRW(ADTFile file) : base(file, "MCRW", Magic) { int nEntry = (int)ChunkSize / 4; entries = new UInt32[nEntry]; for (int i = 0; i < nEntry; i++) entries[i] = file.readUInt32(); }
public Chunk_MVER(ADTFile file) : base(file, "MVER", Magic) { Version = file.readUInt32(); // Ensure we have a v18 ADT chunk. if (Version != 18) throw new ADTException("Unsupported ADT version: " + Version); }
} // Unused public Chunk_MCNK(ADTFile file) : base(file, "MCNK", Magic) { Chunks = new List <Chunk_Base>(); if (file.Type == ADTFileType.ROOT) // Only root has the header. { lqTexMap = new UInt32[4]; // Assign a size so the stuffer knows what to do. Stuffer.Stuff(this, file, GetLogPrefix(), true); } }
public Chunk_MVER(ADTFile file) : base(file, "MVER", Magic) { Version = file.readUInt32(); // Ensure we have a v18 ADT chunk. if (Version != 18) { throw new ADTException("Unsupported ADT version: " + Version); } }
public Chunk_MMID(ADTFile file) : base(file, "MMID", Magic) { int offsetCount = (int)ChunkSize / 4; offsets = new UInt32[offsetCount]; for (int i = 0; i < offsetCount; i++) { offsets[i] = file.readUInt32(); } }
public Chunk_MCRW(ADTFile file) : base(file, "MCRW", Magic) { int nEntry = (int)ChunkSize / 4; entries = new UInt32[nEntry]; for (int i = 0; i < nEntry; i++) { entries[i] = file.readUInt32(); } }
public Chunk_MCLY(ADTFile file) : base(file, "MCLY", Magic) { int nLayers = (int)ChunkSize / 16; layers = new MCLYLayer[nLayers]; for (int i = 0; i < nLayers; i++) { layers[i] = new MCLYLayer(file.readUInt32(), file.readUInt32(), file.readUInt32(), file.readUInt32()); } }
public Chunk_MCNK(ADTFile file) : base(file, "MCNK", Magic) { Chunks = new List<Chunk_Base>(); if (file.Type == ADTFileType.ROOT) // Only root has the header. { lqTexMap = new UInt32[4]; // Assign a size so the stuffer knows what to do. Stuffer.Stuff(this, file, GetLogPrefix(), true); } }
public static ADTFile Process(MpqManager manager, string dataDirectory, string adtName) { var adtFilePath = Path.Combine(dataDirectory, adtName + ".adt"); if (!manager.FileExists(adtFilePath)) return null; using (var fileReader = new BinaryReader(manager.OpenFile(adtFilePath))) { var adt = new ADTFile() { Name = adtName, Path = dataDirectory }; ReadMVER(fileReader, adt); ReadMHDR(fileReader, adt); if (adt.Header.offsInfo != 0) { fileReader.BaseStream.Position = adt.Header.Base + adt.Header.offsInfo; ReadMCIN(fileReader, adt); } if (adt.Header.offsModels != 0) { fileReader.BaseStream.Position = adt.Header.Base + adt.Header.offsModels; ReadMMDX(fileReader, adt); } if (adt.Header.offsMapObjects != 0) { fileReader.BaseStream.Position = adt.Header.Base + adt.Header.offsMapObjects; ReadMWMO(fileReader, adt); } if (adt.Header.offsDoodadDefinitions != 0) { fileReader.BaseStream.Position = adt.Header.Base + adt.Header.offsDoodadDefinitions; ReadMDDF(fileReader, adt); } if (adt.Header.offsObjectDefinitions != 0) { fileReader.BaseStream.Position = adt.Header.Base + adt.Header.offsObjectDefinitions; ReadMODF(fileReader, adt); } if (adt.Header.offsMH2O != 0) { fileReader.BaseStream.Position = adt.Header.Base + adt.Header.offsMH2O; ReadMH2O(fileReader, adt); } ReadMCNKs(fileReader, adt); return adt; } }
public Chunk_MCNR(ADTFile file) : base(file, "MCNR", Magic) { normals = new Position[145]; for (int i = 0; i < 145; i++) { float x = file.readUInt8() / 127; float z = file.readUInt8() / 127; float y = file.readUInt8() / 127; // Super-hacky fix? This does not seem right, but it prevents lighting issues? if (y == 1.0 && z == 0.0) { z = 1.0f; y = 0.0f; } normals[i] = new Position(x, y, z); } }
public Chunk_MDDF(ADTFile file) : base(file, "MDDF", Magic) { int entryCount = (int)ChunkSize / 36; entries = new MDDFEntry[entryCount]; for (int i = 0; i < entryCount; i++) { MDDFEntry entry = new MDDFEntry(); entry.entry = file.readUInt32(); entry.uniqueID = file.readUInt32(); entry.position = Position.Read(file); entry.rotation = Rotation.Read(file); entry.scale = file.readUInt16(); entry.flags = file.readUInt16(); } LogWrite("Loaded " + entryCount + " doodad spawns"); }
public Chunk_MMDX(ADTFile file) : base(file, "MMDX", Magic) { models = new StringBlock(file, (int)ChunkSize); }
public Chunk_MTEX(ADTFile file) : base(file, "MTEX", Magic) { textures = new StringBlock(file, (int)ChunkSize); }
static void ReadMCIN(BinaryReader fileReader, ADTFile adt) { var type = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); adt.MapChunkInfo = new MapChunkInfo[256]; for (var i = 0; i < 256; i++) { var mcin = new MapChunkInfo { Offset = fileReader.ReadUInt32(), Size = fileReader.ReadUInt32(), Flags = fileReader.ReadUInt32(), AsyncId = fileReader.ReadUInt32() }; adt.MapChunkInfo[i] = mcin; } }
public Chunk_MWMO(ADTFile file) : base(file, "MWMO", Magic) { objects = new StringBlock(file, (int)ChunkSize); }
private void FixList(List <string> list) { var bw = new BackgroundWorker(); bw.WorkerReportsProgress = true; bw.DoWork += new DoWorkEventHandler((sender, e) => { var filenames = e.Argument as List <string>; var progress = 0; var worker = sender as BackgroundWorker; foreach (string filename in filenames) { IConverter converter = null; if (filename.EndsWith("m2")) { var m2converter = new M2Converter(filename, helmFix.Checked); if (m2converter.Fix()) { m2converter.Save(); } continue; } else if (filename.EndsWith("adt")) { converter = new ADTFile(filename.Replace(".adt", "_obj0.adt"), filename.Replace(".adt", "_tex0.adt")); } // else if (filename.EndsWith("wdt")) // converter = new WDTConverter(filename); else if (Regex.IsMatch(filename, @".*_[0-9]{3}(_(lod[0-9]))?\.(wmo)")) { var wmoconverter = new WMOGroupConverter(filename, false); if (wmoconverter.Fix()) { wmoconverter.Save(); } continue; } else if (filename.EndsWith(".skin")) { continue; } if (filename.EndsWith("wmo")) { converter = new WMOFile(); } // else if (filename.EndsWith("anim")) // converter = new AnimConverter(filename); converter.Read(File.ReadAllBytes(filename)); converter.Write(filename); if (++progress == PROGRESS) { worker.ReportProgress(1); progress = 0; } } }); bw.ProgressChanged += new ProgressChangedEventHandler((sender, e) => { progress.PerformStep(); }); bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sender, e) => { if (e.Error != null) { MessageBox.Show(e.Error.ToString()); } progress.Value = progress.Maximum; Enabled = true; Clear(); }); bw.RunWorkerAsync(list); }
static void ReadMH2O(BinaryReader fileReader, ADTFile adt) { var sig = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); var ofsMH2O = fileReader.BaseStream.Position; var mh2oHeader = new MH2OHeader[256]; for (var i = 0; i < 256; i++) { mh2oHeader[i] = new MH2OHeader { ofsData1 = fileReader.ReadUInt32(), LayerCount = fileReader.ReadUInt32(), ofsData2 = fileReader.ReadUInt32() }; } for (var y = 0; y < 16; y++) { for (var x = 0; x < 16; x++) { adt.LiquidMaps[x, y] = ProcessMH2O(fileReader, mh2oHeader[y * 16 + x], ofsMH2O); } } }
static void ReadMODF(BinaryReader fileReader, ADTFile adt) { 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.FileName = adt.ObjectFiles[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.DoodadSet = fileReader.ReadUInt16(); // 2 bytes objectDef.NameSet = fileReader.ReadUInt16(); // 2 bytes fileReader.ReadUInt16(); // padding adt.ObjectDefinitions.Add(objectDef); } }
static void ReadMDDF(BinaryReader fileReader, ADTFile adt) { var type = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); var endPos = fileReader.BaseStream.Position + size; while (fileReader.BaseStream.Position < endPos) { var doodadDefinition = new MapDoodadDefinition(); var nameIndex = fileReader.ReadInt32(); doodadDefinition.FilePath = adt.ModelFiles[nameIndex]; // 4 bytes doodadDefinition.UniqueId = fileReader.ReadUInt32(); // 4 bytes doodadDefinition.Position = fileReader.ReadVector3(); // 12 bytes doodadDefinition.OrientationA = fileReader.ReadSingle(); // 4 Bytes doodadDefinition.OrientationB = fileReader.ReadSingle(); // 4 Bytes doodadDefinition.OrientationC = fileReader.ReadSingle(); // 4 Bytes doodadDefinition.Scale = fileReader.ReadUInt32() / 1024f; // 4 bytes adt.DoodadDefinitions.Add(doodadDefinition); } }
static void ReadMCNKs(BinaryReader br, ADTFile adt) { for (var i = 0; i < 256; i++) { var chunk = ProcessChunk(br, adt.MapChunkInfo[i].Offset); adt.MapChunks[chunk.IndexX, chunk.IndexY] = chunk; if (chunk.ofsHeight != 0) { adt.HeightMaps[chunk.IndexX, chunk.IndexY] = ProcessHeights(br, adt.MapChunkInfo[i].Offset + chunk.ofsHeight); } } }
public Chunk_MCAL(ADTFile file) : base(file, "MCAL", Magic) { data = file.readBytes((int)ChunkSize); }
static void ReadMMDX(BinaryReader fileReader, ADTFile adt) { var type = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); long endPos = fileReader.BaseStream.Position + size; while (fileReader.BaseStream.Position < endPos) { if (fileReader.PeekByte() == 0) { fileReader.BaseStream.Position++; } else { adt.ModelFiles.Add(fileReader.ReadCString()); } } }
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); } }