UNPKG

maplibre-gl

Version:

BSD licensed community fork of mapbox-gl, a WebGL interactive maps library

304 lines (287 loc) 12.6 kB
import {type OverscaledTileID} from './tile_id'; import {Tile} from './tile'; import {EXTENT} from '../data/extent'; import {mat4} from 'gl-matrix'; import {Evented} from '../util/evented'; import type {ITransform} from '../geo/transform_interface'; import type {SourceCache} from '../source/source_cache'; import {type Terrain} from '../render/terrain'; import {browser} from '../util/browser'; import {coveringTiles} from '../geo/projection/covering_tiles'; import {createMat4f64} from '../util/util'; import {type CanonicalTileRange} from './image_source'; /** * @internal * This class is a helper for the Terrain-class, it: * * - loads raster-dem tiles * - manages all renderToTexture tiles. * - caches previous rendered tiles. * - finds all necessary renderToTexture tiles for a OverscaledTileID area * - finds the corresponding raster-dem tile for OverscaledTileID */ export class TerrainSourceCache extends Evented { /** * source-cache for the raster-dem source. */ sourceCache: SourceCache; /** * stores all render-to-texture tiles. */ _tiles: {[_: string]: Tile}; /** * contains a list of tileID-keys for the current scene. (only for performance) */ _renderableTilesKeys: Array<string>; /** * raster-dem-tile for a TileID cache. */ _sourceTileCache: {[_: string]: string}; /** * minimum zoomlevel to render the terrain. */ minzoom: number; /** * maximum zoomlevel to render the terrain. */ maxzoom: number; /** * render-to-texture tileSize in scene. */ tileSize: number; /** * raster-dem tiles will load for performance the actualZoom - deltaZoom zoom-level. */ deltaZoom: number; /** * used to determine whether depth & coord framebuffers need updating */ _lastTilesetChange: number = browser.now(); constructor(sourceCache: SourceCache) { super(); this.sourceCache = sourceCache; this._tiles = {}; this._renderableTilesKeys = []; this._sourceTileCache = {}; this.minzoom = 0; this.maxzoom = 22; this.deltaZoom = 1; this.tileSize = sourceCache._source.tileSize * 2 ** this.deltaZoom; sourceCache.usedForTerrain = true; sourceCache.tileSize = this.tileSize; } destruct() { this.sourceCache.usedForTerrain = false; this.sourceCache.tileSize = null; } /** * Load Terrain Tiles, create internal render-to-texture tiles, free GPU memory. * @param transform - the operation to do * @param terrain - the terrain */ update(transform: ITransform, terrain: Terrain): void { // load raster-dem tiles for the current scene. this.sourceCache.update(transform, terrain); // create internal render-to-texture tiles for the current scene. this._renderableTilesKeys = []; const keys = {}; for (const tileID of coveringTiles(transform, { tileSize: this.tileSize, minzoom: this.minzoom, maxzoom: this.maxzoom, reparseOverscaled: false, terrain, calculateTileZoom: this.sourceCache._source.calculateTileZoom })) { keys[tileID.key] = true; this._renderableTilesKeys.push(tileID.key); if (!this._tiles[tileID.key]) { tileID.terrainRttPosMatrix32f = new Float64Array(16) as any; mat4.ortho(tileID.terrainRttPosMatrix32f, 0, EXTENT, EXTENT, 0, 0, 1); this._tiles[tileID.key] = new Tile(tileID, this.tileSize); this._lastTilesetChange = browser.now(); } } // free unused tiles for (const key in this._tiles) { if (!keys[key]) delete this._tiles[key]; } } /** * Free render to texture cache * @param tileID - optional, free only corresponding to tileID. */ freeRtt(tileID?: OverscaledTileID) { for (const key in this._tiles) { const tile = this._tiles[key]; if (!tileID || tile.tileID.equals(tileID) || tile.tileID.isChildOf(tileID) || tileID.isChildOf(tile.tileID)) tile.rtt = []; } } /** * get a list of tiles, which are loaded and should be rendered in the current scene * @returns the renderable tiles */ getRenderableTiles(): Array<Tile> { return this._renderableTilesKeys.map(key => this.getTileByID(key)); } /** * get terrain tile by the TileID key * @param id - the tile id * @returns the tile */ getTileByID(id: string): Tile { return this._tiles[id]; } /** * Searches for the corresponding current renderable terrain-tiles * @param tileID - the tile to look for * @returns the tiles that were found */ getTerrainCoords( tileID: OverscaledTileID, terrainTileRanges?: {[zoom: string]: CanonicalTileRange} ): Record<string, OverscaledTileID> { if (terrainTileRanges) { return this._getTerrainCoordsForTileRanges(tileID, terrainTileRanges); } else { return this._getTerrainCoordsForRegularTile(tileID); } } /** * Searches for the corresponding current renderable terrain-tiles. * Includes terrain tiles that are either: * - the same as the tileID * - a parent of the tileID * - a child of the tileID * @param tileID - the tile to look for * @returns the tiles that were found */ _getTerrainCoordsForRegularTile(tileID: OverscaledTileID): Record<string, OverscaledTileID> { const coords: Record<string, OverscaledTileID> = {}; for (const key of this._renderableTilesKeys) { const terrainTileID = this._tiles[key].tileID; const coord = tileID.clone(); const mat = createMat4f64(); if (terrainTileID.canonical.equals(tileID.canonical)) { mat4.ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1); } else if (terrainTileID.canonical.isChildOf(tileID.canonical)) { const dz = terrainTileID.canonical.z - tileID.canonical.z; const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz); const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz); const size = EXTENT >> dz; mat4.ortho(mat, 0, size, size, 0, 0, 1); // Note: we are using `size` instead of `EXTENT` here mat4.translate(mat, mat, [-dx * size, -dy * size, 0]); } else if (tileID.canonical.isChildOf(terrainTileID.canonical)) { const dz = tileID.canonical.z - terrainTileID.canonical.z; const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz); const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz); const size = EXTENT >> dz; mat4.ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1); mat4.translate(mat, mat, [dx * size, dy * size, 0]); mat4.scale(mat, mat, [1 / (2 ** dz), 1 / (2 ** dz), 0]); } else { continue; } coord.terrainRttPosMatrix32f = new Float32Array(mat); coords[key] = coord; } return coords; } /** * Searches for the corresponding current renderable terrain-tiles. * Includes terrain tiles that are within terrain tile ranges. * @param tileID - the tile to look for * @returns the tiles that were found */ _getTerrainCoordsForTileRanges( tileID: OverscaledTileID, terrainTileRanges: {[zoom: string]: CanonicalTileRange} ): Record<string, OverscaledTileID> { const coords: Record<string, OverscaledTileID> = {}; for (const key of this._renderableTilesKeys) { const terrainTileID = this._tiles[key].tileID; if (!this._isWithinTileRanges(terrainTileID, terrainTileRanges)) { continue; } const coord = tileID.clone(); const mat = createMat4f64(); if (terrainTileID.canonical.z === tileID.canonical.z) { const dx = tileID.canonical.x - terrainTileID.canonical.x; const dy = tileID.canonical.y - terrainTileID.canonical.y; mat4.ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1); mat4.translate(mat, mat, [dx * EXTENT, dy * EXTENT, 0]); } else if (terrainTileID.canonical.z > tileID.canonical.z) { const dz = terrainTileID.canonical.z - tileID.canonical.z; // this translation is needed to project tileID to terrainTileID zoom level const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz); const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz); // this translation is needed if terrainTileID is not a parent of tileID const dx2 = tileID.canonical.x - (terrainTileID.canonical.x >> dz); const dy2 = tileID.canonical.y - (terrainTileID.canonical.y >> dz); const size = EXTENT >> dz; mat4.ortho(mat, 0, size, size, 0, 0, 1); mat4.translate(mat, mat, [-dx * size + dx2 * EXTENT, -dy * size + dy2 * EXTENT, 0]); } else { // terrainTileID.canonical.z < tileID.canonical.z const dz = tileID.canonical.z - terrainTileID.canonical.z; // this translation is needed to project tileID to terrainTileID zoom level const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz); const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz); // this translation is needed if terrainTileID is not a parent of tileID const dx2 = (tileID.canonical.x >> dz) - terrainTileID.canonical.x; const dy2 = (tileID.canonical.y >> dz) - terrainTileID.canonical.y; const size = EXTENT << dz; mat4.ortho(mat, 0, size, size, 0, 0, 1); mat4.translate(mat, mat, [dx * EXTENT + dx2 * size, dy * EXTENT + dy2 * size, 0]); } coord.terrainRttPosMatrix32f = new Float32Array(mat); coords[key] = coord; } return coords; } /** * find the covering raster-dem tile * @param tileID - the tile to look for * @param searchForDEM - Optional parameter to search for (parent) source tiles with loaded dem. * @returns the tile */ getSourceTile(tileID: OverscaledTileID, searchForDEM?: boolean): Tile { const source = this.sourceCache._source; let z = tileID.overscaledZ - this.deltaZoom; if (z > source.maxzoom) z = source.maxzoom; if (z < source.minzoom) return null; // cache for tileID to terrain-tileID if (!this._sourceTileCache[tileID.key]) this._sourceTileCache[tileID.key] = tileID.scaledTo(z).key; let tile = this.sourceCache.getTileByID(this._sourceTileCache[tileID.key]); // during tile-loading phase look if parent tiles (with loaded dem) are available. if (!(tile && tile.dem) && searchForDEM) while (z >= source.minzoom && !(tile && tile.dem)) tile = this.sourceCache.getTileByID(tileID.scaledTo(z--).key); return tile; } /** * gets whether any tiles were loaded after a specific time. This is used to update depth & coords framebuffers. * @param time - the time * @returns true if any tiles came into view at or after the specified time */ anyTilesAfterTime(time = Date.now()): boolean { return this._lastTilesetChange >= time; } /** * Checks whether a tile is within the canonical tile ranges. * @param tileID - Tile to check * @param canonicalTileRanges - Canonical tile ranges * @returns */ private _isWithinTileRanges( tileID: OverscaledTileID, canonicalTileRanges: {[zoom: string]: CanonicalTileRange} ): boolean { return canonicalTileRanges[tileID.canonical.z] && tileID.canonical.x >= canonicalTileRanges[tileID.canonical.z].minTileX && tileID.canonical.x <= canonicalTileRanges[tileID.canonical.z].maxTileX && tileID.canonical.y >= canonicalTileRanges[tileID.canonical.z].minTileY && tileID.canonical.y <= canonicalTileRanges[tileID.canonical.z].maxTileY; } }