diff --git a/CHANGES.md b/CHANGES.md index 66ad21a4760..6707de54eec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,7 @@ - Fix issues with label background when updating properties while `label.show` is `false`. [#12138](https://github.com/CesiumGS/cesium/issues/12138) - Improved performance of `scene.drillPick`. [#12916](https://github.com/CesiumGS/cesium/pull/12916) - Improved performance when removing primitives. [#3018](https://github.com/CesiumGS/cesium/pull/3018) +- Billboards using `imageSubRegion` now render as expected. [#12585](https://github.com/CesiumGS/cesium/issues/12585) - Improved performance of terrain Quadtree handling of custom data [#12907](https://github.com/CesiumGS/cesium/pull/12907) - Fixed picking of `GroundPrimitive` with multiple `PolygonGeometry` instances selecting the wrong instance. [#12978](https://github.com/CesiumGS/cesium/pull/12978) - Fixed a bug where the removal of draped imagery layers did not update the rendered state [#12923](https://github.com/CesiumGS/cesium/issues/12923) diff --git a/packages/engine/Source/Renderer/TextureAtlas.js b/packages/engine/Source/Renderer/TextureAtlas.js index 9fa62b646e3..8c32f6385b2 100644 --- a/packages/engine/Source/Renderer/TextureAtlas.js +++ b/packages/engine/Source/Renderer/TextureAtlas.js @@ -680,13 +680,13 @@ TextureAtlas.prototype.addImage = function (id, image) { }; /** - * Add a sub-region of an existing atlas image as additional image indices. + * Get a sub-region of an existing atlas image as additional image indices. * @private * @param {string} id The identifier of the existing image. * @param {BoundingRectangle} subRegion An {@link BoundingRectangle} defining a region of an existing image, measured in pixels from the bottom-left of the image. - * @returns {Promise} A Promise that resolves to the image region index. -1 is returned if resouces are in the process of being destroyed. + * @returns {number | undefined} The existing subRegion index, or undefined if not yet added. */ -TextureAtlas.prototype.addImageSubRegion = function (id, subRegion) { +TextureAtlas.prototype.getImageSubRegion = function (id, subRegion) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("id", id); Check.defined("subRegion", subRegion); @@ -697,28 +697,36 @@ TextureAtlas.prototype.addImageSubRegion = function (id, subRegion) { throw new RuntimeError(`image with id "${id}" not found in the atlas.`); } - const indexPromise = this._indexPromiseById.get(id); for (const [index, parentIndex] of this._subRegions.entries()) { if (imageIndex === parentIndex) { const boundingRegion = this._rectangles[index]; if (boundingRegion.equals(subRegion)) { // The subregion is already being tracked - return indexPromise.then((resolvedImageIndex) => { - if (resolvedImageIndex === -1) { - // The atlas has been destroyed - return -1; - } - - return index; - }); + return index; } } } +}; - const index = this._nextIndex++; +/** + * Add a sub-region of an existing atlas image as additional image indices. + * @private + * @param {string} id The identifier of the existing image. + * @param {BoundingRectangle} subRegion An {@link BoundingRectangle} defining a region of an existing image, measured in pixels from the bottom-left of the image. + * @returns {Promise} A Promise that resolves to the image region index. -1 is returned if resouces are in the process of being destroyed. + */ +TextureAtlas.prototype.addImageSubRegion = function (id, subRegion) { + let index = this.getImageSubRegion(id, subRegion); + if (index) { + return index; + } + const imageIndex = this._indexById.get(id); + + index = this._nextIndex++; this._subRegions.set(index, imageIndex); this._rectangles[index] = subRegion.clone(); + const indexPromise = this._indexPromiseById.get(id); return indexPromise.then((imageIndex) => { if (imageIndex === -1) { // The atlas has been destroyed diff --git a/packages/engine/Source/Scene/Billboard.js b/packages/engine/Source/Scene/Billboard.js index dc3d900c119..ba90af0d8df 100644 --- a/packages/engine/Source/Scene/Billboard.js +++ b/packages/engine/Source/Scene/Billboard.js @@ -189,8 +189,6 @@ function Billboard(options, billboardCollection) { this._batchIndex = undefined; // Used only by Vector3DTilePoints and BillboardCollection this._imageTexture = new BillboardTexture(billboardCollection); - this._imageWidth = undefined; - this._imageHeight = undefined; this._labelDimensions = undefined; this._labelHorizontalOrigin = undefined; diff --git a/packages/engine/Source/Scene/BillboardTexture.js b/packages/engine/Source/Scene/BillboardTexture.js index 80903fdff05..3ef034cb62d 100644 --- a/packages/engine/Source/Scene/BillboardTexture.js +++ b/packages/engine/Source/Scene/BillboardTexture.js @@ -251,15 +251,33 @@ BillboardTexture.prototype.loadImage = async function (id, image) { * @param {string} id An identifier to detect whether the image already exists in the atlas. * @param {BoundingRectangle} subRegion An {@link BoundingRectangle} defining a region of an existing image, measured in pixels from the bottom-left of the image. */ -BillboardTexture.prototype.addImageSubRegion = async function (id, subRegion) { +BillboardTexture.prototype.addImageSubRegion = function (id, subRegion) { this._id = id; - this._loadState = BillboardLoadState.LOADING; this._loadError = undefined; this._hasSubregion = true; + const atlas = this._billboardCollection.textureAtlas; + const index = atlas.getImageSubRegion(id, subRegion); + + if (index) { + this.setImageSubRegion(index, subRegion); + } else { + this.loadImageSubRegion(id, subRegion); + } +}; + +/** + * @see {TextureAtlas#addImageSubRegion} + * @private + * @param {string} id An identifier to detect whether the image already exists in the atlas. + * @param {BoundingRectangle} subRegion An {@link BoundingRectangle} defining a region of an existing image, measured in pixels from the bottom-left of the image. + * @returns {Promise} + */ +BillboardTexture.prototype.loadImageSubRegion = async function (id, subRegion) { let index; const atlas = this._billboardCollection.textureAtlas; try { + this._loadState = BillboardLoadState.LOADING; index = await atlas.addImageSubRegion(id, subRegion); } catch (error) { // There was an error loading the referenced image @@ -268,6 +286,27 @@ BillboardTexture.prototype.addImageSubRegion = async function (id, subRegion) { return; } + if (this._id !== id) { + // Another load was initiated and resolved resolved before this one. This operation is cancelled. + return; + } + + this._loadState = BillboardLoadState.LOADED; + + this.setImageSubRegion(index, subRegion); +}; + +/** + * @see {TextureAtlas#addImageSubRegion} + * @private + * @param {number | undefined} index The resolved index in the {@link TextureAtlas} + * @param {BoundingRectangle} subRegion An {@link BoundingRectangle} defining a region of an existing image, measured in pixels from the bottom-left of the image. + */ +BillboardTexture.prototype.setImageSubRegion = function (index, subRegion) { + if (index && this._index === index) { + return; + } + if (!defined(index) || index === -1) { this._loadState = BillboardLoadState.FAILED; this._index = -1; @@ -280,7 +319,6 @@ BillboardTexture.prototype.addImageSubRegion = async function (id, subRegion) { this._height = subRegion.height; this._index = index; - this._loadState = BillboardLoadState.LOADED; this.dirty = true; };