protected virtual void ProcessNode(BspNode node, Ray tracingRay, float maxDistance, float traceDistance)
        {
            // check if ray already encountered a solid brush
            if (StopRayTracing) return;

            if (node.IsLeaf)
            {
                ProcessLeaf(node, tracingRay, maxDistance, traceDistance);
                return;
            }

            IntersectResult result = tracingRay.Intersects(node.SplittingPlane);
            if (result.Hit)
            {
                if (result.Distance < maxDistance)
                {
                    if (node.GetSide(tracingRay.Origin) == PlaneSide.Negative)
                    {
                        ProcessNode(node.BackNode, tracingRay, result.Distance, traceDistance);
                        Vector3 splitPoint = tracingRay.Origin + tracingRay.Direction * result.Distance;
                        ProcessNode(node.FrontNode, new Ray(splitPoint, tracingRay.Direction), maxDistance - result.Distance, traceDistance + result.Distance);
                    }
                    else
                    {
                        ProcessNode(node.FrontNode, tracingRay, result.Distance, traceDistance);
                        Vector3 splitPoint = tracingRay.Origin + tracingRay.Direction * result.Distance;
                        ProcessNode(node.BackNode, new Ray(splitPoint, tracingRay.Direction), maxDistance - result.Distance, traceDistance + result.Distance);
                    }
                }
                else
                    ProcessNode(node.GetNextNode(tracingRay.Origin), tracingRay, maxDistance, traceDistance);
            }
            else
                ProcessNode(node.GetNextNode(tracingRay.Origin), tracingRay, maxDistance, traceDistance);
        }
        public bool RayIntersection(Ray ray, out int x, out int z)
        {
            // generate a bounding box for the heightmap in its local space
            AxisAlignedBox axisAlignedBox = new AxisAlignedBox(new Vector3(0, minHeight, 0),
                    new Vector3(width, maxHeight, height));

            Vector3 rayLoc = new Vector3((ray.Origin.x - offsetX) / xzScale,
                                        ray.Origin.y / yScale,
                                        (ray.Origin.z - offsetZ) / xzScale);

            Vector3 rayDir = new Vector3(ray.Direction.x / xzScale, ray.Direction.y / yScale, ray.Direction.z / xzScale);
            rayDir.Normalize();

            // convert the ray to local heightmap space
            Ray tmpRay = new Ray(rayLoc, rayDir);

            // see if the ray intersects with the bounding box
            IntersectResult result = tmpRay.Intersects(axisAlignedBox);

            if (result.Hit)
            {
                // move rayLoc up to just before the point of intersection
                rayLoc = rayLoc + rayDir * ( result.Distance - 1 );

                //
                // deal with edge case where ray is coming from outside the heightmap
                // and is very near edge of map at intersection.
                //
                int insideCounter = 20;
                while ((!Inside(rayLoc)) && (insideCounter > 0))
                {
                    rayLoc += ( rayDir * 0.1f );
                    insideCounter--;
                }
                if (insideCounter == 0)
                {
                    x = 0;
                    z = 0;
                    return false;
                }

                x = (int)Math.Round(rayLoc.x);
                z = (int)Math.Round(rayLoc.z);

                if (x < 0)
                {
                    x = 0;
                }
                if (x >= width)
                {
                    x = width - 1;
                }
                if (z < 0)
                {
                    z = 0;
                }
                if (z >= height)
                {
                    z = height - 1;
                }

                bool above = rayLoc.y > heightData[x + z * width];

                while (Inside(rayLoc))
                {
                    // increment the ray
                    rayLoc += rayDir;

                    x = (int)Math.Round(rayLoc.x);
                    z = (int)Math.Round(rayLoc.z);

                    if (x < 0)
                    {
                        x = 0;
                    }
                    if (x >= width)
                    {
                        x = width - 1;
                    }
                    if (z < 0)
                    {
                        z = 0;
                    }
                    if (z >= height)
                    {
                        z = height - 1;
                    }

                    if (above != (rayLoc.y > heightData[x + z * width]))
                    {
                        // we found a hit
                        return true;
                    }
                }
            }
            x = 0;
            z = 0;
            return false;
        }
        protected virtual void ProcessLeaf(BspNode leaf, Ray tracingRay, float maxDistance, float traceDistance)
        {
            MovableObjectCollection objects = leaf.Objects;
            int numObjects = objects.Count;

            //Check ray against objects
            for(int a = 0; a < numObjects; a++)
            {
                MovableObject obj = objects[a];
                // Skip this object if collision not enabled
                if((obj.QueryFlags & queryMask) == 0)
                    continue;

                //Test object as bounding box
                IntersectResult result = tracingRay.Intersects(obj.GetWorldBoundingBox());
                // if the result came back positive and intersection point is inside
                // the node, fire the event handler
                if(result.Hit && result.Distance <= maxDistance)
                {
                    listener.OnQueryResult(obj, result.Distance + traceDistance);
                }
            }

            PlaneBoundedVolume boundedVolume = new PlaneBoundedVolume(PlaneSide.Positive);
            BspBrush intersectBrush = null;
            float intersectBrushDist = float.PositiveInfinity;

            // Check ray against brushes
            for (int brushPoint=0; brushPoint < leaf.SolidBrushes.Length; brushPoint++)
            {
                BspBrush brush = leaf.SolidBrushes[brushPoint];

                if (brush == null) continue;

                boundedVolume.planes = brush.Planes;

                IntersectResult result = tracingRay.Intersects(boundedVolume);
                // if the result came back positive and intersection point is inside
                // the node, check if this brush is closer
                if(result.Hit && result.Distance <= maxDistance)
                {
                    if (result.Distance < intersectBrushDist)
                    {
                        intersectBrushDist = result.Distance;
                        intersectBrush = brush;
                    }
                }
            }

            if (intersectBrush != null)
            {
                listener.OnQueryResult(intersectBrush.Fragment, intersectBrushDist + traceDistance);
                StopRayTracing = true;
            }
        }