コード例 #1
0
        public ConverterForm()
        {
            new Thread(() =>
            {
                if (!Listfile.IsInitialized)
                {
                    Listfile.Initialize();
                }
            }).Start();

            InitializeComponent();
            lb.HorizontalScrollbar = true;

            var key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\MultiConverter", true);

            if (key == null)
            {
                key = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\MultiConverter");
            }

            if (!key.GetValueNames().Contains("firstTime"))
            {
                key.SetValue("firstTime", false);
            }

            if (!bool.Parse(key.GetValue("firstTime").ToString()))
            {
                MessageBox.Show("First time converting might take a while since it is downloading + loading the listfile..");
                key.SetValue("firstTime", true);
            }
        }
コード例 #2
0
        public ConverterForm()
        {
            new Thread(() =>
            {
                if (!Listfile.IsInitialized)
                {
                    Listfile.Initialize();
                }
            }).Start();

            InitializeComponent();
            lb.HorizontalScrollbar = true;

            new Thread(() =>
            {
                Thread.Sleep(3000);

                var hasUpdate = UpdateManager.HasUpdates(AssemblyVersion);
                if (hasUpdate.Item1)
                {
                    var messageBox = MessageBox.Show($"An update is available! From: {AssemblyVersion} To: {hasUpdate.Item2}.\nPress OK to update.", "Update!", MessageBoxButtons.OK);
                    if (messageBox == DialogResult.OK)
                    {
                        // Start the updater..
                        UpdateManager.StartUpdater();

                        // Close the current window.
                        Invoke((MethodInvoker) delegate
                        {
                            Close();
                        });
                    }
                }
            }).Start();
        }
コード例 #3
0
        public void Read(byte[] inData)
        {
            using (var stream = new MemoryStream(inData))
                using (var reader = new BinaryReader(stream))
                {
                    var modiSize = inData.Length / 4;
                    DoodadFileIds = new uint[modiSize];

                    for (var i = 0; i < modiSize; ++i)
                    {
                        var filedataId = reader.ReadUInt32();
                        var filename   = Listfile.LookupFilename(filedataId, ".wmo", "m2").Replace("m2", "mdx").Replace("/", "\\") + "\0";

                        if (!DoodadNames.ContainsKey(filedataId))
                        {
                            DoodadNames.Add(filedataId, filename);
                        }

                        DoodadFileIds[i] = filedataId;
                    }
                }

            if (WMOFile.DisableDoodads)
            {
                WMOFile.Chunks.Add(new MODN());
            }
            else
            {
                WMOFile.Chunks.Add(new MODN {
                    DoodadFilenames = DoodadNames.Values.ToList()
                });
            }
        }
コード例 #4
0
 public static int LoadTexture(string filename, CacheStorage cache)
 {
     if (Listfile.TryGetFileDataID(filename, out var filedataid))
     {
         return(LoadTexture(filedataid, cache));
     }
     else
     {
         throw new Exception("Couldn't find filedataid for file " + filename + " in listfile!");
     }
 }
コード例 #5
0
 public Stream StreamForTableName(string tableName, string build)
 {
     if (Listfile.TryGetFileDataID("dbfilesclient/" + tableName + ".db2", out var fileDataID))
     {
         return(CASC.OpenFile(fileDataID));
     }
     else
     {
         throw new FileNotFoundException("DBC " + tableName + " not found in listfile, could not look up filedataid!");
     }
 }
コード例 #6
0
 public static void ExportM2(string file, BackgroundWorker exportworker = null, string destinationOverride = null)
 {
     if (!Listfile.TryGetFileDataID(file, out var filedataid))
     {
         CASCLib.Logger.WriteLine("Error! Could not find filedataid for " + file + ", skipping export!");
         return;
     }
     else
     {
         ExportM2(filedataid, exportworker, destinationOverride, file);
     }
 }
コード例 #7
0
 public static void ExportWMO(string file, BackgroundWorker exportworker = null, string destinationOverride = null, short doodadSetExportID = short.MaxValue, bool[] enabledGroups = null, bool[] enabledSets = null)
 {
     if (!Listfile.TryGetFileDataID(file, out var filedataid))
     {
         CASCLib.Logger.WriteLine("Error! Could not find filedataid for " + file + ", skipping export!");
         return;
     }
     else
     {
         ExportWMO(filedataid, exportworker, destinationOverride, doodadSetExportID, file, enabledGroups, enabledSets);
     }
 }
コード例 #8
0
        private void ReadSFID(BinaryReader reader, uint size)
        {
            for (var i = 0u; i < size / 4u; ++i)
            {
                var skinFileId = reader.ReadUInt32();
                var filename   = Listfile.LookupFilename(skinFileId, ".m2").Replace('/', '\\');

                filename = System.IO.Path.GetFileName(filename);

                SkinFiles.Add(filename);
            }
        }
コード例 #9
0
        private static void ExportExtraMaterials(uint tex, WoWFormatLib.Structs.WMO.WMO wmo, List <Structs.Material> mats, int matIdx, string path)
        {
            Stream ms = null;

            if (CASC.FileExists(tex))
            {
                var mat = new Structs.Material();
                if (wmo.textures == null)
                {
                    if (Listfile.TryGetFilename(tex, out var textureFilename))
                    {
                        mat.filename = Path.GetFileNameWithoutExtension(textureFilename).Replace(" ", "");
                    }
                    else
                    {
                        mat.filename = tex.ToString();
                    }

                    ms = CASC.OpenFile(tex);
                }
                else
                {
                    for (var ti = 0; ti < wmo.textures.Count(); ti++)
                    {
                        if (wmo.textures[ti].startOffset == tex)
                        {
                            mat.filename = Path.GetFileNameWithoutExtension(wmo.textures[ti].filename).Replace(" ", "");
                            ms           = CASC.OpenFile(wmo.textures[ti].filename);
                        }
                    }
                }

                if (ms == null)
                {
                    return; // Can this even happen?
                }
                string saveLocation = Path.Combine(path, mat.filename + ".png");
                if (!File.Exists(saveLocation))
                {
                    try
                    {
                        using (BlpFile blp = new BlpFile(ms))
                            blp.GetBitmap(0).Save(saveLocation);
                    }
                    catch (Exception e)
                    {
                        CASCLib.Logger.WriteLine("Exception while saving BLP " + mat.filename + ": " + e.Message);
                    }
                }

                mats.Add(mat);
            }
        }
コード例 #10
0
        private void RetrieveTextures()
        {
            using (var stream = new MemoryStream(tex.Data))
                using (var reader = new BinaryReader(stream))
                {
                    while (reader.BaseStream.Position < reader.BaseStream.Length)
                    {
                        var chunk = new string(reader.ReadChars(4));
                        var size  = reader.ReadUInt32();

                        switch (chunk)
                        {
                        case "PMAM":
                            texPos += (int)size + 8;
                            reader.BaseStream.Position += size;
                            break;

                        case "DIDM":    //< Texures
                            for (var i = 0; i < size / 4; ++i)
                            {
                                var filedataid = reader.ReadUInt32();
                                if (filedataid != 0)
                                {
                                    var filename = Listfile.LookupFilename(filedataid, ".adt", modelname) + "\0";
                                    MapTextures.Add(filedataid, filename);
                                }
                            }

                            texPos += (int)size + 8;
                            break;

                        case "DIHM":    //< Textures as well
                            for (var i = 0; i < size / 4; ++i)
                            {
                                var filedataid = reader.ReadUInt32();
                                if (filedataid != 0)
                                {
                                    var filename = Listfile.LookupFilename(filedataid, ".adt", modelname) + "\0";
                                    MapTextures.Add(filedataid, filename);
                                }
                            }

                            texPos += (int)size + 8;
                            break;

                        default:
                            reader.BaseStream.Position += size;
                            break;
                        }
                    }
                }
        }
コード例 #11
0
ファイル: DisplayStructure.cs プロジェクト: izzyus/WoWExport
 public static void GenerateCASCList()
 {
     Listfile.Load();
     foreach (var line in Listfile.FDIDToFilename)
     {
         if (Managers.ArchiveManager.FileExists(line.Key))
         {
             if (!EndsWithOneOf(line.Value, SkipList) && !IsWMOGroupFile(line.Value))
             {
                 MLF.Add(line.Value);
             }
         }
     }
     //Console.WriteLine("Display list generated");
 }
コード例 #12
0
ファイル: M2.cs プロジェクト: ratkosrb/MultiConverter
        private void ReadTXID(uint size)
        {
            for (var i = 0; i < size / 4; ++i)
            {
                var textureId = Read <uint>();

                var filename = Listfile.LookupFilename(textureId, ".m2", ModelName);
                if (filename != string.Empty)
                {
                    TexturePaths.Add(filename + "\0\0", filename.Length);
                }
                else
                {
                    Console.WriteLine("DBC Defined Texture!");
                }
            }
        }
コード例 #13
0
        private string AddFilename(uint fdid)
        {
            if (fdid != 0)
            {
                var textureFilename = Listfile.LookupFilename(fdid, ".wmo").Replace('/', '\\') + "\0";

                if (!Filenames.ContainsKey(textureFilename))
                {
                    Filenames.Add(textureFilename, textureOffset);
                    textureOffset += (uint)textureFilename.Length;
                }

                return(textureFilename);
            }

            return(string.Empty);
        }
コード例 #14
0
        private string AddFilename(uint fdid)
        {
            if (fdid != 0)
            {
                var textureFilename = Listfile.LookupFilename(fdid, ".wmo", wmoName).Replace('/', '\\');
                var texFilename     = textureFilename + "\0";

                if (!filenamePosition.ContainsKey(texFilename))
                {
                    filenamePosition.Add(texFilename, textureOffset);
                    textureOffset += (uint)texFilename.Length;
                }

                return(texFilename);
            }

            return(string.Empty);
        }
コード例 #15
0
ファイル: Form1.cs プロジェクト: ratkosrb/MultiConverter
        private void fix_btn_Click(object sender, EventArgs e)
        {
            if (!Listfile.IsInitialized)
            {
                Listfile.Initialize();
            }

            if (lb.Items.Count == 0)
            {
                MessageBox.Show("No files");
                return;
            }

            if (lb.Items.Count > 0)
            {
                progress.Value   = 0;
                progress.Maximum = lb.Items.Count;
                progress.Step    = PROGRESS;
                Fix();
            }
        }
コード例 #16
0
ファイル: Program.cs プロジェクト: ratkosrb/MultiConverter
        static void Main(string[] args)
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("| First time converting might take a while since it is downloading + loading the listfile..");
            Console.ForegroundColor = ConsoleColor.Gray;

            Console.WriteLine("Loading listfile...");
            Listfile.Initialize();

            // var printHelp = false;

            // foreach (var arg in args)
            // {
            //     if (arg[0] == '-')
            //     {
            //         switch (arg.ToLower())
            //         {
            //             case "-nomodel": AdtModels = false; break;
            //             case "-nowater": AdtWater = false; break;
            //             case "-fixhelm": FixHelm = true; break;
            //             default: printHelp = true; break;
            //         }
            //     }
            // }

            // Print help if there are no files to convert.
            // printHelp |= FilesToConvert.Count == 0;
            //
            // if (printHelp)
            //     PrintHelp();

            string     m2        = "nzoth - Copy.m2";
            IConverter converter = new M2(m2, FixHelm);

            if (converter.Fix())
            {
                converter.Save();
            }
        }
コード例 #17
0
        private void ReadTXID(BinaryReader reader, uint size)
        {
            for (var i = 0u; i < size / 4u; i++)
            {
                var textureId = reader.ReadUInt32();

                var filename = Listfile.LookupFilename(textureId, ".m2").Replace('/', '\\');
                var texture  = new Texture
                {
                    Filename       = filename + "\0\0",
                    FilenameLength = filename.Length
                };

                if (Textures.ContainsKey(texture))
                {
                    Textures[texture]++;
                }
                else
                {
                    Textures.Add(texture, 1);
                }
            }
        }
コード例 #18
0
        public bool Fix()
        {
            if (!Valid || Size() < 0x5C)
            {
                return(false);
            }

            int pos = 0;// 0x54 + ReadInt(0x58) + 0x8;

            pos = SkipChunk(pos, "MVER");
            pos = SkipChunk(pos, "MOHD");
            // pos += 28 + 44;          // Skip shit
            // WriteUInt(pos, 0);  // Set Doodads to 0
            // pos += 44;

            var motx = MagicToInt("MOTX");
            var ofs  = ChunksOfs(pos, motx);

            if (ofs.ContainsKey(motx))
            {
                pos = SkipChunk(pos, "MOTX");
                ReadMOMT(pos);
                pos = WriteMOMT(pos);
            }
            else
            {
                ReadMOMT(pos);
                var motxSize = CalculateTextureSize(filenamePosition.Keys.ToList());
                AddEmptyBytes(pos, (int)motxSize + 8);
                WriteHeaderMagic(pos, "MOTX");
                WriteUInt(pos + 4, motxSize);

                pos += 8;
                foreach (var texture in filenamePosition)
                {
                    var newFilename = texture.Key.ToUpper();
                    for (var j = 0; j < newFilename.Length; ++j)
                    {
                        WriteChar(pos + j, newFilename[j]);
                    }
                    pos += newFilename.Length;
                }

                pos = WriteMOMT(pos);
            }

            pos = SkipChunk(pos, "MOGN");
            pos = FixMOGI(pos);// MOGI

            int mosb = MagicToInt("MOSB");

            ofs = ChunksOfs(pos, mosb);
            if (!ofs.ContainsKey(mosb))
            {
                AddEmptyBytes(pos, 0xC);
                WriteInt(pos, mosb);
                WriteInt(pos + 0x4, 4);
                pos += 0xC;
            }
            else
            {
                pos = SkipChunk(pos, "MOSB");
            }

            pos = SkipChunk(pos, "MOPV");
            pos = SkipChunk(pos, "MOPT");
            pos = SkipChunk(pos, "MOPR");

            int pos_molt = pos;

            pos = SkipChunk(pos, "MOLT");
            // fix nLights
            WriteInt(0x20, ReadInt(pos_molt + 0x4) / 0x30);

            pos = SkipChunk(pos, "MODS");

            int modi = MagicToInt("MODI");

            ofs = ChunksOfs(pos, modi);
            if (ofs.ContainsKey(modi))
            {
                pos += 4;
                var size = ReadUInt(pos);

                for (var i = 0; i < size / 4; ++i)
                {
                    pos += 4;
                    var filedataid = ReadUInt(pos);
                    var filename   = Listfile.LookupFilename(filedataid, ".wmo", wmoName, "m2").Replace('/', '\\').Replace("m2", "mdx");

                    var remainderCount = 4u - (uint)filename.Length % 4u;
                    if (!doodadsPosition.ContainsValue(filename))
                    {
                        doodadsPosition.Add(doodadsOffset, filename);
                        doodadsOffset += (uint)filename.Length;

                        filenamePadding.Add(filename, remainderCount);
                    }
                }

                pos += 4;

                var modiSize = CalculateDoodadSize();
                AddEmptyBytes(pos, (int)modiSize + 8);
                WriteHeaderMagic(pos, "MODN");
                WriteUInt(pos + 0x4, modiSize);

                pos += 0x8;
                foreach (var filename in doodadsPosition)
                {
                    var paddingCount  = filenamePadding[filename.Value];
                    var upperFilename = filename.Value.ToUpper();

                    for (var i = 0; i < upperFilename.Length; ++i)
                    {
                        WriteChar(pos + i, upperFilename[i]);
                    }
                    pos += upperFilename.Length;

                    for (var i = 0; i < paddingCount; ++i)
                    {
                        WriteChar(pos + i, '\0');
                    }
                    pos += (int)paddingCount;
                }
            }

            pos = FixMODD(pos);  // MODD
            pos = SkipChunk(pos, "MFOG");
            pos = SkipMCVP(pos); // Optional chunk

            return(true);
        }
コード例 #19
0
ファイル: WMOExporter.cs プロジェクト: serayn/WoWExportTools
        public static void ExportWMO(uint filedataid, BackgroundWorker exportworker = null, string destinationOverride = null, ushort doodadSetExportID = ushort.MaxValue, string filename = "")
        {
            if (exportworker == null)
            {
                exportworker = new BackgroundWorker();
                exportworker.WorkerReportsProgress = true;
            }

            if (string.IsNullOrEmpty(filename))
            {
                if (!Listfile.TryGetFilename(filedataid, out filename))
                {
                    CASCLib.Logger.WriteLine("Warning! Could not find filename for " + filedataid + "!");
                }
            }

            Console.WriteLine("Loading WMO file..");

            exportworker.ReportProgress(5, "Reading WMO..");

            var outdir = ConfigurationManager.AppSettings["outdir"];
            var wmo    = new WMOReader().LoadWMO(filedataid);

            var customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();

            customCulture.NumberFormat.NumberDecimalSeparator    = ".";
            System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;

            exportworker.ReportProgress(30, "Reading WMO..");

            uint totalVertices = 0;

            var groups = new Structs.WMOGroup[wmo.group.Count()];

            for (var g = 0; g < wmo.group.Count(); g++)
            {
                Console.WriteLine("Loading group #" + g);
                if (wmo.group[g].mogp.vertices == null)
                {
                    Console.WriteLine("Group has no vertices!"); continue;
                }
                for (var i = 0; i < wmo.groupNames.Count(); i++)
                {
                    if (wmo.group[g].mogp.nameOffset == wmo.groupNames[i].offset)
                    {
                        groups[g].name = wmo.groupNames[i].name.Replace(" ", "_");
                    }
                }

                if (groups[g].name == "antiportal")
                {
                    Console.WriteLine("Group is antiportal"); continue;
                }

                groups[g].verticeOffset = totalVertices;
                groups[g].vertices      = new Structs.Vertex[wmo.group[g].mogp.vertices.Count()];

                for (var i = 0; i < wmo.group[g].mogp.vertices.Count(); i++)
                {
                    groups[g].vertices[i].Position = new Structs.Vector3D()
                    {
                        X = wmo.group[g].mogp.vertices[i].vector.X * -1,
                        Y = wmo.group[g].mogp.vertices[i].vector.Z,
                        Z = wmo.group[g].mogp.vertices[i].vector.Y
                    };

                    groups[g].vertices[i].Normal = new Structs.Vector3D()
                    {
                        X = wmo.group[g].mogp.normals[i].normal.X,
                        Y = wmo.group[g].mogp.normals[i].normal.Z,
                        Z = wmo.group[g].mogp.normals[i].normal.Y
                    };

                    groups[g].vertices[i].TexCoord = new Structs.Vector2D()
                    {
                        X = wmo.group[g].mogp.textureCoords[0][i].X,
                        Y = wmo.group[g].mogp.textureCoords[0][i].Y
                    };

                    totalVertices++;
                }

                var indicelist = new List <uint>();

                for (var i = 0; i < wmo.group[g].mogp.indices.Count(); i++)
                {
                    indicelist.Add(wmo.group[g].mogp.indices[i].indice);
                }

                groups[g].indices = indicelist.ToArray();
            }

            if (destinationOverride == null)
            {
                // Create output directory
                if (!string.IsNullOrEmpty(filename))
                {
                    if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename))))
                    {
                        Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(filename)));
                    }
                }
                else
                {
                    if (!Directory.Exists(outdir))
                    {
                        Directory.CreateDirectory(outdir);
                    }
                }
            }

            StreamWriter doodadSW;

            if (destinationOverride == null)
            {
                if (!string.IsNullOrEmpty(filename))
                {
                    doodadSW = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename.Replace(" ", "")) + "_ModelPlacementInformation.csv"));
                }
                else
                {
                    doodadSW = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(filename), filedataid + "_ModelPlacementInformation.csv"));
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(filename))
                {
                    doodadSW = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileNameWithoutExtension(filename).Replace(" ", "") + "_ModelPlacementInformation.csv"));
                }
                else
                {
                    doodadSW = new StreamWriter(Path.Combine(outdir, destinationOverride, filedataid + "_ModelPlacementInformation.csv"));
                }
            }

            exportworker.ReportProgress(55, "Exporting doodads..");

            doodadSW.WriteLine("ModelFile;PositionX;PositionY;PositionZ;RotationW;RotationX;RotationY;RotationZ;ScaleFactor;DoodadSet");

            for (var i = 0; i < wmo.doodadSets.Count(); i++)
            {
                var doodadSet = wmo.doodadSets[i];

                var currentDoodadSetName = doodadSet.setName.Replace("Set_", "").Replace("SET_", "").Replace("$DefaultGlobal", "Default");

                if (doodadSetExportID != ushort.MaxValue)
                {
                    if (i != 0 && i != doodadSetExportID)
                    {
                        Console.WriteLine("Skipping doodadset with ID " + i + " (" + currentDoodadSetName + ") because export filter is set to " + doodadSetExportID);
                        continue;
                    }
                }

                Console.WriteLine("At doodadset " + i + " (" + currentDoodadSetName + ")");

                for (var j = doodadSet.firstInstanceIndex; j < (doodadSet.firstInstanceIndex + doodadSet.numDoodads); j++)
                {
                    var doodadDefinition = wmo.doodadDefinitions[j];

                    var  doodadFilename   = "";
                    uint doodadFileDataID = 0;

                    if (wmo.doodadIds != null)
                    {
                        doodadFileDataID = wmo.doodadIds[doodadDefinition.offset];
                        if (!Listfile.TryGetFilename(doodadFileDataID, out doodadFilename))
                        {
                            CASCLib.Logger.WriteLine("Could not find filename for " + doodadFileDataID + ", setting filename to filedataid..");
                            doodadFilename = doodadFileDataID.ToString();
                        }
                    }
                    else
                    {
                        CASCLib.Logger.WriteLine("Warning!! File " + filename + " ID: " + filedataid + " still has filenames!");
                        foreach (var doodadNameEntry in wmo.doodadNames)
                        {
                            if (doodadNameEntry.startOffset == doodadDefinition.offset)
                            {
                                doodadFilename = doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2").ToLower();
                                if (!Listfile.TryGetFileDataID(doodadFilename, out doodadFileDataID))
                                {
                                    CASCLib.Logger.WriteLine("Error! Could not find filedataid for " + doodadFilename + "!");
                                    continue;
                                }
                            }
                        }
                    }

                    if (destinationOverride == null)
                    {
                        if (!string.IsNullOrEmpty(doodadFilename))
                        {
                            if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(doodadFilename) + ".obj")))
                            {
                                M2Exporter.ExportM2(doodadFileDataID, null, Path.Combine(outdir, Path.GetDirectoryName(filename)), doodadFilename);
                            }

                            if (File.Exists(Path.Combine(outdir, Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(doodadFilename) + ".obj")))
                            {
                                doodadSW.WriteLine(Path.GetFileNameWithoutExtension(doodadFilename) + ".obj;" + doodadDefinition.position.X.ToString("F09") + ";" + doodadDefinition.position.Y.ToString("F09") + ";" + doodadDefinition.position.Z.ToString("F09") + ";" + doodadDefinition.rotation.W.ToString("F15") + ";" + doodadDefinition.rotation.X.ToString("F15") + ";" + doodadDefinition.rotation.Y.ToString("F15") + ";" + doodadDefinition.rotation.Z.ToString("F15") + ";" + doodadDefinition.scale + ";" + currentDoodadSetName);
                            }
                        }
                        else
                        {
                            if (!File.Exists(Path.Combine(outdir, doodadFileDataID + ".obj")))
                            {
                                M2Exporter.ExportM2(doodadFileDataID, null, outdir, doodadFilename);
                            }

                            if (File.Exists(Path.Combine(outdir, doodadFileDataID + ".obj")))
                            {
                                doodadSW.WriteLine(doodadFileDataID + ".obj;" + doodadDefinition.position.X.ToString("F09") + ";" + doodadDefinition.position.Y.ToString("F09") + ";" + doodadDefinition.position.Z.ToString("F09") + ";" + doodadDefinition.rotation.W.ToString("F15") + ";" + doodadDefinition.rotation.X.ToString("F15") + ";" + doodadDefinition.rotation.Y.ToString("F15") + ";" + doodadDefinition.rotation.Z.ToString("F15") + ";" + doodadDefinition.scale + ";" + currentDoodadSetName);
                            }
                        }
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(doodadFilename))
                        {
                            if (!File.Exists(Path.Combine(destinationOverride, Path.GetFileNameWithoutExtension(doodadFilename) + ".obj")))
                            {
                                M2Exporter.ExportM2(doodadFileDataID, null, destinationOverride, doodadFilename);
                            }

                            if (File.Exists(Path.Combine(destinationOverride, Path.GetFileNameWithoutExtension(doodadFilename) + ".obj")))
                            {
                                doodadSW.WriteLine(Path.GetFileNameWithoutExtension(doodadFilename) + ".obj;" + doodadDefinition.position.X.ToString("F09") + ";" + doodadDefinition.position.Y.ToString("F09") + ";" + doodadDefinition.position.Z.ToString("F09") + ";" + doodadDefinition.rotation.W.ToString("F15") + ";" + doodadDefinition.rotation.X.ToString("F15") + ";" + doodadDefinition.rotation.Y.ToString("F15") + ";" + doodadDefinition.rotation.Z.ToString("F15") + ";" + doodadDefinition.scale + ";" + currentDoodadSetName);
                            }
                        }
                        else
                        {
                            if (!File.Exists(Path.Combine(destinationOverride, doodadFileDataID + ".obj")))
                            {
                                M2Exporter.ExportM2(doodadFileDataID, null, destinationOverride, doodadFilename);
                            }

                            if (File.Exists(Path.Combine(destinationOverride, doodadFileDataID + ".obj")))
                            {
                                doodadSW.WriteLine(doodadFileDataID + ".obj;" + doodadDefinition.position.X.ToString("F09") + ";" + doodadDefinition.position.Y.ToString("F09") + ";" + doodadDefinition.position.Z.ToString("F09") + ";" + doodadDefinition.rotation.W.ToString("F15") + ";" + doodadDefinition.rotation.X.ToString("F15") + ";" + doodadDefinition.rotation.Y.ToString("F15") + ";" + doodadDefinition.rotation.Z.ToString("F15") + ";" + doodadDefinition.scale + ";" + currentDoodadSetName);
                            }
                        }
                    }
                }
            }

            doodadSW.Close();

            exportworker.ReportProgress(65, "Exporting textures..");

            var mtlsb     = new StringBuilder();
            var textureID = 0;

            if (wmo.materials == null)
            {
                CASCLib.Logger.WriteLine("Unable to find materials for WMO " + filedataid + ", not exporting!");
                return;
            }

            var materials      = new Structs.Material[wmo.materials.Count()];
            var extraMaterials = new List <Structs.Material>();

            for (var i = 0; i < wmo.materials.Count(); i++)
            {
                var blpreader = new BLPReader();

                if (wmo.textures == null)
                {
                    if (Listfile.TryGetFilename(wmo.materials[i].texture1, out var textureFilename))
                    {
                        materials[i].filename = Path.GetFileNameWithoutExtension(textureFilename);
                    }
                    else
                    {
                        materials[i].filename = wmo.materials[i].texture1.ToString();
                    }

                    blpreader.LoadBLP(wmo.materials[i].texture1);
                }
                else
                {
                    for (var ti = 0; ti < wmo.textures.Count(); ti++)
                    {
                        if (wmo.textures[ti].startOffset == wmo.materials[i].texture1)
                        {
                            materials[i].filename = Path.GetFileNameWithoutExtension(wmo.textures[ti].filename);
                            blpreader.LoadBLP(wmo.textures[ti].filename);
                        }
                    }
                }

                materials[i].textureID = textureID + i;

                if (wmo.materials[i].blendMode == 0)
                {
                    materials[i].transparent = false;
                }
                else
                {
                    materials[i].transparent = true;
                }

                materials[i].blendMode   = wmo.materials[i].blendMode;
                materials[i].shaderID    = wmo.materials[i].shader;
                materials[i].terrainType = wmo.materials[i].groundType;

                var saveLocation = "";

                if (destinationOverride == null)
                {
                    if (!string.IsNullOrEmpty(filename))
                    {
                        saveLocation = Path.Combine(outdir, Path.GetDirectoryName(filename), materials[i].filename + ".png");
                    }
                    else
                    {
                        saveLocation = Path.Combine(outdir, materials[i].filename + ".png");
                    }
                }
                else
                {
                    saveLocation = Path.Combine(outdir, destinationOverride, materials[i].filename + ".png");
                }

                if (!File.Exists(saveLocation))
                {
                    try
                    {
                        if (materials[i].transparent)
                        {
                            blpreader.bmp.Save(saveLocation);
                        }
                        else
                        {
                            blpreader.bmp.Clone(new Rectangle(0, 0, blpreader.bmp.Width, blpreader.bmp.Height), PixelFormat.Format32bppRgb).Save(saveLocation);
                        }
                    }
                    catch (Exception e)
                    {
                        CASCLib.Logger.WriteLine("Exception while saving BLP " + materials[i].filename + ": " + e.Message);
                    }
                }

                textureID++;

                // Extra materials
                // Texture 2
                if (CASC.FileExists(wmo.materials[i].texture2))
                {
                    var tex2mat = new Structs.Material();
                    if (wmo.textures == null)
                    {
                        if (Listfile.TryGetFilename(wmo.materials[i].texture2, out var textureFilename))
                        {
                            tex2mat.filename = Path.GetFileNameWithoutExtension(textureFilename);
                        }
                        else
                        {
                            tex2mat.filename = wmo.materials[i].texture2.ToString();
                        }

                        blpreader.LoadBLP(wmo.materials[i].texture2);
                    }
                    else
                    {
                        for (var ti = 0; ti < wmo.textures.Count(); ti++)
                        {
                            if (wmo.textures[ti].startOffset == wmo.materials[i].texture2)
                            {
                                tex2mat.filename = Path.GetFileNameWithoutExtension(wmo.textures[ti].filename);
                                blpreader.LoadBLP(wmo.textures[ti].filename);
                            }
                        }
                    }

                    if (destinationOverride == null)
                    {
                        if (!string.IsNullOrEmpty(filename))
                        {
                            saveLocation = Path.Combine(outdir, Path.GetDirectoryName(filename), tex2mat.filename + ".png");
                        }
                        else
                        {
                            saveLocation = Path.Combine(outdir, tex2mat.filename + ".png");
                        }
                    }
                    else
                    {
                        saveLocation = Path.Combine(outdir, destinationOverride, tex2mat.filename + ".png");
                    }

                    if (!File.Exists(saveLocation))
                    {
                        try
                        {
                            blpreader.bmp.Save(saveLocation);
                        }
                        catch (Exception e)
                        {
                            CASCLib.Logger.WriteLine("Exception while saving BLP " + tex2mat.filename + ": " + e.Message);
                        }
                    }

                    extraMaterials.Add(tex2mat);
                }

                // Texture 3
                if (CASC.FileExists(wmo.materials[i].texture3))
                {
                    var tex3mat = new Structs.Material();
                    if (wmo.textures == null)
                    {
                        if (Listfile.TryGetFilename(wmo.materials[i].texture3, out var textureFilename))
                        {
                            tex3mat.filename = Path.GetFileNameWithoutExtension(textureFilename);
                        }
                        else
                        {
                            tex3mat.filename = wmo.materials[i].texture3.ToString();
                        }

                        blpreader.LoadBLP(wmo.materials[i].texture3);
                    }
                    else
                    {
                        for (var ti = 0; ti < wmo.textures.Count(); ti++)
                        {
                            if (wmo.textures[ti].startOffset == wmo.materials[i].texture3)
                            {
                                tex3mat.filename = Path.GetFileNameWithoutExtension(wmo.textures[ti].filename);
                                blpreader.LoadBLP(wmo.textures[ti].filename);
                            }
                        }
                    }

                    if (destinationOverride == null)
                    {
                        if (!string.IsNullOrEmpty(filename))
                        {
                            saveLocation = Path.Combine(outdir, Path.GetDirectoryName(filename), tex3mat.filename + ".png");
                        }
                        else
                        {
                            saveLocation = Path.Combine(outdir, tex3mat.filename + ".png");
                        }
                    }
                    else
                    {
                        saveLocation = Path.Combine(outdir, destinationOverride, tex3mat.filename + ".png");
                    }

                    if (!File.Exists(saveLocation))
                    {
                        try
                        {
                            blpreader.bmp.Save(saveLocation);
                        }
                        catch (Exception e)
                        {
                            CASCLib.Logger.WriteLine("Exception while saving BLP " + tex3mat.filename + ": " + e.Message);
                        }
                    }

                    extraMaterials.Add(tex3mat);
                }
            }

            //No idea how MTL files really work yet. Needs more investigation.
            foreach (var material in materials)
            {
                mtlsb.Append("newmtl " + material.filename + "\n");
                mtlsb.Append("Ns 96.078431\n");
                mtlsb.Append("Ka 1.000000 1.000000 1.000000\n");
                mtlsb.Append("Kd 0.640000 0.640000 0.640000\n");
                mtlsb.Append("Ks 0.000000 0.000000 0.000000\n");
                mtlsb.Append("Ke 0.000000 0.000000 0.000000\n");
                mtlsb.Append("Ni 1.000000\n");
                mtlsb.Append("d 1.000000\n");
                mtlsb.Append("illum 1\n");
                mtlsb.Append("map_Kd " + material.filename + ".png\n");
                if (material.transparent)
                {
                    mtlsb.Append("map_d " + material.filename + ".png\n");
                }

                if (ConfigurationManager.AppSettings["textureMetadata"] == "True")
                {
                    mtlsb.Append("blend " + material.blendMode + "\n");
                    mtlsb.Append("shader " + material.shaderID + "\n");
                    mtlsb.Append("terrain " + material.terrainType + "\n");
                }
            }

            foreach (var material in extraMaterials)
            {
                mtlsb.Append("newmtl " + material.filename + "\n");
                mtlsb.Append("Ns 96.078431\n");
                mtlsb.Append("Ka 1.000000 1.000000 1.000000\n");
                mtlsb.Append("Kd 0.640000 0.640000 0.640000\n");
                mtlsb.Append("Ks 0.000000 0.000000 0.000000\n");
                mtlsb.Append("Ke 0.000000 0.000000 0.000000\n");
                mtlsb.Append("Ni 1.000000\n");
                mtlsb.Append("d 1.000000\n");
                mtlsb.Append("illum 1\n");
                mtlsb.Append("map_Kd " + material.filename + ".png\n");
                if (material.transparent)
                {
                    mtlsb.Append("map_d " + material.filename + ".png\n");
                }
            }


            if (!string.IsNullOrEmpty(filename))
            {
                if (destinationOverride == null)
                {
                    File.WriteAllText(Path.Combine(outdir, filename.Replace(".wmo", ".mtl")), mtlsb.ToString());
                }
                else
                {
                    File.WriteAllText(Path.Combine(outdir, destinationOverride, Path.GetFileName(filename.ToLower()).Replace(".wmo", ".mtl")), mtlsb.ToString());
                }
            }
            else
            {
                if (destinationOverride == null)
                {
                    File.WriteAllText(Path.Combine(outdir, filedataid + ".mtl"), mtlsb.ToString());
                }
                else
                {
                    File.WriteAllText(Path.Combine(outdir, destinationOverride, filedataid + ".mtl"), mtlsb.ToString());
                }
            }

            exportworker.ReportProgress(75, "Exporting model..");

            var numRenderbatches = 0;

            //Get total amount of render batches
            for (var i = 0; i < wmo.group.Count(); i++)
            {
                if (wmo.group[i].mogp.renderBatches == null)
                {
                    continue;
                }
                numRenderbatches = numRenderbatches + wmo.group[i].mogp.renderBatches.Count();
            }


            var rb = 0;

            for (var g = 0; g < wmo.group.Count(); g++)
            {
                groups[g].renderBatches = new Structs.RenderBatch[numRenderbatches];

                var group = wmo.group[g];
                if (group.mogp.renderBatches == null)
                {
                    continue;
                }

                for (var i = 0; i < group.mogp.renderBatches.Count(); i++)
                {
                    var batch = group.mogp.renderBatches[i];

                    groups[g].renderBatches[rb].firstFace = batch.firstFace;
                    groups[g].renderBatches[rb].numFaces  = batch.numFaces;

                    if (batch.flags == 2)
                    {
                        groups[g].renderBatches[rb].materialID = (uint)batch.possibleBox2_3;
                    }
                    else
                    {
                        groups[g].renderBatches[rb].materialID = batch.materialID;
                    }
                    groups[g].renderBatches[rb].blendType = wmo.materials[batch.materialID].blendMode;
                    groups[g].renderBatches[rb].groupID   = (uint)g;
                    rb++;
                }
            }

            exportworker.ReportProgress(95, "Writing files..");

            StreamWriter objsw;

            if (!string.IsNullOrEmpty(filename))
            {
                if (destinationOverride == null)
                {
                    objsw = new StreamWriter(Path.Combine(outdir, filename.Replace(".wmo", ".obj")));
                }
                else
                {
                    objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, Path.GetFileName(filename.ToLower()).Replace(".wmo", ".obj")));
                }

                objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original file: " + filename);
                objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(filename) + ".mtl");
            }
            else
            {
                if (destinationOverride == null)
                {
                    objsw = new StreamWriter(Path.Combine(outdir, filedataid + ".obj"));
                }
                else
                {
                    objsw = new StreamWriter(Path.Combine(outdir, destinationOverride, filedataid + ".obj"));
                }

                objsw.WriteLine("# Written by Marlamin's WoW Export Tools. Original file id: " + filedataid);
                objsw.WriteLine("mtllib " + filedataid + ".mtl");
            }

            foreach (var group in groups)
            {
                if (group.vertices == null)
                {
                    continue;
                }
                Console.WriteLine("Writing " + group.name);
                objsw.WriteLine("g " + group.name);

                foreach (var vertex in group.vertices)
                {
                    objsw.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z);
                    objsw.WriteLine("vt " + vertex.TexCoord.X + " " + (vertex.TexCoord.Y - 1) * -1);
                    objsw.WriteLine("vn " + (-vertex.Normal.X).ToString("F12") + " " + vertex.Normal.Y.ToString("F12") + " " + vertex.Normal.Z.ToString("F12"));
                }

                var indices = group.indices;

                foreach (var renderbatch in group.renderBatches)
                {
                    var i = renderbatch.firstFace;
                    if (renderbatch.numFaces > 0)
                    {
                        objsw.WriteLine("usemtl " + materials[renderbatch.materialID].filename);
                        objsw.WriteLine("s 1");
                        while (i < (renderbatch.firstFace + renderbatch.numFaces))
                        {
                            objsw.WriteLine("f " + (indices[i] + group.verticeOffset + 1) + "/" + (indices[i] + group.verticeOffset + 1) + "/" + (indices[i] + group.verticeOffset + 1) + " " + (indices[i + 1] + group.verticeOffset + 1) + "/" + (indices[i + 1] + group.verticeOffset + 1) + "/" + (indices[i + 1] + group.verticeOffset + 1) + " " + (indices[i + 2] + group.verticeOffset + 1) + "/" + (indices[i + 2] + group.verticeOffset + 1) + "/" + (indices[i + 2] + group.verticeOffset + 1));
                            i = i + 3;
                        }
                    }
                }
            }
            objsw.Close();
            Console.WriteLine("Done loading WMO file!");
        }
コード例 #20
0
        public static void ExportADT(uint wdtFileDataID, byte tileX, byte tileY, BackgroundWorker exportworker = null)
        {
            if (exportworker == null)
            {
                exportworker = new BackgroundWorker();
                exportworker.WorkerReportsProgress = true;
            }

            var outdir = ConfigurationManager.AppSettings["outdir"];

            var customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();

            customCulture.NumberFormat.NumberDecimalSeparator    = ".";
            System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;

            var MaxSize      = 51200 / 3.0;
            var TileSize     = MaxSize / 32.0;
            var ChunkSize    = TileSize / 16.0;
            var UnitSize     = ChunkSize / 8.0;
            var UnitSizeHalf = UnitSize / 2.0;

            if (!Listfile.TryGetFilename(wdtFileDataID, out string wdtFilename))
            {
                Logger.WriteLine("ADT OBJ Exporter: WDT {0} has no known filename, skipping export!", wdtFileDataID);
                return;
            }

            var mapName = Path.GetFileNameWithoutExtension(wdtFilename);
            var file    = "world/maps/" + mapName + "/" + mapName + "_" + tileX.ToString() + "_" + tileY.ToString() + ".adt";

            var reader = new ADTReader();

            reader.LoadADT(wdtFileDataID, tileX, tileY, true, wdtFilename);

            var adt = reader.adtfile;

            if (adt.chunks == null)
            {
                Logger.WriteLine("ADT OBJ Exporter: File {0} has no chunks, skipping export!", file);
                return;
            }

            Logger.WriteLine("ADT OBJ Exporter: Starting export of {0}..", file);

            Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file)));

            exportworker.ReportProgress(0, "Loading ADT " + file);

            var renderBatches = new List <Structs.RenderBatch>();
            var verticelist   = new List <Structs.Vertex>();
            var indicelist    = new List <int>();
            var materials     = new Dictionary <int, string>();

            ConfigurationManager.RefreshSection("appSettings");
            var bakeQuality = ConfigurationManager.AppSettings["bakeQuality"];

            // Calculate ADT offset in world coordinates
            var adtStartX = ((adt.x - 32) * TileSize) * -1;
            var adtStartY = ((adt.y - 32) * TileSize) * -1;

            // Calculate first chunk offset in world coordinates
            var initialChunkX = adtStartY + (adt.chunks[0].header.indexX * ChunkSize) * -1;
            var initialChunkY = adtStartX + (adt.chunks[0].header.indexY * ChunkSize) * -1;

            uint ci = 0;

            for (var x = 0; x < 16; x++)
            {
                double xOfs = x / 16d;
                for (var y = 0; y < 16; y++)
                {
                    double yOfs = y / 16d;

                    var genx = (initialChunkX + (ChunkSize * x) * -1);
                    var geny = (initialChunkY + (ChunkSize * y) * -1);

                    var chunk = adt.chunks[ci];

                    var off = verticelist.Count();

                    var batch = new Structs.RenderBatch();

                    for (int i = 0, idx = 0; i < 17; i++)
                    {
                        bool isSmallRow = (i % 2) != 0;
                        int  rowLength  = isSmallRow ? 8 : 9;

                        for (var j = 0; j < rowLength; j++)
                        {
                            var v = new Structs.Vertex();

                            v.Normal = new Structs.Vector3D
                            {
                                X = (double)chunk.normals.normal_0[idx] / 127,
                                Y = (double)chunk.normals.normal_2[idx] / 127,
                                Z = (double)chunk.normals.normal_1[idx] / 127
                            };

                            var px = geny - (j * UnitSize);
                            var py = chunk.vertices.vertices[idx++] + chunk.header.position.Z;
                            var pz = genx - (i * UnitSizeHalf);

                            v.Position = new Structs.Vector3D
                            {
                                X = px,
                                Y = py,
                                Z = pz
                            };

                            if ((i % 2) != 0)
                            {
                                v.Position.X = (px - UnitSizeHalf);
                            }

                            double ofs = j;
                            if (isSmallRow)
                            {
                                ofs += 0.5;
                            }

                            if (bakeQuality == "none")
                            {
                                v.TexCoord = new Structs.Vector2D {
                                    X = (j + (((i % 2) != 0) ? 0.5f : 0f)) / 8f, Y = (i * 0.5f) / 8f
                                };
                            }
                            else if (bakeQuality == "high")
                            {
                                double tx = ofs / 8d;
                                double ty = 1 - (i / 16d);
                                v.TexCoord = new Structs.Vector2D {
                                    X = tx, Y = ty
                                };
                            }
                            else
                            {
                                double tx = -(v.Position.X - initialChunkY) / TileSize;
                                double ty = (v.Position.Z - initialChunkX) / TileSize;

                                v.TexCoord = new Structs.Vector2D {
                                    X = tx, Y = ty
                                };
                            }
                            verticelist.Add(v);
                        }
                    }

                    batch.firstFace = (uint)indicelist.Count();

                    // Stupid C# and its structs
                    var holesHighRes = new byte[8];
                    holesHighRes[0] = chunk.header.holesHighRes_0;
                    holesHighRes[1] = chunk.header.holesHighRes_1;
                    holesHighRes[2] = chunk.header.holesHighRes_2;
                    holesHighRes[3] = chunk.header.holesHighRes_3;
                    holesHighRes[4] = chunk.header.holesHighRes_4;
                    holesHighRes[5] = chunk.header.holesHighRes_5;
                    holesHighRes[6] = chunk.header.holesHighRes_6;
                    holesHighRes[7] = chunk.header.holesHighRes_7;

                    for (int j = 9, xx = 0, yy = 0; j < 145; j++, xx++)
                    {
                        if (xx >= 8)
                        {
                            xx = 0; ++yy;
                        }
                        var isHole = true;

                        // Check if chunk is using low-res holes
                        if ((chunk.header.flags & 0x10000) == 0)
                        {
                            // Calculate current hole number
                            var currentHole = (int)Math.Pow(2,
                                                            Math.Floor(xx / 2f) * 1f +
                                                            Math.Floor(yy / 2f) * 4f);

                            // Check if current hole number should be a hole
                            if ((chunk.header.holesLowRes & currentHole) == 0)
                            {
                                isHole = false;
                            }
                        }

                        else
                        {
                            // Check if current section is a hole
                            if (((holesHighRes[yy] >> xx) & 1) == 0)
                            {
                                isHole = false;
                            }
                        }

                        if (!isHole)
                        {
                            indicelist.AddRange(new int[] { off + j + 8, off + j - 9, off + j });
                            indicelist.AddRange(new int[] { off + j - 9, off + j - 8, off + j });
                            indicelist.AddRange(new int[] { off + j - 8, off + j + 9, off + j });
                            indicelist.AddRange(new int[] { off + j + 9, off + j + 8, off + j });

                            // Generates quads instead of 4x triangles
                            //indicelist.AddRange(new int[] { off + j + 8, off + j - 9, off + j - 8 });
                            //indicelist.AddRange(new int[] { off + j - 8, off + j + 9, off + j + 8 });
                        }

                        if ((j + 1) % (9 + 8) == 0)
                        {
                            j += 9;
                        }
                    }

                    if (bakeQuality == "high")
                    {
                        materials.Add((int)ci + 1, Path.GetFileNameWithoutExtension(file).Replace(" ", "") + "_" + ci);
                        batch.materialID = ci + 1;
                    }
                    else
                    {
                        if (!materials.ContainsKey(1))
                        {
                            materials.Add(1, Path.GetFileNameWithoutExtension(file).Replace(" ", ""));
                        }
                        batch.materialID = (uint)materials.Count();
                    }

                    batch.numFaces = (uint)(indicelist.Count()) - batch.firstFace;

                    if (bakeQuality == "none")
                    {
                        // Build alpha textures, export raw and height textures
                        var rawMaterials = new List <Renderer.Structs.Material>();

                        if (adt.textures.filenames == null)
                        {
                            for (var ti = 0; ti < adt.diffuseTextureFileDataIDs.Count(); ti++)
                            {
                                var material = new Renderer.Structs.Material();
                                material.filename  = adt.diffuseTextureFileDataIDs[ti].ToString();
                                material.textureID = (int)adt.diffuseTextureFileDataIDs[ti];

                                if (adt.texParams != null && adt.texParams.Count() >= ti)
                                {
                                    material.scale = (float)Math.Pow(2, (adt.texParams[ti].flags & 0xF0) >> 4);
                                    if (adt.texParams[ti].height != 0.0 || adt.texParams[ti].offset != 1.0)
                                    {
                                        material.heightScale  = adt.texParams[ti].height;
                                        material.heightOffset = adt.texParams[ti].offset;

                                        if (!WoWFormatLib.Utils.CASC.FileExists(adt.heightTextureFileDataIDs[ti]))
                                        {
                                            Console.WriteLine("Height texture: " + adt.heightTextureFileDataIDs[ti] + " does not exist! Falling back to original texture (hack)..");
                                            material.heightTexture = (int)adt.diffuseTextureFileDataIDs[ti];
                                        }
                                        else
                                        {
                                            material.heightTexture = (int)adt.heightTextureFileDataIDs[ti];
                                        }
                                    }
                                    else
                                    {
                                        material.heightScale  = 0.0f;
                                        material.heightOffset = 1.0f;
                                    }
                                }
                                else
                                {
                                    material.heightScale  = 0.0f;
                                    material.heightOffset = 1.0f;
                                    material.scale        = 1.0f;
                                }
                                rawMaterials.Add(material);
                            }
                        }
                        else
                        {
                            for (var ti = 0; ti < adt.textures.filenames.Count(); ti++)
                            {
                                var material = new Renderer.Structs.Material();
                                material.filename  = adt.textures.filenames[ti];
                                material.textureID = (int)WoWFormatLib.Utils.CASC.getFileDataIdByName(adt.textures.filenames[ti]);

                                if (adt.texParams != null && adt.texParams.Count() >= ti)
                                {
                                    material.scale = (float)Math.Pow(2, (adt.texParams[ti].flags & 0xF0) >> 4);
                                    if (adt.texParams[ti].height != 0.0 || adt.texParams[ti].offset != 1.0)
                                    {
                                        material.heightScale  = adt.texParams[ti].height;
                                        material.heightOffset = adt.texParams[ti].offset;

                                        var heightName = adt.textures.filenames[ti].Replace(".blp", "_h.blp");
                                        if (!WoWFormatLib.Utils.CASC.FileExists(heightName))
                                        {
                                            Console.WriteLine("Height texture: " + heightName + " does not exist! Falling back to original texture (hack)..");
                                            material.heightTexture = (int)WoWFormatLib.Utils.CASC.getFileDataIdByName(adt.textures.filenames[ti]);
                                        }
                                        else
                                        {
                                            material.heightTexture = (int)WoWFormatLib.Utils.CASC.getFileDataIdByName(heightName);
                                        }
                                    }
                                    else
                                    {
                                        material.heightScale  = 0.0f;
                                        material.heightOffset = 1.0f;
                                    }
                                }
                                else
                                {
                                    material.heightScale  = 0.0f;
                                    material.heightOffset = 1.0f;
                                    material.scale        = 1.0f;
                                }
                                rawMaterials.Add(material);
                            }
                        }

                        var layerMaterials = new List <uint>();
                        var alphalayermats = new List <int>();
                        var layerscales    = new List <float>();
                        var layerheights   = new List <int>();

                        //batch.heightScales = new Vector4();
                        //batch.heightOffsets = new Vector4();
                        exportworker.ReportProgress(10, "Exporting alpha layers " + file);

                        var baseTextureName      = Path.Combine(outdir, Path.GetDirectoryName(file), "terraindiffuse", Path.GetFileNameWithoutExtension(file).Replace(" ", ""));
                        var baseAlphaTextureName = Path.Combine(outdir, Path.GetDirectoryName(file), "terrainalpha", Path.GetFileNameWithoutExtension(file).Replace(" ", ""));
                        Directory.CreateDirectory(Path.GetDirectoryName(baseAlphaTextureName));

                        var pixels = new byte[4, 4096];

                        for (var li = 0; li < adt.texChunks[ci].layers.Count(); li++)
                        {
                            if (adt.texChunks[ci].alphaLayer != null)
                            {
                                var values = adt.texChunks[ci].alphaLayer[li].layer;
                                for (var tx = 0; tx < 64; tx++)
                                {
                                    for (var ty = 0; ty < 64; ty++)
                                    {
                                        pixels[li, tx * 64 + ty] = values[tx * 64 + ty];
                                    }
                                }
                            }

                            Renderer.Structs.Material curMat;

                            if (adt.diffuseTextureFileDataIDs == null)
                            {
                                if (adt.textures.filenames == null)
                                {
                                    throw new Exception("ADT has no textures?");
                                }

                                var texFileDataID = WoWFormatLib.Utils.CASC.getFileDataIdByName(adt.textures.filenames[adt.texChunks[ci].layers[li].textureId]);

                                //layerMaterials.Add((uint)BLPLoader.LoadTexture(texFileDataID));
                                curMat = rawMaterials.Where(material => material.filename == adt.textures.filenames[adt.texChunks[ci].layers[li].textureId]).Single();
                            }
                            else
                            {
                                //layerMaterials.Add((uint)BLPLoader.LoadTexture(adt.diffuseTextureFileDataIDs[adt.texChunks[ci].layers[li].textureId]));
                                curMat = rawMaterials.Where(material => material.filename == adt.diffuseTextureFileDataIDs[adt.texChunks[ci].layers[li].textureId].ToString()).Single();
                                //Console.WriteLine(ci + " " + li + " " + curMat.filename);
                            }

                            //layerscales.Add(curMat.scale);
                            //layerheights.Add(curMat.heightTexture);

                            //batch.heightScales[li] = curMat.heightScale;
                            //batch.heightOffsets[li] = curMat.heightOffset;
                        }

                        //batch.materialID = layerMaterials.ToArray();
                        //batch.alphaMaterialID = alphalayermats.ToArray();
                        //batch.scales = layerscales.ToArray();
                        //batch.heightMaterialIDs = layerheights.ToArray();

                        using (var bmp = new System.Drawing.Bitmap(64, 64))
                        {
                            for (var tx = 0; tx < 64; tx++)
                            {
                                for (var ty = 0; ty < 64; ty++)
                                {
                                    var color = System.Drawing.Color.FromArgb(pixels[0, tx * 64 + ty], pixels[1, tx * 64 + ty], pixels[2, tx * 64 + ty], pixels[3, tx * 64 + ty]);
                                    bmp.SetPixel(ty, tx, color);
                                }
                            }

                            bmp.Save(baseAlphaTextureName + "_" + ci + ".png", System.Drawing.Imaging.ImageFormat.Png);
                        }
                    }

                    renderBatches.Add(batch);
                    ci++;
                }
            }

            ConfigurationManager.RefreshSection("appSettings");

            bool exportWMO     = ConfigurationManager.AppSettings["exportWMO"] == "True";
            bool exportM2      = ConfigurationManager.AppSettings["exportM2"] == "True";
            bool exportFoliage = ConfigurationManager.AppSettings["exportFoliage"] == "True";

            if (exportFoliage)
            {
                exportworker.ReportProgress(65, "Exporting ADT foliage");

                try
                {
                    var build = WoWFormatLib.Utils.CASC.BuildName;
                    var dbcd  = new DBCD.DBCD(new DBC.CASCDBCProvider(), new GithubDBDProvider());
                    var groundEffectTextureDB = dbcd.Load("GroundEffectTexture");
                    var groundEffectDoodadDB  = dbcd.Load("GroundEffectDoodad");
                    for (var c = 0; c < reader.adtfile.texChunks.Length; c++)
                    {
                        for (var l = 0; l < reader.adtfile.texChunks[c].layers.Length; l++)
                        {
                            var effectID = reader.adtfile.texChunks[c].layers[l].effectId;
                            if (effectID == 0)
                            {
                                continue;
                            }

                            if (!groundEffectTextureDB.ContainsKey(effectID))
                            {
                                continue;
                            }

                            dynamic textureEntry = groundEffectTextureDB[effectID];
                            foreach (int doodad in textureEntry.DoodadID)
                            {
                                if (!groundEffectDoodadDB.ContainsKey(doodad))
                                {
                                    continue;
                                }

                                dynamic doodadEntry = groundEffectDoodadDB[doodad];

                                var filedataid = (uint)doodadEntry.ModelFileID;

                                if (!Listfile.TryGetFilename(filedataid, out var filename))
                                {
                                    Logger.WriteLine("Could not find filename for " + filedataid + ", setting filename to filedataid..");
                                    filename = filedataid.ToString();
                                }

                                if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), "foliage")))
                                {
                                    Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file), "foliage"));
                                }

                                if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), "foliage", Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj")))
                                {
                                    M2Exporter.ExportM2(filedataid, null, Path.Combine(outdir, Path.GetDirectoryName(file), "foliage"), filename);
                                }
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.WriteLine("Error exporting GroundEffects: " + e.Message);
                }
            }

            if (exportWMO || exportM2)
            {
                var doodadSW = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file).Replace(" ", "") + "_ModelPlacementInformation.csv"));
                doodadSW.WriteLine("ModelFile;PositionX;PositionY;PositionZ;RotationX;RotationY;RotationZ;ScaleFactor;ModelId;Type");

                if (exportWMO)
                {
                    exportworker.ReportProgress(25, "Exporting ADT worldmodels");

                    for (var mi = 0; mi < reader.adtfile.objects.worldModels.entries.Count(); mi++)
                    {
                        var wmo = reader.adtfile.objects.worldModels.entries[mi];

                        var  filename   = "";
                        uint filedataid = 0;

                        if (reader.adtfile.objects.wmoNames.filenames == null)
                        {
                            filedataid = wmo.mwidEntry;
                            if (!Listfile.TryGetFilename(filedataid, out filename))
                            {
                                Logger.WriteLine("Warning! Could not find filename for " + filedataid + ", setting filename to filedataid..");
                                filename = filedataid.ToString() + ".wmo";
                            }
                        }
                        else
                        {
                            Logger.WriteLine("Warning!! File " + filename + " ID: " + filedataid + " still has filenames!");
                            filename = reader.adtfile.objects.wmoNames.filenames[wmo.mwidEntry];
                            if (!Listfile.TryGetFileDataID(filename, out filedataid))
                            {
                                Logger.WriteLine("Error! Could not find filedataid for " + filename + "!");
                                continue;
                            }
                        }

                        short doodadSet = -1;
                        if (ConfigurationManager.AppSettings["exportWMODoodads"] == "True")
                        {
                            doodadSet = (short)wmo.doodadSet;
                        }

                        if (string.IsNullOrEmpty(filename))
                        {
                            string wmoFile = Path.Combine(outdir, Path.GetDirectoryName(file), filedataid.ToString() + ".obj");
                            if (!File.Exists(wmoFile))
                            {
                                WMOExporter.ExportWMO(filedataid, exportworker, Path.Combine(outdir, Path.GetDirectoryName(file)), doodadSet);
                            }

                            if (File.Exists(wmoFile))
                            {
                                doodadSW.WriteLine(filedataid + ".obj;" + wmo.position.X + ";" + wmo.position.Y + ";" + wmo.position.Z + ";" + wmo.rotation.X + ";" + wmo.rotation.Y + ";" + wmo.rotation.Z + ";" + wmo.scale / 1024f + ";" + wmo.uniqueId + ";wmo");
                            }
                        }
                        else
                        {
                            string wmoFile = Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj");
                            if (!File.Exists(wmoFile))
                            {
                                WMOExporter.ExportWMO(filedataid, exportworker, Path.Combine(outdir, Path.GetDirectoryName(file)), doodadSet, filename);
                            }

                            if (File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj")))
                            {
                                doodadSW.WriteLine(Path.GetFileNameWithoutExtension(filename).ToLower() + ".obj;" + wmo.position.X + ";" + wmo.position.Y + ";" + wmo.position.Z + ";" + wmo.rotation.X + ";" + wmo.rotation.Y + ";" + wmo.rotation.Z + ";" + wmo.scale / 1024f + ";" + wmo.uniqueId + ";wmo");
                            }
                        }
                    }
                }

                if (exportM2)
                {
                    exportworker.ReportProgress(50, "Exporting ADT doodads");

                    for (var mi = 0; mi < reader.adtfile.objects.models.entries.Count(); mi++)
                    {
                        var doodad = reader.adtfile.objects.models.entries[mi];

                        string filename;
                        uint   filedataid;

                        if (reader.adtfile.objects.m2Names.filenames == null)
                        {
                            filedataid = doodad.mmidEntry;
                            if (!Listfile.TryGetFilename(filedataid, out filename))
                            {
                                Logger.WriteLine("Could not find filename for " + filedataid + ", setting filename to filedataid..");
                                filename = filedataid.ToString();
                            }
                        }
                        else
                        {
                            filename = reader.adtfile.objects.m2Names.filenames[doodad.mmidEntry].ToLower();
                            if (!Listfile.TryGetFileDataID(filename, out filedataid))
                            {
                                Logger.WriteLine("Error! Could not find filedataid for " + filename + "!");
                                continue;
                            }
                        }

                        if (!File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(filename) + ".obj")))
                        {
                            M2Exporter.ExportM2(filedataid, null, Path.Combine(outdir, Path.GetDirectoryName(file)), filename);
                        }

                        if (File.Exists(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(filename) + ".obj")))
                        {
                            doodadSW.WriteLine(Path.GetFileNameWithoutExtension(filename) + ".obj;" + doodad.position.X + ";" + doodad.position.Y + ";" + doodad.position.Z + ";" + doodad.rotation.X + ";" + doodad.rotation.Y + ";" + doodad.rotation.Z + ";" + doodad.scale / 1024f + ";" + doodad.uniqueId + ";m2");
                        }
                    }
                }

                doodadSW.Close();
            }

            exportworker.ReportProgress(75, "Exporting terrain textures..");

            if (bakeQuality != "none")
            {
                var mtlsw = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file).Replace(" ", "") + ".mtl"));

                //No idea how MTL files really work yet. Needs more investigation.
                foreach (var material in materials)
                {
                    mtlsw.WriteLine("newmtl " + material.Value.Replace(" ", ""));
                    mtlsw.WriteLine("Ka 1.000000 1.000000 1.000000");
                    mtlsw.WriteLine("Kd 0.640000 0.640000 0.640000");
                    mtlsw.WriteLine("map_Ka " + material.Value.Replace(" ", "") + ".png");
                    mtlsw.WriteLine("map_Kd " + material.Value.Replace(" ", "") + ".png");
                }

                mtlsw.Close();
            }

            exportworker.ReportProgress(85, "Exporting terrain geometry..");

            var indices = indicelist.ToArray();

            var adtname = Path.GetFileNameWithoutExtension(file);

            var objsw = new StreamWriter(Path.Combine(outdir, Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file).Replace(" ", "") + ".obj"));

            objsw.WriteLine("# Written by Marlamin's WoW OBJExporter. Original file: " + file);
            if (bakeQuality != "none")
            {
                objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(file).Replace(" ", "") + ".mtl");
            }

            var verticeCounter = 1;
            var chunkCounter   = 1;

            foreach (var vertex in verticelist)
            {
                //objsw.WriteLine("# C" + chunkCounter + ".V" + verticeCounter);
                objsw.WriteLine("v " + vertex.Position.X.ToString("R") + " " + vertex.Position.Y.ToString("R") + " " + vertex.Position.Z.ToString("R"));
                objsw.WriteLine("vt " + vertex.TexCoord.X + " " + vertex.TexCoord.Y);
                objsw.WriteLine("vn " + vertex.Normal.X.ToString("R") + " " + vertex.Normal.Y.ToString("R") + " " + vertex.Normal.Z.ToString("R"));
                verticeCounter++;
                if (verticeCounter == 146)
                {
                    chunkCounter++;
                    verticeCounter = 1;
                }
            }

            if (bakeQuality == "minimap" || bakeQuality == "low" || bakeQuality == "medium")
            {
                objsw.WriteLine("g " + adtname.Replace(" ", ""));
                objsw.WriteLine("usemtl " + materials[1]);
                objsw.WriteLine("s 1");
            }

            for (int rbi = 0; rbi < renderBatches.Count(); rbi++)
            {
                var renderBatch = renderBatches[rbi];
                var i           = renderBatch.firstFace;
                if (bakeQuality == "high" || bakeQuality == "none")
                {
                    objsw.WriteLine("g " + adtname.Replace(" ", "") + "_" + rbi);
                }

                if (bakeQuality == "high" && materials.ContainsKey((int)renderBatch.materialID))
                {
                    objsw.WriteLine("usemtl " + materials[(int)renderBatch.materialID]);
                }

                while (i < (renderBatch.firstFace + renderBatch.numFaces))
                {
                    objsw.WriteLine("f " +
                                    (indices[i + 2] + 1) + "/" + (indices[i + 2] + 1) + "/" + (indices[i + 2] + 1) + " " +
                                    (indices[i + 1] + 1) + "/" + (indices[i + 1] + 1) + "/" + (indices[i + 1] + 1) + " " +
                                    (indices[i] + 1) + "/" + (indices[i] + 1) + "/" + (indices[i] + 1));
                    i = i + 3;
                }
            }

            objsw.Close();

            Logger.WriteLine("ADT OBJ Exporter: Finished with export of {0}..", file);
        }
コード例 #21
0
ファイル: M2Exporter.cs プロジェクト: Kruithne/WoWExportTools
        public static void ExportM2(M2Reader reader, string fileName, BackgroundWorker exportworker = null, string destinationOverride = null, bool externalOverride = false, bool[] enabledGeosets = null)
        {
            if (exportworker == null)
            {
                exportworker = new BackgroundWorker
                {
                    WorkerReportsProgress = true
                };
            }

            var customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();

            customCulture.NumberFormat.NumberDecimalSeparator    = ".";
            System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;

            var exportDir = ConfigurationManager.AppSettings["outdir"];

            exportworker.ReportProgress(15, "Reading M2..");

            // Don't export models without vertices
            if (reader.model.vertices.Count() == 0)
            {
                return;
            }

            var vertices = new Structs.Vertex[reader.model.vertices.Count()];

            for (var i = 0; i < reader.model.vertices.Count(); i++)
            {
                vertices[i].Position = new Structs.Vector3D()
                {
                    X = reader.model.vertices[i].position.X,
                    Y = reader.model.vertices[i].position.Z,
                    Z = reader.model.vertices[i].position.Y * -1
                };

                vertices[i].Normal = new Structs.Vector3D()
                {
                    X = reader.model.vertices[i].normal.X,
                    Y = reader.model.vertices[i].normal.Z,
                    Z = reader.model.vertices[i].normal.Y
                };

                vertices[i].TexCoord = new Structs.Vector2D()
                {
                    X = reader.model.vertices[i].textureCoordX,
                    Y = reader.model.vertices[i].textureCoordY
                };
            }

            string outDir = exportDir;

            if (destinationOverride != null)
            {
                if (externalOverride)
                {
                    outDir = destinationOverride;
                }
                else
                {
                    outDir = Path.Combine(outDir, destinationOverride);
                }
            }
            else
            {
                outDir = Path.Combine(outDir, Path.GetDirectoryName(fileName));
            }

            Directory.CreateDirectory(outDir);

            string filePath    = Path.Combine(outDir, Path.GetFileName(fileName).Replace(".m2", ""));
            string objFilePath = filePath + ".obj";
            string mtlFilePath = filePath + ".mtl";

            StreamWriter objWriter = new StreamWriter(objFilePath);
            StreamWriter mtlWriter = new StreamWriter(mtlFilePath);

            // Write OBJ header.
            objWriter.WriteLine("# Written by Marlamin's WoW Export Tools. Source file: " + fileName);
            objWriter.WriteLine("mtllib " + Path.GetFileName(mtlFilePath));

            foreach (var vertex in vertices)
            {
                objWriter.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z);
                objWriter.WriteLine("vt " + vertex.TexCoord.X + " " + (vertex.TexCoord.Y - 1) * -1);
                objWriter.WriteLine("vn " + (-vertex.Normal.X).ToString("F12") + " " + vertex.Normal.Y.ToString("F12") + " " + vertex.Normal.Z.ToString("F12"));
            }

            var indicelist = new List <uint>();

            for (var i = 0; i < reader.model.skins[0].triangles.Count(); i++)
            {
                var t = reader.model.skins[0].triangles[i];
                indicelist.Add(t.pt1);
                indicelist.Add(t.pt2);
                indicelist.Add(t.pt3);
            }

            var indices = indicelist.ToArray();

            exportworker.ReportProgress(35, "Writing files..");

            var renderbatches = new Structs.RenderBatch[reader.model.skins[0].submeshes.Count()];

            for (var i = 0; i < reader.model.skins[0].submeshes.Count(); i++)
            {
                if (enabledGeosets != null && !enabledGeosets[i])
                {
                    continue;
                }

                renderbatches[i].firstFace = reader.model.skins[0].submeshes[i].startTriangle;
                renderbatches[i].numFaces  = reader.model.skins[0].submeshes[i].nTriangles;
                renderbatches[i].groupID   = (uint)i;
                for (var tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++)
                {
                    if (reader.model.skins[0].textureunit[tu].submeshIndex == i)
                    {
                        renderbatches[i].blendType  = reader.model.renderflags[reader.model.skins[0].textureunit[tu].renderFlags].blendingMode;
                        renderbatches[i].materialID = reader.model.texlookup[reader.model.skins[0].textureunit[tu].texture].textureID;
                    }
                }
            }

            exportworker.ReportProgress(65, "Exporting textures..");

            uint defaultTexID = DEFAULT_TEXTURE;

            if (!CASC.FileExists(defaultTexID))
            {
                defaultTexID = DEFAULT_TEXTURE_SUB;
            }

            var textureID = 0;
            var materials = new Structs.Material[reader.model.textures.Count()];

            for (var i = 0; i < reader.model.textures.Count(); i++)
            {
                uint textureFileDataID = defaultTexID;

                materials[i].flags = reader.model.textures[i].flags;
                if (reader.model.textures[i].type == 0)
                {
                    if (reader.model.textureFileDataIDs != null && reader.model.textureFileDataIDs.Length > 0 && reader.model.textureFileDataIDs[i] != 0)
                    {
                        textureFileDataID = reader.model.textureFileDataIDs[i];
                    }
                    else
                    {
                        Listfile.TryGetFileDataID(reader.model.textures[i].filename, out textureFileDataID);
                    }
                }
                else
                {
                    Console.WriteLine("Texture type " + reader.model.textures[i].type + " not supported, falling back to placeholder texture");
                }

                materials[i].textureID = textureID + i;

                if (!Listfile.TryGetFilename(textureFileDataID, out var textureFilename))
                {
                    textureFilename = textureFileDataID.ToString();
                }

                materials[i].filename = Path.GetFileNameWithoutExtension(textureFilename).Replace(" ", "");

                try
                {
                    var blpreader = new BLPReader();
                    blpreader.LoadBLP(textureFileDataID);
                    blpreader.bmp.Save(Path.Combine(outDir, materials[i].filename + ".png"));
                }
                catch (Exception e)
                {
                    CASCLib.Logger.WriteLine("Exception while saving BLP " + materials[i].filename + ": " + e.Message);
                }
            }

            exportworker.ReportProgress(85, "Writing files..");

            foreach (var material in materials)
            {
                mtlWriter.WriteLine("newmtl " + material.filename);
                mtlWriter.WriteLine("illum 1");
                mtlWriter.WriteLine("map_Kd " + material.filename + ".png");
                if (ConfigurationManager.AppSettings["textureMetadata"] == "True")
                {
                    foreach (var renderbatch in renderbatches)
                    {
                        if (materials[renderbatch.materialID].filename == material.filename)
                        {
                            mtlWriter.WriteLine("blend " + material.blendMode);
                        }
                    }
                }
            }

            mtlWriter.Close();

            objWriter.WriteLine("o " + Path.GetFileName(fileName));

            foreach (var renderbatch in renderbatches)
            {
                var i = renderbatch.firstFace;

                objWriter.WriteLine("g " + renderbatch.groupID);
                objWriter.WriteLine("usemtl " + materials[renderbatch.materialID].filename);
                objWriter.WriteLine("s 1");
                while (i < (renderbatch.firstFace + renderbatch.numFaces))
                {
                    objWriter.WriteLine("f " + (indices[i] + 1) + "/" + (indices[i] + 1) + "/" + (indices[i] + 1) + " " + (indices[i + 1] + 1) + "/" + (indices[i + 1] + 1) + "/" + (indices[i + 1] + 1) + " " + (indices[i + 2] + 1) + "/" + (indices[i + 2] + 1) + "/" + (indices[i + 2] + 1));
                    i = i + 3;
                }
            }

            objWriter.Close();

            // Only export phys when exporting a single M2, causes issues for some users when combined with WMO/ADT
            if (destinationOverride == null && ConfigurationManager.AppSettings["exportCollision"] == "True")
            {
                exportworker.ReportProgress(90, "Exporting collision..");

                objWriter = new StreamWriter(filePath + ".phys.obj");


                objWriter.WriteLine("# Written by Marlamin's WoW Export Tools. Source file: " + fileName);

                for (var i = 0; i < reader.model.boundingvertices.Count(); i++)
                {
                    objWriter.WriteLine("v " +
                                        reader.model.boundingvertices[i].vertex.X + " " +
                                        reader.model.boundingvertices[i].vertex.Z + " " +
                                        -reader.model.boundingvertices[i].vertex.Y);
                }

                for (var i = 0; i < reader.model.boundingtriangles.Count(); i++)
                {
                    var t = reader.model.boundingtriangles[i];
                    objWriter.WriteLine("f " + (t.index_0 + 1) + " " + (t.index_1 + 1) + " " + (t.index_2 + 1));
                }

                objWriter.Close();
            }

            // https://en.wikipedia.org/wiki/Wavefront_.obj_file#Basic_materials
            // http://wiki.unity3d.com/index.php?title=ExportOBJ
            // http://web.cse.ohio-state.edu/~hwshen/581/Site/Lab3_files/Labhelp_Obj_parser.htm
        }
コード例 #22
0
        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);
        }
コード例 #23
0
        public static void ExportWMO(uint fileDataID, BackgroundWorker exportworker = null, string destinationOverride = null, short doodadSetExportID = short.MaxValue, string fileName = "", bool[] enabledGroups = null, bool[] enabledSets = null)
        {
            if (exportworker == null)
            {
                exportworker = new BackgroundWorker();
                exportworker.WorkerReportsProgress = true;
            }

            if (string.IsNullOrEmpty(fileName))
            {
                if (!Listfile.TryGetFilename(fileDataID, out fileName))
                {
                    CASCLib.Logger.WriteLine("Warning! Could not find filename for " + fileDataID + "!");
                }
            }

            fileName = fileName.ToLower();

            Console.WriteLine("Loading WMO file..");
            exportworker.ReportProgress(5, "Reading WMO..");

            var outDir = ConfigurationManager.AppSettings["outdir"];
            var wmo    = new WMOReader().LoadWMO(fileDataID, 0, fileName);

            var customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();

            customCulture.NumberFormat.NumberDecimalSeparator    = ".";
            System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;

            exportworker.ReportProgress(30, "Reading WMO..");

            uint totalVertices = 0;
            var  groups        = new Structs.WMOGroup[wmo.group.Count()];

            for (var g = 0; g < wmo.group.Count(); g++)
            {
                if (enabledGroups != null && !enabledGroups[g])
                {
                    Console.WriteLine("Skipping group " + g + " due to WMO control");
                    continue;
                }

                Console.WriteLine("Loading group #" + g);
                if (wmo.group[g].mogp.vertices == null)
                {
                    Console.WriteLine("Group has no vertices!");
                    continue;
                }

                for (var i = 0; i < wmo.groupNames.Count(); i++)
                {
                    if (wmo.group[g].mogp.nameOffset == wmo.groupNames[i].offset)
                    {
                        groups[g].name = wmo.groupNames[i].name.Replace(" ", "_");
                    }
                }

                if (groups[g].name == "antiportal")
                {
                    Console.WriteLine("Group is antiportal");
                    continue;
                }

                groups[g].verticeOffset = totalVertices;
                groups[g].vertices      = new Structs.Vertex[wmo.group[g].mogp.vertices.Count()];

                for (var i = 0; i < wmo.group[g].mogp.vertices.Count(); i++)
                {
                    groups[g].vertices[i].Position = new Structs.Vector3D()
                    {
                        X = wmo.group[g].mogp.vertices[i].vector.X * -1,
                        Y = wmo.group[g].mogp.vertices[i].vector.Z,
                        Z = wmo.group[g].mogp.vertices[i].vector.Y
                    };

                    groups[g].vertices[i].Normal = new Structs.Vector3D()
                    {
                        X = wmo.group[g].mogp.normals[i].normal.X,
                        Y = wmo.group[g].mogp.normals[i].normal.Z,
                        Z = wmo.group[g].mogp.normals[i].normal.Y
                    };

                    groups[g].vertices[i].TexCoord = new Structs.Vector2D()
                    {
                        X = wmo.group[g].mogp.textureCoords[0][i].X,
                        Y = wmo.group[g].mogp.textureCoords[0][i].Y
                    };

                    totalVertices++;
                }

                var indicelist = new List <uint>();
                for (var i = 0; i < wmo.group[g].mogp.indices.Count(); i++)
                {
                    indicelist.Add(wmo.group[g].mogp.indices[i].indice);
                }

                groups[g].indices = indicelist.ToArray();
            }

            // Create output directory.
            if (destinationOverride == null)
            {
                Directory.CreateDirectory(fileName == null ? outDir : Path.Combine(outDir, Path.GetDirectoryName(fileName)));
            }

            exportworker.ReportProgress(55, "Exporting WMO doodads..");

            List <string> doodadList = new List <string>();

            doodadList.Add("ModelFile;PositionX;PositionY;PositionZ;RotationW;RotationX;RotationY;RotationZ;ScaleFactor;DoodadSet");

            if (doodadSetExportID > -1)
            {
                for (var i = 0; i < wmo.doodadSets.Count(); i++)
                {
                    if (enabledSets != null && !enabledSets[i])
                    {
                        Console.WriteLine("Skipping doodadSet " + i + " due to WMO control");
                        continue;
                    }

                    var doodadSet            = wmo.doodadSets[i];
                    var currentDoodadSetName = doodadSet.setName.Replace("Set_", "").Replace("SET_", "").Replace("$DefaultGlobal", "Default");

                    if (doodadSetExportID != short.MaxValue)
                    {
                        if (i != 0 && i != doodadSetExportID)
                        {
                            Console.WriteLine("Skipping doodadset with ID " + i + " (" + currentDoodadSetName + ") because export filter is set to " + doodadSetExportID);
                            continue;
                        }
                    }

                    for (var j = doodadSet.firstInstanceIndex; j < (doodadSet.firstInstanceIndex + doodadSet.numDoodads); j++)
                    {
                        var doodadDefinition = wmo.doodadDefinitions[j];

                        var  doodadFileName   = "";
                        uint doodadFileDataID = 0;
                        var  doodadNotFound   = false;

                        if (wmo.doodadIds != null)
                        {
                            doodadFileDataID = wmo.doodadIds[doodadDefinition.offset];
                            if (!Listfile.TryGetFilename(doodadFileDataID, out doodadFileName))
                            {
                                CASCLib.Logger.WriteLine("Could not find filename for " + doodadFileDataID + ", setting filename to filedataid..");
                                doodadFileName = doodadFileDataID.ToString();
                            }
                        }
                        else
                        {
                            CASCLib.Logger.WriteLine("Warning!! File " + fileName + " ID: " + fileDataID + " still has filenames!");
                            foreach (var doodadNameEntry in wmo.doodadNames)
                            {
                                if (doodadNameEntry.startOffset == doodadDefinition.offset)
                                {
                                    doodadFileName = doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2").ToLower();
                                    if (!Listfile.TryGetFileDataID(doodadFileName, out doodadFileDataID))
                                    {
                                        CASCLib.Logger.WriteLine("Error! Could not find filedataid for " + doodadFileName + "!");
                                        doodadNotFound = true;
                                        continue;
                                    }
                                }
                            }
                        }

                        if (!doodadNotFound)
                        {
                            string objFileName = Path.GetFileNameWithoutExtension(doodadFileName ?? doodadFileDataID.ToString()) + ".obj";
                            string objPath     = Path.Combine(destinationOverride ?? outDir, destinationOverride != null ? "" : Path.GetDirectoryName(fileName));
                            string objName     = Path.Combine(objPath, objFileName);

                            if (!File.Exists(objName))
                            {
                                M2Exporter.ExportM2(doodadFileDataID, null, objPath, doodadFileName);
                            }

                            if (File.Exists(objName))
                            {
                                doodadList.Add(objFileName + ";" + doodadDefinition.position.X.ToString("F09") + ";" + doodadDefinition.position.Y.ToString("F09") + ";" + doodadDefinition.position.Z.ToString("F09") + ";" + doodadDefinition.rotation.W.ToString("F15") + ";" + doodadDefinition.rotation.X.ToString("F15") + ";" + doodadDefinition.rotation.Y.ToString("F15") + ";" + doodadDefinition.rotation.Z.ToString("F15") + ";" + doodadDefinition.scale + ";" + currentDoodadSetName);
                            }
                        }
                    }
                }
            }

            if (doodadList.Count > 1)
            {
                string mpiFile = (fileName == null ? fileDataID.ToString() : Path.GetFileNameWithoutExtension(fileName.Replace(" ", ""))) + "_ModelPlacementInformation.csv";
                File.WriteAllText(Path.Combine(outDir, destinationOverride ?? Path.GetDirectoryName(fileName), mpiFile), string.Join("\n", doodadList.ToArray()));
            }

            exportworker.ReportProgress(65, "Exporting WMO textures..");

            var mtlsb     = new StringBuilder();
            var textureID = 0;

            if (wmo.materials == null)
            {
                CASCLib.Logger.WriteLine("Unable to find materials for WMO " + fileDataID + ", not exporting!");
                return;
            }

            var materials      = new Structs.Material[wmo.materials.Count()];
            var extraMaterials = new List <Structs.Material>();

            for (var i = 0; i < wmo.materials.Count(); i++)
            {
                var blpReader = new BLPReader();

                if (wmo.textures == null)
                {
                    if (Listfile.TryGetFilename(wmo.materials[i].texture1, out var textureFilename))
                    {
                        materials[i].filename = Path.GetFileNameWithoutExtension(textureFilename).Replace(" ", "").ToLower();
                    }
                    else
                    {
                        materials[i].filename = wmo.materials[i].texture1.ToString();
                    }

                    blpReader.LoadBLP(wmo.materials[i].texture1);
                }
                else
                {
                    for (var ti = 0; ti < wmo.textures.Count(); ti++)
                    {
                        if (wmo.textures[ti].startOffset == wmo.materials[i].texture1)
                        {
                            materials[i].filename = Path.GetFileNameWithoutExtension(wmo.textures[ti].filename).Replace(" ", "").ToLower();
                            blpReader.LoadBLP(wmo.textures[ti].filename);
                        }
                    }
                }

                materials[i].textureID   = textureID + i;
                materials[i].transparent = wmo.materials[i].blendMode != 0;

                materials[i].blendMode   = wmo.materials[i].blendMode;
                materials[i].shaderID    = wmo.materials[i].shader;
                materials[i].terrainType = wmo.materials[i].groundType;

                string saveLocation = Path.Combine(outDir, destinationOverride ?? Path.GetDirectoryName(fileName), materials[i].filename + ".png");
                if (!File.Exists(saveLocation))
                {
                    try
                    {
                        if (materials[i].transparent)
                        {
                            blpReader.bmp.Save(saveLocation);
                        }
                        else
                        {
                            blpReader.bmp.Clone(new Rectangle(0, 0, blpReader.bmp.Width, blpReader.bmp.Height), PixelFormat.Format32bppRgb).Save(saveLocation);
                        }
                    }
                    catch (Exception e)
                    {
                        CASCLib.Logger.WriteLine("Exception while saving BLP " + materials[i].filename + ": " + e.Message);
                    }
                }

                textureID++;

                string extraPath = Path.Combine(outDir, destinationOverride ?? Path.GetDirectoryName(fileName));
                ExportExtraMaterials(wmo.materials[i].texture2, wmo, extraMaterials, i, extraPath);
                ExportExtraMaterials(wmo.materials[i].texture3, wmo, extraMaterials, i, extraPath);
            }

            var numRenderbatches = 0;

            //Get total amount of render batches
            for (var i = 0; i < wmo.group.Count(); i++)
            {
                if (wmo.group[i].mogp.renderBatches == null)
                {
                    continue;
                }

                numRenderbatches = numRenderbatches + wmo.group[i].mogp.renderBatches.Count();
            }

            exportworker.ReportProgress(75, "Exporting WMO model..");

            bool exportMetadata = ConfigurationManager.AppSettings["textureMetadata"] == "True";

            //No idea how MTL files really work yet. Needs more investigation.
            foreach (var material in materials)
            {
                mtlsb.Append("newmtl " + material.filename + "\n");
                mtlsb.Append("Ns 96.078431\n");
                mtlsb.Append("Ka 1.000000 1.000000 1.000000\n");
                mtlsb.Append("Kd 0.640000 0.640000 0.640000\n");
                mtlsb.Append("Ks 0.000000 0.000000 0.000000\n");
                mtlsb.Append("Ke 0.000000 0.000000 0.000000\n");
                mtlsb.Append("Ni 1.000000\n");
                mtlsb.Append("d 1.000000\n");
                mtlsb.Append("illum 1\n");
                mtlsb.Append("map_Kd " + material.filename + ".png\n");

                if (material.transparent)
                {
                    mtlsb.Append("map_d " + material.filename + ".png\n");
                }

                if (exportMetadata)
                {
                    for (var g = 0; g < wmo.group.Count(); g++)
                    {
                        groups[g].renderBatches = new Structs.RenderBatch[numRenderbatches];

                        var group = wmo.group[g];
                        if (group.mogp.renderBatches == null)
                        {
                            continue;
                        }

                        for (var i = 0; i < group.mogp.renderBatches.Count(); i++)
                        {
                            var batch = group.mogp.renderBatches[i];
                            if (materials[batch.materialID].filename == material.filename)
                            {
                                mtlsb.Append("blend " + material.blendMode + "\n");
                            }
                        }
                    }
                }
            }

            foreach (var material in extraMaterials)
            {
                mtlsb.Append("newmtl " + material.filename + "\n");
                mtlsb.Append("Ns 96.078431\n");
                mtlsb.Append("Ka 1.000000 1.000000 1.000000\n");
                mtlsb.Append("Kd 0.640000 0.640000 0.640000\n");
                mtlsb.Append("Ks 0.000000 0.000000 0.000000\n");
                mtlsb.Append("Ke 0.000000 0.000000 0.000000\n");
                mtlsb.Append("Ni 1.000000\n");
                mtlsb.Append("d 1.000000\n");
                mtlsb.Append("illum 1\n");
                mtlsb.Append("map_Kd " + material.filename + ".png\n");

                if (material.transparent)
                {
                    mtlsb.Append("map_d " + material.filename + ".png\n");
                }
            }

            string mtlFile = fileName != null?fileName.Replace(".wmo", ".mtl") : fileDataID + ".mtl";

            if (destinationOverride != null)
            {
                mtlFile = Path.GetFileName(mtlFile);
            }

            File.WriteAllText(Path.Combine(destinationOverride ?? outDir, mtlFile), mtlsb.ToString());

            var rb = 0;

            for (var g = 0; g < wmo.group.Count(); g++)
            {
                groups[g].renderBatches = new Structs.RenderBatch[numRenderbatches];

                var group = wmo.group[g];
                if (group.mogp.renderBatches == null)
                {
                    continue;
                }

                for (var i = 0; i < group.mogp.renderBatches.Count(); i++)
                {
                    var batch = group.mogp.renderBatches[i];

                    groups[g].renderBatches[rb].firstFace  = batch.firstFace;
                    groups[g].renderBatches[rb].numFaces   = batch.numFaces;
                    groups[g].renderBatches[rb].materialID = batch.flags == 2 ? (uint)batch.possibleBox2_3 : batch.materialID;
                    groups[g].renderBatches[rb].blendType  = wmo.materials[batch.materialID].blendMode;
                    groups[g].renderBatches[rb].groupID    = (uint)g;
                    rb++;
                }
            }

            exportworker.ReportProgress(95, "Writing WMO files..");

            string objFile = fileName != null?fileName.Replace(".wmo", ".obj") : fileDataID + ".obj";

            if (destinationOverride != null)
            {
                objFile = Path.GetFileName(objFile);
            }

            string       fileID    = fileName ?? fileDataID.ToString();
            StreamWriter objWriter = new StreamWriter(Path.Combine(destinationOverride ?? outDir, objFile));

            objWriter.WriteLine("# Written by Marlamin's WoW Export Tools. Original file: " + fileID);
            objWriter.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(fileID) + ".mtl");
            objWriter.WriteLine("o " + Path.GetFileName(fileID));

            foreach (var group in groups)
            {
                if (group.vertices == null)
                {
                    continue;
                }

                Console.WriteLine("Writing " + group.name);

                //Adjusted according to the WaveFront Object (.obj) File Format spec
                //https://people.cs.clemson.edu/~dhouse/courses/405/docs/brief-obj-file-format.html
                foreach (var vertex in group.vertices)
                {
                    objWriter.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z);
                }
                foreach (var vertex in group.vertices)
                {
                    objWriter.WriteLine("vt " + vertex.TexCoord.X + " " + (vertex.TexCoord.Y - 1) * -1 + " 0.0000");
                }
                foreach (var vertex in group.vertices)
                {
                    objWriter.WriteLine("vn " + (-vertex.Normal.X).ToString("F12") + " " + vertex.Normal.Y.ToString("F12") + " " + vertex.Normal.Z.ToString("F12"));
                }


                var indices = group.indices;
                for (int rbi = 0; rbi < group.renderBatches.Count(); rbi++)
                {
                    var renderbatch = group.renderBatches[rbi];
                    var i           = renderbatch.firstFace;
                    if (renderbatch.numFaces > 0)
                    {
                        objWriter.WriteLine("o " + group.name + rbi);
                        objWriter.WriteLine("g " + group.name + rbi); //3DS Max's OBJ importer fails with invalid normal index without groups being defined
                        objWriter.WriteLine("usemtl " + materials[renderbatch.materialID].filename);
                        objWriter.WriteLine("s 1");
                        while (i < (renderbatch.firstFace + renderbatch.numFaces))
                        {
                            objWriter.WriteLine("f " + (indices[i] + group.verticeOffset + 1) + "/" + (indices[i] + group.verticeOffset + 1) + "/" + (indices[i] + group.verticeOffset + 1) + " " + (indices[i + 1] + group.verticeOffset + 1) + "/" + (indices[i + 1] + group.verticeOffset + 1) + "/" + (indices[i + 1] + group.verticeOffset + 1) + " " + (indices[i + 2] + group.verticeOffset + 1) + "/" + (indices[i + 2] + group.verticeOffset + 1) + "/" + (indices[i + 2] + group.verticeOffset + 1));
                            i = i + 3;
                        }
                    }
                }
            }
            objWriter.Close();
        }
コード例 #24
0
        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
        }
コード例 #25
0
        public static Renderer.Structs.WorldModel LoadWMO(string filename, CacheStorage cache, int shaderProgram)
        {
            if (cache.worldModelBatches.ContainsKey(filename))
            {
                return(cache.worldModelBatches[filename]);
            }

            var wmo = new WoWFormatLib.Structs.WMO.WMO();

            if (cache.worldModels.ContainsKey(filename))
            {
                wmo = cache.worldModels[filename];
            }
            else
            {
                if (!Listfile.TryGetFileDataID(filename, out uint fileDataID))
                {
                    CASCLib.Logger.WriteLine("Could not get filedataid for " + filename);
                }

                //Load WMO from file
                if (WoWFormatLib.Utils.CASC.FileExists(fileDataID))
                {
                    var wmofile = new WMOReader().LoadWMO(fileDataID);
                    cache.worldModels.Add(filename, wmofile);
                    wmo = cache.worldModels[filename];
                }
                else
                {
                    throw new Exception("WMO " + filename + " does not exist!");
                }
            }

            if (wmo.group.Count() == 0)
            {
                CASCLib.Logger.WriteLine("WMO has no groups: ", filename);
                throw new Exception("Broken WMO! Report to developer (mail [email protected]) with this filename: " + filename);
            }

            var wmobatch = new Renderer.Structs.WorldModel()
            {
                groupBatches = new Renderer.Structs.WorldModelGroupBatches[wmo.group.Count()]
            };

            var groupNames = new string[wmo.group.Count()];

            for (var g = 0; g < wmo.group.Count(); g++)
            {
                if (wmo.group[g].mogp.vertices == null)
                {
                    continue;
                }

                wmobatch.groupBatches[g].vao          = GL.GenVertexArray();
                wmobatch.groupBatches[g].vertexBuffer = GL.GenBuffer();
                wmobatch.groupBatches[g].indiceBuffer = GL.GenBuffer();

                GL.BindVertexArray(wmobatch.groupBatches[g].vao);

                GL.BindBuffer(BufferTarget.ArrayBuffer, wmobatch.groupBatches[g].vertexBuffer);

                var wmovertices = new Renderer.Structs.M2Vertex[wmo.group[g].mogp.vertices.Count()];

                for (var i = 0; i < wmo.groupNames.Count(); i++)
                {
                    if (wmo.group[g].mogp.nameOffset == wmo.groupNames[i].offset)
                    {
                        groupNames[g] = wmo.groupNames[i].name.Replace(" ", "_");
                    }
                }

                if (groupNames[g] == "antiportal")
                {
                    continue;
                }

                for (var i = 0; i < wmo.group[g].mogp.vertices.Count(); i++)
                {
                    wmovertices[i].Position = new Vector3(wmo.group[g].mogp.vertices[i].vector.X, wmo.group[g].mogp.vertices[i].vector.Y, wmo.group[g].mogp.vertices[i].vector.Z);
                    wmovertices[i].Normal   = new Vector3(wmo.group[g].mogp.normals[i].normal.X, wmo.group[g].mogp.normals[i].normal.Y, wmo.group[g].mogp.normals[i].normal.Z);
                    if (wmo.group[g].mogp.textureCoords[0] == null)
                    {
                        wmovertices[i].TexCoord = new Vector2(0.0f, 0.0f);
                    }
                    else
                    {
                        wmovertices[i].TexCoord = new Vector2(wmo.group[g].mogp.textureCoords[0][i].X, wmo.group[g].mogp.textureCoords[0][i].Y);
                    }
                }

                //Push to buffer
                GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(wmovertices.Length * 8 * sizeof(float)), wmovertices, 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);

                //Switch to Index buffer
                GL.BindBuffer(BufferTarget.ElementArrayBuffer, wmobatch.groupBatches[g].indiceBuffer);

                var wmoindicelist = new List <uint>();
                for (var i = 0; i < wmo.group[g].mogp.indices.Count(); i++)
                {
                    wmoindicelist.Add(wmo.group[g].mogp.indices[i].indice);
                }

                wmobatch.groupBatches[g].indices = wmoindicelist.ToArray();

                GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(wmobatch.groupBatches[g].indices.Length * sizeof(uint)), wmobatch.groupBatches[g].indices, BufferUsageHint.StaticDraw);
            }

            GL.Enable(EnableCap.Texture2D);

            wmobatch.mats = new Renderer.Structs.Material[wmo.materials.Count()];
            for (var i = 0; i < wmo.materials.Count(); i++)
            {
                wmobatch.mats[i].texture1 = wmo.materials[i].texture1;
                wmobatch.mats[i].texture2 = wmo.materials[i].texture2;
                wmobatch.mats[i].texture3 = wmo.materials[i].texture3;

                if (wmo.textures == null)
                {
                    if (WoWFormatLib.Utils.CASC.FileExists(wmo.materials[i].texture1))
                    {
                        wmobatch.mats[i].textureID1 = BLPLoader.LoadTexture(wmo.materials[i].texture1, cache);
                    }

                    if (WoWFormatLib.Utils.CASC.FileExists(wmo.materials[i].texture2))
                    {
                        wmobatch.mats[i].textureID2 = BLPLoader.LoadTexture(wmo.materials[i].texture2, cache);
                    }

                    if (WoWFormatLib.Utils.CASC.FileExists(wmo.materials[i].texture3))
                    {
                        wmobatch.mats[i].textureID3 = BLPLoader.LoadTexture(wmo.materials[i].texture3, cache);
                    }
                }
                else
                {
                    for (var ti = 0; ti < wmo.textures.Count(); ti++)
                    {
                        if (wmo.textures[ti].startOffset == wmo.materials[i].texture1)
                        {
                            wmobatch.mats[i].textureID1 = BLPLoader.LoadTexture(wmo.textures[ti].filename, cache);
                        }

                        if (wmo.textures[ti].startOffset == wmo.materials[i].texture2)
                        {
                            wmobatch.mats[i].textureID2 = BLPLoader.LoadTexture(wmo.textures[ti].filename, cache);
                        }

                        if (wmo.textures[ti].startOffset == wmo.materials[i].texture3)
                        {
                            wmobatch.mats[i].textureID3 = BLPLoader.LoadTexture(wmo.textures[ti].filename, cache);
                        }
                    }
                }
            }

            wmobatch.doodads = new Renderer.Structs.WMODoodad[wmo.doodadDefinitions.Count()];

            for (var i = 0; i < wmo.doodadDefinitions.Count(); i++)
            {
                if (wmo.doodadNames != null)
                {
                    for (var j = 0; j < wmo.doodadNames.Count(); j++)
                    {
                        if (wmo.doodadDefinitions[i].offset == wmo.doodadNames[j].startOffset)
                        {
                            wmobatch.doodads[i].filename = wmo.doodadNames[j].filename;
                        }
                    }
                }
                else
                {
                    wmobatch.doodads[i].filedataid = wmo.doodadDefinitions[i].offset;
                }

                wmobatch.doodads[i].flags    = wmo.doodadDefinitions[i].flags;
                wmobatch.doodads[i].position = new Vector3(wmo.doodadDefinitions[i].position.X, wmo.doodadDefinitions[i].position.Y, wmo.doodadDefinitions[i].position.Z);
                wmobatch.doodads[i].rotation = new Quaternion(wmo.doodadDefinitions[i].rotation.X, wmo.doodadDefinitions[i].rotation.Y, wmo.doodadDefinitions[i].rotation.Z, wmo.doodadDefinitions[i].rotation.W);
                wmobatch.doodads[i].scale    = wmo.doodadDefinitions[i].scale;
                wmobatch.doodads[i].color    = new Vector4(wmo.doodadDefinitions[i].color[0], wmo.doodadDefinitions[i].color[1], wmo.doodadDefinitions[i].color[2], wmo.doodadDefinitions[i].color[3]);
            }

            var numRenderbatches = 0;

            //Get total amount of render batches
            for (var i = 0; i < wmo.group.Count(); i++)
            {
                if (wmo.group[i].mogp.renderBatches == null)
                {
                    continue;
                }
                numRenderbatches = numRenderbatches + wmo.group[i].mogp.renderBatches.Count();
            }

            wmobatch.wmoRenderBatch = new Renderer.Structs.RenderBatch[numRenderbatches];

            var rb = 0;

            for (var g = 0; g < wmo.group.Count(); g++)
            {
                var group = wmo.group[g];
                if (group.mogp.renderBatches == null)
                {
                    continue;
                }
                for (var i = 0; i < group.mogp.renderBatches.Count(); i++)
                {
                    wmobatch.wmoRenderBatch[rb].firstFace = group.mogp.renderBatches[i].firstFace;
                    wmobatch.wmoRenderBatch[rb].numFaces  = group.mogp.renderBatches[i].numFaces;
                    uint matID = 0;

                    if (group.mogp.renderBatches[i].flags == 2)
                    {
                        matID = (uint)group.mogp.renderBatches[i].possibleBox2_3;
                    }
                    else
                    {
                        matID = group.mogp.renderBatches[i].materialID;
                    }

                    wmobatch.wmoRenderBatch[rb].materialID = new uint[3];
                    for (var ti = 0; ti < wmobatch.mats.Count(); ti++)
                    {
                        if (wmo.materials[matID].texture1 == wmobatch.mats[ti].texture1)
                        {
                            wmobatch.wmoRenderBatch[rb].materialID[0] = (uint)wmobatch.mats[ti].textureID1;
                        }

                        if (wmo.materials[matID].texture2 == wmobatch.mats[ti].texture2)
                        {
                            wmobatch.wmoRenderBatch[rb].materialID[1] = (uint)wmobatch.mats[ti].textureID2;
                        }

                        if (wmo.materials[matID].texture3 == wmobatch.mats[ti].texture3)
                        {
                            wmobatch.wmoRenderBatch[rb].materialID[2] = (uint)wmobatch.mats[ti].textureID3;
                        }
                    }

                    wmobatch.wmoRenderBatch[rb].blendType = wmo.materials[matID].blendMode;
                    wmobatch.wmoRenderBatch[rb].groupID   = (uint)g;
                    rb++;
                }
            }
            cache.worldModelBatches.Add(filename, wmobatch);

            return(wmobatch);
        }
コード例 #26
0
ファイル: M2Loader.cs プロジェクト: serayn/WoWExportTools
        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);
        }
コード例 #27
0
        public static void ExportM2(string file, BackgroundWorker exportworker = null, string destinationOverride = null)
        {
            if (exportworker == null)
            {
                exportworker = new BackgroundWorker();
                exportworker.WorkerReportsProgress = true;
            }

            Logger.WriteLine("M2 glTF Exporter: Loading file {0}...", file);

            exportworker.ReportProgress(5, "Reading M2..");

            var outdir = ConfigurationManager.AppSettings["outdir"];
            var reader = new M2Reader();

            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)
            {
                Logger.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 Export Tools " + 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
                            Logger.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 = 840426;
                materials[i].flags = reader.model.textures[i].flags;
                switch (reader.model.textures[i].type)
                {
                case 0:
                    Listfile.TryGetFileDataID(reader.model.textures[i].filename, out textureFileDataID);
                    break;

                case 1:
                case 2:
                case 11:
                /*
                 * var fileDataID = CASC.getFileDataIdByName(file);
                 * var cdifilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(fileDataID, (int)reader.model.textures[i].type);
                 * for (var ti = 0; ti < cdifilenames.Count(); ti++)
                 * {
                 *  textureFileDataID = cdifilenames[0];
                 * }
                 * break;
                 */
                default:
                    Console.WriteLine("      Falling back to placeholder texture");
                    break;
                }

                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
             */
        }