From ed20f55584fdd9032042b566cf6573385f274387 Mon Sep 17 00:00:00 2001 From: Matt Schwartz Date: Fri, 17 Jul 2026 10:49:57 -0400 Subject: [PATCH 1/3] Refactors polygon rendering code for readability --- CLAUDE.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000..cb4ddc0834d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,6 @@ +# Project Instructions + +1. When authoring new code, comments should explain the _why_, not the _what_. Most comments should be brief, reserving longer comments for exceptional cases. Comments should not be so specific that they need to change frequently when the surrounding code changes. +2. Prefer the if-guard pattern / early-return from functions rather and avoid nested or complex conditionals when possible. +3. Prefer new-lines between blocks for readability. +4. After authoring new code, as a final step ask yourself: is this code as simple and readable as it can be without hurting performance? Pretend someone else wrote the code and you're the reviewer. If you can simplify it, do so. From 8ff468e14dcf2845d117066f6a78dc966eac65bf Mon Sep 17 00:00:00 2001 From: Matt Schwartz Date: Fri, 17 Jul 2026 16:48:11 -0400 Subject: [PATCH 2/3] Updates VectorPipeline signature to take ellipsoid directly --- CLAUDE.md | 6 ------ packages/engine/Source/Core/VectorPipeline.js | 10 ++++------ packages/engine/Source/Core/VectorProvider.js | 7 ++++--- 3 files changed, 8 insertions(+), 15 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index cb4ddc0834d..00000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,6 +0,0 @@ -# Project Instructions - -1. When authoring new code, comments should explain the _why_, not the _what_. Most comments should be brief, reserving longer comments for exceptional cases. Comments should not be so specific that they need to change frequently when the surrounding code changes. -2. Prefer the if-guard pattern / early-return from functions rather and avoid nested or complex conditionals when possible. -3. Prefer new-lines between blocks for readability. -4. After authoring new code, as a final step ask yourself: is this code as simple and readable as it can be without hurting performance? Pretend someone else wrote the code and you're the reviewer. If you can simplify it, do so. diff --git a/packages/engine/Source/Core/VectorPipeline.js b/packages/engine/Source/Core/VectorPipeline.js index d3c0083488a..cd1241cac2f 100644 --- a/packages/engine/Source/Core/VectorPipeline.js +++ b/packages/engine/Source/Core/VectorPipeline.js @@ -111,11 +111,11 @@ const scratchSegmentEnd = new Cartesian2(); class VectorPipeline { /** * @param {BufferPolylineCollection} collection - * @param {TilingScheme} tilingScheme + * @param {Ellipsoid} ellipsoid * @param {VectorCollectionData} [result] * @returns {VectorCollectionData} */ - static packPolylineCollectionData(collection, tilingScheme, result) { + static packPolylineCollectionData(collection, ellipsoid, result) { if ( defined(result) && collection._dirtyCount === 0 && @@ -126,7 +126,6 @@ class VectorPipeline { const primitiveCount = collection.primitiveCount; const boundingVolume = collection.boundingVolume; - const ellipsoid = tilingScheme.ellipsoid; const rectangle = Rectangle.fromBoundingSphere(boundingVolume, ellipsoid); const positions = _getProjectedPositions(collection, ellipsoid); @@ -330,11 +329,11 @@ class VectorPipeline { /** * @param {BufferPolygonCollection} collection - * @param {TilingScheme} tilingScheme + * @param {Ellipsoid} ellipsoid * @param {VectorCollectionData} [result] * @returns {VectorCollectionData} */ - static packPolygonCollectionData(collection, tilingScheme, result) { + static packPolygonCollectionData(collection, ellipsoid, result) { if ( defined(result) && collection._dirtyCount === 0 && @@ -345,7 +344,6 @@ class VectorPipeline { const primitiveCount = collection.primitiveCount; const boundingVolume = collection.boundingVolume; - const ellipsoid = tilingScheme.ellipsoid; const rectangle = Rectangle.fromBoundingSphere(boundingVolume, ellipsoid); const positions = _getProjectedPositions(collection, ellipsoid); diff --git a/packages/engine/Source/Core/VectorProvider.js b/packages/engine/Source/Core/VectorProvider.js index 003c65450a6..e3b85ac1bae 100644 --- a/packages/engine/Source/Core/VectorProvider.js +++ b/packages/engine/Source/Core/VectorProvider.js @@ -24,7 +24,7 @@ const scratchIntersectRectangle = new Rectangle(); * * @callback PackCollectionData * @param {*} collection - * @param {TilingScheme} tilingScheme + * @param {Ellipsoid} ellipsoid * @param {VectorCollectionData} [result] * @returns {VectorCollectionData} * @private @@ -370,13 +370,14 @@ class VectorProvider { return cache; } - const data = packCollectionData(collection, this._tilingScheme, cache); + const ellipsoid = this.ellipsoid; + const data = packCollectionData(collection, ellipsoid, cache); // If dirty, the version increments +1 when marked clean below. data.version = collection._version + (dirty ? 1 : 0); data.rectangle = Rectangle.fromBoundingSphere( collection.boundingVolume, - this.ellipsoid, + ellipsoid, data.rectangle, ); this._collectionDataCache.set(collection, data); From ac30d3b87dacb85fcdde522bfbf5f5cf425f1f6d Mon Sep 17 00:00:00 2001 From: Matt Schwartz Date: Fri, 17 Jul 2026 19:48:59 -0400 Subject: [PATCH 3/3] Adds internal API for creating vector textures for clipping --- .../Source/Scene/ClippingPolygonCollection.js | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/packages/engine/Source/Scene/ClippingPolygonCollection.js b/packages/engine/Source/Scene/ClippingPolygonCollection.js index a4271e49473..8fbcfd37528 100644 --- a/packages/engine/Source/Scene/ClippingPolygonCollection.js +++ b/packages/engine/Source/Scene/ClippingPolygonCollection.js @@ -25,10 +25,16 @@ import Pass from "../Renderer/Pass.js"; import BufferPolygonCollection from "./BufferPolygonCollection.js"; import BufferPrimitiveCollection from "./BufferPrimitiveCollection.js"; import BufferPolygon from "./BufferPolygon.js"; +import Ellipsoid from "../Core/Ellipsoid.js"; +import VectorPipeline from "../Core/VectorPipeline.js"; + +/** @import VectorCollectionData from "../Core/VectorPipeline.js" */ // Reused flyweight for reading/writing individual BufferPolygons. const bufferPolygonScratch = new BufferPolygon(); +const scratchRectangle = new Rectangle(); + /** * Specifies a set of clipping polygons. Clipping polygons selectively disable rendering in a region * inside or outside the specified list of {@link ClippingPolygon} objects for a single glTF model, 3D Tileset, or the globe. @@ -43,6 +49,7 @@ const bufferPolygonScratch = new BufferPolygon(); * @param {boolean} [options.enabled=true] Determines whether the clipping polygons are active. * @param {boolean} [options.inverse=false] If true, a region will be clipped if it is outside of every polygon in the collection. Otherwise, a region will only be clipped if it is on the inside of any polygon. * @param {number} [options.quality=1.0] A scalar that controls the resolution of the signed distance texture used for clipping. Values greater than 1.0 increase quality, values less than 1.0 decrease it. Must be greater than 0.0. + * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.default] The ellipsoid to use to project the clipping polygons onto the globe. * * @example * const positions = Cesium.Cartesian3.fromRadiansArray([ @@ -167,6 +174,13 @@ function ClippingPolygonCollection(options) { */ this.polygonRemoved = new Event(); + /** + * The ellipsoid to use to project the clipping polygons onto the globe. + * @type {Ellipsoid} + * @default Ellipsoid.default + */ + this.ellipsoid = options.ellipsoid ?? Ellipsoid.default; + // If this ClippingPolygonCollection has an owner, only its owner should update or destroy it. // This is because in a Cesium3DTileset multiple models may reference the tileset's ClippingPolygonCollection. this._owner = undefined; @@ -180,6 +194,14 @@ function ClippingPolygonCollection(options) { this._signedDistanceTexture = undefined; this._signedDistanceComputeCommand = undefined; + + /** + * @type {VectorCollectionData} + */ + this._vectorCollectionData = VectorPipeline.packPolygonCollectionData( + this._bufferPolygonCollection, + this.ellipsoid, + ); } Object.defineProperties(ClippingPolygonCollection.prototype, { @@ -938,6 +960,12 @@ ClippingPolygonCollection.prototype.update = function (frameState) { } this._signedDistanceComputeCommand = createSignedDistanceTextureCommand(this); + + // Update the vector polygon data + this._vectorCollectionData = VectorPipeline.packPolygonCollectionData( + this._bufferPolygonCollection, + this.ellipsoid, + ); }; function createDebugCommand(texture, context) { @@ -1110,6 +1138,62 @@ ClippingPolygonCollection.setOwner = function ( } }; +/** + * Compute data and pack into textures used for vector-style clipping. + * Consumers must listen to {@link ClippingPolygonCollection#polygonAdded} and {@link ClippingPolygonCollection#polygonRemoved} + * to know when to release stale data and request new data for a given rectangle. + * + * @param {Rectangle} rectangle The region of space to to consider for clipping. Polygons outside of this rectangle + * will not be included in the returned data. + * @param {Context} context The context to use for creating textures. + * @returns {VectorTileData} The data (including textures) for the clipping polygons in the specified rectangle. + * + * @ignore + */ +ClippingPolygonCollection.prototype.requestRectangleData = function ( + rectangle, + context, +) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("rectangle", rectangle); + Check.typeOf.object("context", context); + //>>includeEnd('debug'); + + const vectorTileData = {}; + + // Early out if there are no polygons or the rectangle does not intersect the collection's bounding rectangle + const collectionRectangle = this._vectorCollectionData.rectangle; + if ( + this.length === 0 || + !Rectangle.intersection(rectangle, collectionRectangle, scratchRectangle) + ) { + return vectorTileData; + } + + VectorPipeline.packPolygonRings( + this._bufferPolygonCollection, + this._vectorCollectionData, + rectangle, + vectorTileData, + ); + + VectorPipeline.packPolygonGrid(vectorTileData); + + VectorPipeline.packPolygonTextures(context, vectorTileData); + + return vectorTileData; +}; + +/** + * Destroy resources associated with the given rectangle data. + * + * @param {VectorTileData} rectangleData The data (including textures) for the clipping polygons in the specified rectangle. + * @ignore + */ +ClippingPolygonCollection.prototype.releaseRectangleData = function (data) { + VectorPipeline.freeResources(data); +}; + /** * Function for checking if the context will allow clipping polygons, which require floating point textures. *