public static void exportADT(string file, BackgroundWorker exportworker = null) { if (exportworker == null) { exportworker = new BackgroundWorker(); } var outdir = ConfigurationManager.AppSettings["outdir"]; float TileSize = 1600.0f / 3.0f; //533.333 float ChunkSize = TileSize / 16.0f; //33.333 float UnitSize = ChunkSize / 8.0f; //4.166666 // ~~fun fact time with marlamin~~ this /2 ends up being pixelspercoord on minimap float MapMidPoint = 32.0f / ChunkSize; var mapname = file.Replace("world\\maps\\", "").Substring(0, file.Replace("world\\maps\\", "").IndexOf("\\")); var coord = file.Replace("world\\maps\\" + mapname + "\\" + mapname, "").Replace(".adt", "").Split('_'); var centerx = int.Parse(coord[1]); var centery = int.Parse(coord[2]); List<Structs.RenderBatch> renderBatches = new List<Structs.RenderBatch>(); List<Structs.Vertex> verticelist = new List<Structs.Vertex>(); List<int> indicelist = new List<Int32>(); Dictionary<int, string> materials = new Dictionary<int, string>(); var distance = 1; // Create output directory if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file))); } for (int y = centery; y < centery + distance; y++) { for (int x = centerx; x < centerx + distance; x++) { var curfile = "world\\maps\\" + mapname + "\\" + mapname + "_" + x + "_" + y + ".adt"; if (!CASC.FileExists(file)) { Console.WriteLine("File " + file + " does not exist"); continue; } exportworker.ReportProgress(0, "Loading ADT " + curfile); ADTReader reader = new ADTReader(); reader.LoadADT(curfile); // No chunks? Let's get the hell out of here if (reader.adtfile.chunks == null) { continue; } if (CASC.FileExists("world\\maptextures\\" + mapname + "\\" + mapname + "_" + y + "_" + x + ".blp")) { materials.Add(materials.Count() + 1, "mat" + y.ToString() + x.ToString()); var blpreader = new BLPReader(); blpreader.LoadBLP(curfile.Replace("maps", "maptextures").Replace(".adt", ".blp")); try { blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file), "mat" + y.ToString() + x.ToString() + ".png")); } catch (Exception e) { Console.WriteLine(e.Message); } } //List<Material> materials = new List<Material>(); //for (int ti = 0; ti < reader.adtfile.textures.filenames.Count(); ti++) //{ // Material material = new Material(); // material.filename = reader.adtfile.textures.filenames[ti]; // //if (!WoWFormatLib.Utils.CASC.FileExists(material.filename)) { continue; } // material.textureID = BLPLoader.LoadTexture(reader.adtfile.textures.filenames[ti], cache); // materials.Add(material); //} var initialChunkY = reader.adtfile.chunks[0].header.position.Y; var initialChunkX = reader.adtfile.chunks[0].header.position.X; for (uint c = 0; c < reader.adtfile.chunks.Count(); c++) { var chunk = reader.adtfile.chunks[c]; int off = verticelist.Count(); Structs.RenderBatch batch = new Structs.RenderBatch(); for (int i = 0, idx = 0; i < 17; i++) { for (int j = 0; j < (((i % 2) != 0) ? 8 : 9); j++) { Structs.Vertex v = new Structs.Vertex(); v.Normal = new OpenTK.Vector3(chunk.normals.normal_2[idx] / 127f, chunk.normals.normal_0[idx] / 127f, chunk.normals.normal_1[idx] / 127f); v.Position = new OpenTK.Vector3(chunk.header.position.Y - (j * UnitSize), chunk.vertices.vertices[idx++] + chunk.header.position.Z, chunk.header.position.X - (i * UnitSize * 0.5f)); if ((i % 2) != 0) v.Position.X -= 0.5f * UnitSize; v.TexCoord = new Vector2(-(v.Position.X - initialChunkX) / TileSize, -(v.Position.Z - initialChunkY) / TileSize); verticelist.Add(v); } } batch.firstFace = (uint)indicelist.Count(); for (int j = 9; j < 145; j++) { indicelist.AddRange(new Int32[] { off + j + 8, off + j - 9, off + j }); indicelist.AddRange(new Int32[] { off + j - 9, off + j - 8, off + j }); indicelist.AddRange(new Int32[] { off + j - 8, off + j + 9, off + j }); indicelist.AddRange(new Int32[] { off + j + 9, off + j + 8, off + j }); if ((j + 1) % (9 + 8) == 0) j += 9; } batch.materialID = (uint)materials.Count(); batch.numFaces = (uint)(indicelist.Count()) - batch.firstFace; //var layermats = new List<uint>(); //var alphalayermats = new List<int>(); //for (int li = 0; li < reader.adtfile.texChunks[c].layers.Count(); li++) //{ // if (reader.adtfile.texChunks[c].alphaLayer != null) // { // alphalayermats.Add(BLPLoader.GenerateAlphaTexture(reader.adtfile.texChunks[c].alphaLayer[li].layer)); // } // layermats.Add((uint)cache.materials[reader.adtfile.textures.filenames[reader.adtfile.texChunks[c].layers[li].textureId].ToLower()]); //} //batch.materialID = layermats.ToArray(); //batch.alphaMaterialID = alphalayermats.ToArray(); renderBatches.Add(batch); } } } var mtlsw = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file).Replace(" ", "") + ".mtl")); //No idea how MTL files really work yet. Needs more investigation. foreach (var material in materials) { mtlsw.WriteLine("newmtl " + material.Value); mtlsw.WriteLine("Ka 1.000000 1.000000 1.000000"); mtlsw.WriteLine("Kd 0.640000 0.640000 0.640000"); mtlsw.WriteLine("map_Ka " + material.Value + ".png"); mtlsw.WriteLine("map_Kd " + material.Value + ".png"); } mtlsw.Close(); var indices = indicelist.ToArray(); var adtname = Path.GetFileNameWithoutExtension(file); var objsw = new StreamWriter(Path.Combine(outdir, file.Replace(".adt", ".obj"))); objsw.WriteLine("# Written by Marlamin's WoW OBJExporter. Original file: " + file); objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(file).Replace(" ", "") + ".mtl"); objsw.WriteLine("g " + adtname); foreach (var vertex in verticelist) { objsw.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z); objsw.WriteLine("vt " + vertex.TexCoord.X + " " + -vertex.TexCoord.Y); objsw.WriteLine("vn " + vertex.Normal.X + " " + vertex.Normal.Y + " " + vertex.Normal.Z); } foreach (var renderBatch in renderBatches) { var i = renderBatch.firstFace; if (materials.ContainsKey((int)renderBatch.materialID)) { objsw.WriteLine("usemtl " + materials[(int)renderBatch.materialID]); objsw.WriteLine("s 1"); } while (i < (renderBatch.firstFace + renderBatch.numFaces)) { objsw.WriteLine("f " + (indices[i] + 1) + "/" + (indices[i] + 1) + "/" + (indices[i] + 1) + " " + (indices[i + 1] + 1) + "/" + (indices[i + 1] + 1) + "/" + (indices[i + 1] + 1) + " " + (indices[i + 2] + 1) + "/" + (indices[i + 2] + 1) + "/" + (indices[i + 2] + 1)); i = i + 3; } } objsw.Close(); }
private void LoadMap(string map, int centerx, int centery, int distance) { float TileSize = 1600.0f / 3.0f; //533.333 float ChunkSize = TileSize / 16.0f; //33.333 float UnitSize = ChunkSize / 8.0f; //4.166666 // ~~fun fact time with marlamin~~ this times 0.5 ends up being pixelspercoord on minimap float MapMidPoint = 32.0f / ChunkSize; GL.EnableClientState(ArrayCap.VertexArray); GL.EnableClientState(ArrayCap.NormalArray); List<Vertex> verticelist = new List<Vertex>(); List<Int32> indicelist = new List<Int32>(); GL.Enable(EnableCap.Texture2D); for (int x = centerx; x < centerx + distance; x++) { for (int y = centery; y < centery + distance; y++) { string filename = "World/Maps/" + map + "/" + map + "_" + y + "_" + x + ".adt"; if (WoWFormatLib.Utils.CASC.FileExists(filename)) { ADTReader reader = new ADTReader(); reader.LoadADT(filename); Terrain adt = new Terrain(); adt.vertexBuffer = GL.GenBuffer(); adt.indiceBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, adt.vertexBuffer); GL.BindBuffer(BufferTarget.ElementArrayBuffer, adt.indiceBuffer); List<Material> materials = new List<Material>(); //Check if textures are already loaded or not, multiple ADTs close together probably use the same ones mostly for (int ti = 0; ti < reader.adtfile.textures.filenames.Count(); ti++) { Material material = new Material(); material.filename = reader.adtfile.textures.filenames[ti]; //if (!WoWFormatLib.Utils.CASC.FileExists(material.filename)) { continue; } material.textureID = BLPLoader.LoadTexture(reader.adtfile.textures.filenames[ti], cache); materials.Add(material); } var initialChunkY = reader.adtfile.chunks[0].header.position.Y; var initialChunkX = reader.adtfile.chunks[0].header.position.X; /* if(firstLocation.X == 0) { firstLocation = new Vector3(initialChunkY, initialChunkX, 1.0f); Console.WriteLine("Setting first location to " + firstLocation.ToString()); } */ List<RenderBatch> renderBatches = new List<RenderBatch>(); for (uint c = 0; c < reader.adtfile.chunks.Count(); c++) { var chunk = reader.adtfile.chunks[c]; int off = verticelist.Count(); RenderBatch batch = new RenderBatch(); for (int i = 0, idx = 0; i < 17; i++) { for (int j = 0; j < (((i % 2) != 0) ? 8 : 9); j++) { //var v = new Vector3(chunk.header.position.Y - (j * UnitSize), chunk.vertices.vertices[idx++] + chunk.header.position.Z, -(chunk.header.position.X - (i * UnitSize * 0.5f))); Vertex v = new Vertex(); v.Normal = new Vector3(chunk.normals.normal_0[idx], chunk.normals.normal_1[idx], chunk.normals.normal_2[idx]); if (chunk.vertexShading.red != null && chunk.vertexShading.red[idx] != 127) { v.Color = new Vector3(chunk.vertexShading.blue[idx] / 255.0f, chunk.vertexShading.green[idx] / 255.0f, chunk.vertexShading.red[idx] / 255.0f); //v.Color = new Vector3(1.0f, 1.0f, 1.0f); } else { v.Color = new Vector3(1.0f, 1.0f, 1.0f); } v.TexCoord = new Vector2(((float)j + (((i % 2) != 0) ? 0.5f : 0f)) / 8f, ((float)i * 0.5f) / 8f); v.Position = new Vector3(chunk.header.position.Y - (j * UnitSize), chunk.vertices.vertices[idx++] + chunk.header.position.Z, chunk.header.position.X - (i * UnitSize * 0.5f)); if ((i % 2) != 0) v.Position.X -= 0.5f * UnitSize; verticelist.Add(v); } } batch.firstFace = (uint)indicelist.Count(); for (int j = 9; j < 145; j++) { indicelist.AddRange(new Int32[] { off + j + 8, off + j - 9, off + j }); indicelist.AddRange(new Int32[] { off + j - 9, off + j - 8, off + j }); indicelist.AddRange(new Int32[] { off + j - 8, off + j + 9, off + j }); indicelist.AddRange(new Int32[] { off + j + 9, off + j + 8, off + j }); if ((j + 1) % (9 + 8) == 0) j += 9; } batch.numFaces = (uint)(indicelist.Count()) - batch.firstFace; if (!cache.materials.ContainsKey(reader.adtfile.textures.filenames[reader.adtfile.texChunks[c].layers[0].textureId].ToLower())) { throw new Exception("MaterialCache does not have texture " + reader.adtfile.textures.filenames[reader.adtfile.texChunks[c].layers[0].textureId].ToLower()); } var layermats = new List<uint>(); var alphalayermats = new List<int>(); for (int li = 0; li < reader.adtfile.texChunks[c].layers.Count(); li++) { if(reader.adtfile.texChunks[c].alphaLayer != null){ alphalayermats.Add(BLPLoader.GenerateAlphaTexture(reader.adtfile.texChunks[c].alphaLayer[li].layer)); } layermats.Add((uint)cache.materials[reader.adtfile.textures.filenames[reader.adtfile.texChunks[c].layers[li].textureId].ToLower()]); } batch.materialID = layermats.ToArray(); batch.alphaMaterialID = alphalayermats.ToArray(); renderBatches.Add(batch); } List<Doodad> doodads = new List<Doodad>(); for (int mi = 0; mi < reader.adtfile.objects.models.entries.Count(); mi++) { Console.WriteLine("Loading model #" + mi); var modelentry = reader.adtfile.objects.models.entries[mi]; var mmid = reader.adtfile.objects.m2NameOffsets.offsets[modelentry.mmidEntry]; var modelfilename = ""; for (int mmi = 0; mmi < reader.adtfile.objects.m2Names.offsets.Count(); mmi++) { if (reader.adtfile.objects.m2Names.offsets[mmi] == mmid) { modelfilename = reader.adtfile.objects.m2Names.filenames[mmi].ToLower(); } } var doodad = new Doodad(); doodad.filename = modelfilename; doodad.position = new Vector3(-(modelentry.position.X - 17066), modelentry.position.Y, -(modelentry.position.Z - 17066)); doodad.rotation = new Vector3(modelentry.rotation.X, modelentry.rotation.Y, modelentry.rotation.Z); doodad.scale = modelentry.scale; doodads.Add(doodad); if (cache.doodadBatches.ContainsKey(modelfilename)) { continue; } M2Loader.LoadM2(modelfilename, cache); } List<WorldModelBatch> worldModelBatches = new List<WorldModelBatch>(); // WMO loading goes here for (int wmi = 0; wmi < reader.adtfile.objects.worldModels.entries.Count(); wmi++) { Console.WriteLine("Loading WMO #" + wmi); string wmofilename = ""; var wmodelentry = reader.adtfile.objects.worldModels.entries[wmi]; var mwid = reader.adtfile.objects.wmoNameOffsets.offsets[wmodelentry.mwidEntry]; for (int wmfi = 0; wmfi < reader.adtfile.objects.wmoNames.offsets.Count(); wmfi++) { if (reader.adtfile.objects.wmoNames.offsets[wmfi] == mwid) { wmofilename = reader.adtfile.objects.wmoNames.filenames[wmfi].ToLower(); } } if (wmofilename.Length == 0) { throw new Exception("Unable to find filename for WMO!"); } WorldModelBatch wmobatch = new WorldModelBatch(); wmobatch.position = new Vector3(-(wmodelentry.position.X - 17066), wmodelentry.position.Y, -(wmodelentry.position.Z - 17066)); wmobatch.rotation = new Vector3(wmodelentry.rotation.X, wmodelentry.rotation.Y, wmodelentry.rotation.Z); wmobatch.worldModel = WMOLoader.LoadWMO(wmofilename, cache); worldModelBatches.Add(wmobatch); } adt.renderBatches = renderBatches.ToArray(); adt.doodads = doodads.ToArray(); adt.worldModelBatches = worldModelBatches.ToArray(); int[] indices = indicelist.ToArray(); Vertex[] vertices = verticelist.ToArray(); Console.WriteLine("Vertices in array: " + vertices.Count()); //37120, correct //indices = indicelist.ToArray(); Console.WriteLine("Indices in array: " + indices.Count()); //196608, should be 65.5k which is 196608 / 3. in triangles so its correct? GL.BindBuffer(BufferTarget.ArrayBuffer, adt.vertexBuffer); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Count() * 11 * sizeof(float)), vertices, BufferUsageHint.StaticDraw); GL.BindBuffer(BufferTarget.ElementArrayBuffer, adt.indiceBuffer); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(int)), indices, BufferUsageHint.StaticDraw); int verticeBufferSize = 0; int indiceBufferSize = 0; GL.BindBuffer(BufferTarget.ArrayBuffer, adt.vertexBuffer); GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out verticeBufferSize); GL.BindBuffer(BufferTarget.ArrayBuffer, adt.indiceBuffer); GL.GetBufferParameter(BufferTarget.ElementArrayBuffer, BufferParameterName.BufferSize, out indiceBufferSize); Console.WriteLine("Vertices in buffer: " + verticeBufferSize / 11 / sizeof(float)); Console.WriteLine("Indices in buffer: " + indiceBufferSize / sizeof(int)); adts.Add(adt); } } } }
static void Main(string[] args) { Console.WriteLine("Initializing CASC.."); CASC.InitCasc(null, @"C:\World of Warcraft", "wow"); Console.WriteLine("CASC initialized!"); var dbcreader = new DBCReader<MapRecord>("DBFilesClient\\Map.dbc"); string mapfilter = "Draenor"; //string mapfilter = null; for (int mi = 0; mi < dbcreader.recordCount; mi++) { string mapname = dbcreader[mi].Directory; if (mapfilter != null) { if (mapname != mapfilter) { continue; } } Console.WriteLine("Creating image for map " + mapname + " (" + dbcreader[mi].Mapname_lang + ")"); if (File.Exists("done/" + mapname + ".png")) { continue; } float lowest = float.MaxValue; float highest = float.MinValue; float lowest_chunkpos = float.MaxValue; float highest_chunkpos = float.MinValue; var min_x = 64; var min_y = 64; var max_x = 0; var max_y = 0; Console.Write("Calculating heights.."); for (int x = 0; x < 63; x++) { for (int y = 0; y < 63; y++) { string filename = "World/Maps/" + mapname + "/" + mapname + "_" + y + "_" + x + ".adt"; if (CASC.FileExists(filename)) { if (x > max_x) { max_x = x; } if (y > max_y) { max_y = y; } if (x < min_x) { min_x = x; } if (y < min_y) { min_y = y; } ADTReader reader = new ADTReader(); reader.LoadADT(filename); for (var i = 0; i < 256; i++) { if (reader.adtfile.chunks[i].header.position.Z < lowest_chunkpos) { lowest_chunkpos = reader.adtfile.chunks[i].header.position.Z; } if (reader.adtfile.chunks[i].header.position.Z > highest_chunkpos) { highest_chunkpos = reader.adtfile.chunks[i].header.position.Z; } for (var j = 0; j < 145; ++j) { //Console.WriteLine(reader.adtfile.chunks[i].vertices.vertices[j]); if (reader.adtfile.chunks[i].vertices.vertices[j] < lowest) { lowest = reader.adtfile.chunks[i].vertices.vertices[j]; } if (reader.adtfile.chunks[i].vertices.vertices[j] > highest) { highest = reader.adtfile.chunks[i].vertices.vertices[j]; } } } } } } Console.Write(" ..done!\n"); Console.WriteLine("Highest: " + highest); Console.WriteLine("Lowest: " + lowest); Console.WriteLine("Highest chunkpos: " + highest_chunkpos); Console.WriteLine("Lowest chunkpos: " + lowest_chunkpos); if (lowest_chunkpos < 0) { highest = highest + (lowest * -1) + highest_chunkpos; lowest = (lowest * -1) + (lowest_chunkpos * -1); } else { highest = highest + (lowest * -1) + highest_chunkpos; lowest = (lowest * -1) + lowest_chunkpos; } Console.WriteLine("Highest post-flip: " + highest); Console.WriteLine("Lowest post-flip: " + lowest); Console.WriteLine("Highest chunkpos post-flip: " + highest_chunkpos); Console.WriteLine("Lowest chunkpos post-flip: " + lowest_chunkpos); Console.Write("Creating images.."); for (int x = 0; x < 63; x++) { for (int y = 0; y < 63; y++) { string filename = "World/Maps/" + mapname + "/" + mapname + "_" + y + "_" + x + ".adt"; if (CASC.FileExists(filename)) { ADTReader reader = new ADTReader(); reader.LoadADT(filename); var fullmap = new Bitmap(144, 144); for (var a = 0; a < 256; a++) { //Console.WriteLine(reader.adtfile.chunks[a].header.indexX + " x " + reader.adtfile.chunks[a].header.indexY); int img_x = 0; int img_y = 0; int counter = 0; for (var i = 0; i < 17; ++i) { for (var j = 0; j < (((i % 2) != 0) ? 8 : 9); ++j) { if (i % 2 == 0) { //only render these //Console.Write("(" + j +" " + i + ")" + counter + " |" ); //int greyness = (int)Math.Round(((reader.adtfile.chunks[a].vertices.vertices[counter] + reader.adtfile.chunks[a].header.position.Z) + lowest) / highest * 255); // if (greyness > 255) { greyness = 255; } //those edge cases where rounding just makes it go over 255 if (reader.adtfile.chunks[a].vertexShading.red != null && reader.adtfile.chunks[a].vertexShading.red[counter] != 127) { fullmap.SetPixel(img_x + (int)(reader.adtfile.chunks[a].header.indexX * 9), img_y + (int)(reader.adtfile.chunks[a].header.indexY * 9), Color.FromArgb(reader.adtfile.chunks[a].vertexShading.blue[counter], reader.adtfile.chunks[a].vertexShading.green[counter], reader.adtfile.chunks[a].vertexShading.red[counter]) ); } else { int greyness = (int)Math.Round(((reader.adtfile.chunks[a].vertices.vertices[counter] + reader.adtfile.chunks[a].header.position.Z) + lowest) / highest * 255); if (greyness > 255 || greyness < 0) { greyness = 255; } fullmap.SetPixel(img_x + (int)(reader.adtfile.chunks[a].header.indexX * 9), img_y + (int)(reader.adtfile.chunks[a].header.indexY * 9), Color.FromArgb(greyness, greyness, greyness)); } img_x++; } counter++; } if (i % 2 == 0) { img_y++; img_x = 0; //Console.Write("\n"); } } } fullmap.Save("Z:/adttest/" + mapname + "_" + y + "_" + x + ".png"); } } } Console.Write(" ..done!\n"); //Time to compile the full image var res_x = (((max_y - min_y) * 144) + 144); var res_y = (((max_x - min_x) * 144) + 144); if (res_x > 0 && res_y > 0) { Console.WriteLine("[" + mapname + "] " + "Creating new image of " + res_x + "x" + res_y); Bitmap bmp = new Bitmap(res_x, res_y); Graphics g = Graphics.FromImage(bmp); for (int cur_x = 0; cur_x < 64; cur_x++) { for (int cur_y = 0; cur_y < 64; cur_y++) { if (File.Exists("Z:/adttest/" + mapname + "_" + cur_x + "_" + cur_y + ".png")) { var bmpreader = new Bitmap("Z:/adttest/" + mapname + "_" + cur_x + "_" + cur_y + ".png"); g.DrawImage(bmpreader, (cur_x - min_x) * 144, (cur_y - min_y) * 144, new Rectangle(0, 0, 144, 144), GraphicsUnit.Pixel); bmpreader.Dispose(); File.Delete("Z:/adttest/" + mapname + "_" + cur_x + "_" + cur_y + ".png"); } } } g.Dispose(); if (!Directory.Exists("done")) { Directory.CreateDirectory("done"); } bmp.Save("done/" + mapname + ".png"); } } }