private static ZoneInfo LoadZone(BinaryReader reader)
        {
            var id = reader.ReadUInt32();

            if (_zones[id] == null)
            {
                _zones[id] = new ZoneInfo();
            }

            var zone = _zones[id];

            zone.ID      = id;
            zone.OffsetX = reader.ReadUInt32();
            zone.OffsetY = reader.ReadUInt32();

            // These are not really needed right now, and are just hints anyway
            UInt32 numNifs     = reader.ReadUInt32();
            UInt32 numFixtures = reader.ReadUInt32();

            return(zone);
        }
        public static OcclusionResult OcclusionQuery(
            ushort zoneId0, float x0, float y0, float z0,
            ushort zoneId1, float x1, float y1, float z1, ref Vector3 hitpoint)
        {
            OcclusionResult result;
            SegmentTestInfo info = new SegmentTestInfo();

            long timeBegin = DebugEnable ? Timer.Value : 0;

            try
            {
                ZoneInfo zone = _zones[zoneId0];

                if (zone == null || zone.Collision == null)
                {
                    result = OcclusionResult.NotLoaded;
                }
                else if (!zone.Enabled)
                {
                    return(OcclusionResult.NotEnabled);
                }
                else if (zoneId0 != zoneId1)
                {
                    result = OcclusionResult.NotImplemented;
                }
                else
                {
                    x0 = 65535.0f - x0;
                    x1 = 65535.0f - x1;

                    Vector3 p0 = new Vector3(x0, y0, z0), p1 = new Vector3(x1, y1, z1);


                    if (SegmentTest(p0, p1, zone.Collision, zone.Collision.BSP, 0, ref info))
                    {
                        bool   hit      = true;
                        double nearest  = GetDistance(p0, info.HitPoint);
                        var    hitPoint = new Vector3(info.HitPoint.X, info.HitPoint.Y, info.HitPoint.Z);
                        hitpoint = info.HitPoint;

                        int count = 0;

                        //find the nearest to origin triangle hit
                        while (hit)
                        {
                            var nextPoint = Lerp(info.HitPoint, p0, 0.01);
                            hit = SegmentTest(p0, nextPoint, zone.Collision, zone.Collision.BSP, 0, ref info);


                            if (hit && (info.HitPoint.X != hitPoint.X || info.HitPoint.Y != hitPoint.Y || info.HitPoint.Z != hitPoint.Z) &&
                                GetDistance(p0, info.HitPoint) < nearest)
                            {
                                hitpoint = info.HitPoint;
                            }

                            hitPoint = new Vector3(info.HitPoint.X, info.HitPoint.Y, info.HitPoint.Z);

                            if (count > 100)
                            {
                                break;
                            }

                            count++;
                        }
                        result     = OcclusionResult.Occluded;
                        hitpoint.X = 65535.0f - hitpoint.X;
                    }
                    else
                    {
                        result = OcclusionResult.NotOccluded;
                    }
                }
            }
            catch
            {
                result = OcclusionResult.InternalError;
            }

            if (DebugEnable)
            {
                long  microseconds = (Timer.Value - timeBegin) * 1000000 / Timer.Frequency;
                float timePerTri   = microseconds / (float)info.NumTrianglesTested;

                String msg = String.Format(
                    "LOS Check: ({0}, {1}, {2}, {3}) -> ({4}, {5}, {6}, {7}) -> ",
                    zoneId0, x0, y0, z0, zoneId1, x1, y1, z1);

                Console.Write(msg);
                Console.WriteLine(result.ToString() + " [nodes=" + info.NumNodesTested + ", triangles=" + info.NumTrianglesTested + ", μs=" + microseconds + ", μs/tri=" + timePerTri + "]");
            }

            return(result);
        }
        private static CollisionInfo LoadCollisionChunk(BinaryReader reader)
        {
            CollisionInfo collision = new CollisionInfo();

            uint zoneID = reader.ReadUInt32();

            // Read collision vertices
            uint vertexCount = reader.ReadUInt32();

            collision.Vertices = new Vector3[vertexCount];
            for (uint i = 0; i < vertexCount; i++)
            {
                collision.Vertices[i] = ReadVector3(reader);
            }

            // Read collision triangles
            uint triangleCount = reader.ReadUInt32();
            uint indexSize     = reader.ReadUInt32();

            collision.Triangles = new TriangleInfo[triangleCount];
            //  int ii = 0;
            if (indexSize == 4)
            {
                for (uint i = 0; i < triangleCount; i++)
                {
                    collision.Triangles[i] = new TriangleInfo()
                    {
                        i0      = reader.ReadUInt32(),
                        i1      = reader.ReadUInt32(),
                        i2      = reader.ReadUInt32(),
                        fixture = reader.ReadUInt32()
                    };

                    if (!collision.Fixtures.ContainsKey(collision.Triangles[i].fixture))
                    {
                        collision.Fixtures[collision.Triangles[i].fixture] = new FixtureInfo();
                    }

                    collision.Fixtures[collision.Triangles[i].fixture].Triangles.Add(i);
                }
                //for (uint i = 0; i <3 * triangleCount; i++)
                //{
                //    collision.Indices[i] = reader.ReadUInt32();
                //}
            }
            else if (indexSize == 2)
            {
                throw new NotImplementedException();
            }
            else
            {
                throw new Exception();
            }


            if (_zones[zoneID] == null)
            {
                _zones[zoneID] = new ZoneInfo();
            }

            _zones[zoneID].Collision = collision;
            return(collision);
        }