public static PolygonSoupChunk Read(BinaryReader br)
        {
            PolygonSoupChunk result = new PolygonSoupChunk();

            result.Position = br.ReadVector3I();
            result.Scale    = br.ReadSingle();
            uint propertyListStart = br.ReadUInt32();
            uint pointListStart    = br.ReadUInt32();

            br.ReadInt16(); // Length
            byte propertyListCount = br.ReadByte();

            result.QuadCount = br.ReadByte();
            int pointCount = br.ReadByte();

            result.Unknown10 = br.ReadByte();
            result.Unknown11 = br.ReadInt16();

            br.BaseStream.Position = pointListStart;
            for (int i = 0; i < pointCount; i++)
            {
                result.PointList.Add(br.ReadVector3S());
            }

            br.BaseStream.Position = propertyListStart;

            for (int i = 0; i < propertyListCount; i++)
            {
                result.PropertyList.Add(PolygonSoupProperty.Read(br));
            }

            return(result);
        }
        public Mesh BuildMesh(Vector3I pos, float scale)
        {
            Mesh mesh = new Mesh();

            mesh.Faces = new List <MeshFace>();

            for (int i = 0; i < PropertyList.Count; i++)
            {
                MeshFace            face     = new MeshFace();
                PolygonSoupProperty property = PropertyList[i];
                face.Indices.Add(property.Indices[0]);
                face.Indices.Add(property.Indices[1]);
                face.Indices.Add(property.Indices[2]);

                ushort unknownProperty1 = (ushort)(property.UnknownProperty & 0xFFFF);
                ushort unknownProperty2 = (ushort)((property.UnknownProperty >> 16));  // & 0xFFFF);

                string unknownBytes = property.UnknownBytes[0].ToString("X2") + "_" +
                                      property.UnknownBytes[1].ToString("X2") + "_" +
                                      property.UnknownBytes[2].ToString("X2") + "_" +
                                      property.UnknownBytes[3].ToString("X2");
                face.Material = new Material(
                    unknownProperty1.ToString("X4") + "_" + unknownProperty2.ToString("X4") + "_" + unknownBytes,
                    Color.White);

                mesh.Faces.Add(face);

                if (property.Indices[3] != 0xFF)
                {
                    MeshFace face2 = new MeshFace();
                    face2.Material = face.Material;
                    face2.Indices.Add(property.Indices[3]);
                    face2.Indices.Add(property.Indices[2]);
                    face2.Indices.Add(property.Indices[1]);
                    mesh.Faces.Add(face2);
                }
            }


            List <Vector3S> points = PointList;

            for (int i = 0; i < points.Count; i++)
            {
                Vector3S vert = points[i];
                mesh.Vertices.Add(new Vector3((vert.X + pos.X) * scale, (vert.Y + pos.Y) * scale, (vert.Z + pos.Z) * scale));
            }

            return(mesh);
        }
        public PolygonSoupProperty Copy()
        {
            PolygonSoupProperty result = new PolygonSoupProperty();

            result.UnknownProperty = UnknownProperty;
            for (int i = 0; i < Indices.Length; i++)
            {
                result.Indices[i] = Indices[i];
            }
            for (int i = 0; i < UnknownBytes.Length; i++)
            {
                result.UnknownBytes[i] = UnknownBytes[i];
            }

            return(result);
        }
        public static PolygonSoupProperty Read(BinaryReader br)
        {
            PolygonSoupProperty result = new PolygonSoupProperty();

            result.UnknownProperty = br.ReadUInt32();

            for (int i = 0; i < result.Indices.Length; i++)
            {
                result.Indices[i] = br.ReadByte();
            }

            for (int i = 0; i < result.UnknownBytes.Length; i++)
            {
                result.UnknownBytes[i] = br.ReadByte();
            }

            return(result);
        }
        public void ImportObj(string path)
        {
            GenericModel model = OBJImporter.ImportOBJ(path);

            model.SplitByPointCount(255);

            // Verify data before applying
            foreach (GenericMesh mesh in model.Meshes)
            {
                // Get highest point
                uint pointCount = 0;
                foreach (uint key in mesh.Vertices.Keys)
                {
                    if (pointCount < key)
                    {
                        pointCount = key;
                    }
                }
                pointCount++;
                // Is it too big for a byte?
                if (pointCount > 256)
                {
                    throw new ReadFailedError("Too many points for mesh: " + mesh.Name + ", " + pointCount + " > 256");
                }
                foreach (Face face in mesh.Faces)
                {
                    // Triangulation required for now.
                    if (face.Indices.Count > 3)
                    {
                        throw new ReadFailedError("Please triangulate your mesh: " + mesh.Name);
                    }

                    // Material names are required
                    if (string.IsNullOrEmpty(face.Material?.Name))
                    {
                        throw new ReadFailedError("Invalid Material for mesh: " + mesh.Name);
                    }

                    // Verify that all data is there
                    string[] split = face.Material.Name.Split('_');
                    if (split.Length < 7)
                    {
                        throw new ReadFailedError("Invalid Material Data: " + face.Material.Name + ", for mesh: " + mesh.Name);
                    }

                    // Verify that all data can be parsed
                    try
                    {
                        Utilities.Parse(split[1], true, out ushort _);
                        Utilities.Parse(split[2], true, out ushort _);
                        Utilities.Parse(split[3], true, out byte _);
                        Utilities.Parse(split[4], true, out byte _);
                        Utilities.Parse(split[5], true, out byte _);
                        Utilities.Parse(split[6], true, out byte _);
                    }
                    catch (NotSupportedException)
                    {
                        throw new ReadFailedError("Unable to Parse Material Data: " + face.Material.Name + ", for mesh: " + mesh.Name);
                    }
                }
            }

            // Clear existing data
            Chunks.Clear();
            BoundingBoxes.Clear();

            // Global vertices list to calculate the bounding box
            List <Vector3> vertices = new List <Vector3>();

            // Generate PolygonSoupChunks and BoundingBoxes
            foreach (GenericMesh mesh in model.Meshes)
            {
                PolygonSoupChunk chunk = new PolygonSoupChunk();
                foreach (Face face in mesh.Faces)
                {
                    PolygonSoupProperty property = new PolygonSoupProperty();

                    // Set Indices
                    property.Indices[0] = (byte)face.Indices[0];
                    property.Indices[1] = (byte)face.Indices[1];
                    property.Indices[2] = (byte)face.Indices[2];

                    if (face.Indices.Count > 3)
                    {
                        property.Indices[3] = (byte)face.Indices[3];
                    }
                    else
                    {
                        property.Indices[3] = 0xFF;
                    }

                    // Get data from material name
                    string   materialName = face.Material.Name;
                    string[] split        = materialName.Split('_');

                    // Parse the values
                    Utilities.Parse(split[1], true, out ushort unknownProperty1);
                    Utilities.Parse(split[2], true, out ushort unknownProperty2);
                    Utilities.Parse(split[3], true, out byte unknownByte1);
                    Utilities.Parse(split[4], true, out byte unknownByte2);
                    Utilities.Parse(split[5], true, out byte unknownByte3);
                    Utilities.Parse(split[6], true, out byte unknownByte4);

                    // Combine unknownProperty1 and unknownProperty2
                    property.UnknownProperty = (uint)((unknownProperty2 << 16) | unknownProperty1);

                    // Set unknown bytes
                    property.UnknownBytes[0] = unknownByte1;
                    property.UnknownBytes[1] = unknownByte2;
                    property.UnknownBytes[2] = unknownByte3;
                    property.UnknownBytes[3] = unknownByte4;

                    // Add the property
                    chunk.PropertyList.Add(property);
                }

                // Add the vertices to the global vertices list
                vertices.AddRange(mesh.Vertices.Values.ToArray());

                // Get the minimum and maximum point of the mesh
                Vector3 min = MathUtils.MinBounds(mesh.Vertices.Values.ToArray());
                Vector3 max = MathUtils.MaxBounds(mesh.Vertices.Values.ToArray());

                // Use the minimum as the position
                Vector3 position = min;

                // Set the scale to something standard
                float scale = 0.015f;

                // Get the point count
                uint pointCount = 0;
                foreach (uint key in mesh.Vertices.Keys)
                {
                    if (pointCount < key)
                    {
                        pointCount = key;
                    }
                }
                pointCount++;
                Vector3S[] verts = new Vector3S[pointCount];
                foreach (uint key in mesh.Vertices.Keys)
                {
                    // Convert the point to a short and apply position and scale
                    verts[key] = new Vector3S((mesh.Vertices[key] - position) / scale);
                }
                chunk.PointList = verts.ToList();

                // Quads are currently unsupported
                chunk.QuadCount = 0;

                chunk.Scale = scale;

                // Set the position and apply scale
                chunk.Position = new Vector3I(position / scale);

                // Add the chunk
                Chunks.Add(chunk);

                // Add the bounding box
                BoundingBoxes.Add(new PolygonSoupBoundingBox(new BoxF(min, max), -1));
            }

            // Calculate Bounding Box
            Min = MathUtils.MinBounds(vertices.ToArray());
            Max = MathUtils.MaxBounds(vertices.ToArray());
        }