Example #1
0
        public static void ExportADT(string file, BackgroundWorker exportworker = null)
        {
            if (exportworker == null)
            {
                exportworker = new BackgroundWorker
                {
                    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 TileSize  = 1600.0f / 3.0f;   //533.333
            var ChunkSize = TileSize / 16.0f; //33.333
            var 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);

            var 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;
            }

            var renderBatches = new List <Structs.RenderBatch>();
            var 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 (var 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 (var j = 0; j < (((i % 2) != 0) ? 8 : 9); j++)
                    {
                        var v = new Structs.Vertex
                        {
                            Normal   = new Vector3(chunk.normals.normal_2[idx] / 127f, chunk.normals.normal_0[idx] / 127f, chunk.normals.normal_1[idx] / 127f),
                            Position = new 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;

                var indicelist = new List <int>();

                for (int j = 9, xx = 0, yy = 0; j < 145; j++, xx++)
                {
                    if (xx >= 8)
                    {
                        xx = 0; ++yy;
                    }
                    var 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 int[] { j + 8, j, j - 9 });
                        indicelist.AddRange(new int[] { j - 9, j, j - 8 });
                        indicelist.AddRange(new int[] { j - 8, j, j + 9 });
                        indicelist.AddRange(new int[] { 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 (var 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 = (uint)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["exportWMO"] == "True" || ConfigurationManager.AppSettings["exportM2"] == "True")
            {
                if (ConfigurationManager.AppSettings["exportWMO"] == "True")
                {
                    exportworker.ReportProgress(25, "Exporting WMOs");

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

                        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)));
                        }
                    }
                }

                if (ConfigurationManager.AppSettings["exportM2"] == "True")
                {
                    exportworker.ReportProgress(50, "Exporting M2s");

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

                        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)));
                        }
                    }
                }
            }
        }
Example #2
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 #3
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 Exporter " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(),
                    copyright  = "Contents are owned by Blizzard Entertainment",
                    minVersion = "2.0"
                }
            };

            FileStream stream;

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

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

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

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

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

            for (var i = 0; i < reader.model.vertices.Count(); i++)
            {
                writer.Write(reader.model.vertices[i].position.X);
                writer.Write(reader.model.vertices[i].position.Z);
                writer.Write(reader.model.vertices[i].position.Y * -1);

                if (reader.model.vertices[i].position.X < minPosX)
                {
                    minPosX = reader.model.vertices[i].position.X;
                }
                if (reader.model.vertices[i].position.Z < minPosY)
                {
                    minPosY = reader.model.vertices[i].position.Z;
                }
                if (reader.model.vertices[i].position.Y * -1 < minPosZ)
                {
                    minPosZ = reader.model.vertices[i].position.Y * -1;
                }

                if (reader.model.vertices[i].position.X > maxPosX)
                {
                    maxPosX = reader.model.vertices[i].position.X;
                }
                if (reader.model.vertices[i].position.Z > maxPosY)
                {
                    maxPosY = reader.model.vertices[i].position.Z;
                }
                if (reader.model.vertices[i].position.Y * -1 > maxPosZ)
                {
                    maxPosZ = reader.model.vertices[i].position.Y * -1;
                }
            }

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

            var posLoc = accessorInfo.Count();

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

            bufferViews.Add(vPosBuffer);

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

            for (var i = 0; i < reader.model.vertices.Count(); i++)
            {
                writer.Write(reader.model.vertices[i].normal.X);
                writer.Write(reader.model.vertices[i].normal.Z);
                writer.Write(reader.model.vertices[i].normal.Y);
            }

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

            var normalLoc = accessorInfo.Count();

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

            bufferViews.Add(normalBuffer);

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

            for (var i = 0; i < reader.model.vertices.Count(); i++)
            {
                writer.Write(reader.model.vertices[i].textureCoordX);
                writer.Write(reader.model.vertices[i].textureCoordY);
            }

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

            var texLoc = accessorInfo.Count();

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

            bufferViews.Add(texCoordBuffer);

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

            for (var i = 0; i < reader.model.vertices.Count(); i++)
            {
                writer.Write(reader.model.vertices[i].boneIndices_0);
                writer.Write(reader.model.vertices[i].boneIndices_1);
                writer.Write(reader.model.vertices[i].boneIndices_2);
                writer.Write(reader.model.vertices[i].boneIndices_3);
            }

            jointBuffer.byteOffset = (uint)writer.BaseStream.Position - jointBuffer.byteOffset;

            var jointLoc = accessorInfo.Count();

            accessorInfo.Add(new Accessor()
            {
                name          = "vJoint",
                bufferView    = bufferViews.Count(),
                byteOffset    = 0,
                componentType = 5121,
                count         = (uint)reader.model.vertices.Count(),
                type          = "VEC4"
            });

            bufferViews.Add(jointBuffer);

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

            for (var i = 0; i < reader.model.vertices.Count(); i++)
            {
                writer.Write(reader.model.vertices[i].boneWeight_0);
                writer.Write(reader.model.vertices[i].boneWeight_1);
                writer.Write(reader.model.vertices[i].boneWeight_2);
                writer.Write(reader.model.vertices[i].boneWeight_3);
            }

            weightBuffer.byteOffset = (uint)writer.BaseStream.Position - weightBuffer.byteOffset;

            var weightLoc = accessorInfo.Count();

            accessorInfo.Add(new Accessor()
            {
                name          = "vWeight",
                bufferView    = bufferViews.Count(),
                byteOffset    = 0,
                componentType = 5121,
                count         = (uint)reader.model.vertices.Count(),
                type          = "VEC4"
            });

            bufferViews.Add(weightBuffer);

            // End of element bufferviews
            var indexBufferPos = bufferViews.Count();
            var materialBlends = new Dictionary <int, ushort>();

            for (var i = 0; i < reader.model.skins[0].submeshes.Count(); i++)
            {
                var batch = reader.model.skins[0].submeshes[i];

                accessorInfo.Add(new Accessor()
                {
                    name          = "indices",
                    bufferView    = indexBufferPos,
                    byteOffset    = reader.model.skins[0].submeshes[i].startTriangle * 2,
                    componentType = 5123,
                    count         = reader.model.skins[0].submeshes[i].nTriangles,
                    type          = "SCALAR"
                });

                var mesh = new Mesh();
                mesh.name       = "Group #" + i;
                mesh.primitives = new Primitive[1];
                mesh.primitives[0].attributes = new Dictionary <string, int>
                {
                    { "POSITION", posLoc },
                    { "NORMAL", normalLoc },
                    { "TEXCOORD_0", texLoc },
                    { "JOINTS_0", jointLoc },
                    { "WEIGHTS_0", weightLoc }
                };

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

                meshes.Add(mesh);
                // Texture stuff
                for (var tu = 0; tu < reader.model.skins[0].textureunit.Count(); tu++)
                {
                    if (reader.model.skins[0].textureunit[tu].submeshIndex == i)
                    {
                        mesh.primitives[0].material = reader.model.texlookup[reader.model.skins[0].textureunit[tu].texture].textureID;

                        // todo
                        if (!materialBlends.ContainsKey(i))
                        {
                            // add texture
                            materialBlends.Add(i, reader.model.renderflags[reader.model.skins[0].textureunit[tu].renderFlags].blendingMode);
                        }
                        else
                        {
                            // already exists
                            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:
                    textureFileDataID = CASC.getFileDataIdByName(reader.model.textures[i].filename);
                    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
             */
        }