/// <summary>
        /// After calling this method all tiles within a camera frustum will be marked as being in the frustum set and as being used starting at the root and stopping when:
        /// 1) A tile is found that has a screen space error less than or equal to our target SSE
        /// 2) MaxDepth is reached (Optional)
        /// Tiles with no content are ignored (i.e. we recurse to the nearest complete set of descendents with content)
        ///
        /// If the LoadSiblings criteria is enabled we add additional tiles to the used set. Specificlly, if a tile is in the Frustum set, we gurantee that all of its siblings
        /// are marked as used.  If the siblings have empty conetent, we mark the first set of decendents that have content as used.  This is useful for tree traversals where
        /// we want to load content or do computation on tiles that are outside the users current view but results in a slower traversal.
        ///
        /// After this method is run, only tiles that are in the used set are considered by the rest of the traversal algorithm for this frame.  Unused tiles may be subject to being unloaded.
        /// </summary>
        /// <param name="tile"></param>
        /// <param name="planes"></param>
        /// <returns></returns>
        bool DetermineFrustumSet(Unity3DTile tile, Plane[] planes, SSECalculator sse, Vector3 cameraPosInTilesetFrame,
                                 Vector3 cameraFwdInTilesetFrame, PlaneClipMask mask)
        {
            // Reset frame state if needed
            tile.FrameState.Reset(this.frameCount);
            // Check to see if we are in the fustrum
            mask = tile.BoundingVolume.IntersectPlanes(planes, mask);
            if (mask.Intersection == IntersectionType.OUTSIDE)
            {
                return(false);
            }
            // We are in frustum and at a rendereable level of detail, mark as used and as visible
            tile.MarkUsed();  //  mark content as used in LRUContent so it won't be unloaded
            tile.FrameState.InFrustumSet             = true;
            this.tileset.Statistics.FrustumSetCount += 1;
            // Skip screen space error check if this node has empty content,
            // we need to keep recursing until we find a node with content regardless of error
            if (!tile.HasEmptyContent)
            {
                // Check to see if this tile meets the on screen error level of detail requirement
                float distance = tile.BoundingVolume.MinDistanceTo(cameraPosInTilesetFrame);
                // We take the min in case multiple cameras, reset dist to max float on frame reset
                tile.FrameState.DistanceToCamera = Mathf.Min(distance, tile.FrameState.DistanceToCamera);
                tile.FrameState.ScreenSpaceError = sse.PixelError(tile.GeometricError, distance);

                Ray   cameraRay      = new Ray(cameraPosInTilesetFrame, cameraFwdInTilesetFrame);
                float distToAxis     = tile.BoundingVolume.CenterDistanceTo(cameraRay);
                float pixelsToCamCtr = sse.ProjectDistanceOnTileToScreen(distToAxis, distance);
                tile.FrameState.PixelsToCameraCenter = Mathf.Min(pixelsToCamCtr, tile.FrameState.PixelsToCameraCenter);

                //prune traversal when we hit a tile that meets SSE
                if (tile.FrameState.ScreenSpaceError <= tileset.TilesetOptions.MaximumScreenSpaceError)
                {
                    return(true);
                }
            }
            if (tileset.TilesetOptions.MaxDepth > 0 && tile.Depth >= tileset.TilesetOptions.MaxDepth)
            {
                return(true);
            }
            // Recurse on children
            bool anyChildUsed = false;

            for (int i = 0; i < tile.Children.Count; i++)
            {
                bool r = DetermineFrustumSet(tile.Children[i], planes, sse, cameraPosInTilesetFrame,
                                             cameraFwdInTilesetFrame, mask);
                anyChildUsed = anyChildUsed || r;
            }
            // If any children are in the workingset, mark all of them as being used (siblings/atomic split criteria).
            if (anyChildUsed && this.tileset.TilesetOptions.LoadSiblings)
            {
                for (int i = 0; i < tile.Children.Count; i++)
                {
                    MarkUsedRecursively(tile.Children[i]);
                }
            }
            return(true);
        }
 /// <summary>
 /// Mark this tile as used.  In the case that this tile has empty content,
 /// recurse until a complete set of leaf nodes with content are found
 /// This is only needed to handle the case of empty tiles
 /// </summary>
 /// <param name="tile"></param>
 void MarkUsedRecursively(Unity3DTile tile)
 {
     // We need to reset as we go in case we find tiles that weren't previously explored
     // If they have already been reset this frame this has no effect
     tile.FrameState.Reset(this.frameCount);
     tile.MarkUsed();
     if (tile.HasEmptyContent)
     {
         for (int i = 0; i < tile.Children.Count; i++)
         {
             MarkUsedRecursively(tile.Children[i]);
         }
     }
 }