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 bool Read(BundleEntry entry, ILoader loader = null)
        {
            Clear();

            MemoryStream  ms = entry.MakeStream();
            BinaryReader2 br = new BinaryReader2(ms);

            br.BigEndian = entry.Console;

            Min      = br.ReadVector3F();
            Unknown4 = br.ReadInt32();
            Max      = br.ReadVector3F();
            Unknown8 = br.ReadInt32();
            uint chunkPointerStart = br.ReadUInt32();
            uint boxListStart      = br.ReadUInt32();
            int  chunkCount        = br.ReadInt32();

            br.ReadUInt32();             // FileSize

            List <uint> chunkPointers = new List <uint>();

            // No Data
            if (chunkCount == 0)
            {
                br.Close();
                ms.Close();
                return(true);
            }

            br.BaseStream.Position = chunkPointerStart;

            for (int i = 0; i < chunkCount; i++)
            {
                chunkPointers.Add(br.ReadUInt32());
            }

            for (int i = 0; i < chunkCount; i++)
            {
                // Read Vertically

                long pos = boxListStart + 0x70 * (i / 4) + 4 * (i % 4);

                BoxF boundingBox = new BoxF();
                br.BaseStream.Position = pos;
                float minX = br.ReadSingle();
                br.BaseStream.Position += 12;
                float minY = br.ReadSingle();
                br.BaseStream.Position += 12;
                float minZ = br.ReadSingle();

                boundingBox.Min = new Vector3(minX, minY, minZ);

                br.BaseStream.Position += 12;
                float maxX = br.ReadSingle();
                br.BaseStream.Position += 12;
                float maxY = br.ReadSingle();
                br.BaseStream.Position += 12;
                float maxZ = br.ReadSingle();
                br.BaseStream.Position += 12;

                boundingBox.Max = new Vector3(maxX, maxY, maxZ);

                PolygonSoupBoundingBox box = new PolygonSoupBoundingBox(boundingBox, br.ReadInt32());

                BoundingBoxes.Add(box);
            }

            for (int i = 0; i < chunkPointers.Count; i++)
            {
                br.BaseStream.Position = chunkPointers[i];

                Chunks.Add(PolygonSoupChunk.Read(br));
            }

            br.Close();
            ms.Close();

            return(true);
        }
        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());
        }