diff --git a/packages/engine/Source/Scene/GlobeSurfaceTile.js b/packages/engine/Source/Scene/GlobeSurfaceTile.js index cb250ccd1df..84e818d34ac 100644 --- a/packages/engine/Source/Scene/GlobeSurfaceTile.js +++ b/packages/engine/Source/Scene/GlobeSurfaceTile.js @@ -37,7 +37,8 @@ import TerrainState from "./TerrainState.js"; /** @import TerrainProvider from "../Core/TerrainProvider.js"; */ /** @import TileBoundingRegion from "./TileBoundingRegion.js"; */ /** @import TileImagery from "./TileImagery.js"; */ -/** @import VectorProvider, { VectorTileData } from "../Core/VectorProvider.js"; */ +/** @import VectorProvider from "../Core/VectorProvider.js"; */ +/** @import { VectorTileData } from "../Core/VectorPipeline.js"; */ /** * Contains additional information about a {@link QuadtreeTile} of the globe's surface, and @@ -67,6 +68,12 @@ class GlobeSurfaceTile { /** @type {VectorTileData} */ this.vectorData = undefined; + /** + * Clipping polygons build on top of the vector tile data system (both rely on the same rendering technique). + * @type {VectorTileData} + */ + this.clippingPolygonData = undefined; + /** @type {VertexArray} */ this.vertexArray = undefined; @@ -169,6 +176,11 @@ class GlobeSurfaceTile { this.vectorData = undefined; } + if (defined(this.clippingPolygonData)) { + VectorPipeline.freeResources(this.clippingPolygonData); + this.clippingPolygonData = undefined; + } + this.terrainData = undefined; this.terrainState = TerrainState.UNLOADED; diff --git a/packages/engine/Source/Scene/GlobeSurfaceTileProvider.js b/packages/engine/Source/Scene/GlobeSurfaceTileProvider.js index f97cc62e24d..048dd94b261 100644 --- a/packages/engine/Source/Scene/GlobeSurfaceTileProvider.js +++ b/packages/engine/Source/Scene/GlobeSurfaceTileProvider.js @@ -209,6 +209,12 @@ class GlobeSurfaceTileProvider { */ this._clippingPolygons = undefined; + this._clippingPolygonsDirty = false; + + this._removeClippingPolygonAdded = undefined; + + this._removeClippingPolygonRemoved = undefined; + /** * A property specifying a {@link Rectangle} used to selectively limit terrain and imagery rendering. * @type {Rectangle} @@ -348,7 +354,33 @@ class GlobeSurfaceTileProvider { } set clippingPolygons(value) { + if (value === this._clippingPolygons) { + return; + } + ClippingPolygonCollection.setOwner(value, this, "_clippingPolygons"); + + // First, remove the previous listeners if they exist + this._removeClippingPolygonAdded = + this._removeClippingPolygonAdded && this._removeClippingPolygonAdded(); + this._removeClippingPolygonRemoved = + this._removeClippingPolygonRemoved && + this._removeClippingPolygonRemoved(); + + this._clippingPolygonsDirty = true; + + if (!defined(value)) { + return; + } + + const markDirty = () => { + this._clippingPolygonsDirty = true; + }; + + this._removeClippingPolygonAdded = + value.polygonAdded.addEventListener(markDirty); + this._removeClippingPolygonRemoved = + value.polygonRemoved.addEventListener(markDirty); } /** @@ -381,6 +413,21 @@ class GlobeSurfaceTileProvider { ); } + const clippingPolygons = this._clippingPolygons; + if (defined(clippingPolygons) && this._clippingPolygonsDirty) { + this._quadtree.forEachLoadedTile((tile) => { + const surfaceTile = /** @type {GlobeSurfaceTile} */ (tile.data); + if (defined(surfaceTile?.clippingPolygonData)) { + ClippingPolygonCollection.releaseRectangleData( + surfaceTile.clippingPolygonData, + ); + surfaceTile.clippingPolygonData = undefined; + } + }); + + this._clippingPolygonsDirty = false; + } + // Record regions dirtied by changed collections, re-bake overlapping // tiles, and build vector data for new surface tiles. const vectorProvider = this._vectorProvider; @@ -410,6 +457,30 @@ class GlobeSurfaceTileProvider { ); vectorProvider.makeClean(); + // Similarly, for clipping polygons, re-request data as needed + if (defined(clippingPolygons)) { + this._quadtree.forEachRenderedTile( + /** @param {QuadtreeTile} tile */ + (tile) => { + if (!tile.isClipped) { + return; + } + + const surfaceTile = /** @type {GlobeSurfaceTile} */ (tile.data); + // The surface tile's clipping polygon data is cleared above whenever the clipping polygon collection changes. + if (defined(surfaceTile.clippingPolygonData)) { + return; + } + + surfaceTile.clippingPolygonData = + clippingPolygons.requestRectangleData( + tile.rectangle, + frameState.context, + ); + }, + ); + } + // Add credits for terrain and imagery providers. updateCredits(this, frameState); @@ -1171,6 +1242,11 @@ class GlobeSurfaceTileProvider { this._removeLayerMovedListener && this._removeLayerMovedListener(); this._removeLayerShownListener = this._removeLayerShownListener && this._removeLayerShownListener(); + this._removeClippingPolygonAdded = + this._removeClippingPolygonAdded && this._removeClippingPolygonAdded(); + this._removeClippingPolygonRemoved = + this._removeClippingPolygonRemoved && + this._removeClippingPolygonRemoved(); return destroyObject(this); }