Skip to content
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@

### @cesium/engine

#### Breaking Changes :mega:

- `scene.drillPick` now uses a breadth-first search strategy instead of depth-first. This may change which entities are picked when
using large values of `width` and `height` when providing a `limit`, prioritizing visible entities over obscured ones.

#### Fixes :wrench:

- Fixes an event bug following recent changes, where adding a new listener during an event callback caused an infinite loop. [#12955](https://github.com/CesiumGS/cesium/pull/12955)
- 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)

## 1.134 - 2025-10-01

Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -433,3 +433,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu
- [宋时旺](https://github.com/BlockCnFuture)
- [Marco Zhan](https://github.com/marcoYxz)
- [Mikhail Porotkin](https://github.com/porotkin)
- [Adam Beili](https://github.com/Beilinson)
29 changes: 23 additions & 6 deletions packages/engine/Source/Core/Color.js
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,23 @@ Color.prototype.toBytes = function (result) {
return result;
};

/**
* Converts RGBA values in bytes to a single numeric unsigned 32-bit RGBA value, using the endianness
* of the system.
*
* @returns {number} A single numeric unsigned 32-bit RGBA value.
*
* @see Color.toRgba
*/
Color.bytesToRgba = function (red, green, blue, alpha) {
// scratchUint32Array and scratchUint8Array share an underlying array buffer
scratchUint8Array[0] = red;
scratchUint8Array[1] = green;
scratchUint8Array[2] = blue;
scratchUint8Array[3] = alpha;
return scratchUint32Array[0];
};

/**
* Converts this color to a single numeric unsigned 32-bit RGBA value, using the endianness
* of the system.
Expand All @@ -692,12 +709,12 @@ Color.prototype.toBytes = function (result) {
* @see Color.fromRgba
*/
Color.prototype.toRgba = function () {
// scratchUint32Array and scratchUint8Array share an underlying array buffer
scratchUint8Array[0] = Color.floatToByte(this.red);
scratchUint8Array[1] = Color.floatToByte(this.green);
scratchUint8Array[2] = Color.floatToByte(this.blue);
scratchUint8Array[3] = Color.floatToByte(this.alpha);
return scratchUint32Array[0];
return Color.bytesToRgba(
Color.floatToByte(this.red),
Color.floatToByte(this.green),
Color.floatToByte(this.blue),
Color.floatToByte(this.alpha),
);
};

/**
Expand Down
20 changes: 13 additions & 7 deletions packages/engine/Source/Renderer/Context.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ function Context(canvas, options) {
this._vertexAttribDivisors.push(0);
}

this._pickObjects = {};
this._pickObjects = new Map();
this._nextPickColor = new Uint32Array(1);

/**
Expand Down Expand Up @@ -1562,7 +1562,7 @@ Context.prototype.createViewportQuadCommand = function (
/**
* Gets the object associated with a pick color.
*
* @param {Color} pickColor The pick color.
* @param {number} pickColor The unsigned 32-bit RGBA pick color
* @returns {object} The object associated with the pick color, or undefined if no object is associated with that color.
*
* @example
Expand All @@ -1575,9 +1575,15 @@ Context.prototype.getObjectByPickColor = function (pickColor) {
Check.defined("pickColor", pickColor);
//>>includeEnd('debug');

return this._pickObjects[pickColor.toRgba()];
return this._pickObjects.get(pickColor);
};

/**
*
* @param {Map} pickObjects
* @param {number} key
* @param {Color} color
*/
function PickId(pickObjects, key, color) {
this._pickObjects = pickObjects;
this.key = key;
Expand All @@ -1587,16 +1593,16 @@ function PickId(pickObjects, key, color) {
Object.defineProperties(PickId.prototype, {
object: {
get: function () {
return this._pickObjects[this.key];
return this._pickObjects.get(this.key);
},
set: function (value) {
this._pickObjects[this.key] = value;
this._pickObjects.set(this.key, value);
},
},
});

PickId.prototype.destroy = function () {
delete this._pickObjects[this.key];
this._pickObjects.delete(this.key);
return undefined;
};

Expand Down Expand Up @@ -1633,7 +1639,7 @@ Context.prototype.createPickId = function (object) {
throw new RuntimeError("Out of unique Pick IDs.");
}

this._pickObjects[key] = object;
this._pickObjects.set(key, object);
return new PickId(this._pickObjects, key, Color.fromRgba(key));
};

Expand Down
30 changes: 13 additions & 17 deletions packages/engine/Source/Scene/PickFramebuffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,14 @@ PickFramebuffer.prototype.begin = function (screenSpaceRectangle, viewport) {
return this._passState;
};

const colorScratchForPickFramebuffer = new Color();

/**
* Return the picked object rendered within a given rectangle.
* Return the picked objects rendered within a given rectangle.
*
* @param {BoundingRectangle} screenSpaceRectangle
* @returns {object|undefined} The object rendered in the middle of the rectangle, or undefined if nothing was rendered.
* @param {number} [limit=1] If supplied, stop iterating after collecting this many objects.
* @returns {object[]} A list of rendered objects, ordered by distance to the middle of the rectangle.
*/
PickFramebuffer.prototype.end = function (screenSpaceRectangle) {
PickFramebuffer.prototype.end = function (screenSpaceRectangle, limit = 1) {
const width = screenSpaceRectangle.width ?? 1.0;
const height = screenSpaceRectangle.height ?? 1.0;

Expand Down Expand Up @@ -84,6 +83,7 @@ PickFramebuffer.prototype.end = function (screenSpaceRectangle) {

// The region does not have to square and the dimensions do not have to be odd, but
// loop iterations would be wasted. Prefer square regions where the size is odd.
const objects = new Set();
for (let i = 0; i < length; ++i) {
if (
-halfWidth <= x &&
Expand All @@ -93,22 +93,19 @@ PickFramebuffer.prototype.end = function (screenSpaceRectangle) {
) {
const index = 4 * ((halfHeight - y) * width + x + halfWidth);

colorScratchForPickFramebuffer.red = Color.byteToFloat(pixels[index]);
colorScratchForPickFramebuffer.green = Color.byteToFloat(
const pickColor = Color.bytesToRgba(
pixels[index],
pixels[index + 1],
);
colorScratchForPickFramebuffer.blue = Color.byteToFloat(
pixels[index + 2],
);
colorScratchForPickFramebuffer.alpha = Color.byteToFloat(
pixels[index + 3],
);

const object = context.getObjectByPickColor(
colorScratchForPickFramebuffer,
);
const object = context.getObjectByPickColor(pickColor);
if (defined(object)) {
return object;
objects.add(object);
if (objects.size >= limit) {
break;
}
}
}

Expand All @@ -123,8 +120,7 @@ PickFramebuffer.prototype.end = function (screenSpaceRectangle) {
x += dx;
y += dy;
}

return undefined;
return [...objects];
};

/**
Expand Down
Loading