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 Form_M2Export_Load(object sender, EventArgs e) { button1.Text = "Export"; this.Text = filename; label10.Text = "Export format:"; comboBox1.Items.AddRange(formats); comboBox1.DropDownStyle = ComboBoxStyle.DropDownList; comboBox1.SelectedIndex = 0; label11.Hide(); try { M2Reader reader = new M2Reader(); reader.LoadM2(filename); //Populate model details: label1.Text = "Version: " + reader.model.version; label2.Text = "Name: " + reader.model.name; label3.Text = "Sequences: " + reader.model.sequences.Count(); label4.Text = "Animations: " + reader.model.animations.Count(); label5.Text = "Bones: " + reader.model.bones.Count(); label6.Text = "Vertices: " + reader.model.vertices.Count(); label7.Text = "Skins: " + reader.model.skins.Count(); label8.Text = "Textures: " + reader.model.textures.Count(); label9.Text = "Submeshes:" + reader.model.skins[0].submeshes.Count(); } catch (Exception ex) { throw new Exception("Could not read the model to display details" + " - exception: " + ex.Message); } }
public static void ExportM2(uint fileDataID, BackgroundWorker exportWorker = null, string destinationOverride = null, string fileName = "", bool[] enabledGeosets = null) { if (!CASC.FileExists(fileDataID)) { throw new Exception("404 M2 Not Found!"); } var reader = new M2Reader(); reader.LoadM2(fileDataID); // Default to using fileDataID as a name if nothing is provided. if (string.IsNullOrEmpty(fileName)) { fileName = fileDataID.ToString(); } ExportM2(reader, fileName, exportWorker, destinationOverride, false, enabledGeosets); }
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 LoadM2(string filename, CacheStorage cache, int modelShader) { filename = filename.ToLower().Replace(".mdx", ".m2"); filename = filename.ToLower().Replace(".mdl", ".m2"); if (cache.doodadBatches.ContainsKey(filename)) { return; } WoWFormatLib.Structs.M2.M2Model model = new WoWFormatLib.Structs.M2.M2Model(); if (cache.models.ContainsKey(filename)) { model = cache.models[filename]; } else { //Load model from file if (WoWFormatLib.Utils.CASC.FileExists(filename)) { var modelreader = new M2Reader(); modelreader.LoadM2(filename); cache.models.Add(filename, modelreader.model); model = modelreader.model; } else { throw new Exception("Model " + filename + " does not exist!"); } } var ddBatch = new DoodadBatch(); // Textures ddBatch.mats = new Material[model.textures.Count()]; for (int i = 0; i < model.textures.Count(); i++) { string texturefilename = model.textures[i].filename; ddBatch.mats[i].flags = model.textures[i].flags; switch (model.textures[i].type) { case 0: // Console.WriteLine(" Texture given in file!"); texturefilename = model.textures[i].filename; break; case 1: string[] csfilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(filename, (int)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(System.IO.Path.ChangeExtension(filename, ".blp"))) { // Console.WriteLine(" BLP exists!"); texturefilename = System.IO.Path.ChangeExtension(filename, ".blp"); } else { //Console.WriteLine(" Type 2 does not exist!"); //needs lookup? } break; case 11: string[] cdifilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(filename, (int)model.textures[i].type); for (int ti = 0; ti < cdifilenames.Count(); ti++) { if (WoWFormatLib.Utils.CASC.FileExists(filename.Replace(model.name + ".M2", cdifilenames[ti] + ".blp"))) { texturefilename = filename.Replace(model.name + ".M2", cdifilenames[ti] + ".blp"); } } break; default: //Console.WriteLine(" Falling back to placeholder texture"); texturefilename = "Dungeons\\Textures\\testing\\COLOR_13.blp"; break; } ddBatch.mats[i].textureID = BLPLoader.LoadTexture(texturefilename, cache); ddBatch.mats[i].filename = texturefilename; } // Submeshes ddBatch.submeshes = new Submesh[model.skins[0].submeshes.Count()]; for (int i = 0; i < model.skins[0].submeshes.Count(); i++) { if (filename.StartsWith("character")) { if (model.skins[0].submeshes[i].submeshID != 0) { if (!model.skins[0].submeshes[i].submeshID.ToString().EndsWith("01")) { continue; } } } ddBatch.submeshes[i].firstFace = model.skins[0].submeshes[i].startTriangle; ddBatch.submeshes[i].numFaces = model.skins[0].submeshes[i].nTriangles; for (int tu = 0; tu < model.skins[0].textureunit.Count(); tu++) { if (model.skins[0].textureunit[tu].submeshIndex == i) { ddBatch.submeshes[i].blendType = model.renderflags[model.skins[0].textureunit[tu].renderFlags].blendingMode; if (!cache.materials.ContainsKey(model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower())) { throw new Exception("MaterialCache does not have texture " + model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower()); } ddBatch.submeshes[i].material = (uint)cache.materials[model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower()]; } } } ddBatch.vao = GL.GenVertexArray(); GL.BindVertexArray(ddBatch.vao); // Vertices & indices ddBatch.vertexBuffer = GL.GenBuffer(); ddBatch.indiceBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, ddBatch.vertexBuffer); GL.BindBuffer(BufferTarget.ElementArrayBuffer, ddBatch.indiceBuffer); List <uint> modelindicelist = new List <uint>(); for (int i = 0; i < model.skins[0].triangles.Count(); i++) { modelindicelist.Add(model.skins[0].triangles[i].pt1); modelindicelist.Add(model.skins[0].triangles[i].pt2); modelindicelist.Add(model.skins[0].triangles[i].pt3); } uint[] modelindices = modelindicelist.ToArray(); //Console.WriteLine(modelindicelist.Count() + " indices!"); ddBatch.indices = modelindices; GL.BindBuffer(BufferTarget.ElementArrayBuffer, ddBatch.indiceBuffer); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(ddBatch.indices.Length * sizeof(uint)), ddBatch.indices, BufferUsageHint.StaticDraw); M2Vertex[] modelvertices = new M2Vertex[model.vertices.Count()]; for (int i = 0; i < model.vertices.Count(); i++) { modelvertices[i].Position = new Vector3(model.vertices[i].position.X, model.vertices[i].position.Y, model.vertices[i].position.Z); modelvertices[i].Normal = new Vector3(model.vertices[i].normal.X, model.vertices[i].normal.Y, model.vertices[i].normal.Z); modelvertices[i].TexCoord = new Vector2(model.vertices[i].textureCoordX, model.vertices[i].textureCoordY); } GL.BindBuffer(BufferTarget.ArrayBuffer, ddBatch.vertexBuffer); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(modelvertices.Length * 8 * sizeof(float)), modelvertices, BufferUsageHint.StaticDraw); var texCoordLoc = GL.GetAttribLocation(modelShader, "vTexCoord"); GL.EnableVertexAttribArray(texCoordLoc); GL.VertexAttribPointer(texCoordLoc, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float)); var posLoc = GL.GetAttribLocation(modelShader, "vPosition"); GL.EnableVertexAttribArray(posLoc); GL.VertexAttribPointer(posLoc, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 5 * sizeof(float)); GL.BindVertexArray(0); cache.doodadBatches.Add(filename, ddBatch); }
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 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; }
public static void LoadM2(string filename, CacheStorage cache) { filename = filename.ToLower().Replace(".mdx", ".m2"); filename = filename.ToLower().Replace(".mdl", ".m2"); if (cache.doodadBatches.ContainsKey(filename)) { return; } var fileDataID = CASC.getFileDataIdByName(filename); WoWFormatLib.Structs.M2.M2Model model = new WoWFormatLib.Structs.M2.M2Model(); if (cache.models.ContainsKey(filename)) { model = cache.models[filename]; } else { //Load model from file if (CASC.cascHandler.FileExists(filename)) { var modelreader = new M2Reader(); modelreader.LoadM2(filename); cache.models.Add(filename, modelreader.model); model = modelreader.model; } else { throw new Exception("Model " + filename + " does not exist!"); } } var ddBatch = new TerrainWindow.DoodadBatch(); // Textures ddBatch.mats = new TerrainWindow.Material[model.textures.Count()]; // Always load error texture BLPLoader.LoadTexture(CASC.getFileDataIdByName(@"test/qa_test_blp_1.blp"), cache); for (int i = 0; i < model.textures.Count(); i++) { int textureFileDataID = 840426; ddBatch.mats[i].flags = model.textures[i].flags; switch (model.textures[i].type) { case 0: // Console.WriteLine(" Texture given in file!"); textureFileDataID = CASC.getFileDataIdByName(model.textures[i].filename); break; case 1: uint[] csfilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(fileDataID, (int)model.textures[i].type, i); if (csfilenames.Count() > 0) { textureFileDataID = (int)csfilenames[0]; } else { //Console.WriteLine(" No type 1 texture found, falling back to placeholder texture"); } break; case 2: if (WoWFormatLib.Utils.CASC.cascHandler.FileExists(System.IO.Path.ChangeExtension(filename, ".blp"))) { // Console.WriteLine(" BLP exists!"); textureFileDataID = CASC.getFileDataIdByName(System.IO.Path.ChangeExtension(filename, ".blp")); } else { //Console.WriteLine(" Type 2 does not exist!"); //needs lookup? } break; case 11: uint[] cdifilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(fileDataID, (int)model.textures[i].type); for (int ti = 0; ti < cdifilenames.Count(); ti++) { textureFileDataID = (int)cdifilenames[ti]; } break; default: textureFileDataID = 840426; break; } ddBatch.mats[i].textureID = BLPLoader.LoadTexture(textureFileDataID, cache); ddBatch.mats[i].filename = textureFileDataID.ToString(); } // Submeshes ddBatch.submeshes = new TerrainWindow.Submesh[model.skins[0].submeshes.Count()]; for (int i = 0; i < model.skins[0].submeshes.Count(); i++) { if (filename.StartsWith("character")) { if (model.skins[0].submeshes[i].submeshID != 0) { if (!model.skins[0].submeshes[i].submeshID.ToString().EndsWith("01")) { continue; } } } ddBatch.submeshes[i].firstFace = model.skins[0].submeshes[i].startTriangle; ddBatch.submeshes[i].numFaces = model.skins[0].submeshes[i].nTriangles; for (int tu = 0; tu < model.skins[0].textureunit.Count(); tu++) { if (model.skins[0].textureunit[tu].submeshIndex == i) { ddBatch.submeshes[i].blendType = model.renderflags[model.skins[0].textureunit[tu].renderFlags].blendingMode; if (!cache.materials.ContainsKey(CASC.getFileDataIdByName(model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename).ToString())) { throw new Exception("MaterialCache does not have texture " + model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower()); } ddBatch.submeshes[i].material = (uint)cache.materials[CASC.getFileDataIdByName(model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower()).ToString()]; } } } // Vertices & indices ddBatch.vertexBuffer = GL.GenBuffer(); ddBatch.indiceBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, ddBatch.vertexBuffer); GL.BindBuffer(BufferTarget.ElementArrayBuffer, ddBatch.indiceBuffer); List <uint> modelindicelist = new List <uint>(); for (int i = 0; i < model.skins[0].triangles.Count(); i++) { modelindicelist.Add(model.skins[0].triangles[i].pt1); modelindicelist.Add(model.skins[0].triangles[i].pt2); modelindicelist.Add(model.skins[0].triangles[i].pt3); } uint[] modelindices = modelindicelist.ToArray(); //Console.WriteLine(modelindicelist.Count() + " indices!"); ddBatch.indices = modelindices; GL.BindBuffer(BufferTarget.ElementArrayBuffer, ddBatch.indiceBuffer); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(ddBatch.indices.Length * sizeof(uint)), ddBatch.indices, BufferUsageHint.StaticDraw); TerrainWindow.M2Vertex[] modelvertices = new TerrainWindow.M2Vertex[model.vertices.Count()]; for (int i = 0; i < model.vertices.Count(); i++) { modelvertices[i].Position = new OpenTK.Vector3(model.vertices[i].position.X, model.vertices[i].position.Y, model.vertices[i].position.Z); modelvertices[i].Normal = new OpenTK.Vector3(model.vertices[i].normal.X, model.vertices[i].normal.Y, model.vertices[i].normal.Z); modelvertices[i].TexCoord = new Vector2(model.vertices[i].textureCoordX, model.vertices[i].textureCoordY); } GL.BindBuffer(BufferTarget.ArrayBuffer, ddBatch.vertexBuffer); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(modelvertices.Length * 8 * sizeof(float)), modelvertices, BufferUsageHint.StaticDraw); cache.doodadBatches.Add(filename, ddBatch); }
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 }
private void LoadM2(string modelpath) { if (!WoWFormatLib.Utils.CASC.FileExists(modelpath)) { throw new Exception("Model does not exist!"); } worker.ReportProgress(0, "Loading model.."); M2Reader reader = new M2Reader(); string filename = modelpath; reader.LoadM2(filename); VBOid = new uint[2]; GL.GenBuffers(2, VBOid); GL.BindBuffer(BufferTarget.ArrayBuffer, VBOid[0]); GL.BindBuffer(BufferTarget.ElementArrayBuffer, VBOid[1]); GL.EnableClientState(ArrayCap.VertexArray); GL.EnableClientState(ArrayCap.NormalArray); worker.ReportProgress(20, "Reading model indices.."); List <uint> indicelist = new List <uint>(); 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); } uint[] indices = indicelist.ToArray(); GL.BindBuffer(BufferTarget.ElementArrayBuffer, VBOid[1]); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(uint)), indices, BufferUsageHint.StaticDraw); worker.ReportProgress(30, "Reading model vertices.."); Vertex[] vertices = new Vertex[reader.model.vertices.Count()]; for (int i = 0; i < reader.model.vertices.Count(); i++) { vertices[i].Position = new Vector3(reader.model.vertices[i].position.X, reader.model.vertices[i].position.Z, reader.model.vertices[i].position.Y * -1); vertices[i].Normal = new 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); } GL.BindBuffer(BufferTarget.ArrayBuffer, VBOid[0]); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * 8 * sizeof(float)), vertices, BufferUsageHint.StaticDraw); GL.Enable(EnableCap.Texture2D); worker.ReportProgress(40, "Loading textures.."); materials = new Material[reader.model.textures.Count()]; for (int i = 0; i < reader.model.textures.Count(); i++) { Console.WriteLine("Loading texture " + i); string texturefilename = @"dungeons\textures\testing\color_13.blp"; materials[i].flags = reader.model.textures[i].flags; Console.WriteLine(" Requires type " + reader.model.textures[i].type + " texture"); 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(filename, (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(modelpath, ".blp"))) { Console.WriteLine(" BLP exists!"); texturefilename = Path.ChangeExtension(modelpath, ".blp"); } else { Console.WriteLine(" Type 2 does not exist!"); //needs lookup? } break; case 11: string[] cdifilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(filename, (int)reader.model.textures[i].type); for (int ti = 0; ti < cdifilenames.Count(); ti++) { if (WoWFormatLib.Utils.CASC.FileExists(modelpath.Replace(reader.model.name + ".M2", cdifilenames[ti] + ".blp"))) { texturefilename = modelpath.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 = BLPLoader.LoadTexture(texturefilename, cache); materials[i].filename = texturefilename; } worker.ReportProgress(60, "Loading renderbatches.."); renderbatches = new RenderBatch[reader.model.skins[0].submeshes.Count()]; for (int i = 0; i < reader.model.skins[0].submeshes.Count(); i++) { if (filename.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; } } Console.WriteLine("Loading submesh " + reader.model.skins[0].submeshes[i].submeshID + "(" + reader.model.skins[0].submeshes[i].unk2 + ")"); } 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].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; } } } worker.ReportProgress(100, "Done."); gLoaded = true; }
public static void ExportM2(string filename, string outdir, string destinationOverride = null) { 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(); //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); //Bail if the model has no vertices if (reader.model.vertices.Count() == 0) { return; } //Export individual geosets... 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(); 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; } } } //---------------------------------------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////////////////////////////////// ///REFERENCE SMD(s) //////////////////////////////////////////////////////////////////////////////////////////////////////////// //---------------------------------------------------------------------------------------------------------- int sMeshIndex = 0; // to be used for submeshes names foreach (var renderbatch in renderbatches) { var j = renderbatch.firstFace; StreamWriter smdsw; if (destinationOverride == null) { if (!string.IsNullOrEmpty(filename)) { if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(filename))); } smdsw = new StreamWriter(Path.Combine(outdir, filename.Replace(".m2", sMeshIndex + ".smd"))); } else { if (!Directory.Exists(outdir)) { Directory.CreateDirectory(outdir); } smdsw = new StreamWriter(Path.Combine(outdir, filename + sMeshIndex + ".smd")); } } else { if (!string.IsNullOrEmpty(filename)) { smdsw = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileName(filename.ToLower()).Replace(".m2", sMeshIndex + ".smd"))); } else { smdsw = new StreamWriter(Path.Combine(outdir, destinationOverride, filename + sMeshIndex + ".smd")); } } smdsw.WriteLine("// Written by izzy's SMD Export module. Original file: " + filename); //Version smdsw.WriteLine("version 1"); //Bones block start smdsw.WriteLine("nodes"); //Root bone smdsw.WriteLine(" 0 \"" + reader.model.name + "\" -1"); for (int i = 0; i < reader.model.bones.Count(); i++) { smdsw.WriteLine(" " + (i + 1) + " \"" + reader.model.name + "_bone" + i + "\" " + (reader.model.bones[i].parentBone + 1)); } smdsw.WriteLine("end"); //Bones block end //Skeleton block start smdsw.WriteLine("skeleton"); smdsw.WriteLine("time 0"); smdsw.WriteLine(" 0 0.000000 0.000000 0.000000 0.000000 -0.000000 0.000000"); for (int i = 0; i < reader.model.bones.Count(); i++) { var currentBone = reader.model.bones[i]; var currentBoneParentPos = new WoWFormatLib.Utils.Vector3(); if (currentBone.parentBone != -1) { currentBoneParentPos.X = reader.model.bones[reader.model.bones[i].parentBone].pivot.X; currentBoneParentPos.Y = reader.model.bones[reader.model.bones[i].parentBone].pivot.Y; currentBoneParentPos.Z = reader.model.bones[reader.model.bones[i].parentBone].pivot.Z; } else { currentBoneParentPos.X = 0; currentBoneParentPos.Y = 0; currentBoneParentPos.Z = 0; } smdsw.WriteLine(" " + (i + 1) + " " + (currentBone.pivot.X - currentBoneParentPos.X) + " " + (currentBone.pivot.Z - currentBoneParentPos.Z) + " " + -(currentBone.pivot.Y - currentBoneParentPos.Y) + " " + "0.000000" + " " + "-0.000000" + " " + "0.000000"); } smdsw.WriteLine("end"); //Skeleton block end //Triangles block start smdsw.WriteLine("triangles"); while (j < (renderbatch.firstFace + renderbatch.numFaces)) { var vert1 = reader.model.vertices[indices[j]]; var vert2 = reader.model.vertices[indices[j + 1]]; var vert3 = reader.model.vertices[indices[j + 2]]; smdsw.WriteLine(Path.GetFileNameWithoutExtension(reader.model.textures[0].filename)); string vert1line = " " + (vert1.boneIndices_0 + 1) + " " + vert1.position.X + " " + vert1.position.Z + " " + -vert1.position.Y + " " + vert1.normal.X + " " + vert1.normal.Y + " " + vert1.normal.Z + " " + vert1.textureCoordX + " " + (vert1.textureCoordY - 1) * -1; string vert2line = " " + (vert2.boneIndices_0 + 1) + " " + vert2.position.X + " " + vert2.position.Z + " " + -vert2.position.Y + " " + vert2.normal.X + " " + vert2.normal.Y + " " + vert2.normal.Z + " " + vert2.textureCoordX + " " + (vert2.textureCoordY - 1) * -1; string vert3line = " " + (vert3.boneIndices_0 + 1) + " " + vert3.position.X + " " + vert3.position.Z + " " + -vert3.position.Y + " " + vert3.normal.X + " " + vert3.normal.Y + " " + vert3.normal.Z + " " + vert3.textureCoordX + " " + (vert3.textureCoordY - 1) * -1; int linksVert1 = 1; int linksVert2 = 1; int linksVert3 = 1; string addlink1; string addlink2; string addlink3; addlink1 = (vert1.boneIndices_0 + 1) + " " + vert1.boneWeight_0 / 255f; addlink2 = (vert2.boneIndices_0 + 1) + " " + vert2.boneWeight_0 / 255f; addlink3 = (vert3.boneIndices_0 + 1) + " " + vert3.boneWeight_0 / 255f; //------------------------------------- if (vert1.boneWeight_1 > 0) { addlink1 = addlink1 + " " + (vert1.boneIndices_1 + 1) + " " + vert1.boneWeight_1 / 255f; linksVert1++; } if (vert1.boneWeight_2 > 0) { addlink1 = addlink1 + " " + (vert1.boneIndices_2 + 1) + " " + vert1.boneWeight_2 / 255f; linksVert1++; } if (vert1.boneWeight_3 > 0) { addlink1 = addlink1 + " " + (vert1.boneIndices_3 + 1) + " " + vert1.boneWeight_3 / 255f; linksVert1++; } //------------------------------------- if (vert2.boneWeight_1 > 0) { addlink2 = addlink2 + " " + (vert2.boneIndices_1 + 1) + " " + vert2.boneWeight_1 / 255f; linksVert2++; } if (vert2.boneWeight_2 > 0) { addlink2 = addlink2 + " " + (vert2.boneIndices_2 + 1) + " " + vert2.boneWeight_2 / 255f; linksVert2++; } if (vert2.boneWeight_3 > 0) { addlink2 = addlink2 + " " + (vert2.boneIndices_3 + 1) + " " + vert2.boneWeight_3 / 255f; linksVert2++; } //------------------------------------- if (vert3.boneWeight_1 > 0) { addlink3 = addlink3 + " " + (vert3.boneIndices_1 + 1) + " " + vert3.boneWeight_1 / 255f; linksVert3++; } if (vert3.boneWeight_2 > 0) { addlink3 = addlink3 + " " + (vert3.boneIndices_2 + 1) + " " + vert3.boneWeight_2 / 255f; linksVert3++; } if (vert3.boneWeight_3 > 0) { addlink3 = addlink3 + " " + (vert3.boneIndices_3 + 1) + " " + vert3.boneWeight_3 / 255f; linksVert3++; } //------------------------------------- vert1line = vert1line + " " + linksVert1 + " " + addlink1; vert2line = vert2line + " " + linksVert2 + " " + addlink2; vert3line = vert3line + " " + linksVert3 + " " + addlink3; smdsw.WriteLine(vert1line); smdsw.WriteLine(vert2line); smdsw.WriteLine(vert3line); j += 3; } smdsw.WriteLine("end"); //Triangles block end smdsw.Close(); sMeshIndex++; } }
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!"); }
static void Main(string[] args) { if (args.Length < 2) { throw new Exception("Require buildconfig and cdnconfig (and yes for fullrun)"); } var fullrun = false; if (args.Length == 3 && args[2] == "1") { Console.WriteLine("Doing full run!"); fullrun = true; } // TODO: Use configuration stuff instead, but I don't want to figure that out right now. :) if (!File.Exists("connectionstring.txt")) { throw new Exception("connectionstring.txt not found!"); } var dbConn = new MySqlConnection(File.ReadAllText("connectionstring.txt")); dbConn.Open(); CASCLib.Logger.Init(); CASC.InitCasc("wow.tools", args[0], args[1]); var insertCmd = new MySqlCommand("INSERT INTO wow_rootfiles_links VALUES (@parent, @child, @type)", dbConn); insertCmd.Parameters.AddWithValue("@parent", 0); insertCmd.Parameters.AddWithValue("@child", 0); insertCmd.Parameters.AddWithValue("@type", ""); insertCmd.Prepare(); var insertUVFNCmd = new MySqlCommand("INSERT INTO wow_communityfiles VALUES (@id, @filename)", dbConn); insertUVFNCmd.Parameters.AddWithValue("@id", 0); insertUVFNCmd.Parameters.AddWithValue("@filename", 0); insertCmd.Prepare(); #region M2 var m2ids = new List <uint>(); using (var cmd = dbConn.CreateCommand()) { if (fullrun) { cmd.CommandText = "SELECT id from wow_rootfiles WHERE type = 'm2' ORDER BY id DESC"; } else { Console.WriteLine("[M2] Generating list of files to process.."); cmd.CommandText = "SELECT id from wow_rootfiles WHERE type = 'm2' AND id NOT IN (SELECT parent FROM wow_rootfiles_links) ORDER BY id DESC"; } var reader = cmd.ExecuteReader(); while (reader.Read()) { m2ids.Add(uint.Parse(reader["id"].ToString())); } reader.Close(); } foreach (var m2 in m2ids) { if (CASC.FileExists(m2)) { Console.WriteLine("[M2] Loading " + m2); try { var reader = new M2Reader(); reader.LoadM2(m2, false); insertCmd.Parameters[0].Value = m2; if (reader.model.textureFileDataIDs != null) { foreach (var textureID in reader.model.textureFileDataIDs) { insertEntry(insertCmd, textureID, "m2 texture"); } } if (reader.model.animFileDataIDs != null) { foreach (var animFileID in reader.model.animFileDataIDs) { insertEntry(insertCmd, animFileID.fileDataID, "m2 anim"); } } if (reader.model.skinFileDataIDs != null) { foreach (var skinFileID in reader.model.skinFileDataIDs) { insertEntry(insertCmd, skinFileID, "m2 skin"); } } if (reader.model.boneFileDataIDs != null) { foreach (var boneFileID in reader.model.boneFileDataIDs) { insertEntry(insertCmd, boneFileID, "m2 bone"); } } if (reader.model.recursiveParticleModelFileIDs != null) { foreach (var rpID in reader.model.recursiveParticleModelFileIDs) { insertEntry(insertCmd, rpID, "m2 recursive particle"); } } if (reader.model.geometryParticleModelFileIDs != null) { foreach (var gpID in reader.model.geometryParticleModelFileIDs) { insertEntry(insertCmd, gpID, "m2 geometry particle"); } } insertEntry(insertCmd, reader.model.skelFileID, "m2 skel"); insertEntry(insertCmd, reader.model.physFileID, "m2 phys"); } catch (Exception e) { Console.WriteLine(e.Message); } } } #endregion #region WMO var wmoids = new List <uint>(); var groupFixCMD = new MySqlCommand("UPDATE wow_rootfiles SET type = '_xxxwmo' WHERE id = @id LIMIT 1", dbConn); groupFixCMD.Parameters.AddWithValue("@id", 0); groupFixCMD.Prepare(); using (var cmd = dbConn.CreateCommand()) { if (fullrun) { cmd.CommandText = "SELECT id from wow_rootfiles WHERE type = 'wmo' ORDER BY id DESC"; } else { Console.WriteLine("[WMO] Generating list of files to process.."); cmd.CommandText = "SELECT id from wow_rootfiles WHERE type = 'wmo' AND id NOT IN (SELECT parent FROM wow_rootfiles_links) ORDER BY id DESC"; } var reader = cmd.ExecuteReader(); while (reader.Read()) { wmoids.Add(uint.Parse(reader["id"].ToString())); } reader.Close(); } foreach (var wmoid in wmoids) { if (CASC.FileExists(wmoid)) { Console.WriteLine("[WMO] Loading " + wmoid); try { var reader = new WMOReader(); var wmo = new WoWFormatLib.Structs.WMO.WMO(); try { wmo = reader.LoadWMO(wmoid); } catch (NotSupportedException e) { Console.WriteLine("[WMO] " + wmoid + " is a group WMO, fixing type and skipping.."); groupFixCMD.Parameters[0].Value = wmoid; groupFixCMD.ExecuteNonQuery(); continue; } insertCmd.Parameters[0].Value = wmoid; var inserted = new List <uint>(); if (wmo.groupFileDataIDs != null) { foreach (var groupFileDataID in wmo.groupFileDataIDs) { insertEntry(insertCmd, groupFileDataID, "wmo group"); } } if (wmo.doodadIds != null) { foreach (var doodadID in wmo.doodadIds) { if (inserted.Contains(doodadID)) { continue; } inserted.Add(doodadID); insertEntry(insertCmd, doodadID, "wmo doodad"); } } if (wmo.textures == null && wmo.materials != null) { foreach (var material in wmo.materials) { if (material.texture1 == 0 || inserted.Contains(material.texture1)) { continue; } inserted.Add(material.texture1); insertEntry(insertCmd, material.texture1, "wmo texture"); if (material.texture2 == 0 || inserted.Contains(material.texture2)) { continue; } inserted.Add(material.texture2); insertEntry(insertCmd, material.texture2, "wmo texture"); } } } catch (Exception e) { Console.WriteLine(e.Message); } } } #endregion #region WDT var wdtids = new List <uint>(); var wdtfullnamemap = new Dictionary <string, uint>(); using (var cmd = dbConn.CreateCommand()) { Console.WriteLine("[WDT] Generating list of WDT files.."); cmd.CommandText = "SELECT id, filename from wow_rootfiles WHERE type = 'wdt' AND filename IS NOT NULL ORDER BY id DESC"; var reader = cmd.ExecuteReader(); while (reader.Read()) { var filename = (string)reader["filename"]; var wdtid = uint.Parse(reader["id"].ToString()); if (filename.Contains("_mpv") || filename.Contains("_lgt") || filename.Contains("_occ") || filename.Contains("_fogs")) { continue; } wdtfullnamemap.Add(filename, wdtid); } } using (var cmd = dbConn.CreateCommand()) { if (fullrun) { cmd.CommandText = "SELECT id, filename from wow_rootfiles WHERE type = 'wdt' ORDER BY id DESC"; } else { Console.WriteLine("[WDT] Generating list of files to process.."); cmd.CommandText = "SELECT id, filename from wow_rootfiles WHERE type = 'wdt' AND id NOT IN (SELECT parent FROM wow_rootfiles_links) ORDER BY id DESC"; } var reader = cmd.ExecuteReader(); while (reader.Read()) { //var filename = (string)reader["filename"]; var wdtid = uint.Parse(reader["id"].ToString()); //if (filename.Contains("_mpv") || filename.Contains("_lgt") || filename.Contains("_occ") || filename.Contains("_fogs")) // continue; wdtids.Add(wdtid); } reader.Close(); foreach (var wdtid in wdtids) { Console.WriteLine("[WDT] Loading " + wdtid); insertCmd.Parameters[0].Value = wdtid; try { var wdtreader = new WDTReader(); wdtreader.LoadWDT(wdtid); if (wdtreader.wdtfile.modf.id != 0) { Console.WriteLine("WDT has WMO ID: " + wdtreader.wdtfile.modf.id); insertEntry(insertCmd, wdtreader.wdtfile.modf.id, "wdt wmo"); } foreach (var records in wdtreader.stringTileFiles) { insertEntry(insertCmd, records.Value.rootADT, "root adt"); insertEntry(insertCmd, records.Value.tex0ADT, "tex0 adt"); insertEntry(insertCmd, records.Value.lodADT, "lod adt"); insertEntry(insertCmd, records.Value.obj0ADT, "obj0 adt"); insertEntry(insertCmd, records.Value.obj1ADT, "obj1 adt"); insertEntry(insertCmd, records.Value.mapTexture, "map texture"); insertEntry(insertCmd, records.Value.mapTextureN, "mapn texture"); insertEntry(insertCmd, records.Value.minimapTexture, "minimap texture"); } }catch (Exception e) { Console.WriteLine(e.Message); } } } #endregion #region ADT var adtids = new Dictionary <uint, Dictionary <(byte, byte), uint> >(); var wdtmapping = new Dictionary <string, uint>(); using (var cmd = dbConn.CreateCommand()) { if (fullrun) { cmd.CommandText = " SELECT id, filename from wow_rootfiles WHERE filename LIKE '%adt' AND filename NOT LIKE '%_obj0.adt' AND filename NOT LIKE '%_obj1.adt' AND filename NOT LIKE '%_lod.adt' AND filename NOT LIKE '%tex0.adt' AND filename NOT LIKE '%tex1.adt' ORDER BY id DESC "; } else { Console.WriteLine("[ADT] Generating list of files to process.."); cmd.CommandText = " SELECT id, filename from wow_rootfiles WHERE filename LIKE '%adt' AND filename NOT LIKE '%_obj0.adt' AND filename NOT LIKE '%_obj1.adt' AND filename NOT LIKE '%_lod.adt' AND filename NOT LIKE '%tex0.adt' AND filename NOT LIKE '%tex1.adt' AND id NOT IN (SELECT parent FROM wow_rootfiles_links) ORDER BY id DESC"; } var reader = cmd.ExecuteReader(); while (reader.Read()) { var filename = (string)reader["filename"]; var mapname = filename.Replace("world/maps/", "").Substring(0, filename.Replace("world/maps/", "").IndexOf("/")); var exploded = Path.GetFileNameWithoutExtension(filename).Split('_'); for (var i = 0; i < exploded.Length; i++) { //Console.WriteLine(i + ": " + exploded[i]); } byte tileX = 0; byte tileY = 0; if (!byte.TryParse(exploded[exploded.Length - 2], out tileX) || !byte.TryParse(exploded[exploded.Length - 1], out tileY)) { throw new FormatException("An error occured converting coordinates from " + filename + " to bytes"); } if (!wdtmapping.ContainsKey(mapname)) { var wdtname = "world/maps/" + mapname + "/" + mapname + ".wdt"; if (!wdtfullnamemap.ContainsKey(wdtname)) { Console.WriteLine("Unable to get filedataid for " + mapname + ", skipping..."); wdtmapping.Remove(mapname); continue; } wdtmapping.Add(mapname, wdtfullnamemap[wdtname]); if (wdtmapping[mapname] == 0) { // TODO: Support WDTs removed in current build Console.WriteLine("Unable to get filedataid for " + mapname + ", skipping..."); wdtmapping.Remove(mapname); continue; /* * var wdtconn = new MySqlConnection(File.ReadAllText("connectionstring.txt")); * wdtconn.Open(); * using (var wdtcmd = wdtconn.CreateCommand()) * { * wdtcmd.CommandText = "SELECT id from wow_rootfiles WHERE filename = '" + wdtname + "'"; * var wdtread = wdtcmd.ExecuteReader(); * while (wdtread.Read()) * { * wdtmapping[mapname] = uint.Parse(wdtread["id"].ToString()); * } * } * wdtconn.Close();*/ } adtids.Add(wdtmapping[mapname], new Dictionary <(byte, byte), uint>()); } var id = uint.Parse(reader["id"].ToString()); if (id == 0) { Console.WriteLine("Root ADT " + tileX + ", " + tileY + " with ID 0 on WDT " + wdtmapping[mapname]); continue; } if (wdtmapping.ContainsKey(mapname)) { adtids[wdtmapping[mapname]].Add((tileX, tileY), id); } } reader.Close(); foreach (var wdtid in adtids) { foreach (var adtid in wdtid.Value) { var inserted = new List <uint>(); Console.WriteLine("[ADT] Loading " + adtid.Key.Item1 + ", " + adtid.Key.Item2 + "(" + adtid.Value + ")"); insertCmd.Parameters[0].Value = adtid.Value; var adtreader = new ADTReader(); try { adtreader.LoadADT(wdtid.Key, adtid.Key.Item1, adtid.Key.Item2); } catch (Exception e) { Console.WriteLine(e.Message); continue; } if (adtreader.adtfile.objects.m2Names.filenames != null) { Console.WriteLine(adtid + " is still using old filenames, skipping!"); } else { foreach (var worldmodel in adtreader.adtfile.objects.worldModels.entries) { if (inserted.Contains(worldmodel.mwidEntry)) { continue; } inserted.Add(worldmodel.mwidEntry); insertEntry(insertCmd, worldmodel.mwidEntry, "adt worldmodel"); } foreach (var doodad in adtreader.adtfile.objects.models.entries) { if (inserted.Contains(doodad.mmidEntry)) { continue; } insertEntry(insertCmd, doodad.mmidEntry, "adt doodad"); inserted.Add(doodad.mmidEntry); } } } } } #endregion }
public static void LoadM2(string filename, CacheStorage cache, int shaderProgram) { filename = filename.ToLower().Replace(".mdx", ".m2"); filename = filename.ToLower().Replace(".mdl", ".m2"); if (cache.doodadBatches.ContainsKey(filename)) { return; } var model = new WoWFormatLib.Structs.M2.M2Model(); if (cache.models.ContainsKey(filename)) { model = cache.models[filename]; } else { if (Listfile.TryGetFileDataID(filename, out var filedataid)) { if (WoWFormatLib.Utils.CASC.FileExists(filedataid)) { var modelreader = new M2Reader(); modelreader.LoadM2(filedataid); cache.models.Add(filename, modelreader.model); model = modelreader.model; } else { throw new Exception("Model " + filename + " does not exist!"); } } else { throw new Exception("Filename " + filename + " does not exist in listfile!"); } } if (model.boundingbox == null) { CASCLib.Logger.WriteLine("Error during loading file: {0}, bounding box is not defined", filename); return; } var ddBatch = new Renderer.Structs.DoodadBatch() { boundingBox = new Renderer.Structs.BoundingBox() { min = new Vector3(model.boundingbox[0].X, model.boundingbox[0].Y, model.boundingbox[0].Z), max = new Vector3(model.boundingbox[1].X, model.boundingbox[1].Y, model.boundingbox[1].Z) } }; if (model.textures == null) { CASCLib.Logger.WriteLine("Error during loading file: {0}, model has no textures", filename); return; } if (model.skins == null) { CASCLib.Logger.WriteLine("Error during loading file: {0}, model has no skins", filename); return; } // Textures ddBatch.mats = new Renderer.Structs.Material[model.textures.Count()]; for (var i = 0; i < model.textures.Count(); i++) { uint textureFileDataID = 528732; ddBatch.mats[i].flags = model.textures[i].flags; switch (model.textures[i].type) { case 0: if (model.textureFileDataIDs != null && model.textureFileDataIDs.Length > 0 && model.textureFileDataIDs[i] != 0) { textureFileDataID = model.textureFileDataIDs[i]; } else { textureFileDataID = WoWFormatLib.Utils.CASC.getFileDataIdByName(model.textures[i].filename); } break; case 1: case 2: case 11: default: textureFileDataID = 528732; break; } // Not set in TXID if (textureFileDataID == 0) { textureFileDataID = 528732; } ddBatch.mats[i].textureID = BLPLoader.LoadTexture(textureFileDataID, cache); ddBatch.mats[i].filename = textureFileDataID.ToString(); } // Submeshes ddBatch.submeshes = new Renderer.Structs.Submesh[model.skins[0].submeshes.Count()]; for (var i = 0; i < model.skins[0].submeshes.Count(); i++) { if (filename.StartsWith("character")) { if (model.skins[0].submeshes[i].submeshID != 0) { if (!model.skins[0].submeshes[i].submeshID.ToString().EndsWith("01")) { continue; } } } ddBatch.submeshes[i].firstFace = model.skins[0].submeshes[i].startTriangle; ddBatch.submeshes[i].numFaces = model.skins[0].submeshes[i].nTriangles; for (var tu = 0; tu < model.skins[0].textureunit.Count(); tu++) { if (model.skins[0].textureunit[tu].submeshIndex == i) { ddBatch.submeshes[i].blendType = model.renderflags[model.skins[0].textureunit[tu].renderFlags].blendingMode; uint textureFileDataID = 528732; if (model.textureFileDataIDs != null && model.textureFileDataIDs.Length > 0 && model.textureFileDataIDs[model.texlookup[model.skins[0].textureunit[tu].texture].textureID] != 0) { textureFileDataID = model.textureFileDataIDs[model.texlookup[model.skins[0].textureunit[tu].texture].textureID]; } else { if (Listfile.FilenameToFDID.TryGetValue(model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.Replace('\\', '/').ToLower(), out var filedataid)) { textureFileDataID = filedataid; } else { textureFileDataID = 528732; } } if (!cache.materials.ContainsKey(textureFileDataID)) { throw new Exception("MaterialCache does not have texture " + textureFileDataID); } ddBatch.submeshes[i].material = (uint)cache.materials[textureFileDataID]; } } } ddBatch.vao = GL.GenVertexArray(); GL.BindVertexArray(ddBatch.vao); // Vertices & indices ddBatch.vertexBuffer = GL.GenBuffer(); ddBatch.indiceBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, ddBatch.vertexBuffer); GL.BindBuffer(BufferTarget.ElementArrayBuffer, ddBatch.indiceBuffer); var modelindicelist = new List <uint>(); for (var i = 0; i < model.skins[0].triangles.Count(); i++) { modelindicelist.Add(model.skins[0].triangles[i].pt1); modelindicelist.Add(model.skins[0].triangles[i].pt2); modelindicelist.Add(model.skins[0].triangles[i].pt3); } var modelindices = modelindicelist.ToArray(); ddBatch.indices = modelindices; GL.BindBuffer(BufferTarget.ElementArrayBuffer, ddBatch.indiceBuffer); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(ddBatch.indices.Length * sizeof(uint)), ddBatch.indices, BufferUsageHint.StaticDraw); var modelvertices = new Renderer.Structs.M2Vertex[model.vertices.Count()]; for (var i = 0; i < model.vertices.Count(); i++) { modelvertices[i].Position = new Vector3(model.vertices[i].position.X, model.vertices[i].position.Y, model.vertices[i].position.Z); modelvertices[i].Normal = new Vector3(model.vertices[i].normal.X, model.vertices[i].normal.Y, model.vertices[i].normal.Z); modelvertices[i].TexCoord = new Vector2(model.vertices[i].textureCoordX, model.vertices[i].textureCoordY); } GL.BindBuffer(BufferTarget.ArrayBuffer, ddBatch.vertexBuffer); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(modelvertices.Length * 8 * sizeof(float)), modelvertices, BufferUsageHint.StaticDraw); //Set pointers in buffer //var normalAttrib = GL.GetAttribLocation(shaderProgram, "normal"); //GL.EnableVertexAttribArray(normalAttrib); //GL.VertexAttribPointer(normalAttrib, 3, VertexAttribPointerType.Float, false, sizeof(float) * 8, sizeof(float) * 0); var texCoordAttrib = GL.GetAttribLocation(shaderProgram, "texCoord"); GL.EnableVertexAttribArray(texCoordAttrib); GL.VertexAttribPointer(texCoordAttrib, 2, VertexAttribPointerType.Float, false, sizeof(float) * 8, sizeof(float) * 3); var posAttrib = GL.GetAttribLocation(shaderProgram, "position"); GL.EnableVertexAttribArray(posAttrib); GL.VertexAttribPointer(posAttrib, 3, VertexAttribPointerType.Float, false, sizeof(float) * 8, sizeof(float) * 5); cache.doodadBatches.Add(filename, ddBatch); }
private static uint MISSING_TEXTURE_ID = 186184; // textures/shanecube.blp public static Renderer.Structs.DoodadBatch LoadM2(string fileName, int shaderProgram) { fileName = fileName.ToLower().Replace(".mdx", ".m2"); fileName = fileName.ToLower().Replace(".mdl", ".m2"); M2Model model = new M2Model(); if (Listfile.TryGetFileDataID(fileName, out var fileDataID)) { if (WoWFormatLib.Utils.CASC.FileExists(fileDataID)) { var modelReader = new M2Reader(); modelReader.LoadM2(fileDataID); model = modelReader.model; } else { throw new Exception("Model " + fileName + " does not exist!"); } } else { throw new Exception("Filename " + fileName + " does not exist in listfile!"); } if (model.boundingbox == null) { throw new Exception("Model does not contain bounding box: " + fileName); } var doodadBatch = new Renderer.Structs.DoodadBatch() { boundingBox = new Renderer.Structs.BoundingBox() { min = new Vector3(model.boundingbox[0].X, model.boundingbox[0].Y, model.boundingbox[0].Z), max = new Vector3(model.boundingbox[1].X, model.boundingbox[1].Y, model.boundingbox[1].Z) } }; if (model.textures == null) { throw new Exception("Model does not contain textures: " + fileName); } if (model.skins == null) { throw new Exception("Model does not contain skins: " + fileName); } // Textures doodadBatch.mats = new Renderer.Structs.Material[model.textures.Count()]; for (var i = 0; i < model.textures.Count(); i++) { uint textureFileDataID = DEFAULT_TEXTURE_ID; doodadBatch.mats[i].flags = model.textures[i].flags; switch (model.textures[i].type) { case 0: // NONE if (model.textureFileDataIDs != null && model.textureFileDataIDs.Length > 0 && model.textureFileDataIDs[i] != 0) { textureFileDataID = model.textureFileDataIDs[i]; } else { textureFileDataID = WoWFormatLib.Utils.CASC.getFileDataIdByName(model.textures[i].filename); } break; case 1: // TEX_COMPONENT_SKIN case 2: // TEX_COMPONENT_OBJECT_SKIN case 11: // TEX_COMPONENT_MONSTER_1 break; } // Not set in TXID if (textureFileDataID == 0) { textureFileDataID = DEFAULT_TEXTURE_ID; } if (!WoWFormatLib.Utils.CASC.FileExists(textureFileDataID)) { textureFileDataID = MISSING_TEXTURE_ID; } doodadBatch.mats[i].textureID = BLPLoader.LoadTexture(textureFileDataID); doodadBatch.mats[i].filename = textureFileDataID.ToString(); } // Submeshes doodadBatch.submeshes = new Renderer.Structs.Submesh[model.skins[0].submeshes.Count()]; for (var i = 0; i < model.skins[0].submeshes.Count(); i++) { doodadBatch.submeshes[i].firstFace = model.skins[0].submeshes[i].startTriangle; doodadBatch.submeshes[i].numFaces = model.skins[0].submeshes[i].nTriangles; for (var tu = 0; tu < model.skins[0].textureunit.Count(); tu++) { if (model.skins[0].textureunit[tu].submeshIndex == i) { doodadBatch.submeshes[i].blendType = model.renderflags[model.skins[0].textureunit[tu].renderFlags].blendingMode; uint textureFileDataID = DEFAULT_TEXTURE_ID; if (!WoWFormatLib.Utils.CASC.FileExists(textureFileDataID)) { textureFileDataID = MISSING_TEXTURE_ID; } if (model.textureFileDataIDs != null && model.textureFileDataIDs.Length > 0 && model.textureFileDataIDs[model.texlookup[model.skins[0].textureunit[tu].texture].textureID] != 0) { textureFileDataID = model.textureFileDataIDs[model.texlookup[model.skins[0].textureunit[tu].texture].textureID]; } else { if (Listfile.FilenameToFDID.TryGetValue(model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.Replace('\\', '/').ToLower(), out var filedataid)) { textureFileDataID = filedataid; } else { textureFileDataID = DEFAULT_TEXTURE_ID; if (!WoWFormatLib.Utils.CASC.FileExists(textureFileDataID)) { textureFileDataID = MISSING_TEXTURE_ID; } } } if (!WoWFormatLib.Utils.CASC.FileExists(textureFileDataID)) { textureFileDataID = MISSING_TEXTURE_ID; } doodadBatch.submeshes[i].material = (uint)BLPLoader.LoadTexture(textureFileDataID); } } } doodadBatch.vao = GL.GenVertexArray(); GL.BindVertexArray(doodadBatch.vao); // Vertices & indices doodadBatch.vertexBuffer = GL.GenBuffer(); doodadBatch.indiceBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, doodadBatch.vertexBuffer); GL.BindBuffer(BufferTarget.ElementArrayBuffer, doodadBatch.indiceBuffer); var modelindicelist = new List <uint>(); for (var i = 0; i < model.skins[0].triangles.Count(); i++) { modelindicelist.Add(model.skins[0].triangles[i].pt1); modelindicelist.Add(model.skins[0].triangles[i].pt2); modelindicelist.Add(model.skins[0].triangles[i].pt3); } var modelindices = modelindicelist.ToArray(); doodadBatch.indices = modelindices; GL.BindBuffer(BufferTarget.ElementArrayBuffer, doodadBatch.indiceBuffer); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(doodadBatch.indices.Length * sizeof(uint)), doodadBatch.indices, BufferUsageHint.StaticDraw); var modelvertices = new Renderer.Structs.M2Vertex[model.vertices.Count()]; for (var i = 0; i < model.vertices.Count(); i++) { modelvertices[i].Position = new Vector3(model.vertices[i].position.X, model.vertices[i].position.Y, model.vertices[i].position.Z); modelvertices[i].Normal = new Vector3(model.vertices[i].normal.X, model.vertices[i].normal.Y, model.vertices[i].normal.Z); modelvertices[i].TexCoord = new Vector2(model.vertices[i].textureCoordX, model.vertices[i].textureCoordY); } GL.BindBuffer(BufferTarget.ArrayBuffer, doodadBatch.vertexBuffer); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(modelvertices.Length * 8 * sizeof(float)), modelvertices, BufferUsageHint.StaticDraw); //Set pointers in buffer //var normalAttrib = GL.GetAttribLocation(shaderProgram, "normal"); //GL.EnableVertexAttribArray(normalAttrib); //GL.VertexAttribPointer(normalAttrib, 3, VertexAttribPointerType.Float, false, sizeof(float) * 8, sizeof(float) * 0); var texCoordAttrib = GL.GetAttribLocation(shaderProgram, "texCoord"); GL.EnableVertexAttribArray(texCoordAttrib); GL.VertexAttribPointer(texCoordAttrib, 2, VertexAttribPointerType.Float, false, sizeof(float) * 8, sizeof(float) * 3); var posAttrib = GL.GetAttribLocation(shaderProgram, "position"); GL.EnableVertexAttribArray(posAttrib); GL.VertexAttribPointer(posAttrib, 3, VertexAttribPointerType.Float, false, sizeof(float) * 8, sizeof(float) * 5); return(doodadBatch); }
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)); }