private static void Main(string[] args) { CASC.InitCasc(null, @"C:\World of Warcraft", "wow"); Console.WriteLine("CASC loaded!"); var reader = new M2Reader(); var filename = @"item\objectcomponents\shield\shield_1h_artifactnorgannon_d_01.m2"; reader.LoadM2(filename); var fileDataID = CASC.getFileDataIdByName(filename); for (int i = 0; i < reader.model.textures.Length; i++) { Console.WriteLine("Doing type " + reader.model.textures[i].type + " lookup for texture #" + i); switch (reader.model.textures[i].type) { case 1: case 2: case 11: uint[] cdifilenames = DBCHelper.getTexturesByModelFilename(fileDataID, (int)reader.model.textures[i].type); for (int ti = 0; ti < cdifilenames.Length; ti++) { Console.WriteLine("Found (texture #" + ti + ") " + cdifilenames[ti]); var blpreader = new BLPReader(); blpreader.LoadBLP((int)cdifilenames[ti]); } break; default: throw new Exception("Unhandled texture type"); break; } } Console.WriteLine("Done."); Console.ReadLine(); }
private void LoadTile(int[] tile) { var x = tile[0]; var y = tile[1]; string _SelectedMapName = _map; Rectangle rect = new Rectangle(); rect.Name = _SelectedMapName.Replace("'", string.Empty).Replace(" ", string.Empty) + "_" + x.ToString("D2") + "_" + y.ToString("D2"); //leading zeros just like adts (TODO: NOT REALLY), this breaks when the mapname has special characters (zg)D: rect.Width = WDTGrid.Width / 64; rect.Height = WDTGrid.Height / 64; rect.VerticalAlignment = VerticalAlignment.Top; rect.HorizontalAlignment = HorizontalAlignment.Left; rect.MouseEnter += Rect_MouseEnter; rect.MouseLeave += Rect_MouseLeave; if (CASC.cascHandler.FileExists(System.IO.Path.Combine(@"world\minimaps\" + _SelectedMapName + "\\map" + x.ToString("D2") + "_" + y.ToString("D2") + ".blp"))) { rect.MouseLeftButtonDown += new MouseButtonEventHandler(Rectangle_Mousedown); var xmargin = (x * rect.Width) - (min_x * rect.Width); var ymargin = (y * rect.Height) - (min_y * rect.Height); rect.Margin = new Thickness(xmargin, ymargin, 0, 0); var blp = new BLPReader(); blp.LoadBLP(@"world\minimaps\" + _SelectedMapName + "\\map" + x.ToString("D2") + "_" + y.ToString("D2") + ".blp"); BitmapImage bitmapImage = new BitmapImage(); using (MemoryStream bitmap = blp.asBitmapStream()) { bitmapImage.BeginInit(); bitmapImage.StreamSource = bitmap; bitmapImage.DecodePixelHeight = Convert.ToInt32(rect.Width); bitmapImage.DecodePixelWidth = Convert.ToInt32(rect.Height); bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); } ImageBrush imgBrush = new ImageBrush(bitmapImage); rect.Fill = imgBrush; } else { var xmargin = (x * rect.Width) - (min_x * rect.Width); var ymargin = (y * rect.Height) - (min_y * rect.Height); rect.Margin = new Thickness(xmargin, ymargin, 0, 0); rect.Fill = new SolidColorBrush(Color.FromRgb(0, 0, 0)); } WDTGrid.Children.Add(rect); }
public static void ExportBLP(string filename, string outdir) { BLPReader reader = new BLPReader(); if (!File.Exists(Path.Combine(outdir, filename.Replace(".blp", ".png")))) { if (!string.IsNullOrEmpty(filename)) { if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(filename))); } reader.LoadBLP(Managers.ArchiveManager.ReadThisFile(filename)); reader.bmp.Save(Path.Combine(outdir, filename.Replace(".blp", ".png"))); } } }
public void LoadWMO() { WMOReader reader = new WMOReader(basedir); reader.LoadWMO(modelPath); WMOMaterial[] materials = new WMOMaterial[reader.wmofile.materials.Count()]; for (int i = 0; i < reader.wmofile.materials.Count(); i++) { for (int ti = 0; ti < reader.wmofile.textures.Count(); ti++) { if (reader.wmofile.textures[ti].startOffset == reader.wmofile.materials[i].texture1) { Texture2D texture; var blp = new BLPReader(basedir); blp.LoadBLP(reader.wmofile.textures[ti].filename); if (blp.bmp == null) { texture = Texture2D.FromFile <Texture2D>(device, "missingtexture.jpg"); } else { MemoryStream s = new MemoryStream(); blp.bmp.Save(s, System.Drawing.Imaging.ImageFormat.Png); s.Seek(0, SeekOrigin.Begin); texture = Texture2D.FromMemory <Texture2D>(device, s.ToArray()); s.Dispose(); } materials[i].materialID = (uint)i; materials[i].filename = reader.wmofile.textures[ti].filename; materials[i].texture = texture; } } } WoWWMOGroup[] groups = new WoWWMOGroup[reader.wmofile.header.nGroups]; for (int i = 0; i < reader.wmofile.header.nGroups; i++) { groups[i] = LoadGroupWMO(reader.wmofile.group[i]); } wmo.materials = materials; wmo.groups = groups; }
public static int LoadTexture(string filename, CacheStorage cache) { GL.ActiveTexture(TextureUnit.Texture0); filename = filename.ToLower(); if (cache.materials.ContainsKey(filename)) { // Console.WriteLine("[CACHE HIT] " + filename); return(cache.materials[filename]); } //Console.WriteLine("[CACHE MISS] " + filename); int textureId = GL.GenTexture(); var blp = new BLPReader(); blp.LoadBLP(filename); if (blp.bmp == null) { throw new Exception("BMP is null!"); } else { GL.BindTexture(TextureTarget.Texture2D, textureId); cache.materials.Add(filename, textureId); System.Drawing.Imaging.BitmapData bmp_data = blp.bmp.LockBits(new System.Drawing.Rectangle(0, 0, blp.bmp.Width, blp.bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bmp_data.Width, bmp_data.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bmp_data.Scan0); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); blp.bmp.UnlockBits(bmp_data); } // Console.WriteLine("[CACHE ADD] " + filename); return(textureId); }
private void LoadTile(string basedir, int[] tile) { var x = tile[0]; var y = tile[1]; string _SelectedMapName = ((KeyValuePair <int, string>)MapListBox.SelectedValue).Value; Rectangle rect = new Rectangle(); rect.Name = _SelectedMapName.Replace("'", string.Empty).Replace(" ", string.Empty) + x.ToString("D2") + "_" + y.ToString("D2"); //leading zeros just like adts, this breaks when the mapname has special characters (zg)D: rect.Width = WDTGrid.Width / 64; rect.Height = WDTGrid.Height / 64; rect.VerticalAlignment = VerticalAlignment.Top; rect.HorizontalAlignment = HorizontalAlignment.Left; if (File.Exists(basedir + "World\\Minimaps\\" + _SelectedMapName + "\\map" + x.ToString("D2") + "_" + y.ToString("D2") + ".blp")) { rect.MouseLeftButtonDown += new MouseButtonEventHandler(Rectangle_Mousedown); var xmargin = x * rect.Width; var ymargin = y * rect.Height; rect.Margin = new Thickness(xmargin, ymargin, 0, 0); var blp = new BLPReader(basedir); //Kalimdor takes a few seconds to load, and takes up about ~4xxMB of memory after its loaded, this can be much improved blp.LoadBLP("World\\Minimaps\\" + _SelectedMapName + "\\map" + x.ToString("D2") + "_" + y.ToString("D2") + ".blp"); BitmapImage bitmapImage = new BitmapImage(); using (MemoryStream bitmap = blp.asBitmapStream()) { bitmapImage.BeginInit(); bitmapImage.StreamSource = bitmap; bitmapImage.DecodePixelHeight = Convert.ToInt32(rect.Width); bitmapImage.DecodePixelWidth = Convert.ToInt32(rect.Height); bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); } ImageBrush imgBrush = new ImageBrush(bitmapImage); rect.Fill = imgBrush; } else { rect.Fill = new SolidColorBrush(Color.FromRgb(0, 111, 0)); Console.WriteLine(basedir + "World\\Minimaps\\" + _SelectedMapName + "\\map" + x.ToString("D2") + "_" + y.ToString("D2") + ".blp"); } WDTGrid.Children.Add(rect); }
private Bitmap CompileGroup(string filename, string groupid) { //Console.WriteLine(" group " + groupid); int min_x = 64; int min_y = 64; int max_x = 0; int max_y = 0; int x = 0; int y = 0; var wmoonlyname = Path.GetFileNameWithoutExtension(filename); var wmodir = filename.Replace("world/", "world/minimaps/").Replace(wmoonlyname + ".wmo", ""); var filePaths = new List <(uint FileDataID, string GroupFileName)>(); uint lastpath = 0; var hasMinimaps = false; for (int cur_x = 0; cur_x < 64; cur_x++) { for (int cur_y = 0; cur_y < 64; cur_y++) { string wmogroupfilename = wmodir + wmoonlyname + "_" + groupid + "_" + cur_x.ToString().PadLeft(2, '0') + "_" + cur_y.ToString().PadLeft(2, '0') + ".blp"; if (wmogroupfilename.Contains("000_00_00")) { //Console.WriteLine("CHECKING " + wmogroupfilename); } if (Listfile.nameToFDIDMap.TryGetValue(wmogroupfilename, out var groupFileDataID)) { if (CASC.FileExists(groupFileDataID)) { //Console.WriteLine(wmogroupfilename + " exists!"); filePaths.Add((groupFileDataID, wmogroupfilename)); hasMinimaps = true; } } else { //Console.WriteLine("Group filename " + wmogroupfilename + " could not be found in the listfile"); } } } foreach ((uint fdid, string path) in filePaths) { Console.WriteLine(path); x = int.Parse(path.Substring(path.Length - 9, 2)); y = int.Parse(path.Substring(path.Length - 6, 2)); 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; } // Console.WriteLine("[" + groupid + "] MIN: " + min_x + " " + min_y); // Console.WriteLine("[" + groupid + "] MAX: " + max_x + " " + max_y); lastpath = fdid; } var res_x = 0; var res_y = 0; if (min_x == 0 && max_x == 0 && min_y == 0 && max_y == 0) { var blpreader = new BLPReader(); blpreader.LoadBLP(lastpath); res_x = blpreader.bmp.Width; res_y = blpreader.bmp.Height; } else { res_x = (((max_x - min_x) * 256) + 256); res_y = (((max_y - min_y) * 256) + 256); } //Console.WriteLine("[" + groupid + "] " + "Creating new image of " + res_x + "x" + res_y + " for " + wmoname); if (res_x < 0) { res_x = 1; } if (res_y < 0) { res_y = 1; } if (!hasMinimaps) { throw new FileNotFoundException("No minimaps found for this WMO"); } var bmp = new Bitmap(res_x, res_y); var g = Graphics.FromImage(bmp); foreach ((uint fdid, string path) in filePaths) { x = int.Parse(path.Substring(path.Length - 9, 2)); y = int.Parse(path.Substring(path.Length - 6, 2)); var blpreader = new BLPReader(); blpreader.LoadBLP(fdid); // Console.WriteLine("BLP Width: " + blpreader.bmp.Width); // Console.WriteLine("BLP Height: " + blpreader.bmp.Height); var draw_x = (x - min_x) * 256; var draw_y = (max_y - (y - min_y)) * 256; //Console.WriteLine("Drawing tile at " + draw_x + " & " + draw_y); g.DrawImage(blpreader.bmp, draw_x, draw_y, new Rectangle(0, 0, blpreader.bmp.Width, blpreader.bmp.Height), GraphicsUnit.Pixel); } g.Dispose(); return(bmp); }
public static void exportWMO(string file, BackgroundWorker exportworker = null) { if (exportworker == null) { exportworker = new BackgroundWorker(); } Console.WriteLine("Loading WMO file.."); exportworker.ReportProgress(5, "Reading WMO.."); var outdir = ConfigurationManager.AppSettings["outdir"]; WMOReader reader = new WMOReader(); reader.LoadWMO(file); // TODO: Support doodads! /* * for (int i = 0; i < reader.wmofile.doodadNames.Count(); i++) * { * //Console.WriteLine(reader.wmofile.doodadNames[i].filename); * //reader.wmofile.doodadDefinitions[i]. * //reader.wmofile.doodadDefinitions[i]. * } */ exportworker.ReportProgress(30, "Reading WMO.."); uint totalVertices = 0; var groups = new Structs.WMOGroup[reader.wmofile.group.Count()]; for (int g = 0; g < reader.wmofile.group.Count(); g++) { Console.WriteLine("Loading group #" + g); if (reader.wmofile.group[g].mogp.vertices == null) { Console.WriteLine("Group has no vertices!"); continue; } for (int i = 0; i < reader.wmofile.groupNames.Count(); i++) { if (reader.wmofile.group[g].mogp.nameOffset == reader.wmofile.groupNames[i].offset) { groups[g].name = reader.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[reader.wmofile.group[g].mogp.vertices.Count()]; for (int i = 0; i < reader.wmofile.group[g].mogp.vertices.Count(); i++) { groups[g].vertices[i].Position = new Vector3(reader.wmofile.group[g].mogp.vertices[i].vector.X * -1, reader.wmofile.group[g].mogp.vertices[i].vector.Z, reader.wmofile.group[g].mogp.vertices[i].vector.Y); groups[g].vertices[i].Normal = new Vector3(reader.wmofile.group[g].mogp.normals[i].normal.X, reader.wmofile.group[g].mogp.normals[i].normal.Z, reader.wmofile.group[g].mogp.normals[i].normal.Y); groups[g].vertices[i].TexCoord = new Vector2(reader.wmofile.group[g].mogp.textureCoords[0][i].X, reader.wmofile.group[g].mogp.textureCoords[0][i].Y); totalVertices++; } var indicelist = new List <uint>(); for (int i = 0; i < reader.wmofile.group[g].mogp.indices.Count(); i++) { indicelist.Add(reader.wmofile.group[g].mogp.indices[i].indice); } groups[g].indices = indicelist.ToArray(); } exportworker.ReportProgress(55, "Exporting textures.."); // Create output directory if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file))); } var mtlsb = new StringBuilder(); var textureID = 0; if (reader.wmofile.materials == null) { Console.WriteLine("Materials empty"); return; } var materials = new Structs.Material[reader.wmofile.materials.Count()]; for (int i = 0; i < reader.wmofile.materials.Count(); i++) { for (int ti = 0; ti < reader.wmofile.textures.Count(); ti++) { if (reader.wmofile.textures[ti].startOffset == reader.wmofile.materials[i].texture1) { //materials[i].textureID = BLPLoader.LoadTexture(reader.wmofile.textures[ti].filename, cache); materials[i].textureID = textureID + i; materials[i].filename = Path.GetFileNameWithoutExtension(reader.wmofile.textures[ti].filename); if (reader.wmofile.materials[i].blendMode == 0) { materials[i].transparent = false; } else { materials[i].transparent = true; } if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), materials[i].filename + ".png"))) { var blpreader = new BLPReader(); blpreader.LoadBLP(reader.wmofile.textures[ti].filename); try { blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file), materials[i].filename + ".png")); } catch (Exception e) { Console.WriteLine(e.Message); } } 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 2\n"); mtlsb.Append("map_Kd " + material.filename + ".png\n"); if (material.transparent) { mtlsb.Append("map_d " + material.filename + ".png\n"); } } File.WriteAllText(Path.Combine(outdir, file.Replace(".wmo", ".mtl")), mtlsb.ToString()); exportworker.ReportProgress(75, "Exporting model.."); int numRenderbatches = 0; //Get total amount of render batches for (int i = 0; i < reader.wmofile.group.Count(); i++) { if (reader.wmofile.group[i].mogp.renderBatches == null) { continue; } numRenderbatches = numRenderbatches + reader.wmofile.group[i].mogp.renderBatches.Count(); } int rb = 0; for (int g = 0; g < reader.wmofile.group.Count(); g++) { groups[g].renderBatches = new Structs.RenderBatch[numRenderbatches]; var group = reader.wmofile.group[g]; if (group.mogp.renderBatches == null) { continue; } for (int 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 = reader.wmofile.materials[batch.materialID].blendMode; groups[g].renderBatches[rb].groupID = (uint)g; rb++; } } exportworker.ReportProgress(95, "Writing files.."); var objsw = new StreamWriter(Path.Combine(outdir, file.Replace(".wmo", ".obj"))); objsw.WriteLine("# Written by Marlamin's WoW OBJExporter. Original file: " + file); objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(file) + ".mtl"); foreach (var group in groups) { if (group.vertices == null) { continue; } Console.WriteLine("Writing " + group.name); objsw.WriteLine("g " + group.name); foreach (var vertex in group.vertices) { 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); } var indices = group.indices; foreach (var renderbatch in group.renderBatches) { var i = renderbatch.firstFace; if (renderbatch.numFaces > 0) { 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 = i + 3; } } } } objsw.Close(); Console.WriteLine("Done loading WMO file!"); }
public static void exportADT(string file, BackgroundWorker exportworker = null) { //if (exportworker == null) //{ // exportworker = new BackgroundWorker(); // } //CASC.InitCasc(null, @"C:\World of Warcraft Beta", "wow_beta"); 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 + "_" + x + "_" + y + ".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); } } else { throw new Exception("This map has no maptextures. Try a different map."); } //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 + 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 = i + 3; } } objsw.Close(); }
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { if (treeView1.SelectedNode.FullPath.EndsWith(".blp")) { try { BLPReader reader = new BLPReader(); reader.LoadBLP(Managers.ArchiveManager.ReadThisFile(treeView1.SelectedNode.FullPath.Replace(@"root\", ""))); pictureBox1.Image = reader.bmp; } catch { //Console.WriteLine("Error occured while trying to read " + treeView1.SelectedNode.FullPath.Replace(@"root\", "")); } } if (treeView1.SelectedNode.FullPath.EndsWith(".adt")) { try { if (Managers.ConfigurationManager.Profile >= 4) // Cata and above { string filedirectory = treeView1.SelectedNode.FullPath.Replace("\\maps\\", "\\minimaps\\"); filedirectory = filedirectory.Substring(0, filedirectory.LastIndexOf("\\") + 1); filedirectory = filedirectory.Substring(5, filedirectory.Length - 5); string filename = Path.GetFileNameWithoutExtension(treeView1.SelectedNode.FullPath); filename = filename.Replace(filename.Substring(0, filename.IndexOf("_") + 1), "map") + ".blp"; //Console.WriteLine(filedirectory + filename); try { BLPReader reader = new BLPReader(); if (Managers.ArchiveManager.FileExists(filedirectory + filename)) { reader.LoadBLP(Managers.ArchiveManager.ReadThisFile(filedirectory + filename)); pictureBox1.Image = reader.bmp; } } catch { } } else { string filename = treeView1.SelectedNode.FullPath; try { BLPReader reader = new BLPReader(); if (Managers.ArchiveManager.FileExists(Managers.md5Manager.TranslateThisMap(filename))) { reader.LoadBLP(Managers.ArchiveManager.ReadThisFile(Managers.md5Manager.TranslateThisMap(filename))); pictureBox1.Image = reader.bmp; } } catch { } } } catch { } } }
public static void exportADT(string file, BackgroundWorker exportworker = null) { if (exportworker == null) { exportworker = new BackgroundWorker(); exportworker.WorkerReportsProgress = true; } //CASC.InitCasc(null, @"C:\World of Warcraft Beta", "wow_beta"); var outdir = ConfigurationManager.AppSettings["outdir"]; System.Globalization.CultureInfo customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone(); customCulture.NumberFormat.NumberDecimalSeparator = "."; System.Threading.Thread.CurrentThread.CurrentCulture = customCulture; 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 + "_" + x + "_" + y + ".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); } } else { Console.WriteLine("No maptextures, this map will have missing textures."); } //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)); // var bmp = new System.Drawing.Bitmap(64, 64); // var data = bmp.LockBits(new System.Drawing.Rectangle(0, 0, 64, 64), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb); // int stride = data.Stride; // var values = reader.adtfile.texChunks[c].alphaLayer[li].layer; // unsafe // { // byte* ptr = (byte*)data.Scan0; // for (int ax = 0; ax < 64; ax++) // { // for (int ay = 0; ay < 64; ay++) // { // var color = System.Drawing.Color.FromArgb(values[ax * 64 + ay], values[ax * 64 + ay], values[ax * 64 + ay], values[ax * 64 + ay]); // ptr[(y * 4) + x * stride] = color.B; // ptr[(y * 4) + x * stride + 1] = color.G; // ptr[(y * 4) + x * stride + 2] = color.R; // ptr[(y * 4) + x * stride + 3] = color.A; // } // } // } // bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file).Replace(" ", "") + "_" + c + "_" + li + ".png")); //} } renderBatches.Add(batch); } 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"); exportworker.ReportProgress(25, "Exporting WMOs"); for (int mi = 0; mi < reader.adtfile.objects.worldModels.entries.Count(); mi++) { var wmo = reader.adtfile.objects.worldModels.entries[mi]; var filename = reader.adtfile.objects.wmoNames.filenames[wmo.mwidEntry]; if (!File.Exists(Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj")) { WMOExporter.exportWMO(filename, null, Path.Combine(outdir, Path.GetDirectoryName(file))); } 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 + ";;" + wmo.uniqueId); } exportworker.ReportProgress(50, "Exporting M2s"); for (int mi = 0; mi < reader.adtfile.objects.models.entries.Count(); mi++) { var doodad = reader.adtfile.objects.models.entries[mi]; var filename = reader.adtfile.objects.m2Names.filenames[doodad.mmidEntry]; if (!File.Exists(Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj")) { M2Exporter.exportM2(filename, null, Path.Combine(outdir, Path.GetDirectoryName(file))); } 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); } doodadSW.Close(); } } exportworker.ReportProgress(75, "Exporting terrain textures.."); 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(); exportworker.ReportProgress(85, "Exporting terrain geometry.."); 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 + 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 = i + 3; } } objsw.Close(); }
public static void ExportM2(string filename, string outdir, BackgroundWorker exportworker = null, string destinationOverride = null) { if (exportworker == null) { exportworker = new BackgroundWorker { WorkerReportsProgress = true }; } filename = filename.ToLower(); var customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone(); customCulture.NumberFormat.NumberDecimalSeparator = "."; System.Threading.Thread.CurrentThread.CurrentCulture = customCulture; var reader = new M2Reader(); exportworker.ReportProgress(15, "Reading M2.."); //If the missing file is an ".mdx", try to look for a ".m2" alternative if (!Managers.ArchiveManager.FileExists(filename)) { if (Path.GetExtension(filename) == ".mdx") { if (!Managers.ArchiveManager.FileExists(filename.Replace(".mdx", ".m2"))) { throw new Exception("404 M2 not found!"); } else { filename = filename.Replace(".mdx", ".m2"); } } else { throw new Exception("404 M2 not found!"); } } reader.LoadM2(filename); // Don't export models without vertices if (reader.model.vertices.Count() == 0) { return; } var vertices = new Structs.Vertex[reader.model.vertices.Count()]; for (var i = 0; i < reader.model.vertices.Count(); i++) { vertices[i].Position = new Structs.Vector3D() { X = reader.model.vertices[i].position.X, Y = reader.model.vertices[i].position.Z, Z = reader.model.vertices[i].position.Y * -1 }; vertices[i].Normal = new Structs.Vector3D() { X = reader.model.vertices[i].normal.X, Y = reader.model.vertices[i].normal.Z, Z = reader.model.vertices[i].normal.Y }; vertices[i].TexCoord = new Structs.Vector2D() { X = reader.model.vertices[i].textureCoordX, Y = reader.model.vertices[i].textureCoordY }; } StreamWriter objsw; if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(filename))); } objsw = new StreamWriter(Path.Combine(outdir, filename.Replace(".m2", ".obj"))); } else { if (!Directory.Exists(outdir)) { Directory.CreateDirectory(outdir); } objsw = new StreamWriter(Path.Combine(outdir, filename + ".obj")); } } else { if (!string.IsNullOrEmpty(filename)) { objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileName(filename.ToLower()).Replace(".m2", ".obj"))); } else { objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, filename + ".obj")); } } if (!string.IsNullOrEmpty(filename)) { objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original file: " + filename); objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(filename) + ".mtl"); } else { objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original fileDataID: " + filename); objsw.WriteLine("mtllib " + filename + ".mtl"); } //Added thunderysteak's adjustment (original commit: ed067c7c6e8321c33ef0f3679d33c9c472dcefc3) foreach (var vertex in vertices) { objsw.WriteLine("v " + -vertex.Position.X + " " + vertex.Position.Y + " " + -vertex.Position.Z); } foreach (var vertex in vertices) { objsw.WriteLine("vt " + vertex.TexCoord.X + " " + (vertex.TexCoord.Y - 1) * -1); } foreach (var vertex in vertices) { objsw.WriteLine("vn " + (-vertex.Normal.X).ToString("F12") + " " + vertex.Normal.Y.ToString("F12") + " " + vertex.Normal.Z.ToString("F12")); } var indicelist = new List <uint>(); for (var i = 0; i < reader.model.skins[0].triangles.Count(); i++) { var t = reader.model.skins[0].triangles[i]; indicelist.Add(t.pt1); indicelist.Add(t.pt2); indicelist.Add(t.pt3); } var indices = indicelist.ToArray(); exportworker.ReportProgress(35, "Writing files.."); var renderbatches = new Structs.RenderBatch[reader.model.skins[0].submeshes.Count()]; for (var i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { renderbatches[i].firstFace = reader.model.skins[0].submeshes[i].startTriangle; renderbatches[i].numFaces = reader.model.skins[0].submeshes[i].nTriangles; renderbatches[i].groupID = (uint)i; for (var tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++) { if (reader.model.skins[0].textureunit[tu].submeshIndex == i) { renderbatches[i].blendType = reader.model.renderflags[reader.model.skins[0].textureunit[tu].renderFlags].blendingMode; renderbatches[i].materialID = reader.model.texlookup[reader.model.skins[0].textureunit[tu].texture].textureID; } } } exportworker.ReportProgress(65, "Exporting textures.."); StreamWriter mtlsb; if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { mtlsb = new StreamWriter(Path.Combine(outdir, filename.Replace(".m2", ".mtl"))); } else { mtlsb = new StreamWriter(Path.Combine(outdir, filename + ".mtl")); } } else { if (!string.IsNullOrEmpty(filename)) { mtlsb = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileName(filename.ToLower()).Replace(".m2", ".mtl"))); } else { mtlsb = new StreamWriter(Path.Combine(outdir, destinationOverride, filename + ".mtl")); } } var textureID = 0; var materials = new Structs.Material[reader.model.textures.Count()]; for (var i = 0; i < reader.model.textures.Count(); i++) { //uint textureFileDataID = 840426; string textureUsed = @"Test\QA_TEST_BLP_1.blp"; //Placeholder materials[i].flags = reader.model.textures[i].flags; switch (reader.model.textures[i].type) { case 0: textureUsed = reader.model.textures[i].filename; break; case 1: case 2: case 11: default: //Falling back to placeholder texture break; } materials[i].textureID = textureID + i; materials[i].filename = Path.GetFileNameWithoutExtension(textureUsed); string textureSaveLocation; if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { textureSaveLocation = Path.Combine(outdir, Path.GetDirectoryName(filename), materials[i].filename + ".png"); } else { textureSaveLocation = Path.Combine(outdir, materials[i].filename + ".png"); } } else { textureSaveLocation = Path.Combine(outdir, destinationOverride, materials[i].filename + ".png"); } try { var blpreader = new BLPReader(); blpreader.LoadBLP(Managers.ArchiveManager.ReadThisFile(textureUsed)); blpreader.bmp.Save(textureSaveLocation); } catch { //Error on file save } } exportworker.ReportProgress(85, "Writing files.."); foreach (var material in materials) { mtlsb.WriteLine("newmtl " + material.filename); mtlsb.WriteLine("illum 1"); mtlsb.WriteLine("map_Kd " + material.filename + ".png"); } mtlsb.Close(); if (!string.IsNullOrEmpty(filename)) { objsw.WriteLine("g " + Path.GetFileNameWithoutExtension(filename)); } else { objsw.WriteLine("g " + filename); } foreach (var renderbatch in renderbatches) { var i = renderbatch.firstFace; if (!string.IsNullOrEmpty(filename)) { objsw.WriteLine("o " + Path.GetFileNameWithoutExtension(filename) + renderbatch.groupID); } else { objsw.WriteLine("g " + filename + renderbatch.groupID.ToString()); } objsw.WriteLine("usemtl " + materials[renderbatch.materialID].filename); 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 += 3; } } objsw.Close(); //COLLISION MODEL -- not implemented /* * // Only export phys when exporting a single M2, causes issues for some users when combined with WMO/ADT * if (destinationOverride == null) * { * //exportworker.ReportProgress(90, "Exporting collision.."); * * if (!string.IsNullOrEmpty(filename)) * { * objsw = new StreamWriter(Path.Combine(outdir, Path.GetFileName(filename.ToLower()).Replace(".m2", ".phys.obj"))); * } * else * { * //objsw = new StreamWriter(Path.Combine(outdir, fileDataID + ".phys.obj")); * objsw = new StreamWriter(Path.Combine(outdir, filename + ".phys.obj")); * } * * //objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original file id: " + fileDataID); * objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original file id: " + filename); * * for (var i = 0; i < reader.model.boundingvertices.Count(); i++) * { * objsw.WriteLine("v " + * reader.model.boundingvertices[i].vertex.X + " " + * reader.model.boundingvertices[i].vertex.Z + " " + * -reader.model.boundingvertices[i].vertex.Y); * } * * for (var i = 0; i < reader.model.boundingtriangles.Count(); i++) * { * var t = reader.model.boundingtriangles[i]; * objsw.WriteLine("f " + (t.index_0 + 1) + " " + (t.index_1 + 1) + " " + (t.index_2 + 1)); * } * * objsw.Close(); * } */ // https://en.wikipedia.org/wiki/Wavefront_.obj_file#Basic_materials // http://wiki.unity3d.com/index.php?title=ExportOBJ // http://web.cse.ohio-state.edu/~hwshen/581/Site/Lab3_files/Labhelp_Obj_parser.htm }
public static void ExportWMO(uint filedataid, BackgroundWorker exportworker = null, string destinationOverride = null, ushort doodadSetExportID = ushort.MaxValue, string filename = "") { if (exportworker == null) { exportworker = new BackgroundWorker(); exportworker.WorkerReportsProgress = true; } if (string.IsNullOrEmpty(filename)) { if (!Listfile.TryGetFilename(filedataid, out filename)) { CASCLib.Logger.WriteLine("Warning! Could not find filename for " + filedataid + "!"); } } Console.WriteLine("Loading WMO file.."); exportworker.ReportProgress(5, "Reading WMO.."); var outdir = ConfigurationManager.AppSettings["outdir"]; var wmo = new WMOReader().LoadWMO(filedataid); 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.group.Count()]; for (var g = 0; g < wmo.group.Count(); g++) { Console.WriteLine("Loading group #" + g); if (wmo.group[g].mogp.vertices == null) { Console.WriteLine("Group has no vertices!"); continue; } for (var i = 0; i < wmo.groupNames.Count(); i++) { if (wmo.group[g].mogp.nameOffset == wmo.groupNames[i].offset) { groups[g].name = wmo.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.group[g].mogp.vertices.Count()]; for (var i = 0; i < wmo.group[g].mogp.vertices.Count(); i++) { groups[g].vertices[i].Position = new Structs.Vector3D() { X = wmo.group[g].mogp.vertices[i].vector.X * -1, Y = wmo.group[g].mogp.vertices[i].vector.Z, Z = wmo.group[g].mogp.vertices[i].vector.Y }; groups[g].vertices[i].Normal = new Structs.Vector3D() { X = wmo.group[g].mogp.normals[i].normal.X, Y = wmo.group[g].mogp.normals[i].normal.Z, Z = wmo.group[g].mogp.normals[i].normal.Y }; groups[g].vertices[i].TexCoord = new Structs.Vector2D() { X = wmo.group[g].mogp.textureCoords[0][i].X, Y = wmo.group[g].mogp.textureCoords[0][i].Y }; totalVertices++; } var indicelist = new List <uint>(); for (var i = 0; i < wmo.group[g].mogp.indices.Count(); i++) { indicelist.Add(wmo.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); } } } 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), filedataid + "_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, filedataid + "_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.doodadSets.Count(); i++) { var doodadSet = wmo.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++) { var doodadDefinition = wmo.doodadDefinitions[j]; var doodadFilename = ""; uint doodadFileDataID = 0; if (wmo.doodadIds != null) { doodadFileDataID = wmo.doodadIds[doodadDefinition.offset]; if (!Listfile.TryGetFilename(doodadFileDataID, out doodadFilename)) { CASCLib.Logger.WriteLine("Could not find filename for " + doodadFileDataID + ", setting filename to filedataid.."); doodadFilename = doodadFileDataID.ToString(); } } else { CASCLib.Logger.WriteLine("Warning!! File " + filename + " ID: " + filedataid + " still has filenames!"); foreach (var doodadNameEntry in wmo.doodadNames) { if (doodadNameEntry.startOffset == doodadDefinition.offset) { doodadFilename = doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2").ToLower(); if (!Listfile.TryGetFileDataID(doodadFilename, out doodadFileDataID)) { CASCLib.Logger.WriteLine("Error! Could not find filedataid for " + doodadFilename + "!"); continue; } } } } if (destinationOverride == null) { if (!string.IsNullOrEmpty(doodadFilename)) { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(doodadFilename) + ".obj"))) { M2Exporter.ExportM2(doodadFileDataID, null, Path.Combine(outdir, Path.GetDirectoryName(filename)), doodadFilename); } if (File.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(doodadFilename) + ".obj"))) { doodadSW.WriteLine(Path.GetFileNameWithoutExtension(doodadFilename) + ".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 { if (!File.Exists(Path.Combine(outdir, doodadFileDataID + ".obj"))) { M2Exporter.ExportM2(doodadFileDataID, null, outdir, doodadFilename); } if (File.Exists(Path.Combine(outdir, doodadFileDataID + ".obj"))) { doodadSW.WriteLine(doodadFileDataID + ".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 { if (!string.IsNullOrEmpty(doodadFilename)) { if (!File.Exists(Path.Combine(destinationOverride, Path.GetFileNameWithoutExtension(doodadFilename) + ".obj"))) { M2Exporter.ExportM2(doodadFileDataID, null, destinationOverride, doodadFilename); } if (File.Exists(Path.Combine(destinationOverride, Path.GetFileNameWithoutExtension(doodadFilename) + ".obj"))) { doodadSW.WriteLine(Path.GetFileNameWithoutExtension(doodadFilename) + ".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 { if (!File.Exists(Path.Combine(destinationOverride, doodadFileDataID + ".obj"))) { M2Exporter.ExportM2(doodadFileDataID, null, destinationOverride, doodadFilename); } if (File.Exists(Path.Combine(destinationOverride, doodadFileDataID + ".obj"))) { doodadSW.WriteLine(doodadFileDataID + ".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); } } } } } doodadSW.Close(); exportworker.ReportProgress(65, "Exporting textures.."); var mtlsb = new StringBuilder(); var textureID = 0; if (wmo.materials == null) { CASCLib.Logger.WriteLine("Unable to find materials for WMO " + filedataid + ", not exporting!"); return; } var materials = new Structs.Material[wmo.materials.Count()]; var extraMaterials = new List <Structs.Material>(); for (var i = 0; i < wmo.materials.Count(); i++) { var blpreader = new BLPReader(); if (wmo.textures == null) { if (Listfile.TryGetFilename(wmo.materials[i].texture1, out var textureFilename)) { materials[i].filename = Path.GetFileNameWithoutExtension(textureFilename); } else { materials[i].filename = wmo.materials[i].texture1.ToString(); } blpreader.LoadBLP(wmo.materials[i].texture1); } else { for (var ti = 0; ti < wmo.textures.Count(); ti++) { if (wmo.textures[ti].startOffset == wmo.materials[i].texture1) { materials[i].filename = Path.GetFileNameWithoutExtension(wmo.textures[ti].filename); blpreader.LoadBLP(wmo.textures[ti].filename); } } } materials[i].textureID = textureID + i; if (wmo.materials[i].blendMode == 0) { materials[i].transparent = false; } else { materials[i].transparent = true; } materials[i].blendMode = wmo.materials[i].blendMode; materials[i].shaderID = wmo.materials[i].shader; materials[i].terrainType = wmo.materials[i].groundType; var saveLocation = ""; if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { saveLocation = Path.Combine(outdir, Path.GetDirectoryName(filename), materials[i].filename + ".png"); } else { saveLocation = Path.Combine(outdir, materials[i].filename + ".png"); } } else { saveLocation = Path.Combine(outdir, destinationOverride, materials[i].filename + ".png"); } if (!File.Exists(saveLocation)) { try { if (materials[i].transparent) { blpreader.bmp.Save(saveLocation); } else { blpreader.bmp.Clone(new Rectangle(0, 0, blpreader.bmp.Width, blpreader.bmp.Height), PixelFormat.Format32bppRgb).Save(saveLocation); } } catch (Exception e) { CASCLib.Logger.WriteLine("Exception while saving BLP " + materials[i].filename + ": " + e.Message); } } textureID++; // Extra materials // Texture 2 if (CASC.FileExists(wmo.materials[i].texture2)) { var tex2mat = new Structs.Material(); if (wmo.textures == null) { if (Listfile.TryGetFilename(wmo.materials[i].texture2, out var textureFilename)) { tex2mat.filename = Path.GetFileNameWithoutExtension(textureFilename); } else { tex2mat.filename = wmo.materials[i].texture2.ToString(); } blpreader.LoadBLP(wmo.materials[i].texture2); } else { for (var ti = 0; ti < wmo.textures.Count(); ti++) { if (wmo.textures[ti].startOffset == wmo.materials[i].texture2) { tex2mat.filename = Path.GetFileNameWithoutExtension(wmo.textures[ti].filename); blpreader.LoadBLP(wmo.textures[ti].filename); } } } if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { saveLocation = Path.Combine(outdir, Path.GetDirectoryName(filename), tex2mat.filename + ".png"); } else { saveLocation = Path.Combine(outdir, tex2mat.filename + ".png"); } } else { saveLocation = Path.Combine(outdir, destinationOverride, tex2mat.filename + ".png"); } if (!File.Exists(saveLocation)) { try { blpreader.bmp.Save(saveLocation); } catch (Exception e) { CASCLib.Logger.WriteLine("Exception while saving BLP " + tex2mat.filename + ": " + e.Message); } } extraMaterials.Add(tex2mat); } // Texture 3 if (CASC.FileExists(wmo.materials[i].texture3)) { var tex3mat = new Structs.Material(); if (wmo.textures == null) { if (Listfile.TryGetFilename(wmo.materials[i].texture3, out var textureFilename)) { tex3mat.filename = Path.GetFileNameWithoutExtension(textureFilename); } else { tex3mat.filename = wmo.materials[i].texture3.ToString(); } blpreader.LoadBLP(wmo.materials[i].texture3); } else { for (var ti = 0; ti < wmo.textures.Count(); ti++) { if (wmo.textures[ti].startOffset == wmo.materials[i].texture3) { tex3mat.filename = Path.GetFileNameWithoutExtension(wmo.textures[ti].filename); blpreader.LoadBLP(wmo.textures[ti].filename); } } } if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { saveLocation = Path.Combine(outdir, Path.GetDirectoryName(filename), tex3mat.filename + ".png"); } else { saveLocation = Path.Combine(outdir, tex3mat.filename + ".png"); } } else { saveLocation = Path.Combine(outdir, destinationOverride, tex3mat.filename + ".png"); } if (!File.Exists(saveLocation)) { try { blpreader.bmp.Save(saveLocation); } catch (Exception e) { CASCLib.Logger.WriteLine("Exception while saving BLP " + tex3mat.filename + ": " + e.Message); } } extraMaterials.Add(tex3mat); } } //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"); } if (ConfigurationManager.AppSettings["textureMetadata"] == "True") { mtlsb.Append("blend " + material.blendMode + "\n"); mtlsb.Append("shader " + material.shaderID + "\n"); mtlsb.Append("terrain " + material.terrainType + "\n"); } } foreach (var material in extraMaterials) { 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"); } } 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, filedataid + ".mtl"), mtlsb.ToString()); } else { File.WriteAllText(Path.Combine(outdir, destinationOverride, filedataid + ".mtl"), mtlsb.ToString()); } } exportworker.ReportProgress(75, "Exporting model.."); var numRenderbatches = 0; //Get total amount of render batches for (var i = 0; i < wmo.group.Count(); i++) { if (wmo.group[i].mogp.renderBatches == null) { continue; } numRenderbatches = numRenderbatches + wmo.group[i].mogp.renderBatches.Count(); } var rb = 0; for (var g = 0; g < wmo.group.Count(); g++) { groups[g].renderBatches = new Structs.RenderBatch[numRenderbatches]; var group = wmo.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.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, filedataid + ".obj")); } else { objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, filedataid + ".obj")); } objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original file id: " + filedataid); objsw.WriteLine("mtllib " + filedataid + ".mtl"); } foreach (var group in groups) { if (group.vertices == null) { continue; } Console.WriteLine("Writing " + group.name); objsw.WriteLine("g " + group.name); foreach (var vertex in group.vertices) { objsw.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z); objsw.WriteLine("vt " + vertex.TexCoord.X + " " + (vertex.TexCoord.Y - 1) * -1); 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) { 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 = i + 3; } } } } objsw.Close(); Console.WriteLine("Done loading WMO file!"); }
public static void exportM2(string file, BackgroundWorker exportworker = null) { if (exportworker == null) { exportworker = new BackgroundWorker(); } var outdir = ConfigurationManager.AppSettings["outdir"]; var reader = new M2Reader(); exportworker.ReportProgress(15, "Reading M2.."); if (!CASC.FileExists(file)) { throw new Exception("404 M2 not found!"); } reader.LoadM2(file); Structs.Vertex[] vertices = new Structs.Vertex[reader.model.vertices.Count()]; for (int i = 0; i < reader.model.vertices.Count(); i++) { vertices[i].Position = new OpenTK.Vector3(reader.model.vertices[i].position.X, reader.model.vertices[i].position.Z, reader.model.vertices[i].position.Y * -1); vertices[i].Normal = new OpenTK.Vector3(reader.model.vertices[i].normal.X, reader.model.vertices[i].normal.Z, reader.model.vertices[i].normal.Y); vertices[i].TexCoord = new Vector2(reader.model.vertices[i].textureCoordX, reader.model.vertices[i].textureCoordY); } // Create output directory if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file))); } var objsw = new StreamWriter(Path.Combine(outdir, file.Replace(".m2", ".obj"))); objsw.WriteLine("# Written by Marlamin's WoW OBJExporter. Original file: " + file); objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(file) + ".mtl"); foreach (var vertex in vertices) { 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.Position.X + " " + vertex.Position.Y + " " + vertex.Normal.Z); } List <uint> indicelist = new List <uint>(); for (int i = 0; i < reader.model.skins[0].triangles.Count(); i++) { var t = reader.model.skins[0].triangles[i]; indicelist.Add(t.pt1); indicelist.Add(t.pt2); indicelist.Add(t.pt3); } var indices = indicelist.ToArray(); exportworker.ReportProgress(35, "Writing files.."); var renderbatches = new Structs.RenderBatch[reader.model.skins[0].submeshes.Count()]; for (int i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { if (file.StartsWith("character", StringComparison.CurrentCultureIgnoreCase)) { if (reader.model.skins[0].submeshes[i].submeshID != 0) { if (!reader.model.skins[0].submeshes[i].submeshID.ToString().EndsWith("01")) { continue; } } } renderbatches[i].firstFace = reader.model.skins[0].submeshes[i].startTriangle; renderbatches[i].numFaces = reader.model.skins[0].submeshes[i].nTriangles; renderbatches[i].groupID = (uint)i; for (int tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++) { if (reader.model.skins[0].textureunit[tu].submeshIndex == i) { renderbatches[i].blendType = reader.model.renderflags[reader.model.skins[0].textureunit[tu].renderFlags].blendingMode; renderbatches[i].materialID = reader.model.texlookup[reader.model.skins[0].textureunit[tu].texture].textureID; } } } exportworker.ReportProgress(65, "Exporting textures.."); var mtlsb = new StreamWriter(Path.Combine(outdir, file.Replace(".m2", ".mtl"))); var textureID = 0; var materials = new Structs.Material[reader.model.textures.Count()]; for (int i = 0; i < reader.model.textures.Count(); i++) { string texturefilename = "Dungeons\\Textures\\testing\\COLOR_13.blp"; materials[i].flags = reader.model.textures[i].flags; switch (reader.model.textures[i].type) { case 0: //Console.WriteLine(" Texture given in file!"); texturefilename = reader.model.textures[i].filename; break; case 1: string[] csfilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(file, (int)reader.model.textures[i].type, i); if (csfilenames.Count() > 0) { texturefilename = csfilenames[0]; } else { //Console.WriteLine(" No type 1 texture found, falling back to placeholder texture"); } break; case 2: if (WoWFormatLib.Utils.CASC.FileExists(Path.ChangeExtension(file, ".blp"))) { //Console.WriteLine(" BLP exists!"); texturefilename = Path.ChangeExtension(file, ".blp"); } else { //Console.WriteLine(" Type 2 does not exist!"); //needs lookup? } break; case 11: string[] cdifilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(file, (int)reader.model.textures[i].type); for (int ti = 0; ti < cdifilenames.Count(); ti++) { if (WoWFormatLib.Utils.CASC.FileExists(file.Replace(reader.model.name + ".M2", cdifilenames[ti] + ".blp"))) { texturefilename = file.Replace(reader.model.name + ".M2", cdifilenames[ti] + ".blp"); } } break; default: // Console.WriteLine(" Falling back to placeholder texture"); break; } //Console.WriteLine(" Eventual filename is " + texturefilename); materials[i].textureID = textureID + i; materials[i].filename = Path.GetFileNameWithoutExtension(texturefilename); var blpreader = new BLPReader(); blpreader.LoadBLP(texturefilename); try { blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file), materials[i].filename + ".png")); } catch (Exception e) { Console.WriteLine(e.Message); } } exportworker.ReportProgress(85, "Writing files.."); foreach (var material in materials) { mtlsb.WriteLine("newmtl " + material.filename); mtlsb.WriteLine("illum 2"); mtlsb.WriteLine("map_Ka " + material.filename + ".png"); mtlsb.WriteLine("map_Kd " + material.filename + ".png"); } mtlsb.Close(); objsw.WriteLine("g " + Path.GetFileNameWithoutExtension(file)); foreach (var renderbatch in renderbatches) { var i = renderbatch.firstFace; objsw.WriteLine("o " + Path.GetFileNameWithoutExtension(file) + renderbatch.groupID); objsw.WriteLine("usemtl " + materials[renderbatch.materialID].filename); 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(); // https://en.wikipedia.org/wiki/Wavefront_.obj_file#Basic_materials // http://wiki.unity3d.com/index.php?title=ExportOBJ // http://web.cse.ohio-state.edu/~hwshen/581/Site/Lab3_files/Labhelp_Obj_parser.htm Console.WriteLine("Done loading model!"); }
public static void exportM2(string file, BackgroundWorker exportworker = null, string destinationOverride = null) { if (exportworker == null) { exportworker = new BackgroundWorker(); exportworker.WorkerReportsProgress = true; } System.Globalization.CultureInfo customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone(); customCulture.NumberFormat.NumberDecimalSeparator = "."; System.Threading.Thread.CurrentThread.CurrentCulture = customCulture; var fileDataID = CASC.getFileDataIdByName(file); var outdir = ConfigurationManager.AppSettings["outdir"]; var reader = new M2Reader(); exportworker.ReportProgress(15, "Reading M2.."); if (!CASC.cascHandler.FileExists(fileDataID)) { throw new Exception("404 M2 not found!"); } reader.LoadM2(fileDataID); // Don't export models without vertices if (reader.model.vertices.Count() == 0) { return; } Structs.Vertex[] vertices = new Structs.Vertex[reader.model.vertices.Count()]; for (int i = 0; i < reader.model.vertices.Count(); i++) { vertices[i].Position = new OpenTK.Vector3(reader.model.vertices[i].position.X, reader.model.vertices[i].position.Z, reader.model.vertices[i].position.Y * -1); vertices[i].Normal = new OpenTK.Vector3(reader.model.vertices[i].normal.X, reader.model.vertices[i].normal.Z, reader.model.vertices[i].normal.Y); vertices[i].TexCoord = new Vector2(reader.model.vertices[i].textureCoordX, reader.model.vertices[i].textureCoordY); } StreamWriter objsw; if (destinationOverride == null) { // Create output directory if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file))); } objsw = new StreamWriter(Path.Combine(outdir, file.Replace(".m2", ".obj"))); } else { objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileName(file.ToLower()).Replace(".m2", ".obj"))); } objsw.WriteLine("# Written by Marlamin's WoW Exporter. Original file: " + file); objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(file) + ".mtl"); foreach (var vertex in vertices) { 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.ToString("F12") + " " + vertex.Normal.Y.ToString("F12") + " " + vertex.Normal.Z.ToString("F12")); } List <uint> indicelist = new List <uint>(); for (int i = 0; i < reader.model.skins[0].triangles.Count(); i++) { var t = reader.model.skins[0].triangles[i]; indicelist.Add(t.pt1); indicelist.Add(t.pt2); indicelist.Add(t.pt3); } var indices = indicelist.ToArray(); exportworker.ReportProgress(35, "Writing files.."); var renderbatches = new Structs.RenderBatch[reader.model.skins[0].submeshes.Count()]; for (int i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { if (file.StartsWith("character", StringComparison.CurrentCultureIgnoreCase)) { if (reader.model.skins[0].submeshes[i].submeshID != 0) { if (!reader.model.skins[0].submeshes[i].submeshID.ToString().EndsWith("01")) { continue; } } } renderbatches[i].firstFace = reader.model.skins[0].submeshes[i].startTriangle; renderbatches[i].numFaces = reader.model.skins[0].submeshes[i].nTriangles; renderbatches[i].groupID = (uint)i; for (int tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++) { if (reader.model.skins[0].textureunit[tu].submeshIndex == i) { renderbatches[i].blendType = reader.model.renderflags[reader.model.skins[0].textureunit[tu].renderFlags].blendingMode; renderbatches[i].materialID = reader.model.texlookup[reader.model.skins[0].textureunit[tu].texture].textureID; } } } exportworker.ReportProgress(65, "Exporting textures.."); StreamWriter mtlsb; if (destinationOverride == null) { mtlsb = new StreamWriter(Path.Combine(outdir, file.Replace(".m2", ".mtl"))); } else { mtlsb = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileName(file.ToLower()).Replace(".m2", ".mtl"))); } var textureID = 0; var materials = new Structs.Material[reader.model.textures.Count()]; for (int i = 0; i < reader.model.textures.Count(); i++) { int textureFileDataID = 840426; materials[i].flags = reader.model.textures[i].flags; switch (reader.model.textures[i].type) { case 0: //Console.WriteLine(" Texture given in file!"); textureFileDataID = CASC.getFileDataIdByName(reader.model.textures[i].filename); break; case 1: case 2: case 11: uint[] cdifilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(fileDataID, (int)reader.model.textures[i].type); for (int ti = 0; ti < cdifilenames.Count(); ti++) { textureFileDataID = (int)cdifilenames[0]; } break; default: Console.WriteLine(" Falling back to placeholder texture"); break; } materials[i].textureID = textureID + i; materials[i].filename = textureFileDataID.ToString(); var blpreader = new BLPReader(); blpreader.LoadBLP(textureFileDataID); try { if (destinationOverride == null) { blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file), "tex_" + materials[i].filename + ".png")); } else { blpreader.bmp.Save(Path.Combine(outdir, destinationOverride, "tex_" + materials[i].filename.ToLower() + ".png")); } } catch (Exception e) { Console.WriteLine(e.Message); } } exportworker.ReportProgress(85, "Writing files.."); foreach (var material in materials) { mtlsb.WriteLine("newmtl " + "tex_" + material.filename); mtlsb.WriteLine("illum 2"); mtlsb.WriteLine("map_Ka " + "tex_" + material.filename + ".png"); mtlsb.WriteLine("map_Kd " + "tex_" + material.filename + ".png"); } mtlsb.Close(); objsw.WriteLine("g " + Path.GetFileNameWithoutExtension(file)); foreach (var renderbatch in renderbatches) { var i = renderbatch.firstFace; objsw.WriteLine("o " + Path.GetFileNameWithoutExtension(file) + renderbatch.groupID); objsw.WriteLine("usemtl tex_" + materials[renderbatch.materialID].filename); 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(); // Only export phys when exporting a single M2, causes issues for some users when combined with WMO/ADT if (destinationOverride == null) { exportworker.ReportProgress(90, "Exporting collision.."); objsw = new StreamWriter(Path.Combine(outdir, file.Replace(".m2", ".phys.obj"))); objsw.WriteLine("# Written by Marlamin's WoW Exporter. Original file: " + file); for (int i = 0; i < reader.model.boundingvertices.Count(); i++) { objsw.WriteLine("v " + reader.model.boundingvertices[i].vertex.X + " " + reader.model.boundingvertices[i].vertex.Z + " " + -reader.model.boundingvertices[i].vertex.Y); } for (int i = 0; i < reader.model.boundingtriangles.Count(); i++) { var t = reader.model.boundingtriangles[i]; objsw.WriteLine("f " + (t.index_0 + 1) + " " + (t.index_1 + 1) + " " + (t.index_2 + 1)); } objsw.Close(); } // https://en.wikipedia.org/wiki/Wavefront_.obj_file#Basic_materials // http://wiki.unity3d.com/index.php?title=ExportOBJ // http://web.cse.ohio-state.edu/~hwshen/581/Site/Lab3_files/Labhelp_Obj_parser.htm }
public static void exportWMO(string file, BackgroundWorker exportworker = null, string destinationOverride = null, ushort doodadSetExportID = ushort.MaxValue) { if (exportworker == null) { exportworker = new BackgroundWorker(); exportworker.WorkerReportsProgress = true; } Console.WriteLine("Loading WMO file.."); exportworker.ReportProgress(5, "Reading WMO.."); var outdir = ConfigurationManager.AppSettings["outdir"]; var wmo = new WMOReader().LoadWMO(file); 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.group.Count()]; for (var g = 0; g < wmo.group.Count(); g++) { Console.WriteLine("Loading group #" + g); if (wmo.group[g].mogp.vertices == null) { Console.WriteLine("Group has no vertices!"); continue; } for (var i = 0; i < wmo.groupNames.Count(); i++) { if (wmo.group[g].mogp.nameOffset == wmo.groupNames[i].offset) { groups[g].name = wmo.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.group[g].mogp.vertices.Count()]; for (var i = 0; i < wmo.group[g].mogp.vertices.Count(); i++) { groups[g].vertices[i].Position = new Vector3(wmo.group[g].mogp.vertices[i].vector.X * -1, wmo.group[g].mogp.vertices[i].vector.Z, wmo.group[g].mogp.vertices[i].vector.Y); groups[g].vertices[i].Normal = new Vector3(wmo.group[g].mogp.normals[i].normal.X, wmo.group[g].mogp.normals[i].normal.Z, wmo.group[g].mogp.normals[i].normal.Y); groups[g].vertices[i].TexCoord = new Vector2(wmo.group[g].mogp.textureCoords[0][i].X, wmo.group[g].mogp.textureCoords[0][i].Y); totalVertices++; } var indicelist = new List <uint>(); for (var i = 0; i < wmo.group[g].mogp.indices.Count(); i++) { indicelist.Add(wmo.group[g].mogp.indices[i].indice); } groups[g].indices = indicelist.ToArray(); } if (destinationOverride == null) { // Create output directory if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file))); } } StreamWriter doodadSW; if (destinationOverride == null) { doodadSW = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file).Replace(" ", "") + "_ModelPlacementInformation.csv")); } else { doodadSW = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileNameWithoutExtension(file).Replace(" ", "") + "_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.doodadSets.Count(); i++) { var doodadSet = wmo.doodadSets[i]; var currentDoodadSetName = doodadSet.setName.Replace("Set_", "").Replace("SET_", "").Replace("$DefaultGlobal", "Default"); if (doodadSetExportID != ushort.MaxValue) { //if (i != 0 && i != doodadSetExportID) // Is 0 always exported? Double check? if (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++) { var doodadDefinition = wmo.doodadDefinitions[j]; if (wmo.doodadIds != null) { var doodadFileDataID = wmo.doodadIds[doodadDefinition.offset]; if (destinationOverride == null) { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), doodadFileDataID + ".obj"))) { M2Exporter.ExportM2(doodadFileDataID, null, Path.Combine(outdir, Path.GetDirectoryName(file))); } if (File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), doodadFileDataID + ".obj"))) { doodadSW.WriteLine(doodadFileDataID + ".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 { if (!File.Exists(Path.Combine(destinationOverride, doodadFileDataID + ".obj"))) { M2Exporter.ExportM2(doodadFileDataID, null, destinationOverride); } if (File.Exists(Path.Combine(destinationOverride, doodadFileDataID + ".obj"))) { doodadSW.WriteLine(doodadFileDataID + ".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 { foreach (var doodadNameEntry in wmo.doodadNames) { if (doodadNameEntry.startOffset == doodadDefinition.offset) { var doodadFileName = doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2"); if (destinationOverride == null) { if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileName(doodadFileName.ToLower()).Replace(".m2", ".obj")))) { M2Exporter.ExportM2(doodadFileName, null, Path.Combine(outdir, Path.GetDirectoryName(file))); } if (File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileName(doodadFileName.ToLower()).Replace(".m2", ".obj")))) { 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); } } else { if (!File.Exists(Path.Combine(destinationOverride, Path.GetFileName(doodadFileName.ToLower()).Replace(".m2", ".obj")))) { M2Exporter.ExportM2(doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2"), null, destinationOverride); } if (File.Exists(Path.Combine(destinationOverride, Path.GetFileName(doodadFileName.ToLower()).Replace(".m2", ".obj")))) { 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(); exportworker.ReportProgress(65, "Exporting textures.."); var mtlsb = new StringBuilder(); var textureID = 0; if (wmo.materials == null) { Console.WriteLine("Materials empty"); return; } var materials = new Structs.Material[wmo.materials.Count()]; for (var i = 0; i < wmo.materials.Count(); i++) { if (wmo.textures == null) { materials[i].textureID = textureID + i; materials[i].filename = wmo.materials[i].texture1.ToString(); if (wmo.materials[i].blendMode == 0) { materials[i].transparent = false; } else { materials[i].transparent = true; } if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), materials[i].filename + ".png"))) { var blpreader = new BLPReader(); blpreader.LoadBLP(wmo.materials[i].texture1); try { if (destinationOverride == null) { blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file), materials[i].filename + ".png")); } else { blpreader.bmp.Save(Path.Combine(outdir, destinationOverride, materials[i].filename.ToLower() + ".png")); } } catch (Exception e) { Console.WriteLine(e.Message); } } textureID++; } else { for (var ti = 0; ti < wmo.textures.Count(); ti++) { if (wmo.textures[ti].startOffset == wmo.materials[i].texture1) { materials[i].textureID = textureID + i; materials[i].filename = Path.GetFileNameWithoutExtension(wmo.textures[ti].filename); if (wmo.materials[i].blendMode == 0) { materials[i].transparent = false; } else { materials[i].transparent = true; } if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), materials[i].filename + ".png"))) { var blpreader = new BLPReader(); blpreader.LoadBLP(wmo.textures[ti].filename); try { if (destinationOverride == null) { blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file), materials[i].filename + ".png")); } else { blpreader.bmp.Save(Path.Combine(outdir, destinationOverride, materials[i].filename.ToLower() + ".png")); } } catch (Exception e) { Console.WriteLine(e.Message); } } 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 2\n"); mtlsb.Append("map_Kd " + material.filename + ".png\n"); if (material.transparent) { mtlsb.Append("map_d " + material.filename + ".png\n"); } } if (destinationOverride == null) { File.WriteAllText(Path.Combine(outdir, file.Replace(".wmo", ".mtl")), mtlsb.ToString()); } else { File.WriteAllText(Path.Combine(outdir, destinationOverride, Path.GetFileName(file.ToLower()).Replace(".wmo", ".mtl")), mtlsb.ToString()); } exportworker.ReportProgress(75, "Exporting model.."); var numRenderbatches = 0; //Get total amount of render batches for (var i = 0; i < wmo.group.Count(); i++) { if (wmo.group[i].mogp.renderBatches == null) { continue; } numRenderbatches = numRenderbatches + wmo.group[i].mogp.renderBatches.Count(); } var rb = 0; for (var g = 0; g < wmo.group.Count(); g++) { groups[g].renderBatches = new Structs.RenderBatch[numRenderbatches]; var group = wmo.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.materials[batch.materialID].blendMode; groups[g].renderBatches[rb].groupID = (uint)g; rb++; } } exportworker.ReportProgress(95, "Writing files.."); StreamWriter objsw; if (destinationOverride == null) { objsw = new StreamWriter(Path.Combine(outdir, file.Replace(".wmo", ".obj"))); } else { objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileName(file.ToLower()).Replace(".wmo", ".obj"))); } objsw.WriteLine("# Written by Marlamin's WoW OBJExporter. Original file: " + file); objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(file) + ".mtl"); foreach (var group in groups) { if (group.vertices == null) { continue; } Console.WriteLine("Writing " + group.name); objsw.WriteLine("g " + group.name); foreach (var vertex in group.vertices) { 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.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) { 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 = i + 3; } } } } objsw.Close(); Console.WriteLine("Done loading WMO file!"); }
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!"); }
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 void LoadM2() { //Load model M2Reader reader = new M2Reader(basedir); string filename = modelPath; reader.LoadM2(filename); //Load vertices List <float> verticelist = new List <float>(); for (int i = 0; i < reader.model.vertices.Count(); i++) { verticelist.Add(reader.model.vertices[i].position.X); verticelist.Add(reader.model.vertices[i].position.Z * -1); verticelist.Add(reader.model.vertices[i].position.Y); verticelist.Add(1.0f); verticelist.Add(reader.model.vertices[i].normal.X); verticelist.Add(reader.model.vertices[i].normal.Z * -1); verticelist.Add(reader.model.vertices[i].normal.Y); verticelist.Add(reader.model.vertices[i].textureCoordX); verticelist.Add(reader.model.vertices[i].textureCoordY); } //Load indices List <ushort> indicelist = new List <ushort>(); for (int i = 0; i < reader.model.skins[0].triangles.Count(); i++) { indicelist.Add(reader.model.skins[0].triangles[i].pt1); indicelist.Add(reader.model.skins[0].triangles[i].pt2); indicelist.Add(reader.model.skins[0].triangles[i].pt3); } //Convert to array ushort[] indices = indicelist.ToArray(); float[] vertices = verticelist.ToArray(); //Get texture, what a mess this could be much better M2Material[] materials = new M2Material[reader.model.textures.Count()]; for (int i = 0; i < reader.model.textures.Count(); i++) { materials[i].flags = reader.model.textures[i].flags; var blp = new BLPReader(basedir); if (File.Exists(Path.Combine(basedir, reader.model.filename.Replace("M2", "blp")))) { blp.LoadBLP(reader.model.filename.Replace("M2", "blp")); } else { blp.LoadBLP(reader.model.textures[i].filename); } if (blp.bmp == null) { materials[i].texture = Texture2D.FromFile <Texture2D>(device, "missingtexture.jpg"); } else { MemoryStream s = new MemoryStream(); blp.bmp.Save(s, System.Drawing.Imaging.ImageFormat.Png); s.Seek(0, SeekOrigin.Begin); materials[i].texture = Texture2D.FromMemory <Texture2D>(device, s.ToArray()); } } M2RenderBatch[] renderbatches = new M2RenderBatch[reader.model.skins[0].submeshes.Count()]; for (int i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { renderbatches[i].firstFace = reader.model.skins[0].submeshes[i].startTriangle; renderbatches[i].numFaces = reader.model.skins[0].submeshes[i].nTriangles; for (int tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++) { if (reader.model.skins[0].textureunit[tu].submeshIndex == i) { renderbatches[i].materialID = reader.model.skins[0].textureunit[tu].texture; } } } m2.indices = indices; m2.vertices = vertices; m2.materials = materials; m2.renderBatches = renderbatches; }
private static void Main(string[] args) { string mapname = ""; string basedir = ConfigurationManager.AppSettings["basedir"]; bool buildmaps = Boolean.Parse(ConfigurationManager.AppSettings["buildmaps"]); bool buildWMOmaps = Boolean.Parse(ConfigurationManager.AppSettings["buildwmomaps"]); Console.WriteLine("Initializing CASC.."); if (basedir != String.Empty) { CASC.InitCasc(null, basedir); } else { CASC.InitCasc(null, @"D:\World of Warcraft Beta", "wow_beta"); // Use beta for now } Console.WriteLine("CASC initialized!"); Console.WriteLine("Current patch: " + CASC.cascHandler.Config.BuildName); if (buildmaps == true) { DB2Reader <MapRecordLegion> reader = new DB2Reader <MapRecordLegion>("DBFilesClient\\Map.db2"); for (int i = 0; i < reader.recordCount; i++) { //I used to check if WDT existed, but sometimes minimaps for maps without WDTs slip through the cracks mapname = reader[i].Directory; // if (reader[i].expansionID < 6) { Console.WriteLine("Skipping map " + mapname + " WoD and below!"); continue; } var min_x = 64; var min_y = 64; var max_x = 0; var max_y = 0; for (int cur_x = 0; cur_x < 64; cur_x++) { for (int cur_y = 0; cur_y < 64; cur_y++) { if (CASC.FileExists("World\\Minimaps\\" + mapname + "\\map" + cur_x + "_" + cur_y + ".blp")) { if (cur_x > max_x) { max_x = cur_x; } if (cur_y > max_y) { max_y = cur_y; } if (cur_x < min_x) { min_x = cur_x; } if (cur_y < min_y) { min_y = cur_y; } } } } Console.WriteLine("[" + mapname + "] MIN: (" + min_x + " " + min_y + ") MAX: (" + max_x + " " + max_y + ")"); var res_x = (((max_x - min_x) * 256) + 256); var res_y = (((max_y - min_y) * 256) + 256); if (res_x < 0 || res_y < 0) { Console.WriteLine("[" + mapname + "] " + "Skipping map, has no minimap tiles"); continue; } 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); Font drawFont = new Font("Arial", 16); for (int cur_x = 0; cur_x < 64; cur_x++) { for (int cur_y = 0; cur_y < 64; cur_y++) { if (CASC.FileExists("World\\Minimaps\\" + mapname + "\\map" + cur_x + "_" + cur_y + ".blp")) { var blpreader = new BLPReader(); blpreader.LoadBLP("World\\Minimaps\\" + mapname + "\\map" + cur_x + "_" + cur_y + ".blp"); g.DrawImage(blpreader.bmp, (cur_x - min_x) * 256, (cur_y - min_y) * 256, new Rectangle(0, 0, 256, 256), GraphicsUnit.Pixel); } } } g.Dispose(); if (!Directory.Exists("done")) { Directory.CreateDirectory("done"); } bmp.Save("done/" + mapname + ".png"); // SUPER MINIMAP COMPILER TIME!!!!!!!!!!!!!!! /* * var super_res_x = (((max_x - min_x) * 512) + 512); * var super_res_y = (((max_y - min_y) * 512) + 512); * * if (super_res_x < 0 || super_res_y < 0) * { * Console.WriteLine("[SUPER " + mapname + "] " + "Skipping map, has no minimap tiles"); * continue; * } * * Console.WriteLine("[SUPER " + mapname + "] " + "Creating new image of " + super_res_x + "x" + super_res_y); * * Bitmap super_bmp = new Bitmap(super_res_x, super_res_y); * Graphics super_g = Graphics.FromImage(super_bmp); * * for (int cur_x = 0; cur_x < 64; cur_x++) * { * for (int cur_y = 0; cur_y < 64; cur_y++) * { * if (CASC.FileExists("World\\Minimaps\\" + mapname + "\\map" + cur_x + "_" + cur_y + ".blp")) * { * var blpreader = new BLPReader(); * blpreader.LoadBLP("World\\Minimaps\\" + mapname + "\\map" + cur_x + "_" + cur_y + ".blp"); * super_g.DrawImage(blpreader.bmp, (cur_x - min_x) * 512, (cur_y - min_y) * 512, new Rectangle(0, 0, 512, 512), GraphicsUnit.Pixel); * } * } * } * super_g.Dispose(); * if (!Directory.Exists("done")) { Directory.CreateDirectory("done"); } * super_bmp.Save("done/SUPER_" + mapname + ".png"); */ } } if (buildWMOmaps == true) { List <string> linelist = new List <string>(); if (CASC.FileExists("dbfilesclient/filedatacomplete.dbc")) { var dbcreader = new DBCReader <FileDataRecord>("dbfilesclient/filedatacomplete.dbc"); if (dbcreader.recordCount > 0) { for (int i = 0; i < dbcreader.recordCount; i++) { if (CASC.cascHandler.FileExists(dbcreader[i].ID)) { if (dbcreader[i].ID > 1023304) { linelist.Add(dbcreader[i].FileName + dbcreader[i].FilePath); } } } } } DBCReader <MapRecord> reader = new DBCReader <MapRecord>("DBFilesClient\\Map.dbc"); string[] unwantedExtensions = new string[513]; for (int i = 0; i < 512; i++) { unwantedExtensions[i] = "_" + i.ToString().PadLeft(3, '0') + ".wmo"; } unwantedExtensions[512] = "LOD1.wmo"; foreach (string s in linelist) { if (s.Length > 8 && !unwantedExtensions.Contains(s.Substring(s.Length - 8, 8))) { if (!s.Contains("LOD") && s.EndsWith(".wmo", StringComparison.CurrentCultureIgnoreCase)) { Console.WriteLine(s); WMO wmocompiler = new WMO(); wmocompiler.Compile(s); } } } Console.ReadLine(); } }
public static void ExportM2(M2Reader reader, string fileName, BackgroundWorker exportworker = null, string destinationOverride = null, bool externalOverride = false, bool[] enabledGeosets = 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 exportDir = ConfigurationManager.AppSettings["outdir"]; exportworker.ReportProgress(15, "Reading M2.."); // Don't export models without vertices if (reader.model.vertices.Count() == 0) { return; } var vertices = new Structs.Vertex[reader.model.vertices.Count()]; for (var i = 0; i < reader.model.vertices.Count(); i++) { vertices[i].Position = new Structs.Vector3D() { X = reader.model.vertices[i].position.X, Y = reader.model.vertices[i].position.Z, Z = reader.model.vertices[i].position.Y * -1 }; vertices[i].Normal = new Structs.Vector3D() { X = reader.model.vertices[i].normal.X, Y = reader.model.vertices[i].normal.Z, Z = reader.model.vertices[i].normal.Y }; vertices[i].TexCoord = new Structs.Vector2D() { X = reader.model.vertices[i].textureCoordX, Y = reader.model.vertices[i].textureCoordY }; } string outDir = exportDir; if (destinationOverride != null) { if (externalOverride) { outDir = destinationOverride; } else { outDir = Path.Combine(outDir, destinationOverride); } } else { outDir = Path.Combine(outDir, Path.GetDirectoryName(fileName)); } Directory.CreateDirectory(outDir); string filePath = Path.Combine(outDir, Path.GetFileName(fileName).Replace(".m2", "")); string objFilePath = filePath + ".obj"; string mtlFilePath = filePath + ".mtl"; StreamWriter objWriter = new StreamWriter(objFilePath); StreamWriter mtlWriter = new StreamWriter(mtlFilePath); // Write OBJ header. objWriter.WriteLine("# Written by Marlamin's WoW Export Tools. Source file: " + fileName); objWriter.WriteLine("mtllib " + Path.GetFileName(mtlFilePath)); foreach (var vertex in vertices) { objWriter.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z); objWriter.WriteLine("vt " + vertex.TexCoord.X + " " + (vertex.TexCoord.Y - 1) * -1); objWriter.WriteLine("vn " + (-vertex.Normal.X).ToString("F12") + " " + vertex.Normal.Y.ToString("F12") + " " + vertex.Normal.Z.ToString("F12")); } var indicelist = new List <uint>(); for (var i = 0; i < reader.model.skins[0].triangles.Count(); i++) { var t = reader.model.skins[0].triangles[i]; indicelist.Add(t.pt1); indicelist.Add(t.pt2); indicelist.Add(t.pt3); } var indices = indicelist.ToArray(); exportworker.ReportProgress(35, "Writing files.."); var renderbatches = new Structs.RenderBatch[reader.model.skins[0].submeshes.Count()]; for (var i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { if (enabledGeosets != null && !enabledGeosets[i]) { continue; } renderbatches[i].firstFace = reader.model.skins[0].submeshes[i].startTriangle; renderbatches[i].numFaces = reader.model.skins[0].submeshes[i].nTriangles; renderbatches[i].groupID = (uint)i; for (var tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++) { if (reader.model.skins[0].textureunit[tu].submeshIndex == i) { renderbatches[i].blendType = reader.model.renderflags[reader.model.skins[0].textureunit[tu].renderFlags].blendingMode; renderbatches[i].materialID = reader.model.texlookup[reader.model.skins[0].textureunit[tu].texture].textureID; } } } exportworker.ReportProgress(65, "Exporting textures.."); uint defaultTexID = DEFAULT_TEXTURE; if (!CASC.FileExists(defaultTexID)) { defaultTexID = DEFAULT_TEXTURE_SUB; } var textureID = 0; var materials = new Structs.Material[reader.model.textures.Count()]; for (var i = 0; i < reader.model.textures.Count(); i++) { uint textureFileDataID = defaultTexID; materials[i].flags = reader.model.textures[i].flags; if (reader.model.textures[i].type == 0) { if (reader.model.textureFileDataIDs != null && reader.model.textureFileDataIDs.Length > 0 && reader.model.textureFileDataIDs[i] != 0) { textureFileDataID = reader.model.textureFileDataIDs[i]; } else { Listfile.TryGetFileDataID(reader.model.textures[i].filename, out textureFileDataID); } } else { Console.WriteLine("Texture type " + reader.model.textures[i].type + " not supported, falling back to placeholder texture"); } materials[i].textureID = textureID + i; if (!Listfile.TryGetFilename(textureFileDataID, out var textureFilename)) { textureFilename = textureFileDataID.ToString(); } materials[i].filename = Path.GetFileNameWithoutExtension(textureFilename).Replace(" ", ""); try { var blpreader = new BLPReader(); blpreader.LoadBLP(textureFileDataID); blpreader.bmp.Save(Path.Combine(outDir, materials[i].filename + ".png")); } catch (Exception e) { CASCLib.Logger.WriteLine("Exception while saving BLP " + materials[i].filename + ": " + e.Message); } } exportworker.ReportProgress(85, "Writing files.."); foreach (var material in materials) { mtlWriter.WriteLine("newmtl " + material.filename); mtlWriter.WriteLine("illum 1"); mtlWriter.WriteLine("map_Kd " + material.filename + ".png"); if (ConfigurationManager.AppSettings["textureMetadata"] == "True") { foreach (var renderbatch in renderbatches) { if (materials[renderbatch.materialID].filename == material.filename) { mtlWriter.WriteLine("blend " + material.blendMode); } } } } mtlWriter.Close(); objWriter.WriteLine("o " + Path.GetFileName(fileName)); foreach (var renderbatch in renderbatches) { var i = renderbatch.firstFace; objWriter.WriteLine("g " + renderbatch.groupID); objWriter.WriteLine("usemtl " + materials[renderbatch.materialID].filename); objWriter.WriteLine("s 1"); while (i < (renderbatch.firstFace + renderbatch.numFaces)) { objWriter.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; } } objWriter.Close(); // Only export phys when exporting a single M2, causes issues for some users when combined with WMO/ADT if (destinationOverride == null && ConfigurationManager.AppSettings["exportCollision"] == "True") { exportworker.ReportProgress(90, "Exporting collision.."); objWriter = new StreamWriter(filePath + ".phys.obj"); objWriter.WriteLine("# Written by Marlamin's WoW Export Tools. Source file: " + fileName); for (var i = 0; i < reader.model.boundingvertices.Count(); i++) { objWriter.WriteLine("v " + reader.model.boundingvertices[i].vertex.X + " " + reader.model.boundingvertices[i].vertex.Z + " " + -reader.model.boundingvertices[i].vertex.Y); } for (var i = 0; i < reader.model.boundingtriangles.Count(); i++) { var t = reader.model.boundingtriangles[i]; objWriter.WriteLine("f " + (t.index_0 + 1) + " " + (t.index_1 + 1) + " " + (t.index_2 + 1)); } objWriter.Close(); } // https://en.wikipedia.org/wiki/Wavefront_.obj_file#Basic_materials // http://wiki.unity3d.com/index.php?title=ExportOBJ // http://web.cse.ohio-state.edu/~hwshen/581/Site/Lab3_files/Labhelp_Obj_parser.htm }
protected override void OnLoad(EventArgs e) { // Get OpenGL version Console.WriteLine("OpenGL version: " + GL.GetString(StringName.Version)); Console.WriteLine("OpenGL vendor: " + GL.GetString(StringName.Vendor)); this.CursorVisible = false; // Set up camera ActiveCamera = new OldCamera(Width, Height, new Vector3(0, 0, -1), new Vector3(0, 0, 1), Vector3.UnitY); // Vertex Attribute Object vertexAttribObject = GL.GenVertexArray(); GL.BindVertexArray(vertexAttribObject); // Vertices float[] vertices = new float[] { -1.0f, -1.0f, -10f, 0.0f, 0.0f, // Top-left 1.0f, -1.0f, -10f, 1.0f, 0.0f, // Top-right 1.0f, 1.0f, -10f, 1.0f, 1.0f, // Bottom-right -1.0f, 1.0f, -10f, 0.0f, 1.0f // Bottom-left }; vertexBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Count() * sizeof(float)), vertices, BufferUsageHint.StaticDraw); int verticeBufferSize = 0; GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer); GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out verticeBufferSize); Console.WriteLine("Vertices in buffer: " + verticeBufferSize / 5 / sizeof(float)); // Elements int[] elements = new int[] { 0, 1, 2, 2, 3, 0 }; elementBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ElementArrayBuffer, elementBuffer); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(elements.Count() * sizeof(int)), elements, BufferUsageHint.StaticDraw); // Vertex shader vertexShader = GL.CreateShader(ShaderType.VertexShader); string vertexSource = File.ReadAllText("Shaders/vertex.shader"); Console.WriteLine(vertexSource); GL.ShaderSource(vertexShader, vertexSource); GL.CompileShader(vertexShader); int vertexShaderStatus; GL.GetShader(vertexShader, ShaderParameter.CompileStatus, out vertexShaderStatus); Console.WriteLine("[VERTEX] Shader compile status: " + vertexShaderStatus); string vertexShaderLog; GL.GetShaderInfoLog(vertexShader, out vertexShaderLog); Console.Write(vertexShaderLog); // Fragment shader fragmentShader = GL.CreateShader(ShaderType.FragmentShader); string fragmentSource = File.ReadAllText("Shaders/fragment.shader"); Console.WriteLine(fragmentSource); GL.ShaderSource(fragmentShader, fragmentSource); GL.CompileShader(fragmentShader); int fragmentShaderStatus; GL.GetShader(fragmentShader, ShaderParameter.CompileStatus, out fragmentShaderStatus); Console.WriteLine("[FRAGMENT] Shader compile status: " + fragmentShaderStatus); string fragmentShaderLog; GL.GetShaderInfoLog(fragmentShader, out fragmentShaderLog); Console.Write(fragmentShaderLog); // Shader program shaderProgram = GL.CreateProgram(); GL.AttachShader(shaderProgram, vertexShader); GL.AttachShader(shaderProgram, fragmentShader); GL.BindFragDataLocation(shaderProgram, 0, "outColor"); GL.LinkProgram(shaderProgram); string programInfoLog = GL.GetProgramInfoLog(shaderProgram); Console.Write(programInfoLog); int programStatus; GL.GetProgram(shaderProgram, GetProgramParameterName.LinkStatus, out programStatus); Console.WriteLine("[FRAGMENT] Program link status: " + programStatus); GL.UseProgram(shaderProgram); GL.ValidateProgram(shaderProgram); GL.DetachShader(shaderProgram, vertexShader); GL.DeleteShader(vertexShader); GL.DetachShader(shaderProgram, fragmentShader); GL.DeleteShader(fragmentShader); // Set up matrix ActiveCamera.setupGLRenderMatrix(shaderProgram); // Shader settings int posAttrib = GL.GetAttribLocation(shaderProgram, "position"); GL.EnableVertexAttribArray(posAttrib); GL.VertexAttribPointer(posAttrib, 3, VertexAttribPointerType.Float, false, sizeof(float) * 5, 0); int texCoordAttrib = GL.GetAttribLocation(shaderProgram, "texCoord"); GL.EnableVertexAttribArray(texCoordAttrib); GL.VertexAttribPointer(texCoordAttrib, 2, VertexAttribPointerType.Float, false, sizeof(float) * 5, sizeof(float) * 3); // Clear GL.ClearColor(Color.Black); // Uniforms int uniColor = GL.GetUniformLocation(shaderProgram, "triangleColor"); Vector3 uniCol3 = new Vector3(1.0f, 0.0f, 0.0f); GL.Uniform3(uniColor, uniCol3); // Textures int[] textureIds = new int[2]; GL.GenTextures(2, textureIds); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, textureIds[0]); var blp = new BLPReader(); blp.LoadBLP(File.OpenRead(@"Z:\WoW extracts\20363_full\Textures\ShaneCube.blp")); System.Drawing.Imaging.BitmapData bmp_data = blp.bmp.LockBits(new System.Drawing.Rectangle(0, 0, blp.bmp.Width, blp.bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bmp_data.Width, bmp_data.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bmp_data.Scan0); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); blp.bmp.UnlockBits(bmp_data); GL.Uniform1(GL.GetUniformLocation(shaderProgram, "tex"), 0); GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, textureIds[1]); }
private Bitmap CompileGroup(string wmoname, string groupid) { Console.WriteLine(" group " + groupid); int min_x = 64; int min_y = 64; int max_x = 0; int max_y = 0; int x = 0; int y = 0; string wmoonlyname = Path.GetFileNameWithoutExtension(wmoname); string wmodir = Path.GetDirectoryName(wmoname).Replace("World\\", "World\\Minimaps\\"); List <string> filePaths = new List <string>(); string lastpath = ""; for (int cur_x = 0; cur_x < 64; cur_x++) { for (int cur_y = 0; cur_y < 64; cur_y++) { string wmogroupfilename = wmodir + "\\" + wmoonlyname + "_" + groupid + "_" + cur_x.ToString().PadLeft(2, '0') + "_" + cur_y.ToString().PadLeft(2, '0') + ".blp"; //wmogroupfilename = wmogroupfilename.Replace("\\", "\\\\"); if (wmogroupfilename.Contains("000_00_00")) { Console.WriteLine("CHECKING " + wmogroupfilename); } if (CASC.FileExists(wmogroupfilename)) { Console.WriteLine(wmogroupfilename + " exists!"); filePaths.Add(wmogroupfilename); } } } foreach (string path in filePaths) { Console.WriteLine(path); x = int.Parse(path.Substring(path.Length - 9, 2)); y = int.Parse(path.Substring(path.Length - 6, 2)); 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; } // Console.WriteLine("[" + groupid + "] MIN: " + min_x + " " + min_y); // Console.WriteLine("[" + groupid + "] MAX: " + max_x + " " + max_y); lastpath = path; } var res_x = 0; var res_y = 0; if (min_x == 0 && max_x == 0 && min_y == 0 && max_y == 0) { var blpreader = new BLPReader(); blpreader.LoadBLP(lastpath); res_x = blpreader.bmp.Width; res_y = blpreader.bmp.Height; } else { res_x = (((max_x - min_x) * 256) + 256); res_y = (((max_y - min_y) * 256) + 256); } //Console.WriteLine("[" + groupid + "] " + "Creating new image of " + res_x + "x" + res_y + " for " + wmoname); if (res_x < 0) { res_x = 1; } if (res_y < 0) { res_y = 1; } Bitmap bmp = new Bitmap(res_x, res_y); Graphics g = Graphics.FromImage(bmp); foreach (string path in filePaths) { x = int.Parse(path.Substring(path.Length - 9, 2)); y = int.Parse(path.Substring(path.Length - 6, 2)); var blpreader = new BLPReader(); blpreader.LoadBLP(path); // Console.WriteLine("BLP Width: " + blpreader.bmp.Width); // Console.WriteLine("BLP Height: " + blpreader.bmp.Height); var draw_x = (x - min_x) * 256; var draw_y = (max_y - (y - min_y)) * 256; //Console.WriteLine("Drawing tile at " + draw_x + " & " + draw_y); g.DrawImage(blpreader.bmp, draw_x, draw_y, new Rectangle(0, 0, blpreader.bmp.Width, blpreader.bmp.Height), GraphicsUnit.Pixel); } g.Dispose(); return(bmp); }
public static void ExportWMO(uint fileDataID, BackgroundWorker exportworker = null, string destinationOverride = null, short doodadSetExportID = short.MaxValue, string fileName = "", bool[] enabledGroups = null, bool[] enabledSets = null) { if (exportworker == null) { exportworker = new BackgroundWorker(); exportworker.WorkerReportsProgress = true; } if (string.IsNullOrEmpty(fileName)) { if (!Listfile.TryGetFilename(fileDataID, out fileName)) { CASCLib.Logger.WriteLine("Warning! Could not find filename for " + fileDataID + "!"); } } fileName = fileName.ToLower(); Console.WriteLine("Loading WMO file.."); exportworker.ReportProgress(5, "Reading WMO.."); var outDir = ConfigurationManager.AppSettings["outdir"]; var wmo = new WMOReader().LoadWMO(fileDataID, 0, 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.group.Count()]; for (var g = 0; g < wmo.group.Count(); g++) { if (enabledGroups != null && !enabledGroups[g]) { Console.WriteLine("Skipping group " + g + " due to WMO control"); continue; } Console.WriteLine("Loading group #" + g); if (wmo.group[g].mogp.vertices == null) { Console.WriteLine("Group has no vertices!"); continue; } for (var i = 0; i < wmo.groupNames.Count(); i++) { if (wmo.group[g].mogp.nameOffset == wmo.groupNames[i].offset) { groups[g].name = wmo.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.group[g].mogp.vertices.Count()]; for (var i = 0; i < wmo.group[g].mogp.vertices.Count(); i++) { groups[g].vertices[i].Position = new Structs.Vector3D() { X = wmo.group[g].mogp.vertices[i].vector.X * -1, Y = wmo.group[g].mogp.vertices[i].vector.Z, Z = wmo.group[g].mogp.vertices[i].vector.Y }; groups[g].vertices[i].Normal = new Structs.Vector3D() { X = wmo.group[g].mogp.normals[i].normal.X, Y = wmo.group[g].mogp.normals[i].normal.Z, Z = wmo.group[g].mogp.normals[i].normal.Y }; groups[g].vertices[i].TexCoord = new Structs.Vector2D() { X = wmo.group[g].mogp.textureCoords[0][i].X, Y = wmo.group[g].mogp.textureCoords[0][i].Y }; totalVertices++; } var indicelist = new List <uint>(); for (var i = 0; i < wmo.group[g].mogp.indices.Count(); i++) { indicelist.Add(wmo.group[g].mogp.indices[i].indice); } groups[g].indices = indicelist.ToArray(); } // Create output directory. if (destinationOverride == null) { Directory.CreateDirectory(fileName == null ? outDir : Path.Combine(outDir, Path.GetDirectoryName(fileName))); } exportworker.ReportProgress(55, "Exporting WMO doodads.."); List <string> doodadList = new List <string>(); doodadList.Add("ModelFile;PositionX;PositionY;PositionZ;RotationW;RotationX;RotationY;RotationZ;ScaleFactor;DoodadSet"); if (doodadSetExportID > -1) { for (var i = 0; i < wmo.doodadSets.Count(); i++) { if (enabledSets != null && !enabledSets[i]) { Console.WriteLine("Skipping doodadSet " + i + " due to WMO control"); continue; } var doodadSet = wmo.doodadSets[i]; var currentDoodadSetName = doodadSet.setName.Replace("Set_", "").Replace("SET_", "").Replace("$DefaultGlobal", "Default"); if (doodadSetExportID != short.MaxValue) { if (i != 0 && i != doodadSetExportID) { Console.WriteLine("Skipping doodadset with ID " + i + " (" + currentDoodadSetName + ") because export filter is set to " + doodadSetExportID); continue; } } for (var j = doodadSet.firstInstanceIndex; j < (doodadSet.firstInstanceIndex + doodadSet.numDoodads); j++) { var doodadDefinition = wmo.doodadDefinitions[j]; var doodadFileName = ""; uint doodadFileDataID = 0; var doodadNotFound = false; if (wmo.doodadIds != null) { doodadFileDataID = wmo.doodadIds[doodadDefinition.offset]; if (!Listfile.TryGetFilename(doodadFileDataID, out doodadFileName)) { CASCLib.Logger.WriteLine("Could not find filename for " + doodadFileDataID + ", setting filename to filedataid.."); doodadFileName = doodadFileDataID.ToString(); } } else { CASCLib.Logger.WriteLine("Warning!! File " + fileName + " ID: " + fileDataID + " still has filenames!"); foreach (var doodadNameEntry in wmo.doodadNames) { if (doodadNameEntry.startOffset == doodadDefinition.offset) { doodadFileName = doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2").ToLower(); if (!Listfile.TryGetFileDataID(doodadFileName, out doodadFileDataID)) { CASCLib.Logger.WriteLine("Error! Could not find filedataid for " + doodadFileName + "!"); doodadNotFound = true; continue; } } } } if (!doodadNotFound) { string objFileName = Path.GetFileNameWithoutExtension(doodadFileName ?? doodadFileDataID.ToString()) + ".obj"; string objPath = Path.Combine(destinationOverride ?? outDir, destinationOverride != null ? "" : Path.GetDirectoryName(fileName)); string objName = Path.Combine(objPath, objFileName); if (!File.Exists(objName)) { M2Exporter.ExportM2(doodadFileDataID, null, objPath, doodadFileName); } if (File.Exists(objName)) { doodadList.Add(objFileName + ";" + 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); } } } } } if (doodadList.Count > 1) { string mpiFile = (fileName == null ? fileDataID.ToString() : Path.GetFileNameWithoutExtension(fileName.Replace(" ", ""))) + "_ModelPlacementInformation.csv"; File.WriteAllText(Path.Combine(outDir, destinationOverride ?? Path.GetDirectoryName(fileName), mpiFile), string.Join("\n", doodadList.ToArray())); } exportworker.ReportProgress(65, "Exporting WMO textures.."); var mtlsb = new StringBuilder(); var textureID = 0; if (wmo.materials == null) { CASCLib.Logger.WriteLine("Unable to find materials for WMO " + fileDataID + ", not exporting!"); return; } var materials = new Structs.Material[wmo.materials.Count()]; var extraMaterials = new List <Structs.Material>(); for (var i = 0; i < wmo.materials.Count(); i++) { var blpReader = new BLPReader(); if (wmo.textures == null) { if (Listfile.TryGetFilename(wmo.materials[i].texture1, out var textureFilename)) { materials[i].filename = Path.GetFileNameWithoutExtension(textureFilename).Replace(" ", "").ToLower(); } else { materials[i].filename = wmo.materials[i].texture1.ToString(); } blpReader.LoadBLP(wmo.materials[i].texture1); } else { for (var ti = 0; ti < wmo.textures.Count(); ti++) { if (wmo.textures[ti].startOffset == wmo.materials[i].texture1) { materials[i].filename = Path.GetFileNameWithoutExtension(wmo.textures[ti].filename).Replace(" ", "").ToLower(); blpReader.LoadBLP(wmo.textures[ti].filename); } } } materials[i].textureID = textureID + i; materials[i].transparent = wmo.materials[i].blendMode != 0; materials[i].blendMode = wmo.materials[i].blendMode; materials[i].shaderID = wmo.materials[i].shader; materials[i].terrainType = wmo.materials[i].groundType; string saveLocation = Path.Combine(outDir, destinationOverride ?? Path.GetDirectoryName(fileName), materials[i].filename + ".png"); if (!File.Exists(saveLocation)) { try { if (materials[i].transparent) { blpReader.bmp.Save(saveLocation); } else { blpReader.bmp.Clone(new Rectangle(0, 0, blpReader.bmp.Width, blpReader.bmp.Height), PixelFormat.Format32bppRgb).Save(saveLocation); } } catch (Exception e) { CASCLib.Logger.WriteLine("Exception while saving BLP " + materials[i].filename + ": " + e.Message); } } textureID++; string extraPath = Path.Combine(outDir, destinationOverride ?? Path.GetDirectoryName(fileName)); ExportExtraMaterials(wmo.materials[i].texture2, wmo, extraMaterials, i, extraPath); ExportExtraMaterials(wmo.materials[i].texture3, wmo, extraMaterials, i, extraPath); } var numRenderbatches = 0; //Get total amount of render batches for (var i = 0; i < wmo.group.Count(); i++) { if (wmo.group[i].mogp.renderBatches == null) { continue; } numRenderbatches = numRenderbatches + wmo.group[i].mogp.renderBatches.Count(); } exportworker.ReportProgress(75, "Exporting WMO model.."); bool exportMetadata = ConfigurationManager.AppSettings["textureMetadata"] == "True"; //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"); } if (exportMetadata) { for (var g = 0; g < wmo.group.Count(); g++) { groups[g].renderBatches = new Structs.RenderBatch[numRenderbatches]; var group = wmo.group[g]; if (group.mogp.renderBatches == null) { continue; } for (var i = 0; i < group.mogp.renderBatches.Count(); i++) { var batch = group.mogp.renderBatches[i]; if (materials[batch.materialID].filename == material.filename) { mtlsb.Append("blend " + material.blendMode + "\n"); } } } } } foreach (var material in extraMaterials) { 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"); } } string mtlFile = fileName != null?fileName.Replace(".wmo", ".mtl") : fileDataID + ".mtl"; if (destinationOverride != null) { mtlFile = Path.GetFileName(mtlFile); } File.WriteAllText(Path.Combine(destinationOverride ?? outDir, mtlFile), mtlsb.ToString()); var rb = 0; for (var g = 0; g < wmo.group.Count(); g++) { groups[g].renderBatches = new Structs.RenderBatch[numRenderbatches]; var group = wmo.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; groups[g].renderBatches[rb].materialID = batch.flags == 2 ? (uint)batch.possibleBox2_3 : batch.materialID; groups[g].renderBatches[rb].blendType = wmo.materials[batch.materialID].blendMode; groups[g].renderBatches[rb].groupID = (uint)g; rb++; } } exportworker.ReportProgress(95, "Writing WMO files.."); string objFile = fileName != null?fileName.Replace(".wmo", ".obj") : fileDataID + ".obj"; if (destinationOverride != null) { objFile = Path.GetFileName(objFile); } string fileID = fileName ?? fileDataID.ToString(); StreamWriter objWriter = new StreamWriter(Path.Combine(destinationOverride ?? outDir, objFile)); objWriter.WriteLine("# Written by Marlamin's WoW Export Tools. Original file: " + fileID); objWriter.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(fileID) + ".mtl"); objWriter.WriteLine("o " + Path.GetFileName(fileID)); foreach (var group in groups) { if (group.vertices == null) { continue; } Console.WriteLine("Writing " + group.name); //Adjusted according to the WaveFront Object (.obj) File Format spec //https://people.cs.clemson.edu/~dhouse/courses/405/docs/brief-obj-file-format.html foreach (var vertex in group.vertices) { objWriter.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z); } foreach (var vertex in group.vertices) { objWriter.WriteLine("vt " + vertex.TexCoord.X + " " + (vertex.TexCoord.Y - 1) * -1 + " 0.0000"); } foreach (var vertex in group.vertices) { objWriter.WriteLine("vn " + (-vertex.Normal.X).ToString("F12") + " " + vertex.Normal.Y.ToString("F12") + " " + vertex.Normal.Z.ToString("F12")); } var indices = group.indices; for (int rbi = 0; rbi < group.renderBatches.Count(); rbi++) { var renderbatch = group.renderBatches[rbi]; var i = renderbatch.firstFace; if (renderbatch.numFaces > 0) { objWriter.WriteLine("o " + group.name + rbi); objWriter.WriteLine("g " + group.name + rbi); //3DS Max's OBJ importer fails with invalid normal index without groups being defined objWriter.WriteLine("usemtl " + materials[renderbatch.materialID].filename); objWriter.WriteLine("s 1"); while (i < (renderbatch.firstFace + renderbatch.numFaces)) { objWriter.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 = i + 3; } } } } objWriter.Close(); }
public static void ExportM2(uint fileDataID, BackgroundWorker exportworker = null, string destinationOverride = null, string filename = "") { 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 outdir = ConfigurationManager.AppSettings["outdir"]; var reader = new M2Reader(); exportworker.ReportProgress(15, "Reading M2.."); if (!CASC.FileExists(fileDataID)) { throw new Exception("404 M2 not found!"); } reader.LoadM2(fileDataID); // Don't export models without vertices if (reader.model.vertices.Count() == 0) { return; } var vertices = new Structs.Vertex[reader.model.vertices.Count()]; for (var i = 0; i < reader.model.vertices.Count(); i++) { vertices[i].Position = new Structs.Vector3D() { X = reader.model.vertices[i].position.X, Y = reader.model.vertices[i].position.Z, Z = reader.model.vertices[i].position.Y * -1 }; vertices[i].Normal = new Structs.Vector3D() { X = reader.model.vertices[i].normal.X, Y = reader.model.vertices[i].normal.Z, Z = reader.model.vertices[i].normal.Y }; vertices[i].TexCoord = new Structs.Vector2D() { X = reader.model.vertices[i].textureCoordX, Y = reader.model.vertices[i].textureCoordY }; } StreamWriter objsw; if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(filename))); } objsw = new StreamWriter(Path.Combine(outdir, filename.Replace(".m2", ".obj"))); } else { if (!Directory.Exists(outdir)) { Directory.CreateDirectory(outdir); } objsw = new StreamWriter(Path.Combine(outdir, fileDataID + ".obj")); } } else { if (!string.IsNullOrEmpty(filename)) { objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileName(filename.ToLower()).Replace(".m2", ".obj"))); } else { objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, fileDataID + ".obj")); } } if (!string.IsNullOrEmpty(filename)) { objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original file: " + filename); objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(filename) + ".mtl"); } else { objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original fileDataID: " + fileDataID); objsw.WriteLine("mtllib " + fileDataID + ".mtl"); } foreach (var vertex in vertices) { objsw.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z); objsw.WriteLine("vt " + vertex.TexCoord.X + " " + (vertex.TexCoord.Y - 1) * -1); objsw.WriteLine("vn " + (-vertex.Normal.X).ToString("F12") + " " + vertex.Normal.Y.ToString("F12") + " " + vertex.Normal.Z.ToString("F12")); } var indicelist = new List <uint>(); for (var i = 0; i < reader.model.skins[0].triangles.Count(); i++) { var t = reader.model.skins[0].triangles[i]; indicelist.Add(t.pt1); indicelist.Add(t.pt2); indicelist.Add(t.pt3); } var indices = indicelist.ToArray(); exportworker.ReportProgress(35, "Writing files.."); var renderbatches = new Structs.RenderBatch[reader.model.skins[0].submeshes.Count()]; for (var i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { renderbatches[i].firstFace = reader.model.skins[0].submeshes[i].startTriangle; renderbatches[i].numFaces = reader.model.skins[0].submeshes[i].nTriangles; renderbatches[i].groupID = (uint)i; for (var tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++) { if (reader.model.skins[0].textureunit[tu].submeshIndex == i) { renderbatches[i].blendType = reader.model.renderflags[reader.model.skins[0].textureunit[tu].renderFlags].blendingMode; renderbatches[i].materialID = reader.model.texlookup[reader.model.skins[0].textureunit[tu].texture].textureID; } } } exportworker.ReportProgress(65, "Exporting textures.."); StreamWriter mtlsb; if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { mtlsb = new StreamWriter(Path.Combine(outdir, filename.Replace(".m2", ".mtl"))); } else { mtlsb = new StreamWriter(Path.Combine(outdir, fileDataID + ".mtl")); } } else { if (!string.IsNullOrEmpty(filename)) { mtlsb = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileName(filename.ToLower()).Replace(".m2", ".mtl"))); } else { mtlsb = new StreamWriter(Path.Combine(outdir, destinationOverride, fileDataID + ".mtl")); } } var textureID = 0; var materials = new Structs.Material[reader.model.textures.Count()]; for (var i = 0; i < reader.model.textures.Count(); i++) { uint textureFileDataID = 840426; materials[i].flags = reader.model.textures[i].flags; switch (reader.model.textures[i].type) { case 0: if (reader.model.textureFileDataIDs != null && reader.model.textureFileDataIDs.Length > 0 && reader.model.textureFileDataIDs[i] != 0) { textureFileDataID = reader.model.textureFileDataIDs[i]; } else { Listfile.TryGetFileDataID(reader.model.textures[i].filename, out textureFileDataID); } break; case 1: case 2: case 11: default: Console.WriteLine("Texture type " + reader.model.textures[i].type + " not supported, falling back to placeholder texture"); break; } materials[i].textureID = textureID + i; if (!Listfile.TryGetFilename(textureFileDataID, out var textureFilename)) { textureFilename = textureFileDataID.ToString(); } materials[i].filename = Path.GetFileNameWithoutExtension(textureFilename); string textureSaveLocation; if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { textureSaveLocation = Path.Combine(outdir, Path.GetDirectoryName(filename), materials[i].filename + ".png"); } else { textureSaveLocation = Path.Combine(outdir, materials[i].filename + ".png"); } } else { textureSaveLocation = Path.Combine(outdir, destinationOverride, materials[i].filename + ".png"); } try { var blpreader = new BLPReader(); blpreader.LoadBLP(textureFileDataID); blpreader.bmp.Save(textureSaveLocation); } catch (Exception e) { CASCLib.Logger.WriteLine("Exception while saving BLP " + materials[i].filename + ": " + e.Message); } } exportworker.ReportProgress(85, "Writing files.."); foreach (var material in materials) { mtlsb.WriteLine("newmtl " + material.filename); mtlsb.WriteLine("illum 1"); //mtlsb.WriteLine("map_Ka " + material.filename + ".png"); mtlsb.WriteLine("map_Kd " + material.filename + ".png"); } mtlsb.Close(); if (!string.IsNullOrEmpty(filename)) { objsw.WriteLine("g " + Path.GetFileNameWithoutExtension(filename)); } else { objsw.WriteLine("g " + fileDataID); } foreach (var renderbatch in renderbatches) { var i = renderbatch.firstFace; if (!string.IsNullOrEmpty(filename)) { objsw.WriteLine("o " + Path.GetFileNameWithoutExtension(filename) + renderbatch.groupID); } else { objsw.WriteLine("g " + fileDataID.ToString() + renderbatch.groupID.ToString()); } objsw.WriteLine("usemtl " + materials[renderbatch.materialID].filename); 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(); // Only export phys when exporting a single M2, causes issues for some users when combined with WMO/ADT if (destinationOverride == null) { exportworker.ReportProgress(90, "Exporting collision.."); if (!string.IsNullOrEmpty(filename)) { objsw = new StreamWriter(Path.Combine(outdir, Path.GetFileName(filename.ToLower()).Replace(".m2", ".phys.obj"))); } else { objsw = new StreamWriter(Path.Combine(outdir, fileDataID + ".phys.obj")); } objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original file id: " + fileDataID); for (var i = 0; i < reader.model.boundingvertices.Count(); i++) { objsw.WriteLine("v " + reader.model.boundingvertices[i].vertex.X + " " + reader.model.boundingvertices[i].vertex.Z + " " + -reader.model.boundingvertices[i].vertex.Y); } for (var i = 0; i < reader.model.boundingtriangles.Count(); i++) { var t = reader.model.boundingtriangles[i]; objsw.WriteLine("f " + (t.index_0 + 1) + " " + (t.index_1 + 1) + " " + (t.index_2 + 1)); } objsw.Close(); } // https://en.wikipedia.org/wiki/Wavefront_.obj_file#Basic_materials // http://wiki.unity3d.com/index.php?title=ExportOBJ // http://web.cse.ohio-state.edu/~hwshen/581/Site/Lab3_files/Labhelp_Obj_parser.htm }
public static void exportWMO(string file, BackgroundWorker exportworker = null, string destinationOverride = null) { if (exportworker == null) { exportworker = new BackgroundWorker(); exportworker.WorkerReportsProgress = true; } Logger.WriteLine("WMO glTF Exporter: Loading file {0}...", file); exportworker.ReportProgress(5, "Reading WMO.."); var outdir = ConfigurationManager.AppSettings["outdir"]; var reader = new WMOReader(); reader.LoadWMO(file); System.Globalization.CultureInfo customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone(); customCulture.NumberFormat.NumberDecimalSeparator = "."; System.Threading.Thread.CurrentThread.CurrentCulture = customCulture; if (destinationOverride == null) { if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file))); } } exportworker.ReportProgress(25, "Generating glTF.."); var glTF = new glTF() { asset = new Asset() { version = "2.0", generator = "Marlamin's WoW Exporter " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), copyright = "Contents are owned by Blizzard Entertainment", minVersion = "2.0" } }; var groups = new Structs.WMOGroup[reader.wmofile.group.Count()]; FileStream stream; if (destinationOverride == null) { stream = new FileStream(Path.Combine(outdir, file.Replace(".wmo", ".bin")), FileMode.OpenOrCreate); } else { stream = new FileStream(Path.Combine(destinationOverride, Path.GetFileNameWithoutExtension(file) + ".bin"), FileMode.OpenOrCreate); } var writer = new BinaryWriter(stream); var bufferViews = new List <BufferView>(); var accessorInfo = new List <Accessor>(); var meshes = new List <Mesh>(); for (int g = 0; g < reader.wmofile.group.Count(); g++) { if (reader.wmofile.group[g].mogp.vertices == null) { Console.WriteLine("Group has no vertices!"); continue; } for (int i = 0; i < reader.wmofile.groupNames.Count(); i++) { if (reader.wmofile.group[g].mogp.nameOffset == reader.wmofile.groupNames[i].offset) { groups[g].name = reader.wmofile.groupNames[i].name.Replace(" ", "_"); } } if (groups[g].name == "antiportal") { Console.WriteLine("Group is antiportal"); continue; } // Position bufferview var vPosBuffer = new BufferView() { buffer = 0, byteOffset = (uint)writer.BaseStream.Position, target = 34962 }; var minPosX = float.MaxValue; var minPosY = float.MaxValue; var minPosZ = float.MaxValue; var maxPosX = float.MinValue; var maxPosY = float.MinValue; var maxPosZ = float.MinValue; for (int i = 0; i < reader.wmofile.group[g].mogp.vertices.Count(); i++) { writer.Write(reader.wmofile.group[g].mogp.vertices[i].vector.X * -1); writer.Write(reader.wmofile.group[g].mogp.vertices[i].vector.Z); writer.Write(reader.wmofile.group[g].mogp.vertices[i].vector.Y); if (reader.wmofile.group[g].mogp.vertices[i].vector.X * -1 < minPosX) { minPosX = reader.wmofile.group[g].mogp.vertices[i].vector.X * -1; } if (reader.wmofile.group[g].mogp.vertices[i].vector.Z < minPosY) { minPosY = reader.wmofile.group[g].mogp.vertices[i].vector.Z; } if (reader.wmofile.group[g].mogp.vertices[i].vector.Y < minPosZ) { minPosZ = reader.wmofile.group[g].mogp.vertices[i].vector.Y; } if (reader.wmofile.group[g].mogp.vertices[i].vector.X * -1 > maxPosX) { maxPosX = reader.wmofile.group[g].mogp.vertices[i].vector.X * -1; } if (reader.wmofile.group[g].mogp.vertices[i].vector.Z > maxPosY) { maxPosY = reader.wmofile.group[g].mogp.vertices[i].vector.Z; } if (reader.wmofile.group[g].mogp.vertices[i].vector.Y > maxPosZ) { maxPosZ = reader.wmofile.group[g].mogp.vertices[i].vector.Y; } } vPosBuffer.byteLength = (uint)writer.BaseStream.Position - vPosBuffer.byteOffset; var posLoc = accessorInfo.Count(); accessorInfo.Add(new Accessor() { name = "vPos", bufferView = bufferViews.Count(), byteOffset = 0, componentType = 5126, count = (uint)reader.wmofile.group[g].mogp.vertices.Count(), type = "VEC3", min = new float[] { minPosX, minPosY, minPosZ }, max = new float[] { maxPosX, maxPosY, maxPosZ } }); bufferViews.Add(vPosBuffer); // Normal bufferview var normalBuffer = new BufferView() { buffer = 0, byteOffset = (uint)writer.BaseStream.Position, target = 34962 }; for (int i = 0; i < reader.wmofile.group[g].mogp.vertices.Count(); i++) { writer.Write(reader.wmofile.group[g].mogp.normals[i].normal.X); writer.Write(reader.wmofile.group[g].mogp.normals[i].normal.Z); writer.Write(reader.wmofile.group[g].mogp.normals[i].normal.Y); } normalBuffer.byteLength = (uint)writer.BaseStream.Position - normalBuffer.byteOffset; var normalLoc = accessorInfo.Count(); accessorInfo.Add(new Accessor() { name = "vNormal", bufferView = bufferViews.Count(), byteOffset = 0, componentType = 5126, count = (uint)reader.wmofile.group[g].mogp.vertices.Count(), type = "VEC3" }); bufferViews.Add(normalBuffer); // TexCoord bufferview var texCoordBuffer = new BufferView() { buffer = 0, byteOffset = (uint)writer.BaseStream.Position, target = 34962 }; for (int i = 0; i < reader.wmofile.group[g].mogp.vertices.Count(); i++) { writer.Write(reader.wmofile.group[g].mogp.textureCoords[0][i].X); writer.Write(reader.wmofile.group[g].mogp.textureCoords[0][i].Y); } texCoordBuffer.byteLength = (uint)writer.BaseStream.Position - texCoordBuffer.byteOffset; var texLoc = accessorInfo.Count(); accessorInfo.Add(new Accessor() { name = "vTex", bufferView = bufferViews.Count(), byteOffset = 0, componentType = 5126, count = (uint)reader.wmofile.group[g].mogp.vertices.Count(), type = "VEC2" }); bufferViews.Add(texCoordBuffer); var indexBufferPos = bufferViews.Count(); for (int i = 0; i < reader.wmofile.group[g].mogp.renderBatches.Count(); i++) { var batch = reader.wmofile.group[g].mogp.renderBatches[i]; accessorInfo.Add(new Accessor() { name = "indices", bufferView = indexBufferPos, byteOffset = batch.firstFace * 2, componentType = 5123, count = batch.numFaces, type = "SCALAR" }); var mesh = new Mesh(); mesh.name = groups[g].name + "_" + i; mesh.primitives = new Primitive[1]; mesh.primitives[0].attributes = new Dictionary <string, int> { { "POSITION", posLoc }, { "NORMAL", normalLoc }, { "TEXCOORD_0", texLoc } }; mesh.primitives[0].indices = (uint)accessorInfo.Count() - 1; if (batch.flags == 2) { mesh.primitives[0].material = (uint)batch.possibleBox2_3; } else { mesh.primitives[0].material = batch.materialID; } mesh.primitives[0].mode = 4; meshes.Add(mesh); } var indiceBuffer = new BufferView() { buffer = 0, byteOffset = (uint)writer.BaseStream.Position, target = 34963 }; for (int i = 0; i < reader.wmofile.group[g].mogp.indices.Count(); i++) { writer.Write(reader.wmofile.group[g].mogp.indices[i].indice); } indiceBuffer.byteLength = (uint)writer.BaseStream.Position - indiceBuffer.byteOffset; bufferViews.Add(indiceBuffer); } glTF.bufferViews = bufferViews.ToArray(); glTF.accessors = accessorInfo.ToArray(); glTF.buffers = new Buffer[1]; glTF.buffers[0].byteLength = (uint)writer.BaseStream.Length; glTF.buffers[0].uri = Path.GetFileNameWithoutExtension(file) + ".bin"; writer.Close(); writer.Dispose(); exportworker.ReportProgress(65, "Exporting textures.."); if (reader.wmofile.materials == null) { Logger.WriteLine("WMO glTF exporter: Materials empty"); return; } var materialCount = reader.wmofile.materials.Count(); glTF.images = new Image[materialCount]; glTF.textures = new Texture[materialCount]; glTF.materials = new Material[materialCount]; for (int i = 0; i < materialCount; i++) { for (int ti = 0; ti < reader.wmofile.textures.Count(); ti++) { if (reader.wmofile.textures[ti].startOffset == reader.wmofile.materials[i].texture1) { var textureFilename = Path.GetFileNameWithoutExtension(reader.wmofile.textures[ti].filename).ToLower(); glTF.images[i].uri = textureFilename + ".png"; glTF.textures[i].sampler = 0; glTF.textures[i].source = i; glTF.materials[i].name = textureFilename; glTF.materials[i].pbrMetallicRoughness = new PBRMetallicRoughness(); glTF.materials[i].pbrMetallicRoughness.baseColorTexture = new TextureIndex(); glTF.materials[i].pbrMetallicRoughness.baseColorTexture.index = i; glTF.materials[i].pbrMetallicRoughness.metallicFactor = 0.0f; switch (reader.wmofile.materials[i].blendMode) { case 0: glTF.materials[i].alphaMode = "OPAQUE"; glTF.materials[i].alphaCutoff = 0.0f; break; case 1: glTF.materials[i].alphaMode = "MASK"; glTF.materials[i].alphaCutoff = 0.90393700787f; break; case 2: glTF.materials[i].alphaMode = "MASK"; glTF.materials[i].alphaCutoff = 0.5f; break; default: glTF.materials[i].alphaMode = "OPAQUE"; glTF.materials[i].alphaCutoff = 0.0f; break; } var saveLocation = ""; if (destinationOverride == null) { saveLocation = Path.Combine(outdir, Path.GetDirectoryName(file), textureFilename + ".png"); } else { saveLocation = Path.Combine(outdir, destinationOverride, textureFilename + ".png"); } if (!File.Exists(saveLocation)) { var blpreader = new BLPReader(); blpreader.LoadBLP(reader.wmofile.textures[ti].filename); try { blpreader.bmp.Save(saveLocation); } catch (Exception e) { Console.WriteLine("Error exporting texture " + reader.wmofile.textures[ti].filename + ": " + e.Message); } } } } } glTF.samplers = new Sampler[1]; glTF.samplers[0].name = "Default Sampler"; glTF.samplers[0].minFilter = 9986; glTF.samplers[0].magFilter = 9729; glTF.samplers[0].wrapS = 10497; glTF.samplers[0].wrapT = 10497; glTF.scenes = new Scene[1]; glTF.scenes[0].name = Path.GetFileNameWithoutExtension(file); glTF.nodes = new Node[meshes.Count()]; var meshIDs = new List <int>(); for (var i = 0; i < meshes.Count(); i++) { glTF.nodes[i].name = meshes[i].name; glTF.nodes[i].mesh = i; meshIDs.Add(i); } glTF.scenes[0].nodes = meshIDs.ToArray(); glTF.meshes = meshes.ToArray(); glTF.scene = 0; string currentDoodadSetName = ""; for (int i = 0; i < reader.wmofile.doodadDefinitions.Count(); i++) { var doodadDefinition = reader.wmofile.doodadDefinitions[i]; foreach (var doodadSet in reader.wmofile.doodadSets) { if (doodadSet.firstInstanceIndex == i) { Console.WriteLine("At set: " + doodadSet.setName); currentDoodadSetName = doodadSet.setName.Replace("Set_", "").Replace("SET_", "").Replace("$DefaultGlobal", "Default"); } } foreach (var doodadNameEntry in reader.wmofile.doodadNames) { if (doodadNameEntry.startOffset == doodadDefinition.offset) { if (!File.Exists(Path.GetFileNameWithoutExtension(doodadNameEntry.filename).ToLower() + ".gltf")) { if (destinationOverride == null) { M2Exporter.exportM2(doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2").ToLower(), null, Path.Combine(outdir, Path.GetDirectoryName(file))); } else { M2Exporter.exportM2(doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2").ToLower(), null, destinationOverride); } } } } } exportworker.ReportProgress(95, "Writing to file.."); if (destinationOverride == null) { File.WriteAllText(Path.Combine(outdir, file.Replace(".wmo", ".gltf")), JsonConvert.SerializeObject(glTF, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); } else { File.WriteAllText(Path.Combine(destinationOverride, Path.GetFileName(file.ToLower()).Replace(".wmo", ".gltf")), JsonConvert.SerializeObject(glTF, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); } Logger.WriteLine("Done exporting WMO file!"); }
public static void ExportM2(string file, BackgroundWorker exportworker = null, string destinationOverride = null, string outdir = "", uint filedataid = 0) { if (exportworker == null) { exportworker = new BackgroundWorker(); exportworker.WorkerReportsProgress = true; } Console.WriteLine("M2 glTF Exporter: Loading file {0}...", file); exportworker.ReportProgress(5, "Reading M2.."); var reader = new M2Reader(); if (filedataid != 0) { reader.LoadM2(filedataid); } else { reader.LoadM2(file); } var customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone(); customCulture.NumberFormat.NumberDecimalSeparator = "."; System.Threading.Thread.CurrentThread.CurrentCulture = customCulture; if (destinationOverride == null) { if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file))); } } file = file.ToLower(); if (reader.model.vertices.Count() == 0) { Console.WriteLine("M2 glTF Exporter: File {0} has no vertices, skipping export!", file); return; } exportworker.ReportProgress(25, "Generating glTF.."); var glTF = new glTF() { asset = new Asset() { version = "2.0", generator = "Marlamin's WoW Exporter " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), copyright = "Contents are owned by Blizzard Entertainment", minVersion = "2.0" } }; FileStream stream; if (destinationOverride == null) { stream = new FileStream(Path.Combine(outdir, file.Replace(".m2", ".bin")), FileMode.OpenOrCreate); } else { stream = new FileStream(Path.Combine(destinationOverride, Path.GetFileNameWithoutExtension(file).ToLower() + ".bin"), FileMode.OpenOrCreate); } var writer = new BinaryWriter(stream); var bufferViews = new List <BufferView>(); var accessorInfo = new List <Accessor>(); var meshes = new List <Mesh>(); // Position bufferview var vPosBuffer = new BufferView() { buffer = 0, byteOffset = (uint)writer.BaseStream.Position, target = 34962 }; var minPosX = float.MaxValue; var minPosY = float.MaxValue; var minPosZ = float.MaxValue; var maxPosX = float.MinValue; var maxPosY = float.MinValue; var maxPosZ = float.MinValue; for (var i = 0; i < reader.model.vertices.Count(); i++) { writer.Write(reader.model.vertices[i].position.X); writer.Write(reader.model.vertices[i].position.Z); writer.Write(reader.model.vertices[i].position.Y * -1); if (reader.model.vertices[i].position.X < minPosX) { minPosX = reader.model.vertices[i].position.X; } if (reader.model.vertices[i].position.Z < minPosY) { minPosY = reader.model.vertices[i].position.Z; } if (reader.model.vertices[i].position.Y * -1 < minPosZ) { minPosZ = reader.model.vertices[i].position.Y * -1; } if (reader.model.vertices[i].position.X > maxPosX) { maxPosX = reader.model.vertices[i].position.X; } if (reader.model.vertices[i].position.Z > maxPosY) { maxPosY = reader.model.vertices[i].position.Z; } if (reader.model.vertices[i].position.Y * -1 > maxPosZ) { maxPosZ = reader.model.vertices[i].position.Y * -1; } } vPosBuffer.byteLength = (uint)writer.BaseStream.Position - vPosBuffer.byteOffset; var posLoc = accessorInfo.Count(); accessorInfo.Add(new Accessor() { name = "vPos", bufferView = bufferViews.Count(), byteOffset = 0, componentType = 5126, count = (uint)reader.model.vertices.Count(), type = "VEC3", min = new float[] { minPosX, minPosY, minPosZ }, max = new float[] { maxPosX, maxPosY, maxPosZ } }); bufferViews.Add(vPosBuffer); // Normal bufferview var normalBuffer = new BufferView() { buffer = 0, byteOffset = (uint)writer.BaseStream.Position, target = 34962 }; for (var i = 0; i < reader.model.vertices.Count(); i++) { writer.Write(reader.model.vertices[i].normal.X); writer.Write(reader.model.vertices[i].normal.Z); writer.Write(reader.model.vertices[i].normal.Y); } normalBuffer.byteLength = (uint)writer.BaseStream.Position - normalBuffer.byteOffset; var normalLoc = accessorInfo.Count(); accessorInfo.Add(new Accessor() { name = "vNormal", bufferView = bufferViews.Count(), byteOffset = 0, componentType = 5126, count = (uint)reader.model.vertices.Count(), type = "VEC3" }); bufferViews.Add(normalBuffer); // TexCoord bufferview var texCoordBuffer = new BufferView() { buffer = 0, byteOffset = (uint)writer.BaseStream.Position, target = 34962 }; for (var i = 0; i < reader.model.vertices.Count(); i++) { writer.Write(reader.model.vertices[i].textureCoordX); writer.Write(reader.model.vertices[i].textureCoordY); } texCoordBuffer.byteLength = (uint)writer.BaseStream.Position - texCoordBuffer.byteOffset; var texLoc = accessorInfo.Count(); accessorInfo.Add(new Accessor() { name = "vTex", bufferView = bufferViews.Count(), byteOffset = 0, componentType = 5126, count = (uint)reader.model.vertices.Count(), type = "VEC2" }); bufferViews.Add(texCoordBuffer); // Joints bufferview var jointBuffer = new BufferView() { buffer = 0, byteOffset = (uint)writer.BaseStream.Position, target = 34962 }; for (var i = 0; i < reader.model.vertices.Count(); i++) { writer.Write(reader.model.vertices[i].boneIndices_0); writer.Write(reader.model.vertices[i].boneIndices_1); writer.Write(reader.model.vertices[i].boneIndices_2); writer.Write(reader.model.vertices[i].boneIndices_3); } jointBuffer.byteOffset = (uint)writer.BaseStream.Position - jointBuffer.byteOffset; var jointLoc = accessorInfo.Count(); accessorInfo.Add(new Accessor() { name = "vJoint", bufferView = bufferViews.Count(), byteOffset = 0, componentType = 5121, count = (uint)reader.model.vertices.Count(), type = "VEC4" }); bufferViews.Add(jointBuffer); // Weight bufferview var weightBuffer = new BufferView() { buffer = 0, byteOffset = (uint)writer.BaseStream.Position, target = 34962 }; for (var i = 0; i < reader.model.vertices.Count(); i++) { writer.Write(reader.model.vertices[i].boneWeight_0); writer.Write(reader.model.vertices[i].boneWeight_1); writer.Write(reader.model.vertices[i].boneWeight_2); writer.Write(reader.model.vertices[i].boneWeight_3); } weightBuffer.byteOffset = (uint)writer.BaseStream.Position - weightBuffer.byteOffset; var weightLoc = accessorInfo.Count(); accessorInfo.Add(new Accessor() { name = "vWeight", bufferView = bufferViews.Count(), byteOffset = 0, componentType = 5121, count = (uint)reader.model.vertices.Count(), type = "VEC4" }); bufferViews.Add(weightBuffer); // End of element bufferviews var indexBufferPos = bufferViews.Count(); var materialBlends = new Dictionary <int, ushort>(); for (var i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { var batch = reader.model.skins[0].submeshes[i]; accessorInfo.Add(new Accessor() { name = "indices", bufferView = indexBufferPos, byteOffset = reader.model.skins[0].submeshes[i].startTriangle * 2, componentType = 5123, count = reader.model.skins[0].submeshes[i].nTriangles, type = "SCALAR" }); var mesh = new Mesh(); mesh.name = "Group #" + i; mesh.primitives = new Primitive[1]; mesh.primitives[0].attributes = new Dictionary <string, int> { { "POSITION", posLoc }, { "NORMAL", normalLoc }, { "TEXCOORD_0", texLoc }, { "JOINTS_0", jointLoc }, { "WEIGHTS_0", weightLoc } }; mesh.primitives[0].indices = (uint)accessorInfo.Count() - 1; mesh.primitives[0].mode = 4; meshes.Add(mesh); // Texture stuff for (var tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++) { if (reader.model.skins[0].textureunit[tu].submeshIndex == i) { mesh.primitives[0].material = reader.model.texlookup[reader.model.skins[0].textureunit[tu].texture].textureID; // todo if (!materialBlends.ContainsKey(i)) { // add texture materialBlends.Add(i, reader.model.renderflags[reader.model.skins[0].textureunit[tu].renderFlags].blendingMode); } else { // already exists Console.WriteLine("Material " + mesh.primitives[0].material + " already exists in blend map with value " + materialBlends[i]); } } } } var indiceBuffer = new BufferView() { buffer = 0, byteOffset = (uint)writer.BaseStream.Position, target = 34963 }; for (var i = 0; i < reader.model.skins[0].triangles.Count(); i++) { var t = reader.model.skins[0].triangles[i]; writer.Write(t.pt1); writer.Write(t.pt2); writer.Write(t.pt3); } indiceBuffer.byteLength = (uint)writer.BaseStream.Position - indiceBuffer.byteOffset; bufferViews.Add(indiceBuffer); glTF.bufferViews = bufferViews.ToArray(); glTF.accessors = accessorInfo.ToArray(); glTF.buffers = new Buffer[1]; glTF.buffers[0].byteLength = (uint)writer.BaseStream.Length; glTF.buffers[0].uri = Path.GetFileNameWithoutExtension(file) + ".bin"; writer.Close(); writer.Dispose(); exportworker.ReportProgress(65, "Exporting textures.."); var materialCount = reader.model.textures.Count(); glTF.images = new Image[materialCount]; glTF.textures = new Texture[materialCount]; glTF.materials = new Material[materialCount]; var textureID = 0; var materials = new Structs.Material[reader.model.textures.Count()]; for (var i = 0; i < reader.model.textures.Count(); i++) { uint textureFileDataID = 903224; materials[i].flags = reader.model.textures[i].flags; switch (reader.model.textures[i].type) { case 0: textureFileDataID = reader.model.textureFileDataIDs[i]; break; case 1: case 2: case 11: default: Console.WriteLine(" Falling back to placeholder texture"); break; } // Check if texture is a replacable texture, return test texture for now if (textureFileDataID == 0) { textureFileDataID = 903224; } materials[i].textureID = textureID + i; materials[i].filename = textureFileDataID.ToString(); glTF.materials[i].name = materials[i].filename; glTF.materials[i].pbrMetallicRoughness = new PBRMetallicRoughness(); glTF.materials[i].pbrMetallicRoughness.baseColorTexture = new TextureIndex(); glTF.materials[i].pbrMetallicRoughness.baseColorTexture.index = i; glTF.materials[i].pbrMetallicRoughness.metallicFactor = 0.0f; glTF.materials[i].alphaMode = "MASK"; glTF.materials[i].alphaCutoff = 0.5f; glTF.images[i].uri = "tex_" + materials[i].filename + ".png"; glTF.textures[i].sampler = 0; glTF.textures[i].source = i; var blpreader = new BLPReader(); blpreader.LoadBLP(textureFileDataID); try { if (destinationOverride == null) { blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file), glTF.images[i].uri)); } else { blpreader.bmp.Save(Path.Combine(outdir, destinationOverride, glTF.images[i].uri)); } } catch (Exception e) { Console.WriteLine(e.Message); } } exportworker.ReportProgress(85, "Writing files.."); glTF.samplers = new Sampler[1]; glTF.samplers[0].name = "Default Sampler"; glTF.samplers[0].minFilter = 9986; glTF.samplers[0].magFilter = 9729; glTF.samplers[0].wrapS = 10497; glTF.samplers[0].wrapT = 10497; glTF.scenes = new Scene[1]; glTF.scenes[0].name = Path.GetFileNameWithoutExtension(file); glTF.nodes = new Node[meshes.Count()]; var meshIDs = new List <int>(); for (var i = 0; i < meshes.Count(); i++) { glTF.nodes[i].name = meshes[i].name; glTF.nodes[i].mesh = i; meshIDs.Add(i); } glTF.scenes[0].nodes = meshIDs.ToArray(); glTF.meshes = meshes.ToArray(); glTF.scene = 0; exportworker.ReportProgress(95, "Writing to file.."); if (destinationOverride == null) { File.WriteAllText(Path.Combine(outdir, file.Replace(".m2", ".gltf")), JsonConvert.SerializeObject(glTF, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); } else { File.WriteAllText(Path.Combine(destinationOverride, Path.GetFileName(file.ToLower()).Replace(".m2", ".gltf")), JsonConvert.SerializeObject(glTF, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); } /* * objsw.WriteLine("g " + Path.GetFileNameWithoutExtension(file)); * * foreach (var renderbatch in renderbatches) * { * var i = renderbatch.firstFace; * objsw.WriteLine("o " + Path.GetFileNameWithoutExtension(file) + renderbatch.groupID); * objsw.WriteLine("usemtl tex_" + materials[renderbatch.materialID].filename); * 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(); * * // Only export phys when exporting a single M2, causes issues for some users when combined with WMO/ADT * if (destinationOverride == null) * { * exportworker.ReportProgress(90, "Exporting collision.."); * * objsw = new StreamWriter(Path.Combine(outdir, file.Replace(".m2", ".phys.obj"))); * * objsw.WriteLine("# Written by Marlamin's WoW Exporter. Original file: " + file); * * for (int i = 0; i < reader.model.boundingvertices.Count(); i++) * { * objsw.WriteLine("v " + * reader.model.boundingvertices[i].vertex.X + " " + * reader.model.boundingvertices[i].vertex.Z + " " + * -reader.model.boundingvertices[i].vertex.Y); * } * * for (int i = 0; i < reader.model.boundingtriangles.Count(); i++) * { * var t = reader.model.boundingtriangles[i]; * objsw.WriteLine("f " + (t.index_0 + 1) + " " + (t.index_1 + 1) + " " + (t.index_2 + 1)); * } * * objsw.Close(); * } * * // https://en.wikipedia.org/wiki/Wavefront_.obj_file#Basic_materials * // http://wiki.unity3d.com/index.php?title=ExportOBJ * // http://web.cse.ohio-state.edu/~hwshen/581/Site/Lab3_files/Labhelp_Obj_parser.htm */ }
public Render() { using (var dg = new DisposeGroup()) { //Load Shaders var pVSBlob = dg.Add(ShaderBytecode.CompileFromFile("RenderWithCam.fx", "VS", "vs_4_0")); var inputSignature = dg.Add(ShaderSignature.GetInputSignature(pVSBlob)); m_pVertexShader = new VertexShader(Device, pVSBlob); var pPSBlob = dg.Add(ShaderBytecode.CompileFromFile("RenderWithCam.fx", "PS", "ps_4_0")); m_pPixelShader = new PixelShader(Device, pPSBlob); //Define layout var layout = dg.Add(new InputLayout(Device, inputSignature, new[] { new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), new InputElement("TEXCOORD", 0, Format.R32G32_Float, 16, 0) })); //Load model M2Reader reader = new M2Reader("Z:\\18566_full\\"); reader.LoadM2(@"World\ArtTest\Boxtest\xyz.m2"); //Load vertices List <float> verticelist = new List <float>(); for (int i = 0; i < reader.model.vertices.Count(); i++) { verticelist.Add(reader.model.vertices[i].position.X); verticelist.Add(reader.model.vertices[i].position.Z * -1); verticelist.Add(reader.model.vertices[i].position.Y); verticelist.Add(1.0f); verticelist.Add(reader.model.vertices[i].textureCoordX); verticelist.Add(reader.model.vertices[i].textureCoordY); } //Load indices List <ushort> indicelist = new List <ushort>(); for (int i = 0; i < reader.model.skins[0].triangles.Count(); i++) { indicelist.Add(reader.model.skins[0].triangles[i].pt1); indicelist.Add(reader.model.skins[0].triangles[i].pt2); indicelist.Add(reader.model.skins[0].triangles[i].pt3); } //Convert to array ushort[] indices = indicelist.ToArray(); float[] vertices = verticelist.ToArray(); //Set count for use in draw later on indicecount = indices.Count(); //Create buffers var vertexBuffer = dg.Add(Buffer.Create(Device, BindFlags.VertexBuffer, vertices)); var vertexBufferBinding = new VertexBufferBinding(vertexBuffer, Utilities.SizeOf <Vector4>() + Utilities.SizeOf <Vector2>(), 0); var indexBuffer = dg.Add(Buffer.Create(Device, BindFlags.IndexBuffer, indices)); Device.ImmediateContext.InputAssembler.InputLayout = (layout); Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, vertexBufferBinding); Device.ImmediateContext.InputAssembler.SetIndexBuffer(indexBuffer, Format.R16_UInt, 0); Device.ImmediateContext.InputAssembler.PrimitiveTopology = (PrimitiveTopology.TriangleList); //Get texture, what a mess this could be much better var blp = new BLPReader("Z:\\18566_full\\"); if (File.Exists(Path.Combine("Z:\\18566_full\\", reader.model.filename.Replace("M2", "blp")))) { blp.LoadBLP(reader.model.filename.Replace("M2", "blp")); } else { if (reader.model.textures.Count() > 0) { blp.LoadBLP(reader.model.textures[0]); } else { throw new Exception("No forking textures, mate."); } } MemoryStream s = new MemoryStream(); blp.bmp.Save(s, System.Drawing.Imaging.ImageFormat.Png); s.Seek(0, SeekOrigin.Begin); Texture2D texture = Texture2D.FromMemory <Texture2D>(Device, s.ToArray()); var textureView = new ShaderResourceView(Device, texture); var sampler = new SamplerState(Device, new SamplerStateDescription() { Filter = Filter.MinMagMipLinear, AddressU = TextureAddressMode.Wrap, AddressV = TextureAddressMode.Wrap, AddressW = TextureAddressMode.Wrap, BorderColor = SharpDX.Color.Black, ComparisonFunction = Comparison.Never, MaximumAnisotropy = 16, MipLodBias = 0, MinimumLod = 0, MaximumLod = 16, }); Device.ImmediateContext.PixelShader.SetSampler(0, sampler); Device.ImmediateContext.PixelShader.SetShaderResource(0, textureView); //End of texture stuff, Set(ref m_pConstantBuffer, new ConstantBuffer <Projections>(Device)); Device.ImmediateContext.VertexShader.SetConstantBuffer(0, m_pConstantBuffer.Buffer); } //Make camera Camera = new FirstPersonCamera(); Camera.SetProjParams((float)Math.PI / 2, 1, 0.01f, 100.0f); Camera.SetViewParams(new Vector3(0.0f, 0.0f, -5.0f), new Vector3(0.0f, 1.0f, 0.0f)); }