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;
        }
Пример #2
0
 // By the time this method is called, we know for sure that
 // the object has collision volumes.
 protected float CheckCollisionWithCollisionShapes(ObjectNode objNode, Ray ray)
 {
     float distance = float.MaxValue;
     if (objNode.Collider != null)
     {
         MovingObject mo = objNode.Collider;
         foreach (MovingPart part in mo.parts)
         {
             CollisionShape partShape = mo.parts[0].shape;
             // Check for intersection of a line 1000 meters long from the ray origin toward the ray direction
             float distanceToShape = partShape.RayIntersectionDistance(ray.Origin, ray.Origin + ray.Direction * (1000f * OneMeter));
             if (distanceToShape == float.MaxValue)
                 continue;
             else if (distanceToShape < distance)
                 distance = distanceToShape;
         }
     }
     else if (objNode.CollisionShapes != null)
     {
         foreach (CollisionShape shape in objNode.CollisionShapes)
         {
             // Check for intersection of a line 1000 meters long from the ray origin toward the ray direction
             float distanceToShape = shape.RayIntersectionDistance(ray.Origin, ray.Origin + ray.Direction * (1000f * OneMeter));
             if (distanceToShape == float.MaxValue)
                 continue;
             else if (distanceToShape < distance)
             {
                 //                         log.DebugFormat("Client.CheckCollisionWithCollisionShapes: objNode {0}, distanceToShape {1}, shape {2}",
                 //                             objNode.Name,distanceToShape, shape);
                 distance = distanceToShape;
             }
         }
     }
     return distance;
 }
Пример #3
0
 protected bool CheckCollision(Ray ray, SceneObject sceneObj, out float t)
 {
     t = float.MaxValue;
     if (!(sceneObj is Entity))
         return false;
     bool intersect = false;
     Entity entity = (Entity)sceneObj;
     VertexData vData;
     if (entity.Mesh.HasSkeleton) {
         // The blended vertex buffers have already been transformed
         for (int i = 0; i < entity.SubEntityCount; ++i) {
             SubEntity subEntity = entity.GetSubEntity(i);
             if (!subEntity.IsVisible)
                 continue;
             if (subEntity.SubMesh.useSharedVertices)
                 vData = entity.SharedBlendedVertexData;
             else
                 vData = subEntity.BlendedVertexData;
             IndexData iData = subEntity.SubMesh.indexData;
             if (CheckCollision(vData, iData, ray.Origin, ray.Direction, ref t))
                 intersect = true;
         }
     } else {
         // Transform the ray based on the inverse of the sceneObj transform, so
         // we don't need to transform each point in the submesh.
         Matrix4 inverseTransform = sceneObj.ParentNodeFullTransform.Inverse();
         Vector3 origin = inverseTransform * ray.Origin;
         Vector3 direction = (inverseTransform * (ray.Origin + ray.Direction)) - origin;
         for (int i = 0; i < entity.SubEntityCount; ++i) {
             SubEntity subEntity = entity.GetSubEntity(i);
             if (!subEntity.IsVisible)
                 continue;
             if (subEntity.SubMesh.useSharedVertices)
                 vData = entity.Mesh.SharedVertexData;
             else
                 vData = subEntity.SubMesh.vertexData;
             IndexData iData = subEntity.SubMesh.indexData;
             if (CheckCollision(vData, iData, origin, direction, ref t))
                 intersect = true;
         }
     }
     return intersect;
 }
        public Vector3 PickTerrain(Ray pointRay)
        {
            Axiom.Core.RaySceneQuery q = scene.CreateRayQuery(pointRay);

            q.QueryMask = (ulong)Axiom.SceneManagers.Multiverse.RaySceneQueryType.FirstTerrain;
            List<RaySceneQueryResultEntry> results = q.Execute();

            if (results.Count > 0)
            {
                RaySceneQueryResultEntry result = results[0];
                return (result.worldFragment.SingleIntersection);
            }
            else
            {
                return Vector3.Zero;
            }
        }
Пример #5
0
        /// <summary>
        ///   This overloading permits the user to pass in a query mask
        ///   mouseX and mouseY are in terms of Overlay coordinates
        ///   (0 to 1 starting from the upper left corner of the screen).
        /// </summary>
        /// <param name="mouseX"></param>
        /// <param name="mouseY"></param>
        /// <param name="queryMas"></param>
        /// <returns></returns>
        public ObjectNode CastRay(float mouseX, float mouseY, ulong queryMask)
        {
            Ray ray = camera.GetCameraToViewportRay(mouseX, mouseY);
            #if NOT
            Vector3 origin, direction;
            float winWidth = (float)(2 * camera.Near *
                              Math.Tan(MathUtil.DegreesToRadians(camera.FOV / 2)));
            direction.x = (mouseX - .5f) * winWidth;
            direction.y = (.5f - mouseY) * winWidth / camera.AspectRatio;
            direction.z = -camera.Near;
            origin = camera.Position;
            direction = camera.Orientation * direction;
            Ray ray = new Ray(origin, direction);
            #endif
            RaySceneQuery query = scene.CreateRayQuery(ray, queryMask);
            query.SortByDistance = true;
            List<RaySceneQueryResultEntry> results = query.Execute();
            if (results.Count == 0)
                return null;

            MovableObject closestObj = null;
            float closestMatch = float.MaxValue;

            // Check that the picked object is not terrain, not a prop, and
            // that it is the closest object.
            foreach (RaySceneQueryResultEntry result in results)
            {
                MovableObject sceneObj = result.SceneObject;
                if (sceneObj == null || sceneObj.UserData == null || !(sceneObj.UserData is ObjectNode))
                    continue;

                // Skip player and buildings (props)
                ObjectNode objNode = (ObjectNode)sceneObj.UserData;
                float distanceToObject = result.Distance;
                bool hitCollisionVolume = false;
                if (targetBasedOnCollisionVolumes)
                {
                    // If there are are no collision shapes to collide
                    // with, fall back to the distance to the bounding
                    // box.
                    if ((objNode.Collider != null && objNode.Collider.parts.Count > 0) ||
                        (objNode.CollisionShapes != null && objNode.CollisionShapes.Count > 0))
                    {
                        float previousDistance = distanceToObject;
                        distanceToObject = CheckCollisionWithCollisionShapes(objNode, ray);
                        hitCollisionVolume = true;
                        //                         log.DebugFormat("Client.CastRay: objNode {0}, distanceToObject {1}, previousDistance {2}",
                        //                             objNode.Name, distanceToObject, previousDistance);
                    }
                }
                float dist;
            #if PER_POLY_PICKING
                if (!CheckCollision(ray, sceneObj, out dist))
                    continue;
            #else
                // If we aren't doing the per-poly stuff, we would use this instead.
                dist = distanceToObject;
            #endif
                if (dist < closestMatch)
                {
                    // If the closest thing is a collision volume, and
                    // it's not targetable and not the player, set
                    // closestObj to null.
                    if (!objNode.Targetable && objNode != worldManager.Player && hitCollisionVolume)
                    {
                        closestMatch = dist;
                        closestObj = null;
                    }
                    else if (objNode.Targetable)
                    {
                        closestMatch = dist;
                        closestObj = sceneObj;
                    }
                }
            }
            if (closestObj == null)
                return null;
            else
                return closestObj.UserData as ObjectNode;
        }
        /// <summary>
        ///		<see cref="RaySceneQuery"/>
        /// </summary>
        public void Execute(IRaySceneQueryListener listener, Camera camera)
        {
            clearFragmentList();

            // if the world is not initialized, then just quit out now with no hits
            if (!TerrainManager.Instance.Initialized)
            {
                return;
            }

            ulong mask = QueryMask;
            SceneQuery.WorldFragment frag;

            bool terrainQuery = (mask & (ulong)RaySceneQueryType.AllTerrain) != 0 || (mask & (ulong)RaySceneQueryType.FirstTerrain) != 0;

            // if the query is a terrain query that is exactly vertical, then force it into the "Height" fastpath
            if (((mask & (ulong)RaySceneQueryType.Height) != 0) || (terrainQuery && (this.Ray.Direction.x == 0.0f) && (this.Ray.Direction.z == 0.0f)))
            {
                // we don't want to bother checking for entities because a
                // UNIT_Y ray is assumed to be a height test, not a ray test
                frag = new SceneQuery.WorldFragment();
                fragmentList.Add(frag);

                frag.FragmentType = WorldFragmentType.SingleIntersection;
                Vector3 origin = this.Ray.Origin;
                origin.y = 0; // ensure that it's within bounds
                frag.SingleIntersection = getHeightAt(origin);
                listener.OnQueryResult(frag, Math.Abs(frag.SingleIntersection.y - this.Ray.Origin.y));
            }
            else
            {
                // Check for all entity contacts
                if ((mask & (ulong)RaySceneQueryType.Entities) != 0)
                {
                    base.Execute(listener);
                }

                // Check for contact with the closest entity triangle
                // or all entity triangles.  Ignores entities that
                // don't have a TriangleIntersector associated with
                // their meshes.
                bool firstTriangleQuery = (mask & (ulong)(RaySceneQueryType.FirstEntityTriangle)) != 0;
                bool allTrianglesQuery = (mask & (ulong)(RaySceneQueryType.AllEntityTriangles)) != 0;
                if (firstTriangleQuery | allTrianglesQuery)
                {
                    rayOrigin = this.ray.Origin;
                    // Start by getting the entities whose bounding
                    // boxes intersect the ray.  If there are none,
                    // we're done.
                    List<MovableObject> candidates = new List<MovableObject>();
                    foreach (Dictionary<string, MovableObject> objectMap in creator.MovableObjectMaps)
                    {
                        foreach (MovableObject obj in objectMap.Values)
                        {
                            // skip if unattached or filtered out by query flags
                            if (!obj.IsAttached || (obj.QueryFlags & queryMask) == 0)
                                continue;

                            // test the intersection against the world bounding box of the entity
                            IntersectResult results = MathUtil.Intersects(ray, obj.GetWorldBoundingBox());
                            if (results.Hit)
                                candidates.Add(obj);
                        }
                    }

                    // Get the camera.Near value
                    Camera cam = camera;
                    float nearDistance = cam.Near;
                    float closestDistance = float.MaxValue;
                    Vector3 closestIntersection = Vector3.Zero;
                    Entity closestEntity = null;
                    List<EntityAndIntersection> allEntities = new List<EntityAndIntersection>();
                    foreach (MovableObject obj in candidates)
                    {
                        // skip if unattached or filtered out by query flags
                        if (!obj.IsAttached || (obj.QueryFlags & queryMask) == 0)
                            continue;

                        Entity entity = obj as Entity;
                        if (entity == null)
                            continue;

                        // skip if its mesh doesn't have triangles
                        if (entity.Mesh == null || entity.Mesh.TriangleIntersector == null)
                            continue;

                        // transform the ray to the space of the mesh
                        Matrix4 inverseTransform = entity.ParentNodeFullTransform.Inverse();
                        Matrix4 inverseWithoutTranslation = inverseTransform;
                        inverseWithoutTranslation.Translation = Vector3.Zero;
                        Vector3 transformedOrigin = inverseTransform * ray.Origin;
                        Ray transformedRay = new Ray(transformedOrigin,
                                                     (inverseWithoutTranslation * ray.Direction).ToNormalized());

                        // test the intersection against the world bounding box of the entity
                        Vector3 untransformedIntersection;
                        if (entity.Mesh.TriangleIntersector.ClosestRayIntersection(transformedRay, Vector3.Zero,
                                                                                   nearDistance, out untransformedIntersection))
                        {
                            Vector3 intersection = entity.ParentNodeFullTransform * untransformedIntersection;
                            if (allTrianglesQuery)
                                allEntities.Add(new EntityAndIntersection(entity, intersection));
                            float distance = (ray.Origin - intersection).Length;
                            if (firstTriangleQuery && distance < closestDistance)
                            {
                                closestDistance = distance;
                                closestEntity = entity;
                                closestIntersection = intersection;
                            }
                        }
                    }
                    if (firstTriangleQuery && closestEntity != null)
                    {
                        frag = new SceneQuery.WorldFragment();
                        fragmentList.Add(frag);
                        frag.FragmentType = WorldFragmentType.SingleIntersection;
                        frag.SingleIntersection = closestIntersection;
                        listener.OnQueryResult(frag, closestDistance);
                    }
                    else if (allTrianglesQuery && allEntities.Count > 0)
                    {
                        allEntities.Sort(distanceToCameraCompare);
                        foreach (EntityAndIntersection ei in allEntities)
                            listener.OnQueryResult(ei.entity, (rayOrigin - ei.intersection).Length);
                    }
                }

                if (terrainQuery)
                {
                    Vector3 ray = Ray.Origin;
                    Vector3 land = getHeightAt(ray);
                    float dist = 0, resFactor = TerrainManager.oneMeter;

                    // find the larger of x and z directions of the ray direction
                    float maxXZ = Math.Max(Math.Abs(Ray.Direction.x), Math.Abs(Ray.Direction.z));

                    // Only bother if the non-default mask has been set
                    if ((mask & (ulong)RaySceneQueryType.OnexRes) != 0)
                    {
                        if ((mask & (ulong)RaySceneQueryType.TwoxRes) != 0)
                        {
                            resFactor = TerrainManager.oneMeter / 2;
                        }
                        else if ((mask & (ulong)RaySceneQueryType.FourxRes) != 0)
                        {
                            resFactor = TerrainManager.oneMeter / 4;
                        }
                        else if ((mask & (ulong)RaySceneQueryType.EightxRes) != 0)
                        {
                            resFactor = TerrainManager.oneMeter / 8;
                        }
                    }

                    // this scales the res factor so that we move along the ray by a distance that results
                    // in shift of one meter along either the X or Z axis (whichever is longer)
                    resFactor = resFactor / maxXZ;

                    SubPageHeightMap sp;
                    // bool east = false; (unused)
                    // bool south = false; (unused)

                    // if ( Ray.Origin.x > 0 )
                    // {
                    //	east = true;
                    // }
                    // if ( Ray.Origin.z > 0 )
                    // {
                    //	south = true;
                    // }

                    ray = Ray.Origin;
                    sp = TerrainManager.Instance.LookupSubPage(ray);

                    while (sp != null)
                    {
                        SubPageHeightMap newsp;
                        AxisAlignedBox tileBounds = sp.BoundingBox;

                        IntersectResult intersect = MathUtil.Intersects(Ray, tileBounds);

                        if (intersect.Hit)
                        {
                            // step through this tile
                            while ((newsp = TerrainManager.Instance.LookupSubPage(RoundRay(ray))) == sp)
                            {
                                land = getHeightAt(ray);
                                if (ray.y < land.y)
                                {
                                    frag = new SceneQuery.WorldFragment();
                                    fragmentList.Add(frag);

                                    frag.FragmentType = WorldFragmentType.SingleIntersection;
                                    frag.SingleIntersection = land;
                                    listener.OnQueryResult(frag, dist);

                                    if ((mask & (ulong)RaySceneQueryType.FirstTerrain) != 0)
                                    {
                                        return;
                                    }
                                }

                                ray += Ray.Direction * resFactor;
                                dist += 1 * resFactor;
                            }
                            // if we fall off the end of the above loop without getting a hit, then the hit should be
                            // right at the far edge of the tile, so handle that case.
                            land = getHeightAt(ray);
                            if (ray.y < land.y)
                            {
                                frag = new SceneQuery.WorldFragment();
                                fragmentList.Add(frag);

                                frag.FragmentType = WorldFragmentType.SingleIntersection;
                                frag.SingleIntersection = land;
                                listener.OnQueryResult(frag, dist);

                                //LogManager.Instance.Write("MVSM:RaySceneQuery:End of tile ray collision");

                                if ((mask & (ulong)RaySceneQueryType.FirstTerrain) != 0)
                                {
                                    return;
                                }
                            }
                            else
                            {
                                //LogManager.Instance.Write("MVSM:RaySceneQuery:End of tile reached without expected intersection");
                            }
                        }
                        else
                        {
                            // step over this tile
                            while ((newsp = TerrainManager.Instance.LookupSubPage(RoundRay(ray))) == sp)
                            { // XXX - this is not the most efficient method...
                                ray += Ray.Direction * resFactor;
                                dist += 1 * resFactor;
                            }
                        }

                        sp = newsp;
                    }
                }
            }
        }
        public Vector3 ObjectPlacementLocation(Vector3 objPosition)
        {
            Ray pointRay = new Ray(new Vector3(objPosition.x, camera.Position.y, objPosition.z), Vector3.NegativeUnitY);
            Axiom.Core.RaySceneQuery q = scene.CreateRayQuery(pointRay);
            q.QueryMask = (ulong)Axiom.SceneManagers.Multiverse.RaySceneQueryType.AllEntityTriangles;
            List<RaySceneQueryResultEntry> results = q.Execute();

            if (results.Count > 0)
            {
                foreach (RaySceneQueryResultEntry result in results)
                {
                    IWorldObject obj = DisplayObject.LookupName(result.SceneObject.Name);
                    if (obj is StaticObject && obj.AcceptObjectPlacement)
                    {
                        Vector3 position = pointRay.Origin + (pointRay.Direction * result.Distance);
                        return position;
                    }
                }
            }
            return PickTerrain(pointRay);
        }
 /// <summary>
 ///    Creates a query to return objects found along the ray.
 /// </summary>
 /// <param name="ray">Ray to use for the intersection query.</param>
 /// <returns>A specialized implementation of RaySceneQuery for this scene manager.</returns>
 public virtual RaySceneQuery CreateRayQuery(Ray ray)
 {
     return CreateRayQuery(ray, 0xffffffff);
 }
 /// <summary>
 ///    Creates a query to return objects found along the ray. 
 /// </summary>
 /// <param name="ray">Ray to use for the intersection query.</param>
 /// <returns>A specialized implementation of RaySceneQuery for this scene manager.</returns>
 public virtual RaySceneQuery CreateRayQuery(Ray ray, ulong mask)
 {
     DefaultRaySceneQuery query = new DefaultRaySceneQuery(this);
     query.Ray = ray;
     query.QueryMask = mask;
     return query;
 }
 public override Axiom.Core.RaySceneQuery CreateRayQuery(Ray ray)
 {
     return CreateRayQuery(ray, defaultRayQueryFlags);
 }
 /// <summary>
 /// Creates a RaySceneQuery for this scene manager.
 /// </summary>
 /// <param name="ray">Details of the ray which describes the region for this query.</param>
 /// <param name="mask">The query mask to apply to this query; can be used to filter out certain objects; see SceneQuery for details.</param>
 /// <returns>
 ///	The instance returned from this method must be destroyed by calling
 ///	SceneManager::destroyQuery when it is no longer required.
 /// </returns>
 /// <remarks>
 ///	This method creates a new instance of a query object for this scene manager, 
 ///	looking for objects which fall along a ray. See SceneQuery and RaySceneQuery 
 ///	for full details.
 /// </remarks>
 public override Axiom.Core.RaySceneQuery CreateRayQuery(Ray ray, ulong mask)
 {
     Axiom.Core.RaySceneQuery q = new Axiom.SceneManagers.Multiverse.RaySceneQuery(this);
     q.Ray = ray;
     q.QueryMask = mask;
     return q;
 }
        /// <summary>
        ///		Creates a RaySceneQuery for this scene manager. 
        /// </summary>
        /// <remarks>
        ///		This method creates a new instance of a query object for this scene manager, 
        ///		looking for objects which fall along a ray. See SceneQuery and RaySceneQuery 
        ///		for full details.
        /// </remarks>
        /// <param name="ray">Details of the ray which describes the region for this query.</param>
        /// <param name="mask">The query mask to apply to this query; can be used to filter out certain objects; see SceneQuery for details.</param>
        public override RaySceneQuery CreateRayQuery(Ray ray, ulong mask)
        {
            BspRaySceneQuery q = new BspRaySceneQuery(this);
            q.Ray = ray;
            q.QueryMask = mask;

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