Example #1
0
        public static void exportWMO(string file, BackgroundWorker exportworker = null, string destinationOverride = null)
        {
            if (exportworker == null)
            {
                exportworker = new BackgroundWorker();
                exportworker.WorkerReportsProgress = true;
            }

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

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

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

            reader.LoadWMO(file);

            System.Globalization.CultureInfo customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
            customCulture.NumberFormat.NumberDecimalSeparator    = ".";
            System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;

            if (destinationOverride == null)
            {
                if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file))))
                {
                    Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file)));
                }
            }

            exportworker.ReportProgress(25, "Generating glTF..");

            var glTF = new glTF()
            {
                asset = new Asset()
                {
                    version    = "2.0",
                    generator  = "Marlamin's WoW Exporter " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(),
                    copyright  = "Contents are owned by Blizzard Entertainment",
                    minVersion = "2.0"
                }
            };

            var        groups = new Structs.WMOGroup[reader.wmofile.group.Count()];
            FileStream stream;

            if (destinationOverride == null)
            {
                stream = new FileStream(Path.Combine(outdir, file.Replace(".wmo", ".bin")), FileMode.OpenOrCreate);
            }
            else
            {
                stream = new FileStream(Path.Combine(destinationOverride, Path.GetFileNameWithoutExtension(file) + ".bin"), FileMode.OpenOrCreate);
            }

            var writer = new BinaryWriter(stream);

            var bufferViews  = new List <BufferView>();
            var accessorInfo = new List <Accessor>();
            var meshes       = new List <Mesh>();

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

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

                // Position bufferview
                var vPosBuffer = new BufferView()
                {
                    buffer     = 0,
                    byteOffset = (uint)writer.BaseStream.Position,
                    target     = 34962
                };

                var minPosX = float.MaxValue;
                var minPosY = float.MaxValue;
                var minPosZ = float.MaxValue;

                var maxPosX = float.MinValue;
                var maxPosY = float.MinValue;
                var maxPosZ = float.MinValue;

                for (int i = 0; i < reader.wmofile.group[g].mogp.vertices.Count(); i++)
                {
                    writer.Write(reader.wmofile.group[g].mogp.vertices[i].vector.X * -1);
                    writer.Write(reader.wmofile.group[g].mogp.vertices[i].vector.Z);
                    writer.Write(reader.wmofile.group[g].mogp.vertices[i].vector.Y);

                    if (reader.wmofile.group[g].mogp.vertices[i].vector.X * -1 < minPosX)
                    {
                        minPosX = reader.wmofile.group[g].mogp.vertices[i].vector.X * -1;
                    }
                    if (reader.wmofile.group[g].mogp.vertices[i].vector.Z < minPosY)
                    {
                        minPosY = reader.wmofile.group[g].mogp.vertices[i].vector.Z;
                    }
                    if (reader.wmofile.group[g].mogp.vertices[i].vector.Y < minPosZ)
                    {
                        minPosZ = reader.wmofile.group[g].mogp.vertices[i].vector.Y;
                    }

                    if (reader.wmofile.group[g].mogp.vertices[i].vector.X * -1 > maxPosX)
                    {
                        maxPosX = reader.wmofile.group[g].mogp.vertices[i].vector.X * -1;
                    }
                    if (reader.wmofile.group[g].mogp.vertices[i].vector.Z > maxPosY)
                    {
                        maxPosY = reader.wmofile.group[g].mogp.vertices[i].vector.Z;
                    }
                    if (reader.wmofile.group[g].mogp.vertices[i].vector.Y > maxPosZ)
                    {
                        maxPosZ = reader.wmofile.group[g].mogp.vertices[i].vector.Y;
                    }
                }

                vPosBuffer.byteLength = (uint)writer.BaseStream.Position - vPosBuffer.byteOffset;

                var posLoc = accessorInfo.Count();

                accessorInfo.Add(new Accessor()
                {
                    name          = "vPos",
                    bufferView    = bufferViews.Count(),
                    byteOffset    = 0,
                    componentType = 5126,
                    count         = (uint)reader.wmofile.group[g].mogp.vertices.Count(),
                    type          = "VEC3",
                    min           = new float[] { minPosX, minPosY, minPosZ },
                    max           = new float[] { maxPosX, maxPosY, maxPosZ }
                });

                bufferViews.Add(vPosBuffer);

                // Normal bufferview
                var normalBuffer = new BufferView()
                {
                    buffer     = 0,
                    byteOffset = (uint)writer.BaseStream.Position,
                    target     = 34962
                };

                for (int i = 0; i < reader.wmofile.group[g].mogp.vertices.Count(); i++)
                {
                    writer.Write(reader.wmofile.group[g].mogp.normals[i].normal.X);
                    writer.Write(reader.wmofile.group[g].mogp.normals[i].normal.Z);
                    writer.Write(reader.wmofile.group[g].mogp.normals[i].normal.Y);
                }

                normalBuffer.byteLength = (uint)writer.BaseStream.Position - normalBuffer.byteOffset;

                var normalLoc = accessorInfo.Count();

                accessorInfo.Add(new Accessor()
                {
                    name          = "vNormal",
                    bufferView    = bufferViews.Count(),
                    byteOffset    = 0,
                    componentType = 5126,
                    count         = (uint)reader.wmofile.group[g].mogp.vertices.Count(),
                    type          = "VEC3"
                });

                bufferViews.Add(normalBuffer);

                // TexCoord bufferview
                var texCoordBuffer = new BufferView()
                {
                    buffer     = 0,
                    byteOffset = (uint)writer.BaseStream.Position,
                    target     = 34962
                };

                for (int i = 0; i < reader.wmofile.group[g].mogp.vertices.Count(); i++)
                {
                    writer.Write(reader.wmofile.group[g].mogp.textureCoords[0][i].X);
                    writer.Write(reader.wmofile.group[g].mogp.textureCoords[0][i].Y);
                }

                texCoordBuffer.byteLength = (uint)writer.BaseStream.Position - texCoordBuffer.byteOffset;

                var texLoc = accessorInfo.Count();

                accessorInfo.Add(new Accessor()
                {
                    name          = "vTex",
                    bufferView    = bufferViews.Count(),
                    byteOffset    = 0,
                    componentType = 5126,
                    count         = (uint)reader.wmofile.group[g].mogp.vertices.Count(),
                    type          = "VEC2"
                });

                bufferViews.Add(texCoordBuffer);

                var indexBufferPos = bufferViews.Count();

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

                    accessorInfo.Add(new Accessor()
                    {
                        name          = "indices",
                        bufferView    = indexBufferPos,
                        byteOffset    = batch.firstFace * 2,
                        componentType = 5123,
                        count         = batch.numFaces,
                        type          = "SCALAR"
                    });

                    var mesh = new Mesh();
                    mesh.name       = groups[g].name + "_" + i;
                    mesh.primitives = new Primitive[1];
                    mesh.primitives[0].attributes = new Dictionary <string, int>
                    {
                        { "POSITION", posLoc },
                        { "NORMAL", normalLoc },
                        { "TEXCOORD_0", texLoc }
                    };

                    mesh.primitives[0].indices = (uint)accessorInfo.Count() - 1;

                    if (batch.flags == 2)
                    {
                        mesh.primitives[0].material = (uint)batch.possibleBox2_3;
                    }
                    else
                    {
                        mesh.primitives[0].material = batch.materialID;
                    }

                    mesh.primitives[0].mode = 4;

                    meshes.Add(mesh);
                }

                var indiceBuffer = new BufferView()
                {
                    buffer     = 0,
                    byteOffset = (uint)writer.BaseStream.Position,
                    target     = 34963
                };

                for (int i = 0; i < reader.wmofile.group[g].mogp.indices.Count(); i++)
                {
                    writer.Write(reader.wmofile.group[g].mogp.indices[i].indice);
                }

                indiceBuffer.byteLength = (uint)writer.BaseStream.Position - indiceBuffer.byteOffset;

                bufferViews.Add(indiceBuffer);
            }

            glTF.bufferViews = bufferViews.ToArray();
            glTF.accessors   = accessorInfo.ToArray();

            glTF.buffers = new Buffer[1];
            glTF.buffers[0].byteLength = (uint)writer.BaseStream.Length;
            glTF.buffers[0].uri        = Path.GetFileNameWithoutExtension(file) + ".bin";

            writer.Close();
            writer.Dispose();

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

            if (reader.wmofile.materials == null)
            {
                Logger.WriteLine("WMO glTF exporter: Materials empty"); return;
            }

            var materialCount = reader.wmofile.materials.Count();

            glTF.images    = new Image[materialCount];
            glTF.textures  = new Texture[materialCount];
            glTF.materials = new Material[materialCount];

            for (int i = 0; i < materialCount; i++)
            {
                for (int ti = 0; ti < reader.wmofile.textures.Count(); ti++)
                {
                    if (reader.wmofile.textures[ti].startOffset == reader.wmofile.materials[i].texture1)
                    {
                        var textureFilename = Path.GetFileNameWithoutExtension(reader.wmofile.textures[ti].filename).ToLower();

                        glTF.images[i].uri = textureFilename + ".png";

                        glTF.textures[i].sampler = 0;
                        glTF.textures[i].source  = i;

                        glTF.materials[i].name = textureFilename;
                        glTF.materials[i].pbrMetallicRoughness = new PBRMetallicRoughness();
                        glTF.materials[i].pbrMetallicRoughness.baseColorTexture       = new TextureIndex();
                        glTF.materials[i].pbrMetallicRoughness.baseColorTexture.index = i;
                        glTF.materials[i].pbrMetallicRoughness.metallicFactor         = 0.0f;

                        switch (reader.wmofile.materials[i].blendMode)
                        {
                        case 0:
                            glTF.materials[i].alphaMode   = "OPAQUE";
                            glTF.materials[i].alphaCutoff = 0.0f;
                            break;

                        case 1:
                            glTF.materials[i].alphaMode   = "MASK";
                            glTF.materials[i].alphaCutoff = 0.90393700787f;
                            break;

                        case 2:
                            glTF.materials[i].alphaMode   = "MASK";
                            glTF.materials[i].alphaCutoff = 0.5f;
                            break;

                        default:
                            glTF.materials[i].alphaMode   = "OPAQUE";
                            glTF.materials[i].alphaCutoff = 0.0f;
                            break;
                        }

                        var saveLocation = "";

                        if (destinationOverride == null)
                        {
                            saveLocation = Path.Combine(outdir, Path.GetDirectoryName(file), textureFilename + ".png");
                        }
                        else
                        {
                            saveLocation = Path.Combine(outdir, destinationOverride, textureFilename + ".png");
                        }

                        if (!File.Exists(saveLocation))
                        {
                            var blpreader = new BLPReader();

                            blpreader.LoadBLP(reader.wmofile.textures[ti].filename);

                            try
                            {
                                blpreader.bmp.Save(saveLocation);
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine("Error exporting texture " + reader.wmofile.textures[ti].filename + ": " + e.Message);
                            }
                        }
                    }
                }
            }

            glTF.samplers              = new Sampler[1];
            glTF.samplers[0].name      = "Default Sampler";
            glTF.samplers[0].minFilter = 9986;
            glTF.samplers[0].magFilter = 9729;
            glTF.samplers[0].wrapS     = 10497;
            glTF.samplers[0].wrapT     = 10497;

            glTF.scenes         = new Scene[1];
            glTF.scenes[0].name = Path.GetFileNameWithoutExtension(file);

            glTF.nodes = new Node[meshes.Count()];
            var meshIDs = new List <int>();

            for (var i = 0; i < meshes.Count(); i++)
            {
                glTF.nodes[i].name = meshes[i].name;
                glTF.nodes[i].mesh = i;
                meshIDs.Add(i);
            }

            glTF.scenes[0].nodes = meshIDs.ToArray();

            glTF.meshes = meshes.ToArray();

            glTF.scene = 0;

            string currentDoodadSetName = "";

            for (int i = 0; i < reader.wmofile.doodadDefinitions.Count(); i++)
            {
                var doodadDefinition = reader.wmofile.doodadDefinitions[i];

                foreach (var doodadSet in reader.wmofile.doodadSets)
                {
                    if (doodadSet.firstInstanceIndex == i)
                    {
                        Console.WriteLine("At set: " + doodadSet.setName);
                        currentDoodadSetName = doodadSet.setName.Replace("Set_", "").Replace("SET_", "").Replace("$DefaultGlobal", "Default");
                    }
                }

                foreach (var doodadNameEntry in reader.wmofile.doodadNames)
                {
                    if (doodadNameEntry.startOffset == doodadDefinition.offset)
                    {
                        if (!File.Exists(Path.GetFileNameWithoutExtension(doodadNameEntry.filename).ToLower() + ".gltf"))
                        {
                            if (destinationOverride == null)
                            {
                                M2Exporter.exportM2(doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2").ToLower(), null, Path.Combine(outdir, Path.GetDirectoryName(file)));
                            }
                            else
                            {
                                M2Exporter.exportM2(doodadNameEntry.filename.Replace(".MDX", ".M2").Replace(".MDL", ".M2").ToLower(), null, destinationOverride);
                            }
                        }
                    }
                }
            }

            exportworker.ReportProgress(95, "Writing to file..");

            if (destinationOverride == null)
            {
                File.WriteAllText(Path.Combine(outdir, file.Replace(".wmo", ".gltf")), JsonConvert.SerializeObject(glTF, Formatting.Indented, new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore
                }));
            }
            else
            {
                File.WriteAllText(Path.Combine(destinationOverride, Path.GetFileName(file.ToLower()).Replace(".wmo", ".gltf")), JsonConvert.SerializeObject(glTF, Formatting.Indented, new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore
                }));
            }

            Logger.WriteLine("Done exporting WMO file!");
        }
Example #2
0
        public static void exportADT(string file, BackgroundWorker exportworker = null)
        {
            if (exportworker == null)
            {
                exportworker = new BackgroundWorker();
                exportworker.WorkerReportsProgress = true;
            }

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

            System.Globalization.CultureInfo customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
            customCulture.NumberFormat.NumberDecimalSeparator    = ".";
            System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;

            float TileSize  = 1600.0f / 3.0f;   //533.333
            float ChunkSize = TileSize / 16.0f; //33.333
            float UnitSize  = ChunkSize / 8.0f; //4.166666

            var mapname = file.Replace("world/maps/", "").Substring(0, file.Replace("world/maps/", "").IndexOf("/"));
            var coord   = file.Replace("world/maps/" + mapname + "/" + mapname, "").Replace(".adt", "").Split('_');

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

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

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

            ADTReader reader = new ADTReader();

            reader.LoadADT(file.Replace('/', '\\'));

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

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

            var glTF = new glTF()
            {
                asset = new Asset()
                {
                    version   = "2.0",
                    generator = "Marlamin's WoW Exporter " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(),
                    copyright = "Contents are owned by Blizzard Entertainment"
                }
            };

            var initialChunkY = reader.adtfile.chunks[0].header.position.Y;
            var initialChunkX = reader.adtfile.chunks[0].header.position.X;

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

            var bufferViews  = new List <BufferView>();
            var accessorInfo = new List <Accessor>();
            var meshes       = new List <Mesh>();

            glTF.buffers = new Buffer[1];

            if (bakeQuality == "low" || bakeQuality == "medium")
            {
                glTF.images    = new Image[1];
                glTF.materials = new Material[1];
                glTF.textures  = new Texture[1];
            }
            else if (bakeQuality == "high")
            {
                glTF.images    = new Image[256];
                glTF.materials = new Material[256];
                glTF.textures  = new Texture[256];
            }
            var stream = new FileStream(Path.Combine(outdir, file.Replace(".adt", ".bin")), FileMode.OpenOrCreate);
            var writer = new BinaryWriter(stream);

            for (uint c = 0; c < reader.adtfile.chunks.Count(); c++)
            {
                var chunk = reader.adtfile.chunks[c];

                var localVertices = new Structs.Vertex[145];
                for (int i = 0, idx = 0; i < 17; i++)
                {
                    for (int j = 0; j < (((i % 2) != 0) ? 8 : 9); j++)
                    {
                        Structs.Vertex v = new Structs.Vertex();
                        v.Normal   = new OpenTK.Vector3(chunk.normals.normal_2[idx] / 127f, chunk.normals.normal_0[idx] / 127f, chunk.normals.normal_1[idx] / 127f);
                        v.Position = new OpenTK.Vector3(chunk.header.position.Y - (j * UnitSize), chunk.vertices.vertices[idx++] + chunk.header.position.Z, chunk.header.position.X - (i * UnitSize * 0.5f));
                        if ((i % 2) != 0)
                        {
                            v.Position.X -= 0.5f * UnitSize;
                        }
                        if (bakeQuality == "low" || bakeQuality == "medium")
                        {
                            v.TexCoord = new Vector2(-(v.Position.X - initialChunkX) / TileSize, -(v.Position.Z - initialChunkY) / TileSize);
                        }
                        else if (bakeQuality == "high")
                        {
                            v.TexCoord = new Vector2(-(v.Position.X - initialChunkX) / ChunkSize, -(v.Position.Z - initialChunkY) / ChunkSize);
                        }
                        localVertices[idx - 1] = v;
                    }
                }

                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;

                // Position buffer
                foreach (var vertex in localVertices)
                {
                    writer.Write(vertex.Position.X);
                    writer.Write(vertex.Position.Y);
                    writer.Write(vertex.Position.Z);

                    if (vertex.Position.X < minPosX)
                    {
                        minPosX = vertex.Position.X;
                    }
                    if (vertex.Position.Y < minPosY)
                    {
                        minPosY = vertex.Position.Y;
                    }
                    if (vertex.Position.Z < minPosZ)
                    {
                        minPosZ = vertex.Position.Z;
                    }

                    if (vertex.Position.X > maxPosX)
                    {
                        maxPosX = vertex.Position.X;
                    }
                    if (vertex.Position.Y > maxPosY)
                    {
                        maxPosY = vertex.Position.Y;
                    }
                    if (vertex.Position.Z > maxPosZ)
                    {
                        maxPosZ = vertex.Position.Z;
                    }
                }

                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         = 145,
                    type          = "VEC3",
                    min           = new float[] { minPosX, minPosY, minPosZ },
                    max           = new float[] { maxPosX, maxPosY, maxPosZ }
                });

                bufferViews.Add(vPosBuffer);

                // Normal buffer
                var normalBuffer = new BufferView()
                {
                    buffer     = 0,
                    byteOffset = (uint)writer.BaseStream.Position,
                    target     = 34962
                };

                foreach (var vertex in localVertices)
                {
                    writer.Write(vertex.Normal.X);
                    writer.Write(vertex.Normal.Y);
                    writer.Write(vertex.Normal.Z);
                }

                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         = 145,
                    type          = "VEC3"
                });

                bufferViews.Add(normalBuffer);

                // Texcoord buffer
                var texCoordBuffer = new BufferView()
                {
                    buffer     = 0,
                    byteOffset = (uint)writer.BaseStream.Position,
                    target     = 34962
                };

                foreach (var vertex in localVertices)
                {
                    writer.Write(vertex.TexCoord.X);
                    writer.Write(vertex.TexCoord.Y);
                }

                texCoordBuffer.byteLength = (uint)writer.BaseStream.Position - texCoordBuffer.byteOffset;

                var texLoc = accessorInfo.Count();

                accessorInfo.Add(new Accessor()
                {
                    name          = "vTex",
                    bufferView    = bufferViews.Count(),
                    byteOffset    = 0,
                    componentType = 5126,
                    count         = 145,
                    type          = "VEC2"
                });

                bufferViews.Add(texCoordBuffer);

                var indexBufferPos = bufferViews.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;

                List <int> indicelist = new List <Int32>();

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

                    if ((chunk.header.flags & 0x10000) == 0)
                    {
                        var currentHole = (int)Math.Pow(2,
                                                        Math.Floor(xx / 2f) * 1f +
                                                        Math.Floor(yy / 2f) * 4f);

                        if ((chunk.header.holesLowRes & currentHole) == 0)
                        {
                            isHole = false;
                        }
                    }

                    else
                    {
                        if (((holesHighRes[yy] >> xx) & 1) == 0)
                        {
                            isHole = false;
                        }
                    }

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

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

                accessorInfo.Add(new Accessor()
                {
                    name          = "indices",
                    bufferView    = indexBufferPos,
                    byteOffset    = 0,
                    componentType = 5125,
                    count         = (uint)indicelist.Count(),
                    type          = "SCALAR"
                });

                var indiceBuffer = new BufferView()
                {
                    buffer     = 0,
                    byteOffset = (uint)writer.BaseStream.Position,
                    target     = 34963
                };

                for (int i = 0; i < indicelist.Count(); i++)
                {
                    writer.Write(indicelist[i]);
                }

                indiceBuffer.byteLength = (uint)writer.BaseStream.Position - indiceBuffer.byteOffset;

                bufferViews.Add(indiceBuffer);

                var mesh = new Mesh();
                mesh.primitives = new Primitive[1];
                mesh.primitives[0].attributes = new Dictionary <string, int>
                {
                    { "POSITION", posLoc },
                    { "NORMAL", normalLoc },
                    { "TEXCOORD_0", texLoc }
                };

                mesh.primitives[0].indices = (uint)accessorInfo.Count() - 1;
                if (bakeQuality == "low" || bakeQuality == "medium")
                {
                    mesh.primitives[0].material = 0;
                }
                else if (bakeQuality == "high")
                {
                    mesh.primitives[0].material = c;
                }

                mesh.primitives[0].mode = 4;
                mesh.name = "MCNK #" + c;
                meshes.Add(mesh);

                glTF.buffers[0].byteLength = (uint)writer.BaseStream.Length;
                glTF.buffers[0].uri        = Path.GetFileNameWithoutExtension(file) + ".bin";

                if (bakeQuality == "low" || bakeQuality == "medium")
                {
                    glTF.images[0].uri       = Path.GetFileNameWithoutExtension(file) + ".png";
                    glTF.textures[0].sampler = 0;
                    glTF.textures[0].source  = 0;
                    glTF.materials[0].pbrMetallicRoughness = new PBRMetallicRoughness();
                    glTF.materials[0].pbrMetallicRoughness.baseColorTexture       = new TextureIndex();
                    glTF.materials[0].pbrMetallicRoughness.baseColorTexture.index = 0;
                    glTF.materials[0].pbrMetallicRoughness.metallicFactor         = 0.0f;
                    glTF.materials[0].alphaMode   = "OPAQUE";
                    glTF.materials[0].alphaCutoff = 0.0f;
                }
                else if (bakeQuality == "high")
                {
                    glTF.images[c].uri       = Path.GetFileNameWithoutExtension(file) + "_" + c + ".png";
                    glTF.textures[c].sampler = 0;
                    glTF.textures[c].source  = (int)c;
                    glTF.materials[c].pbrMetallicRoughness = new PBRMetallicRoughness();
                    glTF.materials[c].pbrMetallicRoughness.baseColorTexture       = new TextureIndex();
                    glTF.materials[c].pbrMetallicRoughness.baseColorTexture.index = (int)c;
                    glTF.materials[c].pbrMetallicRoughness.metallicFactor         = 0.0f;
                    glTF.materials[c].alphaMode   = "OPAQUE";
                    glTF.materials[c].alphaCutoff = 0.0f;
                }
            }

            writer.Close();
            writer.Dispose();

            glTF.bufferViews = bufferViews.ToArray();
            glTF.accessors   = accessorInfo.ToArray();

            glTF.samplers = new Sampler[1];
            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..");

            File.WriteAllText(Path.Combine(outdir, file.Replace(".adt", ".gltf")), JsonConvert.SerializeObject(glTF, Formatting.Indented, new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore
            }));

            ConfigurationManager.RefreshSection("appSettings");

            if (ConfigurationManager.AppSettings["exportEverything"] == "True")
            {
                exportworker.ReportProgress(25, "Exporting WMOs");

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

                    var filename = reader.adtfile.objects.wmoNames.filenames[wmo.mwidEntry];

                    if (!File.Exists(Path.GetFileNameWithoutExtension(filename).ToLower() + ".gltf"))
                    {
                        WMOExporter.exportWMO(filename.ToLower(), null, Path.Combine(outdir, Path.GetDirectoryName(file)));
                    }
                }

                exportworker.ReportProgress(50, "Exporting M2s");

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

                    var filename = reader.adtfile.objects.m2Names.filenames[doodad.mmidEntry];

                    if (!File.Exists(Path.GetFileNameWithoutExtension(filename).ToLower() + ".gltf"))
                    {
                        M2Exporter.exportM2(filename.ToLower(), null, Path.Combine(outdir, Path.GetDirectoryName(file)));
                    }
                }
            }
        }