/// <summary>
 /// Request a tile
 /// If the tile has siblings in the used set, request them at the same time since we will need all of them to split the parent tile
 /// Set request priority based on the closest sibling, request all siblings with the same priority since we need all of them to load before
 /// any of them can be made visible
 /// </summary>
 /// <param name="tile"></param>
 void RequestTile(Unity3DTile tile)
 {
     if (tile.Parent == null)
     {
         tile.RequestContent(-tile.FrameState.DistanceToCamera);
     }
     else
     {
         float minDist = float.MaxValue;
         for (int i = 0; i < tile.Parent.Children.Count; i++)
         {
             if (tile.Parent.Children[i].FrameState.IsUsedThisFrame(this.frameCount))
             {
                 minDist = Mathf.Min(tile.Parent.Children[i].FrameState.DistanceToCamera, minDist);
             }
         }
         for (int i = 0; i < tile.Parent.Children.Count; i++)
         {
             if (tile.Parent.Children[i].FrameState.IsUsedThisFrame(this.frameCount))
             {
                 tile.Parent.Children[i].RequestContent(-minDist);
             }
         }
     }
 }
        /// <summary>
        /// Identify the deepest set of tiles that are in the used set this frame and mark them as "used set leaves"
        /// After this point we will not consider any tiles that are beyond a used set leaf.
        /// Leafs are the content we ideally want to show this frame
        /// </summary>
        void MarkUsedSetLeaves(Unity3DTile tile)
        {
            // A used leaf is a node that is used but has no children in the used set
            if (!tile.FrameState.IsUsedThisFrame)
            {
                // Not used this frame, can't be a used leaf and neither can anything beneath us
                return;
            }
            tileset.Statistics.UsedSet++;
            // If any child is used, then we are not a leaf
            bool anyChildrenUsed = false;

            for (int i = 0; i < tile.Children.Count; i++)
            {
                anyChildrenUsed = anyChildrenUsed || tile.Children[i].FrameState.IsUsedThisFrame;
            }
            if (!anyChildrenUsed || ForceTiles.Contains(tile))
            {
                tile.FrameState.IsUsedSetLeaf = true;
            }
            else
            {
                for (int i = 0; i < tile.Children.Count; i++)
                {
                    MarkUsedSetLeaves(tile.Children[i]);
                }
            }
        }
Пример #3
0
 /// <summary>
 /// Request a tile
 /// If the tile has siblings in the used set, request them at the same time since we will need all of them to split the parent tile
 /// Set request priority based on the closest sibling, request all siblings with the same priority since we need all of them to load before
 /// any of them can be made visible
 /// </summary>
 /// <param name="tile"></param>
 void RequestTile(Unity3DTile tile)
 {
     if (tile.Parent == null)
     {
         tile.RequestContent(this.tileset.TilesetOptions.TilePriority(tile)); // was -tile.FrameState.DistanceToCamera, bug?
     }
     else
     {
         float priority = float.MaxValue;
         for (int i = 0; i < tile.Parent.Children.Count; i++)
         {
             if (tile.Parent.Children[i].FrameState.IsUsedThisFrame(this.frameCount))
             {
                 priority = Mathf.Min(this.tileset.TilesetOptions.TilePriority(tile.Parent.Children[i]), priority);
             }
         }
         for (int i = 0; i < tile.Parent.Children.Count; i++)
         {
             if (tile.Parent.Children[i].FrameState.IsUsedThisFrame(this.frameCount))
             {
                 tile.Parent.Children[i].RequestContent(priority);
             }
         }
     }
 }
Пример #4
0
        public bool Add(Unity3DTile tile, out bool duplicate)
        {
            duplicate = false;
            if (tile == null)
            {
                return(false);
            }
            if (nodeLookup.ContainsKey(tile))
            {
                duplicate = true;
                return(false);
            }
            if (Full)
            {
                MarkLowPriorityUnused(t => t.FrameState.Priority > tile.FrameState.Priority, 1);
                if (UnloadUnusedContent(1, 1) == 0)
                {
                    return(false);
                }
            }
            var node = new LinkedListNode <Unity3DTile>(tile);

            nodeLookup.Add(tile, node);
            list.AddLast(node);
            return(true);
        }
        float TilePriority(Unity3DTile tile)
        {
            if (this.tileset.TilesetOptions.TilePriority != null)
            {
                return(this.tileset.TilesetOptions.TilePriority(tile));
            }

            //prioritize by distance from camera
            //return tile.FrameState.DistanceToCamera;

            //prioritize coarse to fine
            //return tile.Depth;
            //return DepthFromFirstUsedAncestor(tile);

            //prioritize first coarse to fine, then by distance
            ////int depth = tileDepth;
            //int depth = DepthFromFirstUsedAncestor(tile); //integer part
            //float distLimit = 1000;
            //float dist = Mathf.Min(tile.FrameState.DistanceToCamera, distLimit);
            //float relDist = dist / distLimit; //fractional part
            //return depth + relDist;

            //prioritize first by distance, then coarse to fine
            //float quantizedDist = (int)(tile.FrameState.DistanceToCamera * 100); //integer part
            //float quantizedDist = (int)(tile.FrameState.DistanceToCameraAxis * 100); //integer part
            //float quantizedDist = (int)(tile.FrameState.PixelsToCameraCenter / 100);
            float quantizedDist = (int)(MinUsedAncestorPixelsToCameraCenter(tile) / 100);
            float depthLimit    = 100;
            //float depth = Math.Min(tile.Depth, depthLimit);
            float depth    = Math.Min(DepthFromFirstUsedAncestor(tile), depthLimit);
            float relDepth = depth / depthLimit; //fractional part

            return(quantizedDist + relDepth);
        }
 /// <summary>
 /// Request a tile
 /// If the tile has siblings in the used set, request them at the same time since we will need all of them to split the parent tile
 /// Set request priority based on the closest sibling, request all siblings with the same priority since we need all of them to load before
 /// any of them can be made visible
 /// </summary>
 /// <param name="tile"></param>
 void RequestTile(Unity3DTile tile)
 {
     if (tile.Parent == null)
     {
         tile.RequestContent(TilePriority(tile));
     }
     else
     {
         float priority = float.MaxValue;
         for (int i = 0; i < tile.Parent.Children.Count; i++)
         {
             if (tile.Parent.Children[i].FrameState.IsUsedThisFrame(this.frameCount))
             {
                 priority = Mathf.Min(TilePriority(tile.Parent.Children[i]), priority);
             }
         }
         for (int i = 0; i < tile.Parent.Children.Count; i++)
         {
             if (tile.Parent.Children[i].FrameState.IsUsedThisFrame(this.frameCount))
             {
                 tile.Parent.Children[i].RequestContent(priority);
             }
         }
     }
 }
        /// <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);
        }
Пример #8
0
 public void MarkUnused(Unity3DTile tile)
 {
     if (nodeLookup.ContainsKey(tile))
     {
         var node = nodeLookup[tile];
         list.Remove(node);
         list.AddFirst(node);
     }
 }
 float MinUsedAncestorPixelsToCameraCenter(Unity3DTile tile, float pixels = 10000)
 {
     if (!tile.FrameState.IsUsedThisFrame || tile.Parent == null || !tile.Parent.FrameState.IsUsedThisFrame)
     {
         return(pixels);
     }
     pixels = Mathf.Min(pixels, tile.FrameState.PixelsToCameraCenter);
     return(MinUsedAncestorPixelsToCameraCenter(tile.Parent, pixels));
 }
 void UpdateVisibleStatstics(Unity3DTile tile)
 {
     tileset.Statistics.VisibleTileCount += 1;
     if (tile.Content != null)
     {
         tileset.Statistics.VisibleFaces    += tile.Content.FaceCount;
         tileset.Statistics.VisibleTextures += tile.Content.TextureCount;
         tileset.Statistics.VisiblePixels   += tile.Content.PixelCount;
     }
 }
        int DepthFromFirstUsedAncestor(Unity3DTile tile)
        {
            if (!tile.FrameState.IsUsedThisFrame(this.frameCount) ||
                tile.Parent == null || !tile.Parent.FrameState.IsUsedThisFrame(this.frameCount))
            {
                return(0);
            }

            return(1 + DepthFromFirstUsedAncestor(tile.Parent));
        }
 float MinUsedAncestorPixelsToCameraCenter(Unity3DTile tile, float pixels = float.MaxValue)
 {
     if (!tile.FrameState.IsUsedThisFrame(this.frameCount) ||
         tile.Parent == null || !tile.Parent.FrameState.IsUsedThisFrame(this.frameCount))
     {
         return(pixels);
     }
     pixels = Mathf.Min(pixels, tile.FrameState.PixelsToCameraCenter);
     return(MinUsedAncestorPixelsToCameraCenter(tile.Parent, pixels));
 }
        float TilePriority(Unity3DTile tile)
        {
            if (ForceTiles.Contains(tile))
            {
                return(0);
            }

            if (!tile.FrameState.IsUsedThisFrame || !tile.FrameState.InFrustumSet || tile.HasEmptyContent)
            {
                return(float.MaxValue);
            }

            if (tilesetOptions.TilePriority != null)
            {
                return(tilesetOptions.TilePriority(tile));
            }

            float distLimit = 1000;
            float d2c       = tile.FrameState.DistanceToCamera;

            d2c = float.IsNaN(d2c) ? distLimit : Mathf.Max(0, Mathf.Min(distLimit, d2c));

            float pixelsLimit = 10000;
            float p2c         = tile.FrameState.PixelsToCameraCenter;

            //float p2c = MinUsedAncestorPixelsToCameraCenter(tile, pixelsLimit);
            p2c = float.IsNaN(p2c) ? pixelsLimit : Mathf.Max(0, Mathf.Min(pixelsLimit, p2c));

            float depthLimit = 100;
            float depth      = tile.Depth;

            //float depth = DepthFromFirstUsedAncestor(tile);
            depth = float.IsNaN(depth) ? depthLimit : Mathf.Max(0, Mathf.Min(depthLimit, depth));

            //prioritize by distance from camera
            //return d2c;

            //prioritize by pixels from camera
            //return p2c;

            //prioritize by depth
            //return depth;

            //prioritize first by depth, then pixels to camera
            //return depth + (p2c / pixelsLimit);

            //prioritize first by pixels to camera, then depth
            //return (int)(p2c / 100) + (depth / depthLimit);

            //prioritize first by used set leaf, then distance to camera, then depth, then pixels to camera
            return((tile.FrameState.IsUsedSetLeaf ? 0 : 100) +
                   (d2c < 1 ? 0 : 10) +
                   (int)(9 * depth / depthLimit) +
                   (p2c / pixelsLimit));
        }
Пример #14
0
 /// <summary>
 /// This method should be called on the Tilesets style on each of the individual tile
 /// </summary>
 /// <param name=""></param>
 /// <returns></returns>
 public void ApplyStyle(Unity3DTile tile)
 {
     // If tile doesn't have a style or its type doesn't match us (the target style), set the tile style to a clone of ourselves
     if (tile.Style == null || tile.Style.GetType() != this.GetType())
     {
         tile.Style = (Unity3DTilesetStyle)this.CreateDefault();
     }
     if (tile.Content != null)
     {
         tile.Style.UpdateAndApply(this, tile.Content);
     }
 }
 public void TallyVisibleTile(Unity3DTile tile)
 {
     VisibleTiles += 1;
     if (tile.Content != null)
     {
         VisibleFaces       += tile.Content.FaceCount;
         VisibleTextures    += tile.Content.TextureCount;
         VisiblePixels      += tile.Content.PixelCount;
         MinVisibleTileDepth = MinPositive(MinVisibleTileDepth, tile.Depth);
         MaxVisibleTileDepth = MaxPositive(MaxVisibleTileDepth, tile.Depth);
     }
 }
Пример #16
0
        private Unity3DTile LoadTileset(string tilesetUrl, Schema.Tileset tileset, Unity3DTile parentTile)
        {
            if (tileset.Asset == null)
            {
                Debug.LogError("Tileset must have an asset property");
                return(null);
            }
            if (tileset.Asset.Version != "0.0" && tileset.Asset.Version != "1.0")
            {
                Debug.LogError("Tileset must be 3D Tiles version 0.0 or 1.0");
                return(null);
            }
            // Add tileset version to base path
            bool hasVersionQuery = new Regex(@"/[?&]v=/").IsMatch(tilesetUrl);

            if (!hasVersionQuery && !new Uri(tilesetUrl).IsFile)
            {
                string version = "0.0";
                if (tileset.Asset.TilesetVersion != null)
                {
                    version = tileset.Asset.TilesetVersion;
                }
                string versionQuery = "v=" + version;

                this.basePath = UrlUtils.SetQuery(this.basePath, versionQuery);
                tilesetUrl    = UrlUtils.SetQuery(tilesetUrl, versionQuery);
            }
            // A tileset.json referenced from a tile may exist in a different directory than the root tileset.
            // Get the basePath relative to the external tileset.
            string      basePath = UrlUtils.GetBaseUri(tilesetUrl);
            Unity3DTile rootTile = new Unity3DTile(this, basePath, tileset.Root, parentTile);

            Statistics.NumberOfTilesTotal++;

            // Loop through the Tile json data and create a tree of Unity3DTiles
            Stack <Unity3DTile> stack = new Stack <Unity3DTile>();

            stack.Push(rootTile);
            while (stack.Count > 0)
            {
                Unity3DTile tile3D = stack.Pop();
                for (int i = 0; i < tile3D.tile.Children.Count; i++)
                {
                    Unity3DTile child = new Unity3DTile(this, basePath, tile3D.tile.Children[i], tile3D);
                    this.DeepestDepth = Math.Max(child.Depth, this.DeepestDepth);
                    Statistics.NumberOfTilesTotal++;
                    stack.Push(child);
                }
                // TODO consider using CullWithChildrenBounds optimization here
            }
            this.loadTimestamp = DateTime.UtcNow;
            return(rootTile);
        }
Пример #17
0
        public bool Remove(Unity3DTile tile)
        {
            if (!nodeLookup.ContainsKey(tile))
            {
                return(false);
            }
            var node = nodeLookup[tile];

            nodeLookup.Remove(tile);
            list.Remove(node);
            tile.UnloadContent();
            return(true);
        }
 void DebugDrawFrustumSet(Unity3DTile tile)
 {
     if (tile.FrameState.IsUsedThisFrame(this.frameCount) && tile.FrameState.InFrustumSet)
     {
         if (tile.FrameState.IsUsedSetLeaf)
         {
             tile.BoundingVolume.DebugDraw(Color.green, this.tileset.Behaviour.transform);
         }
         for (int i = 0; i < tile.Children.Count; i++)
         {
             DebugDrawFrustumSet(tile.Children[i]);
         }
     }
 }
 void DebugDrawUsedSet(Unity3DTile tile)
 {
     if (tile.FrameState.IsUsedThisFrame)
     {
         if (tile.FrameState.IsUsedSetLeaf)
         {
             tile.BoundingVolume.DebugDraw(Color.white, tileset.Behaviour.transform);
         }
         for (int i = 0; i < tile.Children.Count; i++)
         {
             DebugDrawUsedSet(tile.Children[i]);
         }
     }
 }
 /// <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]);
         }
     }
 }
 void ToggleTiles(Unity3DTile tile)
 {
     // Only consider tiles that were used this frame or the previous frame
     if (tile.FrameState.IsUsedThisFrame || tile.FrameState.UsedLastFrame)
     {
         tile.FrameState.UsedLastFrame = false;
         if (!tile.FrameState.IsUsedThisFrame)
         {
             // This tile was active last frame but isn't active any more
             if (tile.Content != null)
             {
                 tile.Content.SetActive(false);
             }
         }
         else
         {
             // this tile is in the used set this frame
             if (tile.Content != null)
             {
                 tile.Content.SetActive(tile.FrameState.InColliderSet || tile.FrameState.InRenderSet);
                 tile.Content.EnableColliders(tile.FrameState.InColliderSet);
                 if (tilesetOptions.Show)
                 {
                     if (tile.FrameState.InRenderSet)
                     {
                         tile.Content.EnableRenderers(true);
                         tile.Content.SetShadowMode(tilesetOptions.ShadowCastingMode,
                                                    tilesetOptions.RecieveShadows);
                         if (tilesetOptions.Style != null)
                         {
                             tilesetOptions.Style.ApplyStyle(tile);
                         }
                     }
                     else if (tile.FrameState.InColliderSet &&
                              tilesetOptions.ShadowCastingMode != ShadowCastingMode.Off)
                     {
                         tile.Content.SetShadowMode(ShadowCastingMode.ShadowsOnly, false);
                     }
                 }
             }
             tile.FrameState.UsedLastFrame = true;
         }
         for (int i = 0; i < tile.Children.Count; i++)
         {
             ToggleTiles(tile.Children[i]);
         }
     }
 }
 void RequestTile(Unity3DTile tile)
 {
     if (tile.Parent == null)
     {
         tile.RequestContent(tile.FrameState.Priority);
     }
     else
     {
         for (int i = 0; i < tile.Parent.Children.Count; i++)
         {
             if (tile.Parent.Children[i].FrameState.IsUsedThisFrame)
             {
                 tile.Parent.Children[i].RequestContent(tile.FrameState.Priority);
             }
         }
     }
 }
 private void DerateUnusedTilePriority(Unity3DTile tile)
 {
     if (!tile.FrameState.IsUsedThisFrame && !tile.FrameState.UsedLastFrame)
     {
         if (tile.FrameState.LastVisitedFrame < 0)
         {
             tile.FrameState.Priority = float.MaxValue;
         }
         else if (!float.IsNaN(tile.FrameState.Priority) && tile.FrameState.Priority > 0 &&
                  tile.FrameState.Priority < float.MaxValue)
         {
             float maxFrames       = 1000;
             float framesSinceUsed = Time.frameCount - tile.FrameState.LastVisitedFrame;
             if (framesSinceUsed > 0 && framesSinceUsed <= maxFrames)
             {
                 tile.FrameState.Priority *= (maxFrames + framesSinceUsed) / (maxFrames + framesSinceUsed - 1);
             }
         }
     }
 }
 /// <summary>
 /// If the tile has siblings in the used set, request them at the same time since we will need all of
 /// them to split the parent tile.
 /// Set request priority based on the closest sibling, request all siblings with
 /// the same priority since we need all of them to load before any of them can be made visible.
 /// </summary>
 void AssignPrioritiesRecursively(Unity3DTile tile)
 {
     if (tile.FrameState.IsUsedThisFrame)
     {
         if (!tile.HasEmptyContent)
         {
             tile.FrameState.Priority = TilePriority(tile);
         }
         float minChildPriority = float.MaxValue;
         foreach (var child in tile.Children)
         {
             AssignPrioritiesRecursively(child);
             minChildPriority = Mathf.Min(minChildPriority, child.FrameState.Priority);
         }
         foreach (var child in tile.Children)
         {
             child.FrameState.Priority = minChildPriority;
         }
     }
 }
        /// <summary>
        /// Identify the deepest set of tiles that are in the used set this frame and mark them as "used set leaves"
        /// After this point we will not consider any tiles that are beyond a used set leaf.
        /// Leafs are the content we ideally want to show this frame
        /// </summary>
        /// <param name="tile"></param>
        void MarkUsedSetLeaves(Unity3DTile tile)
        {
            // A used leaf is a node that is used but has no children in the used set
            if (!tile.FrameState.IsUsedThisFrame(this.frameCount))
            {
                // Not used this frame, can't be a used leaf and neither can anything beneath us
                return;
            }
            this.tileset.Statistics.UsedSetCount += 1;
            // If any child is used, then we are not a leaf
            bool anyChildrenUsed = false;

            for (int i = 0; i < tile.Children.Count; i++)
            {
                anyChildrenUsed = anyChildrenUsed || tile.Children[i].FrameState.IsUsedThisFrame(this.frameCount);
            }
            if (!anyChildrenUsed || ForceTiles.Contains(tile))
            {
                tile.FrameState.IsUsedSetLeaf = true;
                if (!tile.HasEmptyContent)
                {
                    this.tileset.Statistics.LeafContentRequired++;
                    if (tile.ContentState == Unity3DTileContentState.READY)
                    {
                        this.tileset.Statistics.LeafContentLoaded++;
                    }
                }
            }
            else
            {
                for (int i = 0; i < tile.Children.Count; i++)
                {
                    MarkUsedSetLeaves(tile.Children[i]);
                }
            }
        }
Пример #26
0
 public Request(Unity3DTile tile, Promise started, Promise <bool> finished)
 {
     Tile     = tile;
     Started  = started;
     Finished = finished;
 }
Пример #27
0
        public Unity3DTile(Unity3DTileset tileset, string basePath, Schema.Tile tile, Unity3DTile parent)
        {
            this.hashCode   = (int)Random.Range(0, int.MaxValue);
            this.tileset    = tileset;
            this.tile       = tile;
            this.FrameState = new TileFrameState();
            if (tile.Content != null)
            {
                this.Id = Path.GetFileNameWithoutExtension(tile.Content.Url);
            }
            if (parent != null)
            {
                parent.Children.Add(this);
                this.Depth = parent.Depth + 1;
            }

            // TODO: Consider using a double percision Matrix library for doing 3d tiles root transform calculations
            // Set the local transform for this tile, default to identity matrix
            this.transform = this.tile.UnityTransform();

            var parentTransform = (parent != null) ? parent.computedTransform : Matrix4x4.identity;

            this.computedTransform = parentTransform * this.transform;
            this.BoundingVolume    = CreateBoundingVolume(tile.BoundingVolume, this.computedTransform);
            // TODO: Add 2D bounding volumes

            if (tile.Content != null && tile.Content.BoundingVolume.IsDefined())
            {
                // Non-leaf tiles may have a content bounding-volume, which is a tight-fit bounding volume
                // around only the features in the tile.  This box is useful for culling for rendering,
                // but not for culling for traversing the tree since it does not guarantee spatial coherence, i.e.,
                // since it only bounds features in the tile, not the entire tile, children may be
                // outside of this box.
                this.ContentBoundingVolume = CreateBoundingVolume(tile.Content.BoundingVolume, this.computedTransform);
            }
            else
            {
                // Default to tile bounding volume
                this.ContentBoundingVolume = CreateBoundingVolume(tile.BoundingVolume, this.computedTransform);
            }
            // TODO: Add viewer request volume support
            //if(tile.ViewerRequestVolume != null && tile.ViewerRequestVolume.IsDefined())
            //{
            //    this.viewerRequestVolume = CreateBoundingVolume(tile.ViewerRequestVolume, transform);
            //}

            if (!tile.Refine.HasValue)
            {
                tile.Refine = (parent == null) ? Schema.TileRefine.REPLACE : parent.tile.Refine.Value;
            }

            this.Parent = parent;

            if (this.HasEmptyContent)
            {
                this.ContentState = Unity3DTileContentState.READY;
            }
            else
            {
                ContentState    = Unity3DTileContentState.UNLOADED;
                this.ContentUrl = UriHelper.JoinUrls(basePath, tile.Content.Url);
            }

            this.HasRenderableContent = false;
            this.HasTilesetContent    = false;
        }
        /// <summary>
        /// Traverse the tree, request tiles, and enable visible tiles
        /// Skip parent tiles that have a screen space error larger than MaximumScreenSpaceError*SkipScreenSpaceErrorMultiplier
        /// </summary>
        /// <param name="tile"></param>
        void SkipTraversal(Unity3DTile tile)
        {
            if (!tile.FrameState.IsUsedThisFrame(this.frameCount))
            {
                return;
            }
            if (tile.FrameState.IsUsedSetLeaf)
            {
                if (tile.ContentState == Unity3DTileContentState.READY)
                {
                    if (tile.FrameState.InFrustumSet)
                    {
                        tile.FrameState.InRenderSet = true;
                        tileset.Statistics.TallyVisibleTile(tile);
                    }
                    tile.FrameState.InColliderSet              = true;
                    this.tileset.Statistics.ColliderTileCount += 1;
                }
                else if (this.CanRequest)
                {
                    RequestTile(tile);
                }
                return;
            }

            // Draw a parent tile iff
            // 1) meets SSE cuttoff
            // 2) has content and is not empty
            // 3) one or more of its chidlren don't have content
            bool meetsSSE               = tile.FrameState.ScreenSpaceError < (tileset.TilesetOptions.MaximumScreenSpaceError * tileset.TilesetOptions.SkipScreenSpaceErrorMultiplier);
            bool hasContent             = tile.ContentState == Unity3DTileContentState.READY && !tile.HasEmptyContent;
            bool allChildrenHaveContent = true;

            for (int i = 0; i < tile.Children.Count; i++)
            {
                if (tile.Children[i].FrameState.IsUsedThisFrame(this.frameCount))
                {
                    bool childContent = tile.Children[i].ContentState == Unity3DTileContentState.READY || tile.HasEmptyContent;
                    allChildrenHaveContent = allChildrenHaveContent && childContent;
                }
            }
            if (meetsSSE && !hasContent && this.CanRequest)
            {
                RequestTile(tile);
            }
            if (meetsSSE && hasContent && !allChildrenHaveContent)
            {
                if (tile.FrameState.InFrustumSet)
                {
                    tile.FrameState.InRenderSet = true;
                    tileset.Statistics.TallyVisibleTile(tile);
                }
                tile.FrameState.InColliderSet              = true;
                this.tileset.Statistics.ColliderTileCount += 1;
                // Request children
                for (int i = 0; i < tile.Children.Count; i++)
                {
                    if (tile.Children[i].FrameState.IsUsedThisFrame(this.frameCount) && this.CanRequest)
                    {
                        RequestTile(tile.Children[i]);
                    }
                }
                return;
            }
            // Otherwise keep decending
            for (int i = 0; i < tile.Children.Count; i++)
            {
                if (tile.Children[i].FrameState.IsUsedThisFrame(this.frameCount))
                {
                    SkipTraversal(tile.Children[i]);
                }
            }
        }