private static bool SegmentTest(Vector3 p0, Vector3 p1, CollisionInfo collision, BSPNodeInfo node, int depth, ref SegmentTestInfo info)
        {
            if (node == null)
            {
                return(false);
            }

            info.NumNodesTested++;
            info.MaxDepthReached = Math.Max(info.MaxDepthReached, depth);

            if (node.IsLeaf)
            {
                TriangleInfo hitTriangle = new TriangleInfo();
                int          tri         = NodeTriangleTest(p0, p1, collision, node, ref info.HitPoint);
                info.NumTrianglesTested += (tri != -1) ? (tri + 1) : node.Triangles.Length;
                info.HitTriangle         = hitTriangle;
                return(tri != -1);
            }
            else
            {
                float d0 = Vector3.Dot(node.P.N, p0) - node.P.D;
                float d1 = Vector3.Dot(node.P.N, p1) - node.P.D;

                if (d0 < -Epsilon && d1 < -Epsilon)
                {
                    // Both points behind plane
                    return(SegmentTest(p0, p1, collision, node.Back, depth + 1, ref info));
                }
                else if (d0 > Epsilon && d1 > Epsilon)
                {
                    // Both points in front of plane
                    return(SegmentTest(p0, p1, collision, node.Front, depth + 1, ref info));
                }
                else
                {
                    // Points span plane
                    return(SegmentTest(p0, p1, collision, node.Front, depth + 1, ref info) || SegmentTest(p0, p1, collision, node.Back, depth + 1, ref info));
                }
            }
        }
        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);
        }