示例#1
0
        public override void Export(Asset asset, string path)
        {
            Model model = (asset as Model);

            int materialIndex = 1;

            ModelManipulator.PreProcess(model, PreProcessOptions.SplitMeshPart); //PreProcessOptions.Dedupe |  | PreProcessOptions.ResolveNonManifold

            foreach (ModelMesh mesh in model.Meshes)
            {
                MDL mdl = new MDL();
                //mdl.ModelFlags = MDL.Flags.USERData;

                materialIndex = 0;

                foreach (ModelMeshPart meshpart in mesh.MeshParts.OrderByDescending(m => m.VertexCount).ToList())
                {
                    MDLMaterialGroup mdlmesh = new MDLMaterialGroup(materialIndex, (meshpart.Material != null ? meshpart.Material.Name : "DEFAULT"));

                    int masterVertOffset = mdl.Vertices.Count;

                    foreach (Vertex v in meshpart.VertexBuffer.Data)
                    {
                        mdl.Vertices.Add(
                            new MDLVertex(
                                v.Position.X, v.Position.Y, v.Position.Z,
                                v.Normal.X, v.Normal.Y, v.Normal.Z,
                                v.UV.X, v.UV.Y,
                                v.UV.Z, v.UV.W,
                                (byte)(v.Colour.R * 255), (byte)(v.Colour.G * 255), (byte)(v.Colour.B * 255), (byte)(v.Colour.A * 255)
                                )
                            );
                    }

                    for (int i = 0; i < meshpart.IndexBuffer.Data.Count; i += 3)
                    {
                        mdl.Faces.Add(new MDLFace(materialIndex, 0, masterVertOffset + meshpart.IndexBuffer.Data[i + 0], masterVertOffset + meshpart.IndexBuffer.Data[i + 1], masterVertOffset + meshpart.IndexBuffer.Data[i + 2]));
                    }

                    Stripper stripper = new Stripper(meshpart.IndexBuffer.Data.Count / 3, meshpart.IndexBuffer.Data.ToArray())
                    {
                        OneSided         = true,
                        ConnectAllStrips = true
                    };

                    stripper.ShakeItBaby();

                    if (stripper.Strips[0].Count > 3)
                    {
                        List <int> strip = stripper.Strips[0];

                        if ((strip.Count & 1) == 1)
                        {
                            strip.Reverse();
                        }
                        else
                        {
                            strip.Insert(0, strip[0]);
                        }

                        HashSet <int> uniqueVerts = new HashSet <int> {
                            strip[0], strip[1]
                        };

                        mdlmesh.StripOffset = masterVertOffset;
                        mdlmesh.StripList.Add(new MDLPoint(strip[0], false));
                        mdlmesh.StripList.Add(new MDLPoint(strip[1], false));

                        for (int i = 2; i < strip.Count; i++)
                        {
                            uniqueVerts.Add(strip[i]);

                            MDLPoint point = new MDLPoint(strip[i], (strip.GetRange(i - 2, 3).Distinct().Count() != 3));

                            mdlmesh.StripList.Add(point);
                        }

                        mdlmesh.StripVertCount = uniqueVerts.Count;
                    }

                    int patchOffset = int.MaxValue;

                    for (int i = 1; i < stripper.Strips.Count; i++)
                    {
                        patchOffset = Math.Min(patchOffset, stripper.Strips[i].Min());
                    }

                    for (int i = 1; i < stripper.Strips.Count; i++)
                    {
                        List <int> patch = stripper.Strips[i];

                        mdlmesh.TriListOffset = masterVertOffset + patchOffset;

                        for (int j = 2; j >= 0; j--)
                        {
                            int index = patch[j] - patchOffset;

                            mdlmesh.TriList.Add(new MDLPoint(index));
                            mdlmesh.TriListVertCount = Math.Max(mdlmesh.TriListVertCount, index + 1);
                        }
                    }

                    mdlmesh.CalculateExtents(mdl.Vertices);
                    mdl.Meshes.Add(mdlmesh);
                    materialIndex++;

                    Console.WriteLine("Saved mesh");
                }

                mdl.Save(path + mesh.Name + ".mdl");
                Console.WriteLine("Saved MDL");
            }
        }
示例#2
0
文件: sMDL.cs 项目: q4a/ToxicRagers
        public void Save(string path)
        {
            int nameLength, padding;

            CalculateExtents();

            using (BinaryWriter bw = new BinaryWriter(new FileStream(path, FileMode.Create)))
            {
                bw.Write(new byte[] { 0x45, 0x23 });    // Magic Number
                bw.Write(new byte[] { 2, 6 });          // Version (6.2)

                bw.Write(new byte[] { 0, 0, 0, 0 });    // Checksum, to calculate
                bw.Write((int)ModelFlags);

                int prepDataSize = 4 + (Faces.Count * 16) + 4 + (Vertices.Count * 44) + 2;

                for (int i = 0; i < Meshes.Count; i++)
                {
                    MDLMaterialGroup mesh = Meshes[i];

                    prepDataSize += 52;
                    prepDataSize += (4 * mesh.StripList.Count);

                    prepDataSize += 12;
                    prepDataSize += (4 * mesh.TriList.Count);
                }

                bw.Write(prepDataSize);            // PREP data size

                bw.Write(Faces.Count);             // USER face count
                bw.Write(Vertices.Count);          // USER vert count

                bw.Write(0);                       // Back filled post save

                bw.Write(Extents.Radius);
                bw.Write(Extents.Min.X);
                bw.Write(Extents.Min.Y);
                bw.Write(Extents.Min.Z);
                bw.Write(Extents.Max.X);
                bw.Write(Extents.Max.Y);
                bw.Write(Extents.Max.Z);
                bw.Write(Extents.Centre.X);
                bw.Write(Extents.Centre.Y);
                bw.Write(Extents.Centre.Z);

                bw.Write((short)Meshes.Count);

                for (int i = 0; i < Meshes.Count; i++)
                {
                    nameLength = Meshes[i].Name.Length;
                    padding    = (((nameLength / 4) + (nameLength % 4 > 0 ? 1 : 0)) * 4) - nameLength + 4;

                    bw.Write(nameLength);
                    bw.WriteString(Meshes[i].Name);
                    bw.Write(new byte[padding]);
                }

                bw.Write(Faces.Count);

                for (int i = 0; i < Faces.Count; i++)
                {
                    bw.Write((short)Faces[i].MaterialID);
                    bw.Write((short)0);
                    bw.Write(Faces[i].Verts[0]);
                    bw.Write(Faces[i].Verts[1]);
                    bw.Write(Faces[i].Verts[2]);
                }

                bw.Write(Vertices.Count);

                for (int i = 0; i < Vertices.Count; i++)
                {
                    bw.Write(Vertices[i].Position.X);
                    bw.Write(Vertices[i].Position.Y);
                    bw.Write(Vertices[i].Position.Z);

                    bw.Write(Vertices[i].Normal.X);
                    bw.Write(Vertices[i].Normal.Y);
                    bw.Write(Vertices[i].Normal.Z);

                    bw.Write(Vertices[i].UV.X);
                    bw.Write(Vertices[i].UV.Y);

                    bw.Write(Vertices[i].UV2.X);
                    bw.Write(Vertices[i].UV2.Y);

                    bw.Write(Vertices[i].Colour.R);
                    bw.Write(Vertices[i].Colour.G);
                    bw.Write(Vertices[i].Colour.B);
                    bw.Write(Vertices[i].Colour.A);
                }

                bw.Write((short)Meshes.Count);

                for (int i = 0; i < Meshes.Count; i++)
                {
                    MDLMaterialGroup mesh = Meshes[i];

                    bw.Write(mesh.Extents.Centre.X);
                    bw.Write(mesh.Extents.Centre.Y);
                    bw.Write(mesh.Extents.Centre.Z);
                    bw.Write(mesh.Extents.Radius);
                    bw.Write(mesh.Extents.Min.X);
                    bw.Write(mesh.Extents.Min.Y);
                    bw.Write(mesh.Extents.Min.Z);
                    bw.Write(mesh.Extents.Max.X);
                    bw.Write(mesh.Extents.Max.Y);
                    bw.Write(mesh.Extents.Max.Z);

                    // TriangleStrips
                    bw.Write(mesh.StripOffset);
                    bw.Write(mesh.StripVertCount);
                    bw.Write(mesh.StripList.Count);

                    for (int j = 0; j < mesh.StripList.Count; j++)
                    {
                        bw.Write((uint)mesh.StripList[j].Index | (mesh.StripList[j].Degenerate ? 0x80000000 : 0x0));
                    }

                    bw.Write(mesh.TriListOffset);
                    bw.Write(mesh.TriListVertCount);
                    bw.Write(mesh.TriList.Count);

                    for (int j = 0; j < mesh.TriList.Count; j++)
                    {
                        bw.Write(mesh.TriList[j].Index);
                    }
                }

                if (ModelFlags.HasFlag(Flags.USERData))
                {
                    bw.Write(0);

                    for (int i = 0; i < Vertices.Count; i++)
                    {
                        bw.Write(Vertices[i].Position.X);
                        bw.Write(Vertices[i].Position.Y);
                        bw.Write(Vertices[i].Position.Z);
                        bw.Write(1);
                    }

                    for (int i = 0; i < Faces.Count; i++)
                    {
                        Vector3 v12 = Vertices[Faces[i].Verts[1]].Normal - Vertices[Faces[i].Verts[0]].Normal;
                        Vector3 v13 = Vertices[Faces[i].Verts[2]].Normal - Vertices[Faces[i].Verts[0]].Normal;
                        Vector3 n   = Vector3.Cross(v12, v13).Normalised;
                        float   d   = Vector3.Dot(n, Vertices[Faces[i].Verts[0]].Normal);

                        bw.Write(d);
                        bw.Write(n.X);
                        bw.Write(n.Y);
                        bw.Write(n.Z);
                        bw.Write(Vertices[Faces[i].Verts[0]].Normal.X);
                        bw.Write(Vertices[Faces[i].Verts[0]].Normal.Y);
                        bw.Write(Vertices[Faces[i].Verts[0]].Normal.Z);
                        bw.Write(Vertices[Faces[i].Verts[1]].Normal.X);
                        bw.Write(Vertices[Faces[i].Verts[1]].Normal.Y);
                        bw.Write(Vertices[Faces[i].Verts[1]].Normal.Z);
                        bw.Write(Vertices[Faces[i].Verts[2]].Normal.X);
                        bw.Write(Vertices[Faces[i].Verts[2]].Normal.Y);
                        bw.Write(Vertices[Faces[i].Verts[2]].Normal.Z);
                        bw.Write(Faces[i].MaterialID);
                        bw.Write(0);
                        bw.Write(Faces[i].Verts[0]);
                        bw.Write(Faces[i].Verts[1]);
                        bw.Write(Faces[i].Verts[2]);
                        bw.Write(Vertices[Faces[i].Verts[0]].Colour.R); bw.Write(Vertices[Faces[i].Verts[0]].Colour.G); bw.Write(Vertices[Faces[i].Verts[0]].Colour.B); bw.Write(Vertices[Faces[i].Verts[0]].Colour.A);
                        bw.Write(Vertices[Faces[i].Verts[1]].Colour.R); bw.Write(Vertices[Faces[i].Verts[1]].Colour.G); bw.Write(Vertices[Faces[i].Verts[1]].Colour.B); bw.Write(Vertices[Faces[i].Verts[1]].Colour.A);
                        bw.Write(Vertices[Faces[i].Verts[2]].Colour.R); bw.Write(Vertices[Faces[i].Verts[2]].Colour.G); bw.Write(Vertices[Faces[i].Verts[2]].Colour.B); bw.Write(Vertices[Faces[i].Verts[2]].Colour.A);
                        bw.Write(Vertices[Faces[i].Verts[0]].UV.X);
                        bw.Write(Vertices[Faces[i].Verts[0]].UV.Y);
                        bw.Write(Vertices[Faces[i].Verts[0]].UV2.X);
                        bw.Write(Vertices[Faces[i].Verts[0]].UV2.Y);
                        bw.Write(Vertices[Faces[i].Verts[1]].UV.X);
                        bw.Write(Vertices[Faces[i].Verts[1]].UV.Y);
                        bw.Write(Vertices[Faces[i].Verts[1]].UV2.X);
                        bw.Write(Vertices[Faces[i].Verts[1]].UV2.Y);
                        bw.Write(Vertices[Faces[i].Verts[2]].UV.X);
                        bw.Write(Vertices[Faces[i].Verts[2]].UV.Y);
                        bw.Write(Vertices[Faces[i].Verts[2]].UV2.X);
                        bw.Write(Vertices[Faces[i].Verts[2]].UV2.Y);
                        bw.Write((byte)0);
                        bw.Write(0);
                    }

                    for (int i = 0; i < Faces.Count; i++)
                    {
                        bw.Write(i);
                    }

                    bw.Write(Vertices.Count);

                    for (int i = 0; i < Vertices.Count; i++)
                    {
                        bw.Write(i);
                    }

                    if (ModelFlags.HasFlag(Flags.USERSkinData))
                    {
                    }
                }
            }

            using (BinaryWriter bw = new BinaryWriter(new FileStream(path, FileMode.Open)))
            {
                bw.Seek(24, SeekOrigin.Begin);
                bw.Write((int)(bw.BaseStream.Length - 28));
            }
        }
示例#3
0
        //public override string GetHints(string currentPath)
        //{
        //    string hints = string.Empty;

        //    while (Directory.Exists(Directory.GetParent(currentPath).FullName))
        //    {
        //        if (Directory.Exists(Path.Combine(Directory.GetParent(currentPath).FullName, "Models")))
        //        {

        //        }
        //    }

        //    if (currentPath != null && Directory.Exists(Directory.GetParent(currentPath).FullName))
        //    {
        //        hints = $"{currentPath};";

        //        if (Directory.Exists(Path.Combine(Directory.GetParent(currentPath).FullName, "PIXELMAP")))
        //        {
        //            hints += $"{Path.Combine(Directory.GetParent(currentPath).FullName, "PIXELMAP")};";
        //        }
        //    }

        //    return hints;
        //}

        public override Asset Import(string path)
        {
            MDL       mdl   = MDL.Load(path);
            Model     model = new Model();
            ModelMesh mesh  = new ModelMesh();

            // 2015-07-12 : Commenting out SupportingDocuments["Source"] to see if anything breaks
            // model.SupportingDocuments["Source"] = mdl;

            bool bUsePrepData = true;

            for (int i = 0; i < mdl.Meshes.Count; i++)
            {
                Dictionary <int, int> newIndex = new Dictionary <int, int>();
                ModelMeshPart         meshpart = new ModelMeshPart();

                MDLMaterialGroup mdlmesh = mdl.GetMesh(i);

                meshpart.Material = SceneManager.Current.Content.Load <Material, MaterialImporter>(mdlmesh.Name, Path.GetDirectoryName(path), true);

                if (bUsePrepData)
                {
                    foreach (MDLFace f in mdl.Faces.Where(f => f.MaterialID == i))
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            if (!newIndex.ContainsKey(f.Verts[j]))
                            {
                                MDLVertex v     = mdl.Vertices[f.Verts[j]];
                                int       index = meshpart.AddVertex(new Vector3(v.Position.X, v.Position.Y, v.Position.Z), new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z), new Vector2(v.UV.X, v.UV.Y), new Vector2(v.UV2.X, v.UV2.Y), v.Colour, false);
                                newIndex.Add(f.Verts[j], index);
                            }
                        }

                        meshpart.AddFace(
                            newIndex[f.Verts[0]],
                            newIndex[f.Verts[1]],
                            newIndex[f.Verts[2]]
                            );
                    }
                }
                else
                {
                    int[] verts = new int[3];

                    for (int j = 0; j < mdlmesh.StripList.Count - 2; j++)
                    {
                        if (mdlmesh.StripList[j + 2].Degenerate)
                        {
                            continue;
                        }

                        verts[0] = mdlmesh.StripList[j + 0].Index;

                        if (j % 2 == 0)
                        {
                            verts[1] = mdlmesh.StripList[j + 1].Index;
                            verts[2] = mdlmesh.StripList[j + 2].Index;
                        }
                        else
                        {
                            verts[1] = mdlmesh.StripList[j + 2].Index;
                            verts[2] = mdlmesh.StripList[j + 1].Index;
                        }

                        for (int k = 0; k < 3; k++)
                        {
                            if (!newIndex.ContainsKey(verts[k]))
                            {
                                MDLVertex v     = mdl.Vertices[verts[k]];
                                int       index = meshpart.AddVertex(new Vector3(v.Position.X, v.Position.Y, v.Position.Z), new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z), new Vector2(v.UV.X, v.UV.Y), new Vector2(v.UV2.X, v.UV2.Y), v.Colour, false);
                                newIndex.Add(verts[k], index);
                            }
                        }

                        meshpart.AddFace(
                            newIndex[verts[0]],
                            newIndex[verts[1]],
                            newIndex[verts[2]]
                            );
                    }

                    // Process patch list
                    for (int j = 0; j < mdlmesh.TriList.Count; j += 3)
                    {
                        verts[0] = mdlmesh.TriList[j + 0].Index;
                        verts[1] = mdlmesh.TriList[j + 1].Index;
                        verts[2] = mdlmesh.TriList[j + 2].Index;

                        for (int k = 0; k < 3; k++)
                        {
                            if (!newIndex.ContainsKey(verts[k]))
                            {
                                MDLVertex v     = mdl.Vertices[verts[k]];
                                int       index = meshpart.AddVertex(new Vector3(v.Position.X, v.Position.Y, v.Position.Z), new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z), new Vector2(v.UV.X, v.UV.Y), new Vector2(v.UV2.X, v.UV2.Y), v.Colour, false);
                                newIndex.Add(verts[k], index);
                            }
                        }

                        meshpart.AddFace(
                            newIndex[verts[0]],
                            newIndex[verts[1]],
                            newIndex[verts[2]]
                            );
                    }
                }

                mesh.AddModelMeshPart(meshpart);

                Console.WriteLine(meshpart.VertexCount / 3);
            }

            mesh.Name = mdl.Name;
            model.SetName(mdl.Name, model.AddMesh(mesh));

            return(model);
        }
示例#4
0
文件: sMDL.cs 项目: q4a/ToxicRagers
        public static MDL Load(Stream stream, string name = null)
        {
            MDL mdl = new MDL {
                Name = name
            };

            using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
            {
                if (br.ReadByte() != 0x45 ||
                    br.ReadByte() != 0x23)
                {
                    Logger.LogToFile(Logger.LogLevel.Error, "This isn't a valid MDL file");
                    return(null);
                }

                byte minor = br.ReadByte();
                byte major = br.ReadByte();

                mdl.Version = new Version(major, minor);

                if (!SupportedVersions.Contains(mdl.Version))
                {
                    Logger.LogToFile(Logger.LogLevel.Error, "Unsupported MDL version: v{0}", mdl.Version.ToString());
                    return(null);
                }

                Logger.LogToFile(Logger.LogLevel.Info, "MDL v{0}", mdl.Version.ToString());

                // TODO: v5.6
                // Ref : Novadrome_Demo\WADs\data\DATA\SFX\CAR_EXPLOSION\DEBPOOL\DEBPOOL.MDL
                // Ref : Carmageddon Mobile\Data_IOS\DATA\CONTENT\SFX\SHRAPNEL.MDL
                // 01 00 00 00 EE 02 00 00 02 00 00 00 04 00 00 00 01 00 00 00 49 33 35 3F 89 41 00 BF 18 B7 51 BA 89 41 00 BF 00 00 00 3F 52 49 9D 3A 00 00 00 3F 00 12 03 BA 18 B7 51 39 00 12 03 BA 01 00 66 69 72 65 70 6F 6F 6C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 17 B7 51 39 17 B7 D1 38 00 00 80 3F 17 B7 D1 B8 00 00 00 00 03 00 00 00 02 00 00 00 01 00 00 00 17 B7 51 39 17 B7 D1 B8 00 00 80 3F 17 B7 D1 38 04 00 00 00 00 00 00 3F 17 B7 D1 38 00 00 00 BF 17 B7 D1 38 00 00 80 3F 17 B7 D1 B8 00 00 80 3F 00 00 80 3F 00 00 00 00 00 00 00 00 80 80 80 FF 00 00 00 BF 17 B7 51 39 00 00 00 BF 00 00 00 00 00 00 80 3F 00 00 00 00 00 00 00 00 00 00 80 3F 00 00 00 00 00 00 00 00 80 80 80 FF 00 00 00 3F 17 B7 51 39 00 00 00 3F 00 00 00 00 00 00 80 3F 00 00 00 00 00 00 80 3F 00 00 00 00 00 00 00 00 00 00 00 00 80 80 80 FF 00 00 00 BF 17 B7 D1 38 00 00 00 3F 17 B7 D1 B8 00 00 80 3F 17 B7 D1 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 80 80 FF 01 00 00 00 00 00 17 B7 D1 38 00 00 00 00 00 00 00 00 04 00 00 00 04 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF

                mdl.Checksum = (int)br.ReadUInt32();

                mdl.ModelFlags = (Flags)br.ReadUInt32();
                Logger.LogToFile(Logger.LogLevel.Debug, "Flags {0}", (Flags)mdl.ModelFlags);

                mdl.PrepDataSize = (int)br.ReadUInt32();    // PREP data size

                mdl.USERFaceCount   = (int)br.ReadUInt32();
                mdl.USERVertexCount = (int)br.ReadUInt32();

                Logger.LogToFile(Logger.LogLevel.Debug, "USER Faces: {0}", mdl.USERFaceCount);
                Logger.LogToFile(Logger.LogLevel.Debug, "USER Verts: {0}", mdl.USERVertexCount);

                mdl.FileSize = (int)br.ReadUInt32();

                mdl.Extents.Radius = br.ReadSingle();
                mdl.Extents.Min    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                mdl.Extents.Max    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                br.ReadBytes(12);   //  BoundingBox centre, Flummery auto calculates from min and max

                int materialCount = br.ReadInt16();

                Logger.LogToFile(Logger.LogLevel.Debug, "Material count: {0}", materialCount);

                for (int i = 0; i < materialCount; i++)
                {
                    string materialName;
                    int    nameLength;
                    int    padding;

                    if (mdl.Version.Major < 6)
                    {
                        materialName = br.ReadBytes(32).ToName();
                    }
                    else
                    {
                        nameLength   = br.ReadInt32();
                        padding      = (((nameLength / 4) + (nameLength % 4 > 0 ? 1 : 0)) * 4) - nameLength + (mdl.Version.Major == 6 && mdl.Version.Minor > 0 ? 4 : 0);
                        materialName = br.ReadString(nameLength);
                        br.ReadBytes(padding);
                    }

                    mdl.Meshes.Add(new MDLMaterialGroup(i, materialName));
                }

                // START PREP DATA
                mdl.PREPFaceCount = (int)br.ReadUInt32();

                Logger.LogToFile(Logger.LogLevel.Debug, "PREP Faces: {0}", mdl.PREPFaceCount);

                for (int i = 0; i < mdl.PREPFaceCount; i++)
                {
                    MDLFace face = new MDLFace(
                        br.ReadUInt16(),        // Material index
                        br.ReadUInt16(),        // Material Flags
                        (int)br.ReadUInt32(),   // Vert index A
                        (int)br.ReadUInt32(),   // Vert index B
                        (int)br.ReadUInt32()    // Vert index C
                        );

                    mdl.Faces.Add(face);

                    Logger.LogToFile(Logger.LogLevel.Debug, "{0} : {1}", i, face);
                }

                mdl.PREPVertexCount = (int)br.ReadUInt32();

                Logger.LogToFile(Logger.LogLevel.Debug, "PREP Verts: {0}", mdl.PREPVertexCount);

                for (int i = 0; i < mdl.PREPVertexCount; i++)
                {
                    MDLVertex vert = new MDLVertex(
                        br.ReadSingle(),        // X
                        br.ReadSingle(),        // Y
                        br.ReadSingle(),        // Z
                        br.ReadSingle(),        // N.X
                        br.ReadSingle(),        // N.Y
                        br.ReadSingle(),        // N.Z
                        br.ReadSingle(),        // U
                        br.ReadSingle(),        // V
                        br.ReadSingle(),        // U2
                        br.ReadSingle(),        // V2
                        br.ReadByte(),          // R
                        br.ReadByte(),          // G
                        br.ReadByte(),          // B
                        br.ReadByte()           // A
                        );

                    mdl.Vertices.Add(vert);

                    Logger.LogToFile(Logger.LogLevel.Debug, "{0} : {1}", i, vert);
                }

                int materialGroups = br.ReadUInt16();

                for (int i = 0; i < materialGroups; i++)
                {
                    MDLMaterialGroup mesh = mdl.Meshes[i];

                    br.ReadBytes(12);   // BoundingBox Centre, we recalculate it from Min and Max
                    mesh.Extents.Radius = br.ReadSingle();
                    mesh.Extents.Min    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                    mesh.Extents.Max    = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());

                    mesh.StripOffset    = (int)br.ReadUInt32();
                    mesh.StripVertCount = (int)br.ReadUInt32();
                    int stripPointCount = (int)br.ReadUInt32();

                    Logger.LogToFile(Logger.LogLevel.Info, "{0} : {1} : {2}", mesh.StripOffset, mesh.StripVertCount, stripPointCount);

                    for (int j = 0; j < stripPointCount; j++)
                    {
                        uint index       = br.ReadUInt32();
                        bool bDegenerate = ((index & 0x80000000) != 0);
                        index &= ~0x80000000;

                        mesh.StripList.Add(new MDLPoint((int)index + mesh.StripOffset, bDegenerate));

                        Logger.LogToFile(Logger.LogLevel.Debug, "{0} ] {1} : {2}", j, index, bDegenerate);
                    }

                    mesh.TriListOffset    = (int)br.ReadUInt32();
                    mesh.TriListVertCount = (int)br.ReadUInt32();
                    int listPointCount = (int)br.ReadUInt32();

                    Logger.LogToFile(Logger.LogLevel.Info, "{0} : {1} : {2}", mesh.TriListOffset, mesh.TriListVertCount, listPointCount);

                    for (int j = 0; j < listPointCount; j++)
                    {
                        uint index = br.ReadUInt32();

                        mesh.TriList.Add(new MDLPoint((int)index + mesh.TriListOffset));

                        Logger.LogToFile(Logger.LogLevel.Debug, "{0} ] {1}", j, index);
                    }
                }

                if (mdl.ModelFlags.HasFlag(Flags.PREPSkinData))
                {
                    Logger.LogToFile(Logger.LogLevel.Debug, "Processing PREP skin data");

                    int      bodyPartCount     = br.ReadUInt16();
                    int      maxBonesPerVertex = br.ReadUInt16();
                    int      rootBoneIndex     = br.ReadUInt16();
                    string[] boneNames         = br.ReadStrings(bodyPartCount);

                    Logger.LogToFile(Logger.LogLevel.Debug, "Body Part Count: {0}. Max Bones per Vertex : {1}. Root Bone Index : {2}", bodyPartCount, maxBonesPerVertex, rootBoneIndex);

                    for (int i = 0; i < bodyPartCount; i++)
                    {
                        MDLBone bone = new MDLBone()
                        {
                            Name       = boneNames[i],
                            MinExtents = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),   // Min and Max bone local space
                            MaxExtents = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
                            Offset     = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),   // Offset is in parents local space

                            Parent  = br.ReadByte(),
                            Child   = br.ReadByte(),
                            Sibling = br.ReadByte()
                        };

                        mdl.prepBoneList.Add(bone);
                    }

                    for (int i = 0; i < bodyPartCount; i++)
                    {
                        mdl.prepBoneList[i].Rotation = new Vector4(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                        mdl.prepBoneList[i].Position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                        br.ReadBytes(4);

                        Logger.LogToFile(Logger.LogLevel.Debug, "{0}) {1}", i, mdl.prepBoneList[i].Name);
                        Logger.LogToFile(Logger.LogLevel.Debug, "P{0} C{1} S{2}", mdl.prepBoneList[i].Parent, mdl.prepBoneList[i].Child, mdl.prepBoneList[i].Sibling);
                        Logger.LogToFile(Logger.LogLevel.Debug, "{0}", mdl.prepBoneList[i].MinExtents);
                        Logger.LogToFile(Logger.LogLevel.Debug, "{0}", mdl.prepBoneList[i].MaxExtents);
                        Logger.LogToFile(Logger.LogLevel.Debug, "{0}", mdl.prepBoneList[i].Offset);
                        Logger.LogToFile(Logger.LogLevel.Debug, "{0}", mdl.prepBoneList[i].Rotation);
                        Logger.LogToFile(Logger.LogLevel.Debug, "{0}", mdl.prepBoneList[i].Position);
                        Logger.LogToFile(Logger.LogLevel.Debug, "");
                    }

                    Logger.LogToFile(Logger.LogLevel.Debug, "PREP skin vert weight table");

                    for (int i = 0; i < mdl.PREPVertexCount; i++)
                    {
                        int weightCount = br.ReadUInt16();
                        br.ReadBytes(2);
                        int weightOffset = (int)br.ReadUInt32();

                        mdl.prepVertSkinWeightLookup.Add(new MDLPrepSkinWeightLookup {
                            Count = weightCount, Index = weightOffset
                        });

                        Logger.LogToFile(Logger.LogLevel.Debug, "{0,5}]  {1} @ {2}", i, weightCount, weightOffset);
                    }

                    int prepSkinWeightCount = (int)br.ReadUInt32();

                    Logger.LogToFile(Logger.LogLevel.Debug, "PREP Skin Weight Count: {0}", prepSkinWeightCount);

                    for (int i = 0; i < prepSkinWeightCount; i++)
                    {
                        int boneIndex = br.ReadUInt16();
                        Logger.LogToFile(Logger.LogLevel.Debug, "{0,5}] {1}", i, boneIndex);
                    }

                    for (int i = 0; i < prepSkinWeightCount; i++)
                    {
                        float weight = br.ReadSingle();
                        Logger.LogToFile(Logger.LogLevel.Debug, "{0,5}] {1}", i, weight);
                    }
                }
                // END PREP DATA

                if (mdl.ModelFlags.HasFlag(Flags.LODData))
                {
                    ushort lodLevel;

                    do
                    {
                        lodLevel = br.ReadUInt16();
                        int nameLength = br.ReadInt32();

                        if (nameLength > 0)
                        {
                            int    padding      = (((nameLength / 4) + (nameLength % 4 > 0 ? 1 : 0)) * 4) - nameLength + (mdl.Version.Major == 6 && mdl.Version.Minor > 0 ? 4 : 0);
                            string lodLevelName = br.ReadString(nameLength);
                            br.ReadBytes(padding);

                            mdl.LODs.Add(Load(br.BaseStream, lodLevelName));
                        }
                    } while (lodLevel < 4);
                }

                // START USER DATA
                if (mdl.ModelFlags.HasFlag(Flags.USERData))
                {
                    mdl.UserFlags = (Flags)br.ReadUInt32();

                    // v5.6 successfully parses from this point down

                    Logger.LogToFile(Logger.LogLevel.Debug, "USER vertex list with index count");
                    for (int i = 0; i < mdl.USERVertexCount; i++)
                    {
                        mdl.userVertexList.Add(new MDLUserVertexEntry(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), (int)br.ReadUInt32()));
                    }

                    Logger.LogToFile(Logger.LogLevel.Debug, "USER face data");
                    for (int i = 0; i < mdl.USERFaceCount; i++)
                    {
                        if (mdl.Version.Major == 5 && mdl.Version.Minor == 6)
                        {
                            br.ReadBytes(133);
                        }
                        else
                        {
                            mdl.userFaceList.Add(
                                new MDLUserFaceEntry(
                                    br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), // plane equation
                                    new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),     // vertex[0] normal
                                    new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),     // vertex[1] normal
                                    new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),     // vertex[2] normal
                                    (int)br.ReadUInt32(),                                               // material index
                                    (int)br.ReadUInt32(),                                               // smoothing group
                                    (int)br.ReadUInt32(),                                               // vertex[0]
                                    (int)br.ReadUInt32(),                                               // vertex[1]
                                    (int)br.ReadUInt32(),                                               // vertex[2]
                                    br.ReadByte(), br.ReadByte(), br.ReadByte(), br.ReadByte(),         // colour[0] RGBA
                                    br.ReadByte(), br.ReadByte(), br.ReadByte(), br.ReadByte(),         // colour[1] RGBA
                                    br.ReadByte(), br.ReadByte(), br.ReadByte(), br.ReadByte(),         // colour[2] RGBA
                                    new Vector2(br.ReadSingle(), br.ReadSingle()),                      // uv[0]
                                    new Vector2(br.ReadSingle(), br.ReadSingle()),                      // uv2[0]
                                    new Vector2(br.ReadSingle(), br.ReadSingle()),                      // uv[1]
                                    new Vector2(br.ReadSingle(), br.ReadSingle()),                      // uv2[1]
                                    new Vector2(br.ReadSingle(), br.ReadSingle()),                      // uv[2]
                                    new Vector2(br.ReadSingle(), br.ReadSingle()),                      // uv2[2]
                                    br.ReadByte(),                                                      // flags
                                    (int)br.ReadUInt32()                                                // application specific flags
                                    )                                                                   // 137 bytes || 0x89
                                );
                        }
                    }

                    Logger.LogToFile(Logger.LogLevel.Debug, "PREP to USER face lookup");
                    for (int i = 0; i < mdl.PREPFaceCount; i++)
                    {
                        mdl.ptouFaceLookup.Add((int)br.ReadUInt32());
                    }

                    int prepVertexMapCount = (int)br.ReadUInt32();
                    Logger.LogToFile(Logger.LogLevel.Debug, "PREP to USER vertex lookup");

                    for (int i = 0; i < prepVertexMapCount; i++)
                    {
                        mdl.ptouVertexLookup.Add((int)br.ReadUInt32());
                    }

                    if (mdl.UserFlags.HasFlag(Flags.USERSkinData))
                    {
                        Logger.LogToFile(Logger.LogLevel.Debug, "Processing USER skin data");

                        int boneCount = br.ReadUInt16();

                        Logger.LogToFile(Logger.LogLevel.Debug, "Bone count: {0}", boneCount);
                        for (int i = 0; i < boneCount; i++)
                        {
                            string   boneName        = br.ReadString(32);
                            short    parentBoneIndex = br.ReadInt16();
                            Matrix3D boneTransform   = new Matrix3D(
                                br.ReadSingle(), br.ReadSingle(), br.ReadSingle(),
                                br.ReadSingle(), br.ReadSingle(), br.ReadSingle(),
                                br.ReadSingle(), br.ReadSingle(), br.ReadSingle(),
                                br.ReadSingle(), br.ReadSingle(), br.ReadSingle()
                                );

                            Logger.LogToFile(Logger.LogLevel.Debug, "{0}) {1}", i, boneName);
                            Logger.LogToFile(Logger.LogLevel.Debug, "{0}", parentBoneIndex);
                            Logger.LogToFile(Logger.LogLevel.Debug, "{0}", boneTransform);
                            Logger.LogToFile(Logger.LogLevel.Debug, "");
                        }

                        int userDataCount = (int)br.ReadUInt32();
                        Logger.LogToFile(Logger.LogLevel.Debug, "{0} == {1}", userDataCount, mdl.USERVertexCount);

                        for (int i = 0; i < mdl.USERVertexCount; i++)
                        {
                            int entryCount = br.ReadUInt16();

                            for (int j = 0; j < entryCount; j++)
                            {
                                int     boneIndex      = br.ReadUInt16();
                                float   weight         = br.ReadSingle();
                                Vector3 vertexPosition = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                                Logger.LogToFile(Logger.LogLevel.Debug, "{0}.{1}]  {2,2} : {3,6:0.00}% : {4}", i, j, boneIndex, (weight * 100.0f), vertexPosition);
                            }
                        }
                    }
                }

                if (br.BaseStream.Position != br.BaseStream.Length)
                {
                    Logger.LogToFile(Logger.LogLevel.Warning, "Still has data remaining (processed {0:x2} of {1:x2}", br.BaseStream.Position, br.BaseStream.Length);
                }
            }

            return(mdl);
        }