public static void exportADT(string file, string outdir, string bakeQuality, BackgroundWorker exportworker = null) { if (exportworker == null) { exportworker = new BackgroundWorker { WorkerReportsProgress = true }; } var customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone(); customCulture.NumberFormat.NumberDecimalSeparator = "."; System.Threading.Thread.CurrentThread.CurrentCulture = customCulture; var MaxSize = 51200 / 3.0; var TileSize = MaxSize / 32.0; var ChunkSize = TileSize / 16.0; var UnitSize = ChunkSize / 8.0; var UnitSizeHalf = UnitSize / 2.0; string mapname = file; mapname = mapname.Substring(mapname.LastIndexOf("\\", mapname.Length - 2) + 1); mapname = mapname.Substring(0, mapname.Length - 4); var reader = new ADTReader(); var ADTfile = file; exportworker.ReportProgress(0, "Loading ADT " + file); if (Managers.ConfigurationManager.Profile <= 3) //WoTLK and below { reader.Load335ADT(ADTfile); } else { reader.LoadADT(ADTfile); } if (reader.adtfile.chunks == null) { throw new Exception("ADT OBJ Exporter: File has no chunks, skipping export!"); } if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file))); } var renderBatches = new List <Structs.RenderBatch>(); var verticelist = new List <Structs.Vertex>(); var indicelist = new List <int>(); var materials = new Dictionary <int, string>(); // Calculate ADT offset in world coordinates var adtStartX = reader.adtfile.chunks[0].header.position.X; var adtStartY = reader.adtfile.chunks[0].header.position.Y; // Calculate first chunk offset in world coordinates var initialChunkX = adtStartX + (reader.adtfile.chunks[0].header.indexX * ChunkSize) * -1; var initialChunkY = adtStartY + (reader.adtfile.chunks[0].header.indexY * ChunkSize) * -1; uint ci = 0; for (var x = 0; x < 16; x++) { for (var y = 0; y < 16; y++) { var genx = (initialChunkX + (ChunkSize * x) * -1); var geny = (initialChunkY + (ChunkSize * y) * -1); var chunk = reader.adtfile.chunks[ci]; var off = verticelist.Count(); var batch = new Structs.RenderBatch(); for (int row = 0, idx = 0; row < 17; row++) { bool isSmallRow = (row % 2) != 0; int rowLength = isSmallRow ? 8 : 9; for (var col = 0; col < rowLength; col++) { var v = new Structs.Vertex(); v.Normal = new Structs.Vector3D { X = (double)chunk.normals.normal_0[idx] / 127, Y = (double)chunk.normals.normal_2[idx] / 127, Z = (double)chunk.normals.normal_1[idx] / 127 }; var px = geny - (col * UnitSize); var py = chunk.vertices.vertices[idx++] + chunk.header.position.Z; var pz = genx - (row * UnitSizeHalf); v.Position = new Structs.Vector3D { X = px, Y = py, Z = pz }; if ((row % 2) != 0) { v.Position.X = (px - UnitSizeHalf); } double ofs = col; if (isSmallRow) { ofs += 0.5; } if (bakeQuality == "high") { double tx = ofs / 8d; double ty = 1 - (row / 16d); v.TexCoord = new Structs.Vector2D { X = tx, Y = ty }; } else { double tx = -(v.Position.X - initialChunkY) / TileSize; double ty = (v.Position.Z - initialChunkX) / TileSize; v.TexCoord = new Structs.Vector2D { X = tx, Y = ty }; } verticelist.Add(v); } } batch.firstFace = (uint)indicelist.Count(); // Stupid C# and its structs var holesHighRes = new byte[8]; holesHighRes[0] = chunk.header.holesHighRes_0; holesHighRes[1] = chunk.header.holesHighRes_1; holesHighRes[2] = chunk.header.holesHighRes_2; holesHighRes[3] = chunk.header.holesHighRes_3; holesHighRes[4] = chunk.header.holesHighRes_4; holesHighRes[5] = chunk.header.holesHighRes_5; holesHighRes[6] = chunk.header.holesHighRes_6; holesHighRes[7] = chunk.header.holesHighRes_7; for (int j = 9, xx = 0, yy = 0; j < 145; j++, xx++) { if (xx >= 8) { xx = 0; ++yy; } var isHole = true; // Check if chunk is using low-res holes if ((chunk.header.flags & 0x10000) == 0) { // Calculate current hole number var currentHole = (int)Math.Pow(2, Math.Floor(xx / 2f) * 1f + Math.Floor(yy / 2f) * 4f); // Check if current hole number should be a hole if ((chunk.header.holesLowRes & currentHole) == 0) { isHole = false; } } //Sloppy ignore holes: if (Managers.ConfigurationManager.ADTIgnoreHoles) { isHole = false; } else { // Check if current section is a hole if (((holesHighRes[yy] >> xx) & 1) == 0) { isHole = false; } } if (!isHole) { indicelist.AddRange(new int[] { off + j + 8, off + j - 9, off + j }); indicelist.AddRange(new int[] { off + j - 9, off + j - 8, off + j }); indicelist.AddRange(new int[] { off + j - 8, off + j + 9, off + j }); indicelist.AddRange(new int[] { off + j + 9, off + j + 8, off + j }); // Generates quads instead of 4x triangles //indicelist.AddRange(new int[] { off + j + 8, off + j - 9, off + j - 8 }); //indicelist.AddRange(new int[] { off + j - 8, off + j + 9, off + j + 8 }); } if ((j + 1) % (9 + 8) == 0) { j += 9; } } if (bakeQuality == "high") { materials.Add((int)ci + 1, Path.GetFileNameWithoutExtension(file) + "_" + ci); batch.materialID = ci + 1; } else { if (!materials.ContainsKey(1)) { materials.Add(1, Path.GetFileNameWithoutExtension(file)); } batch.materialID = (uint)materials.Count(); } batch.numFaces = (uint)(indicelist.Count()) - batch.firstFace; renderBatches.Add(batch); ci++; } } bool exportWMO = Managers.ConfigurationManager.ADTExportWMO; bool exportM2 = Managers.ConfigurationManager.ADTExportM2; bool exportTextures = Managers.ConfigurationManager.ADTexportTextures; bool exportAlphaMaps = Managers.ConfigurationManager.ADTexportAlphaMaps; bool exportHeightmap = Managers.ConfigurationManager.ADTExportHeightmap; bool exportFoliage = Managers.ConfigurationManager.ADTExportFoliage; //FOLIAGE if (exportFoliage && Managers.ArchiveManager.usingCasc) //ONLY EXPROT IF CASC; PRE CASC NEEDS TO BE IMPLEMEMNTED { exportworker.ReportProgress(65, "Exporting foliage"); try { var build = Managers.ArchiveManager.cascHandler.Config.VersionName; //TODO: IMPLEMENT PRE CASC BUILDS var dbcd = new DBCD.DBCD(new WoWExport.DBC.ArchiveDBCProvider(), new WoWExport.DBC.LocalDBCDProvider()); var groundEffectTextureDB = dbcd.Load("GroundEffectTexture", build); var groundEffectDoodadDB = dbcd.Load("GroundEffectDoodad", build); for (var c = 0; c < reader.adtfile.texChunks.Length; c++) { for (var l = 0; l < reader.adtfile.texChunks[c].layers.Length; l++) { var effectID = reader.adtfile.texChunks[c].layers[l].effectId; if (effectID == 0) { continue; } if (!groundEffectTextureDB.ContainsKey(effectID)) { continue; } dynamic textureEntry = groundEffectTextureDB[effectID]; foreach (int doodad in textureEntry.DoodadID) { if (!groundEffectDoodadDB.ContainsKey(doodad)) { continue; } dynamic doodadEntry = groundEffectDoodadDB[doodad]; var filedataid = (uint)doodadEntry.ModelFileID; if (!WoWExport.Listfile.TryGetFilename(filedataid, out var filename)) { Console.WriteLine("Could not find filename for " + filedataid + ", setting filename to filedataid.."); filename = filedataid.ToString(); } if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), "foliage"))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file), "foliage")); } if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), "foliage", Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj"))) { M2Exporter.ExportM2(filename, Path.Combine(outdir, Path.GetDirectoryName(file), "foliage")); } } } } } catch (Exception e) { Console.WriteLine("Error exporting GroundEffects: " + e.Message); } } if (exportWMO || exportM2) { var doodadSW = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file).Replace(" ", "") + "_ModelPlacementInformation.csv")); doodadSW.WriteLine("ModelFile;PositionX;PositionY;PositionZ;RotationX;RotationY;RotationZ;ScaleFactor;ModelId;Type"); if (exportWMO) { exportworker.ReportProgress(25, "Exporting WMOs"); for (var mi = 0; mi < reader.adtfile.objects.worldModels.entries.Count(); mi++) { var wmo = reader.adtfile.objects.worldModels.entries[mi]; float wmoScale; if (wmo.scale != 0) { wmoScale = wmo.scale / 1024f; } else { wmoScale = 1; } var filename = reader.adtfile.objects.wmoNames.filenames[wmo.mwidEntry]; if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj"))) { WMOExporter.ExportWMO(filename, Path.Combine(outdir, Path.GetDirectoryName(file)), exportworker, null, wmo.doodadSet); } if (Managers.ConfigurationManager.ADTModelsPlacementGlobalPath) { doodadSW.WriteLine(Path.Combine(Path.GetDirectoryName(filename).ToLower(), Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj") + ";" + wmo.position.X + ";" + wmo.position.Y + ";" + wmo.position.Z + ";" + wmo.rotation.X + ";" + wmo.rotation.Y + ";" + wmo.rotation.Z + ";" + wmoScale + ";" + wmo.uniqueId + ";wmo"); } else { doodadSW.WriteLine(Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj;" + wmo.position.X + ";" + wmo.position.Y + ";" + wmo.position.Z + ";" + wmo.rotation.X + ";" + wmo.rotation.Y + ";" + wmo.rotation.Z + ";" + wmoScale + ";" + wmo.uniqueId + ";wmo"); } } } if (exportM2) { exportworker.ReportProgress(50, "Exporting M2s"); for (var mi = 0; mi < reader.adtfile.objects.models.entries.Count(); mi++) { var doodad = reader.adtfile.objects.models.entries[mi]; string filename; filename = reader.adtfile.objects.m2Names.filenames[doodad.mmidEntry]; if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj"))) { M2Exporter.ExportM2(filename, Path.Combine(outdir, Path.GetDirectoryName(file)), exportworker); } if (Managers.ConfigurationManager.ADTModelsPlacementGlobalPath) { doodadSW.WriteLine(Path.Combine(Path.GetDirectoryName(filename).ToLower(), Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj") + ";" + doodad.position.X + ";" + doodad.position.Y + ";" + doodad.position.Z + ";" + doodad.rotation.X + ";" + doodad.rotation.Y + ";" + doodad.rotation.Z + ";" + doodad.scale / 1024f + ";" + doodad.uniqueId + ";m2"); } else { doodadSW.WriteLine(Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj;" + doodad.position.X + ";" + doodad.position.Y + ";" + doodad.position.Z + ";" + doodad.rotation.X + ";" + doodad.rotation.Y + ";" + doodad.rotation.Z + ";" + doodad.scale / 1024f + ";" + doodad.uniqueId + ";m2"); } } } doodadSW.Close(); } #region Alpha&Tex //---------------------------------------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////////////////////////////////// ///TEXTURE & ALPHA & HEIGHTMAP RELATED START //////////////////////////////////////////////////////////////////////////////////////////////////////////// //---------------------------------------------------------------------------------------------------------- if (exportTextures) //Export ground textures { { if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures"))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures")); } } List <String> GroundTextures = reader.adtfile.textures.filenames.ToList(); var blpreader = new BLPReader(); foreach (string texture in GroundTextures) { if (Managers.ConfigurationManager.ADTPreserveTextureStruct) { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures", Path.GetDirectoryName(texture) + Path.GetFileNameWithoutExtension(texture) + ".png"))) { if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures", Path.GetDirectoryName(texture)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures", Path.GetDirectoryName(texture))); } if (Managers.ArchiveManager.FileExists(texture)) { try { blpreader.LoadBLP(Managers.ArchiveManager.ReadThisFile(texture)); blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures", Path.GetDirectoryName(texture), Path.GetFileNameWithoutExtension(texture) + ".png")); if (Managers.ConfigurationManager.ADTExportSpecularTextures) { if (Managers.ArchiveManager.FileExists(Path.Combine(Path.GetDirectoryName(texture), Path.GetFileNameWithoutExtension(texture) + "_s.blp"))) { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures", Path.GetDirectoryName(texture), Path.GetFileNameWithoutExtension(texture) + "_s.png"))) { blpreader.LoadBLP(Managers.ArchiveManager.ReadThisFile(Path.Combine(Path.GetDirectoryName(texture), Path.GetFileNameWithoutExtension(texture) + "_s.blp"))); blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures", Path.GetDirectoryName(texture), Path.GetFileNameWithoutExtension(texture) + "_s.png")); } } } } catch (Exception e) { //Error on file save throw new Exception(e.Message); } } else { //Missing file } } } else { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures\\", Path.GetFileNameWithoutExtension(texture) + ".png"))) { if (Managers.ArchiveManager.FileExists(texture)) { try { blpreader.LoadBLP(Managers.ArchiveManager.ReadThisFile(texture)); blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures\\", Path.GetFileNameWithoutExtension(texture) + ".png")); if (Managers.ConfigurationManager.ADTExportSpecularTextures) { if (Managers.ArchiveManager.FileExists(Path.Combine(Path.GetDirectoryName(texture), Path.GetFileNameWithoutExtension(texture) + "_s.blp"))) { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures\\", Path.GetFileNameWithoutExtension(texture) + "_s.png"))) { blpreader.LoadBLP(Managers.ArchiveManager.ReadThisFile(Path.Combine(Path.GetDirectoryName(texture), Path.GetFileNameWithoutExtension(texture) + "_s.blp"))); blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\GroundTextures\\", Path.GetFileNameWithoutExtension(texture) + "_s.png")); } } } } catch (Exception e) { //Error on file save throw new Exception(e.Message); } } else { //Missing file } } } } } if (exportAlphaMaps) { Generators.ADT_Alpha.ADT_Alpha AlphaMapsGenerator = new Generators.ADT_Alpha.ADT_Alpha(); AlphaMapsGenerator.GenerateAlphaMaps(reader.adtfile, Managers.ConfigurationManager.ADTAlphaMode); List <System.Drawing.Bitmap> AlphaLayers = new List <System.Drawing.Bitmap>(); AlphaLayers = AlphaMapsGenerator.AlphaLayers; if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\AlphaMaps"))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\AlphaMaps")); } if (AlphaLayers != null) { if (Managers.ConfigurationManager.ADTAlphaMode == 0 || Managers.ConfigurationManager.ADTAlphaMode == 1) { for (int m = 0; m < AlphaLayers.ToArray().Length; m++) { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\AlphaMaps\\", mapname + "_" + m + ".png"))) { try { AlphaLayers[m].Save(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\AlphaMaps\\", mapname + "_" + m + ".png")); } catch { //Error on file save } } } } if (Managers.ConfigurationManager.ADTAlphaMode == 2 || Managers.ConfigurationManager.ADTAlphaMode == 3) { List <String> AlphaLayersNames = new List <String>(AlphaMapsGenerator.AlphaLayersNames); for (int m = 0; m < AlphaLayers.ToArray().Length; m++) { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\AlphaMaps\\", mapname + "_" + AlphaLayersNames[m].Replace(";", "_") + ".png"))) { try { AlphaLayers[m].Save(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\AlphaMaps\\", mapname + "_" + AlphaLayersNames[m].Replace(";", "_") + ".png")); } catch { //Error on file save } } } } if (Managers.ConfigurationManager.ADTAlphaMode == 4) //Splatmaps { //Save the splatmaps for (int m = 0; m < AlphaLayers.ToArray().Length; m++) { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\AlphaMaps\\", mapname + "_splatmap_" + m + ".png"))) { try { AlphaLayers[m].Save(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\AlphaMaps\\", mapname + "_splatmap_" + m + ".png")); } catch { //Error on file save } } } //Save the material info JSON if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\AlphaMaps\\", mapname + "_MaterialData.json"))) { try { File.WriteAllText(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\AlphaMaps\\", mapname + "_MaterialData.json"), AlphaMapsGenerator.SplatmapJSON); } catch { //Error on file save } } } } //Check if the CSV already exists, if not, create it if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\" + mapname + "_" + "layers.csv"))) { //Generate layer information CSV StreamWriter layerCsvSR = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\" + mapname + "_" + "layers.csv")); //Insert CSV scheme layerCsvSR.WriteLine("chunk;tex0;tex1;tex2;tex3"); for (uint c = 0; c < reader.adtfile.chunks.Count(); c++) { string csvLine = c.ToString(); for (int li = 0; li < reader.adtfile.texChunks[c].layers.Count(); li++) { var AlphaLayerName = reader.adtfile.textures.filenames[reader.adtfile.texChunks[c].layers[li].textureId].ToLower(); if (Managers.ConfigurationManager.ADTPreserveTextureStruct) { csvLine = csvLine + ";" + Path.Combine(Path.GetDirectoryName(AlphaLayerName), Path.GetFileNameWithoutExtension(AlphaLayerName)); } else { csvLine = csvLine + ";" + Path.GetFileNameWithoutExtension(AlphaLayerName); } } layerCsvSR.WriteLine(csvLine); } layerCsvSR.Close(); } } if (exportHeightmap) { Generators.ADT_Height.ADT_Height heightmapGenerator = new Generators.ADT_Height.ADT_Height(); heightmapGenerator.GenerateHeightmap(reader.adtfile); if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\HeightMaps"))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\HeightMaps")); } try { File.WriteAllText(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\HeightMaps\\", mapname + "_HeightData.json"), Newtonsoft.Json.JsonConvert.SerializeObject(heightmapGenerator.heightArray2d)); heightmapGenerator.heightMap.Save(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\HeightMaps\\", mapname + "_heightmap.png")); } catch { //Error on save } } //---------------------------------------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////////////////////////////////// ///TEXTURE & ALPHA & HEIGHTMAP RELATED END //////////////////////////////////////////////////////////////////////////////////////////////////////////// //---------------------------------------------------------------------------------------------------------- #endregion //VERTEX COLORS -- not implemented /* * //Vertex color data * if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\" + mapname + "_" + "vertex_colors.csv"))) * { * StreamWriter vertesColorCSV = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(file) + "\\" + mapname + "_" + "vertex_colors.csv")); * vertesColorCSV.WriteLine("chunk;vert;a;r;g;b"); //header * for (uint c = 0; c < reader.adtfile.chunks.Count(); c++) * { * if (reader.adtfile.chunks[c].vertexShading.red != null) * { * for (int i = 0; i < 145; i++) * { * //Console.WriteLine(c + "_" + i + "-" + reader.adtfile.chunks[c].vertexShading.alpha[i] + " " + reader.adtfile.chunks[c].vertexShading.red[i] + " " + reader.adtfile.chunks[c].vertexShading.green[i] + " " + reader.adtfile.chunks[c].vertexShading.blue[i]); * vertesColorCSV.WriteLine(c + ";" + i + ";" + reader.adtfile.chunks[c].vertexShading.alpha[i] + ";" + reader.adtfile.chunks[c].vertexShading.red[i] + ";" + reader.adtfile.chunks[c].vertexShading.green[i] + ";" + reader.adtfile.chunks[c].vertexShading.blue[i]); * } * } * else * { * //Console.WriteLine(c + "- null"); * vertesColorCSV.WriteLine(c + ";0;0;0;0;0"); * } * } * vertesColorCSV.Close(); * } */ exportworker.ReportProgress(75, "Exporting terrain textures.."); if (bakeQuality != "none") { 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.Replace(" ", "")); mtlsw.WriteLine("Ka 1.000000 1.000000 1.000000"); mtlsw.WriteLine("Kd 0.640000 0.640000 0.640000"); mtlsw.WriteLine("map_Ka " + material.Value.Replace(" ", "") + ".png"); mtlsw.WriteLine("map_Kd " + material.Value.Replace(" ", "") + ".png"); } mtlsw.Close(); } exportworker.ReportProgress(85, "Exporting terrain geometry.."); var indices = indicelist.ToArray(); var adtname = Path.GetFileNameWithoutExtension(file); var objsw = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file).Replace(" ", "") + ".obj")); objsw.WriteLine("# Written by Marlamin's WoW OBJExporter. Original file: " + file); if (bakeQuality != "none") { objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(file).Replace(" ", "") + ".mtl"); } objsw.WriteLine("g " + adtname.Replace(" ", "")); var verticeCounter = 1; var chunkCounter = 1; foreach (var vertex in verticelist) { objsw.WriteLine("# C" + chunkCounter + ".V" + verticeCounter); objsw.WriteLine("v " + vertex.Position.X.ToString("R") + " " + vertex.Position.Y.ToString("R") + " " + vertex.Position.Z.ToString("R")); objsw.WriteLine("vt " + vertex.TexCoord.X + " " + vertex.TexCoord.Y); objsw.WriteLine("vn " + vertex.Normal.X.ToString("R") + " " + vertex.Normal.Y.ToString("R") + " " + vertex.Normal.Z.ToString("R")); verticeCounter++; if (verticeCounter == 146) { chunkCounter++; verticeCounter = 1; } } if (bakeQuality != "high") { objsw.WriteLine("usemtl " + materials[1]); objsw.WriteLine("s 1"); } int currentChunk = 0; foreach (var renderBatch in renderBatches) { var i = renderBatch.firstFace; if (bakeQuality == "high" && materials.ContainsKey((int)renderBatch.materialID)) { if (Managers.ConfigurationManager.ADTSplitChunks) { objsw.WriteLine("g " + materials[(int)renderBatch.materialID]); objsw.WriteLine("usemtl " + materials[(int)renderBatch.materialID]); objsw.WriteLine("s 1"); } else { objsw.WriteLine("usemtl " + materials[(int)renderBatch.materialID]); } } while (i < (renderBatch.firstFace + renderBatch.numFaces)) { objsw.WriteLine("f " + (indices[i + 2] + 1) + "/" + (indices[i + 2] + 1) + "/" + (indices[i + 2] + 1) + " " + (indices[i + 1] + 1) + "/" + (indices[i + 1] + 1) + "/" + (indices[i + 1] + 1) + " " + (indices[i] + 1) + "/" + (indices[i] + 1) + "/" + (indices[i] + 1)); i += 3; } currentChunk++; } objsw.Close(); }
public static void ExportWMO(string filename, string outdir, BackgroundWorker exportworker = null, string destinationOverride = null, ushort doodadSetExportID = ushort.MaxValue) { filename = filename.ToLower(); if (exportworker == null) { exportworker = new BackgroundWorker(); exportworker.WorkerReportsProgress = true; } exportworker.ReportProgress(5, "Reading WMO.."); var wmo = new WMOReader(); wmo.LoadWMO(filename); var customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone(); customCulture.NumberFormat.NumberDecimalSeparator = "."; System.Threading.Thread.CurrentThread.CurrentCulture = customCulture; exportworker.ReportProgress(30, "Reading WMO.."); uint totalVertices = 0; var groups = new Structs.WMOGroup[wmo.wmofile.group.Count()]; for (var g = 0; g < wmo.wmofile.group.Count(); g++) { if (wmo.wmofile.group[g].mogp.vertices == null) { continue; } for (var i = 0; i < wmo.wmofile.groupNames.Count(); i++) { if (wmo.wmofile.group[g].mogp.nameOffset == wmo.wmofile.groupNames[i].offset) { groups[g].name = wmo.wmofile.groupNames[i].name.Replace(" ", "_"); } } if (groups[g].name == "antiportal") { //Console.WriteLine("Group is antiportal"); continue; } groups[g].verticeOffset = totalVertices; groups[g].vertices = new Structs.Vertex[wmo.wmofile.group[g].mogp.vertices.Count()]; for (var i = 0; i < wmo.wmofile.group[g].mogp.vertices.Count(); i++) { groups[g].vertices[i].Position = new Structs.Vector3D() { X = wmo.wmofile.group[g].mogp.vertices[i].vector.X * -1, Y = wmo.wmofile.group[g].mogp.vertices[i].vector.Z, Z = wmo.wmofile.group[g].mogp.vertices[i].vector.Y }; groups[g].vertices[i].Normal = new Structs.Vector3D() { X = wmo.wmofile.group[g].mogp.normals[i].normal.X, Y = wmo.wmofile.group[g].mogp.normals[i].normal.Z, Z = wmo.wmofile.group[g].mogp.normals[i].normal.Y }; groups[g].vertices[i].TexCoord = new Structs.Vector2D() { X = wmo.wmofile.group[g].mogp.textureCoords[0][i].X, Y = wmo.wmofile.group[g].mogp.textureCoords[0][i].Y }; totalVertices++; } var indicelist = new List <uint>(); for (var i = 0; i < wmo.wmofile.group[g].mogp.indices.Count(); i++) { indicelist.Add(wmo.wmofile.group[g].mogp.indices[i].indice); } groups[g].indices = indicelist.ToArray(); } if (destinationOverride == null) { // Create output directory if (!string.IsNullOrEmpty(filename)) { if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(filename))); } } else { if (!Directory.Exists(outdir)) { Directory.CreateDirectory(outdir); } } } #region M2Export bool exportM2 = Managers.ConfigurationManager.WMOExportM2; if (exportM2) { StreamWriter doodadSW; if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { doodadSW = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename.Replace(" ", "")) + "_ModelPlacementInformation.csv")); } else { doodadSW = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(filename), filename + "_ModelPlacementInformation.csv")); } } else { if (!string.IsNullOrEmpty(filename)) { doodadSW = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileNameWithoutExtension(filename).Replace(" ", "") + "_ModelPlacementInformation.csv")); } else { doodadSW = new StreamWriter(Path.Combine(outdir, destinationOverride, filename + "_ModelPlacementInformation.csv")); } } exportworker.ReportProgress(55, "Exporting doodads.."); doodadSW.WriteLine("ModelFile;PositionX;PositionY;PositionZ;RotationW;RotationX;RotationY;RotationZ;ScaleFactor;DoodadSet"); for (var i = 0; i < wmo.wmofile.doodadSets.Count(); i++) { var doodadSet = wmo.wmofile.doodadSets[i]; var currentDoodadSetName = doodadSet.setName.Replace("Set_", "").Replace("SET_", "").Replace("$DefaultGlobal", "Default"); if (doodadSetExportID != ushort.MaxValue) { if (i != 0 && i != doodadSetExportID) { //Console.WriteLine("Skipping doodadset with ID " + i + " (" + currentDoodadSetName + ") because export filter is set to " + doodadSetExportID); continue; } } //Console.WriteLine("At doodadset " + i + " (" + currentDoodadSetName + ")"); for (var j = doodadSet.firstInstanceIndex; j < (doodadSet.firstInstanceIndex + doodadSet.numDoodads); j++) { foreach (var doodadNameEntry in wmo.wmofile.doodadNames) { var doodadDefinition = wmo.wmofile.doodadDefinitions[j]; if (doodadNameEntry.startOffset == doodadDefinition.offset) { var doodadFileName = doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2"); if (destinationOverride == null) { if (Managers.ConfigurationManager.WMODoodadsGlobalPath) { if (!File.Exists(Path.Combine(outdir, Path.GetFileName(doodadFileName.ToLower()).Replace(".m2", ".obj")))) { M2Exporter.ExportM2(doodadFileName, outdir, exportworker); } } else { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename), Path.GetFileName(doodadFileName.ToLower()).Replace(".m2", ".obj")))) { M2Exporter.ExportM2(doodadFileName, Path.Combine(outdir, Path.GetDirectoryName(filename)), exportworker); } } } else { if (!File.Exists(Path.Combine(destinationOverride, Path.GetFileName(doodadFileName.ToLower()).Replace(".m2", ".obj")))) { M2Exporter.ExportM2(doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2"), destinationOverride, exportworker); } } if (Managers.ConfigurationManager.WMODoodadsPlacementGlobalPath) { doodadSW.WriteLine(doodadNameEntry.filename.ToLower().Replace(".mdx", ".m2").Replace(".mdl", ".m2").Replace(".m2", ".obj;") + doodadDefinition.position.X.ToString("F09") + ";" + doodadDefinition.position.Y.ToString("F09") + ";" + doodadDefinition.position.Z.ToString("F09") + ";" + doodadDefinition.rotation.W.ToString("F15") + ";" + doodadDefinition.rotation.X.ToString("F15") + ";" + doodadDefinition.rotation.Y.ToString("F15") + ";" + doodadDefinition.rotation.Z.ToString("F15") + ";" + doodadDefinition.scale + ";" + currentDoodadSetName); } else { doodadSW.WriteLine(Path.GetFileNameWithoutExtension(doodadNameEntry.filename).ToLower() + ".obj;" + doodadDefinition.position.X.ToString("F09") + ";" + doodadDefinition.position.Y.ToString("F09") + ";" + doodadDefinition.position.Z.ToString("F09") + ";" + doodadDefinition.rotation.W.ToString("F15") + ";" + doodadDefinition.rotation.X.ToString("F15") + ";" + doodadDefinition.rotation.Y.ToString("F15") + ";" + doodadDefinition.rotation.Z.ToString("F15") + ";" + doodadDefinition.scale + ";" + currentDoodadSetName); } break; } } } } doodadSW.Close(); } #endregion var mtlsb = new StringBuilder(); var textureID = 0; if (wmo.wmofile.materials == null) { return; } var materials = new Structs.Material[wmo.wmofile.materials.Count()]; for (var i = 0; i < wmo.wmofile.materials.Count(); i++) { for (var ti = 0; ti < wmo.wmofile.textures.Count(); ti++) { if (wmo.wmofile.textures[ti].startOffset == wmo.wmofile.materials[i].texture1) { materials[i].textureID = textureID + i; materials[i].filename = Path.GetFileNameWithoutExtension(wmo.wmofile.textures[ti].filename); if (wmo.wmofile.materials[i].blendMode == 0) { materials[i].transparent = false; } else { materials[i].transparent = true; } if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename), materials[i].filename + ".png"))) { var blpreader = new BLPReader(); blpreader.LoadBLP(Managers.ArchiveManager.ReadThisFile(wmo.wmofile.textures[ti].filename)); try { if (destinationOverride == null) { blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(filename), materials[i].filename + ".png")); } else { blpreader.bmp.Save(Path.Combine(outdir, destinationOverride, materials[i].filename.ToLower() + ".png")); } } catch { //Error on file save } } textureID++; } } } //No idea how MTL files really work yet. Needs more investigation. foreach (var material in materials) { mtlsb.Append("newmtl " + material.filename + "\n"); mtlsb.Append("Ns 96.078431\n"); mtlsb.Append("Ka 1.000000 1.000000 1.000000\n"); mtlsb.Append("Kd 0.640000 0.640000 0.640000\n"); mtlsb.Append("Ks 0.000000 0.000000 0.000000\n"); mtlsb.Append("Ke 0.000000 0.000000 0.000000\n"); mtlsb.Append("Ni 1.000000\n"); mtlsb.Append("d 1.000000\n"); mtlsb.Append("illum 1\n"); mtlsb.Append("map_Kd " + material.filename + ".png\n"); if (material.transparent) { mtlsb.Append("map_d " + material.filename + ".png\n"); } /* //temporary removed * if (ConfigurationManager.AppSettings["textureMetadata"] == "True") * { * mtlsb.Append("blend " + material.blendMode + "\n"); * mtlsb.Append("shader " + material.shaderID + "\n"); * mtlsb.Append("terrain " + material.terrainType + "\n"); * } */ } if (!string.IsNullOrEmpty(filename)) { if (destinationOverride == null) { File.WriteAllText(Path.Combine(outdir, filename.Replace(".wmo", ".mtl")), mtlsb.ToString()); } else { File.WriteAllText(Path.Combine(outdir, destinationOverride, Path.GetFileName(filename.ToLower()).Replace(".wmo", ".mtl")), mtlsb.ToString()); } } else { if (destinationOverride == null) { File.WriteAllText(Path.Combine(outdir, filename + ".mtl"), mtlsb.ToString()); } else { File.WriteAllText(Path.Combine(outdir, destinationOverride, filename + ".mtl"), mtlsb.ToString()); } } exportworker.ReportProgress(75, "Exporting model.."); var numRenderbatches = 0; //Get total amount of render batches for (var i = 0; i < wmo.wmofile.group.Count(); i++) { if (wmo.wmofile.group[i].mogp.renderBatches == null) { continue; } numRenderbatches = numRenderbatches + wmo.wmofile.group[i].mogp.renderBatches.Count(); } var rb = 0; for (var g = 0; g < wmo.wmofile.group.Count(); g++) { groups[g].renderBatches = new Structs.RenderBatch[numRenderbatches]; var group = wmo.wmofile.group[g]; if (group.mogp.renderBatches == null) { continue; } for (var i = 0; i < group.mogp.renderBatches.Count(); i++) { var batch = group.mogp.renderBatches[i]; groups[g].renderBatches[rb].firstFace = batch.firstFace; groups[g].renderBatches[rb].numFaces = batch.numFaces; if (batch.flags == 2) { groups[g].renderBatches[rb].materialID = (uint)batch.possibleBox2_3; } else { groups[g].renderBatches[rb].materialID = batch.materialID; } groups[g].renderBatches[rb].blendType = wmo.wmofile.materials[batch.materialID].blendMode; groups[g].renderBatches[rb].groupID = (uint)g; rb++; } } exportworker.ReportProgress(95, "Writing files.."); StreamWriter objsw; if (!string.IsNullOrEmpty(filename)) { if (destinationOverride == null) { objsw = new StreamWriter(Path.Combine(outdir, filename.Replace(".wmo", ".obj"))); } else { objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileName(filename.ToLower()).Replace(".wmo", ".obj"))); } objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original file: " + filename); objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(filename) + ".mtl"); } else { if (destinationOverride == null) { objsw = new StreamWriter(Path.Combine(outdir, filename + ".obj")); } else { objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, filename + ".obj")); } objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original file id: " + filename); objsw.WriteLine("mtllib " + filename + ".mtl"); } foreach (var group in groups) { if (group.vertices == null) { continue; } //Console.WriteLine("Writing " + group.name); objsw.WriteLine("o " + group.name); //Added thunderysteak's adjustment (original commit: ed067c7c6e8321c33ef0f3679d33c9c472dcefc3) foreach (var vertex in group.vertices) { objsw.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z); } foreach (var vertex in group.vertices) { objsw.WriteLine("vt " + vertex.TexCoord.X + " " + (vertex.TexCoord.Y - 1) * -1); } foreach (var vertex in group.vertices) { objsw.WriteLine("vn " + (-vertex.Normal.X).ToString("F12") + " " + vertex.Normal.Y.ToString("F12") + " " + vertex.Normal.Z.ToString("F12")); } var indices = group.indices; foreach (var renderbatch in group.renderBatches) { var i = renderbatch.firstFace; if (renderbatch.numFaces > 0) { //thunderysteak's adjustment //objsw.WriteLine("o " + group.name); //? objsw.WriteLine("g " + group.name);//3DS Max's OBJ importer fails with invalid normal index without groups being defined //-------------------------- objsw.WriteLine("usemtl " + materials[renderbatch.materialID].filename); objsw.WriteLine("s 1"); while (i < (renderbatch.firstFace + renderbatch.numFaces)) { objsw.WriteLine("f " + (indices[i] + group.verticeOffset + 1) + "/" + (indices[i] + group.verticeOffset + 1) + "/" + (indices[i] + group.verticeOffset + 1) + " " + (indices[i + 1] + group.verticeOffset + 1) + "/" + (indices[i + 1] + group.verticeOffset + 1) + "/" + (indices[i + 1] + group.verticeOffset + 1) + " " + (indices[i + 2] + group.verticeOffset + 1) + "/" + (indices[i + 2] + group.verticeOffset + 1) + "/" + (indices[i + 2] + group.verticeOffset + 1)); i += 3; } } } } objsw.Close(); //Console.WriteLine("Done loading WMO file!"); }