public override TilePyramid VisibleTiles(GridRectangle VisibleBounds, double DownSample) { int roundedDownsample = NearestAvailableLevel(DownSample); GridQuad VisibleQuad = null; //Add any corners of the VisibleBounds that we can transform to the list of points List<MappingGridVector2> VisiblePoints = VisibleBoundsCorners(VolumeTransform, VisibleBounds); if (VisiblePoints.Count != 4) { //If we can't map all four corners then add all the points from the transform falling inside the visible rectangle or //connected to those points by an edge List<MappingGridVector2> listTransformPoints = this.VolumeTransform.IntersectingControlRectangle(VisibleBounds, true); VisiblePoints.AddRange(listTransformPoints); } else { VisiblePoints.Sort(new MappingGridVector2SortByMapPoints()); VisibleQuad = new GridQuad(VisiblePoints[0].MappedPoint, VisiblePoints[1].MappedPoint, VisiblePoints[2].MappedPoint, VisiblePoints[3].MappedPoint); } //OK, transform all points falling inside the section border //Starting with low-res tiles, add tiles to the list until we reach desired resolution //List<Tile> TilesToDraw = new List<Tile>(); TilePyramid TilesToDraw = new TilePyramid(VisibleBounds); if (VisiblePoints.Count < 3) return TilesToDraw; GridRectangle SectionBorder = MappingGridVector2.CalculateMappedBounds(VisiblePoints.ToArray()); int iLevel = AvailableLevels.Length - 1; int level = AvailableLevels[iLevel]; do { List<Tile> newTiles = RecursiveVisibleTiles(VisibleBounds, SectionBorder, VisibleQuad, level //PORT: AsynchTextureLoad ); //Insert at the beginning so we overwrite earlier tiles with poorer resolution TilesToDraw.AddTiles(level, newTiles.ToArray()); iLevel--; if (iLevel >= 0) level = AvailableLevels[iLevel]; } while (level >= roundedDownsample && iLevel >= 0); // Trace.WriteLine("Drawing " + TilesToDraw.Count.ToString() + " Tiles", "VolumeModel"); return TilesToDraw; }
protected virtual TilePyramid VisibleTiles(GridRectangle VisibleBounds, GridQuad SectionVisibleBounds, double DownSample) { TilePyramid VisibleTiles = new TilePyramid(VisibleBounds); //Setup a larger boundary outside of which we release textures GridRectangle releaseBounds = VisibleBounds; //Tiles outside this quad will have textures released GridRectangle loadBounds = VisibleBounds; //Tiles inside this quad will have textures loaded GridRectangle abortBounds = VisibleBounds; //Tiles outside this quad will have HTTP requests aborted releaseBounds.Scale(1.25 * DownSample); loadBounds.Scale(1.1f); abortBounds.Scale(1.20f * DownSample); //Get ready by loading a smaller texture in case the user scrolls this direction //Once we have smaller textures then increase the quality // int predictiveDownsample = DownSample * 4 > 64 ? 64 : (int)DownSample * 4; int roundedDownsample = NearestAvailableLevel(DownSample); ReferencePointBasedTransform[] Tranforms = this.TileTransforms; if (TileTransforms == null) return VisibleTiles; int ExpectedTileCount = Tranforms.Length; List<Tile> TilesToDraw = new List<Tile>(ExpectedTileCount); // List<Tile> TilesToLoad = new List<Tile>(ExpectedTileCount); foreach (TriangulationTransform T in Tranforms) { //If this tile has been transformed out of existence then skip it if (T.MapPoints.Length < 3) continue; if (VisibleBounds.Intersects(T.ControlBounds)) { // bool LoadOnly = false; TileTransformInfo info = T.Info as TileTransformInfo; string name = TileFileName(info.TileFileName, roundedDownsample); /* if (SectionVisibleBounds != null) { GridRectangle MosaicPosition = new GridRectangle(T.mapPoints[0].ControlPoint, T.ImageWidth, T.ImageHeight); if (SectionVisibleBounds.Contains(MosaicPosition) == false) { name = TileFileName(T.Number,predictiveDownsample); LoadOnly = true; continue; } } */ string UniqueID = Tile.CreateUniqueKey(Section.Number, Name, CurrentPyramid.Name, roundedDownsample, info.TileFileName); Tile tile = Global.TileCache.Fetch(UniqueID); if(tile == null) { int MipMapLevels = 1; //No mip maps if (roundedDownsample == this.AvailableLevels[AvailableLevels.Length - 1]) MipMapLevels = 0; //Generate mipmaps for lowest res texture //First create a new tile PositionNormalTextureVertex[] verticies = Tile.CalculateVerticies(T, info); if(T.TriangleIndicies != null) { tile = Global.TileCache.ConstructTile(UniqueID, verticies, T.TriangleIndicies, this.TilePath + '/' + name, name, //PORT: TileCacheName(T.Number, roundedDownsample), this.Name, roundedDownsample, MipMapLevels);//T.ImageHeight * T.ImageWidth / roundedDownsample); } } if(tile != null) VisibleTiles.AddTile(roundedDownsample, tile); /* if (LoadOnly) { TilesToLoad.Add(tile); continue; } */ //I used to call draw here, but when exporting frames I want to have all tiles launch threads to load thier textures and then wait. //It is much faster than doing one texture at a time //PORT: Removed //tile.GetTexture(graphicsDevice, true); TilesToDraw.Add(tile); //PORT: Modified to return all tiles of lower resolution /* //See if any tiles of alternate resolution are already loaded //Use them if the correct resolution doesn't have a texture, otherwise abort any requests bool TextureFound = tile.HasTexture; foreach(int testLevel in UI.State.DownsampleLevels) { if(testLevel == roundedDownsample) continue; string altResName = TileFileName(T.Number, testLevel); Tile altResTile = Global.TileCache.GetTile(altResName, this.Name); if (altResTile != null) { //Stop the network request if we've already got a texture //TODO: Once we have a list of textures to draw, send it to TextureConstructor //and have it abort textures not in the list if (TextureFound) altResTile.AbortRequest(); else { if (altResTile.HasTexture) { TilesToDraw.Add(altResTile); TextureFound = true; } } } } */ } /*PORT We no longer handle tiles to be loaded and the aborting tiles here else { if (loadBounds.Intersects(T.CachedControlBounds)) { string name = TileFileName(T.Number, predictiveDownsample); Tile tile = Global.TileCache.GetTile(name, this.Name); if(tile == null) { //First create a new tile int MipMapLevels = 1; //No mip maps if (roundedDownsample == this.AvailableLevels[AvailableLevels.Length - 1]) MipMapLevels = 0; //Generate mipmaps for lowest res texture VertexPositionNormalTexture[] verticies = Tile.CalculateVerticies(T); tile = Global.TileCache.ConstructTile(verticies, T.TriangleIndicies, name, TileCacheName(T.Number, predictiveDownsample), this.Name, roundedDownsample, MipMapLevels, T.ImageHeight * T.ImageWidth / predictiveDownsample); } TilesToLoad.Add(tile); } //Sometimes tiles can overlap both zones, so else if is used else if (abortBounds.Intersects(T.CachedControlBounds) == false) { foreach (int testLevel in UI.State.DownsampleLevels) { string AbortTileName = TileFileName(T.Number, testLevel); Tile tile = Global.TileCache.GetTile(AbortTileName, this.Name); if (tile != null) { tile.AbortRequest(); } } } } */ } /*PORT Model does not load textures foreach (Tile tile in TilesToLoad) { //We are asking for a lower quality texture, but tile will keep any existing higher quality textures if (AsynchTextureLoad) tile.GetTexture(graphicsDevice, true); } //TODO: Give a list of tiles to the TileConstructor that are OK to have outstanding load requests. //All other tiles will be aborted List<Tile> SafeTiles = new List<Tile>(TilesToLoad.Count + TilesToDraw.Count); SafeTiles.AddRange(TilesToLoad); SafeTiles.AddRange(TilesToDraw); Trace.WriteLine("Drawing " + TilesToDraw.Count.ToString() + " Tiles", "VolumeModel"); return TilesToDraw.ToArray(); */ return VisibleTiles; }
public override TilePyramid VisibleTiles(GridRectangle VisibleBounds, double DownSample) { TilePyramid VisibleTiles = new TilePyramid(VisibleBounds); int roundedDownsample = NearestAvailableLevel(DownSample); //Starting with low-res tiles, add tiles to the list until we reach desired resolution // List<Tile> TilesToDraw = new List<Tile>(); //Find the starting level of our rendering int iLevel = AvailableLevels.Length - 1; int level = AvailableLevels[iLevel]; do { List<Tile> newTiles = RecursiveVisibleTiles( VisibleBounds, level //PORT: AsynchTextureLoad ); //Insert at the beginning so we overwrite earlier tiles with poorer resolution VisibleTiles.AddTiles(level, newTiles); //TilesToDraw.AddRange(newTiles); iLevel--; if(iLevel >= 0) level = AvailableLevels[iLevel]; } while (level >= roundedDownsample && iLevel >= 0); //Trace.WriteLine("Drawing " + TilesToDraw.Count.ToString() + " Tiles", "VolumeModel"); return VisibleTiles; }
protected void UpdateTiles(Rect visRect, double downsample) { System.Diagnostics.Trace.WriteLine("UpdateTiles()"); if (visRect.Width == 0 || visRect.Height == 0) return; //Find which tiles changed //Rect visRect = this.VisibleRect; Geometry.GridRectangle visibleGridRect = new Geometry.GridRectangle(visRect.Left, visRect.Right, visRect.Top, visRect.Bottom); TilePyramid oldTilePyramid = this.TilePyramid; Trace.WriteLine(TileMapping.ToString()); TilePyramid newTilePyramid = TileMapping.VisibleTiles(visibleGridRect, downsample); if (oldTilePyramid != null) { if (false == oldTilePyramid.Bounds.Intersects(newTilePyramid.Bounds)) Tiles.Clear(); else { //Might be able to really speed up culling dead tiles with QuadTrees //Sort out which of the old tiles are no longer visible for (int iTile = 0; iTile < Tiles.Count; iTile++) { TileViewModel t = Tiles[iTile]; if (false == visibleGridRect.Intersects(t.Bounds)) { Tiles.RemoveAt(iTile); iTile--; } else { //SortedDictionary<string, Tile> tilesForLevel = newTilePyramid.GetTilesForLevel(t.Downsample); } } } } //Create tiles which are new for(int iLevel = newTilePyramid.AvailableLevels.Length -1; iLevel >= 0; iLevel--) //for (int iLevel = 0; iLevel >= 0; iLevel--) //for (int iLevel = 0; iLevel < newTilePyramid.AvailableLevels.Length; iLevel++) { int level = newTilePyramid.AvailableLevels[iLevel]; SortedDictionary<string, Tile> tiles = newTilePyramid.GetTilesForLevel(level); bool IgnoreOldPyramid = oldTilePyramid == null; //Don't check the old pyramid if it doesn't have the downsample level we are looking at if (false == IgnoreOldPyramid) { if (oldTilePyramid.AvailableLevels.Length == 0) { IgnoreOldPyramid = true; } else { IgnoreOldPyramid = level < oldTilePyramid.AvailableLevels.Min(); if (false == IgnoreOldPyramid) { IgnoreOldPyramid = level > oldTilePyramid.AvailableLevels.Max(); } } } foreach (Tile t in tiles.Values) { //We already loaded this one // if (!IgnoreOldPyramid && oldTilePyramid.Bounds.Intersects(t.Bounds)) // continue; // else { TileViewModel tileViewModel = null; tileViewModel = TileCache.Fetch(t.UniqueKey); if (tileViewModel == null) { tileViewModel = new TileViewModel(t, TileMapping.TilePath); TileCache.Add(t.UniqueKey, tileViewModel); } // else // { //Don't add it to the collection again if (Tiles.Contains(tileViewModel)) continue; //} this.Tiles.Add(tileViewModel); } } } //This is horrible, but I do it to trigger the Dependancy property listeners because 3D collections don't subribe to the collection change notifications ObservableCollection<TileViewModel> newTiles = new ObservableCollection<TileViewModel>(this.Tiles); this.Tiles = newTiles; TilePyramid = newTilePyramid; }