Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 99 additions & 13 deletions Apps/Sandcastle/gallery/3D Tiles Feature Picking.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
terrain: Cesium.Terrain.fromWorldTerrain(),
});

let async = false;
viewer.scene.globe.depthTestAgainstTerrain = true;

// Set the initial camera view to look at Manhattan
Expand Down Expand Up @@ -133,6 +134,34 @@
return description;
}

Sandcastle.addToggleButton("Async Picking", async, function (checked) {
async = checked;
});

let picking = false;
let debounceTime = 0;
const DEBOUNCE_TIMEOUT_MS = 34; // One pick per frame (30FPS)

async function doPick(scene, position, debounce, async, result) {
const now = performance.now();
if (debounce && picking && now - debounceTime < DEBOUNCE_TIMEOUT_MS) {
return true;
}
if (debounce) {
picking = true;
debounceTime = now;
}
if (async) {
result[0] = await scene.pickAsync(position);
} else {
result[0] = scene.pick(position);
}
if (debounce) {
picking = false;
}
return false;
}

// If silhouettes are supported, silhouette features in blue on mouse over and silhouette green on mouse click.
// If silhouettes are not supported, change the feature color to yellow on mouse over and green on mouse click.
if (Cesium.PostProcessStageLibrary.isSilhouetteSupported(viewer.scene)) {
Expand All @@ -157,13 +186,26 @@
);

// Silhouette a feature blue on hover.
viewer.screenSpaceEventHandler.setInputAction(function onMouseMove(movement) {
viewer.screenSpaceEventHandler.setInputAction(async function onMouseMove(
movement,
) {
// Pick a new feature
let pickedFeature = [];
const debounced = await doPick(
viewer.scene,
movement.endPosition,
true,
async,
pickedFeature,
);
if (debounced) {
return;
}
pickedFeature = pickedFeature[0];

// If a feature was previously highlighted, undo the highlight
silhouetteBlue.selected = [];

// Pick a new feature
const pickedFeature = viewer.scene.pick(movement.endPosition);

updateNameOverlay(pickedFeature, movement.endPosition);

if (!Cesium.defined(pickedFeature)) {
Expand All @@ -177,12 +219,26 @@
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

// Silhouette a feature on selection and show metadata in the InfoBox.
viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) {
viewer.screenSpaceEventHandler.setInputAction(async function onLeftClick(
movement,
) {
// Pick a new feature
let pickedFeature = [];
const debounced = await doPick(
viewer.scene,
movement.position,
false,
async,
pickedFeature,
);
if (debounced) {
return;
}
pickedFeature = pickedFeature[0];

// If a feature was previously selected, undo the highlight
silhouetteGreen.selected = [];

// Pick a new feature
const pickedFeature = viewer.scene.pick(movement.position);
if (!Cesium.defined(pickedFeature)) {
clickHandler(movement);
return;
Expand Down Expand Up @@ -216,14 +272,29 @@
};

// Color a feature yellow on hover.
viewer.screenSpaceEventHandler.setInputAction(function onMouseMove(movement) {
viewer.screenSpaceEventHandler.setInputAction(async function onMouseMove(
movement,
) {
// Pick a new feature
let pickedFeature = [];
const debounced = await doPick(
viewer.scene,
movement.endPosition,
true,
async,
pickedFeature,
);
if (debounced) {
return;
}
pickedFeature = pickedFeature[0];

// If a feature was previously highlighted, undo the highlight
if (Cesium.defined(highlighted.feature)) {
highlighted.feature.color = highlighted.originalColor;
highlighted.feature = undefined;
}
// Pick a new feature
const pickedFeature = viewer.scene.pick(movement.endPosition);

updateNameOverlay(pickedFeature, movement.endPosition);

if (!Cesium.defined(pickedFeature)) {
Expand All @@ -239,14 +310,29 @@
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

// Color a feature on selection and show metadata in the InfoBox.
viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) {
viewer.screenSpaceEventHandler.setInputAction(async function onLeftClick(
movement,
) {
// Pick a new feature
let pickedFeature = [];
const debounced = await doPick(
viewer.scene,
movement.position,
false,
async,
pickedFeature,
);
if (debounced) {
return;
}
pickedFeature = pickedFeature[0];

// If a feature was previously selected, undo the highlight
if (Cesium.defined(selected.feature)) {
selected.feature.color = selected.originalColor;
selected.feature = undefined;
}
// Pick a new feature
const pickedFeature = viewer.scene.pick(movement.position);

if (!Cesium.defined(pickedFeature)) {
clickHandler(movement);
return;
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu
- [Jason Crow](https://github.com/jason-crow)
- [Erin Ingram](https://github.com/eringram)
- [Mark Schlosser](https://github.com/markschlosseratbentley)
- [Adam Larkeryd](https://github.com/alarkbentley)
- [Flightradar24 AB](https://www.flightradar24.com)
- [Aleksei Kalmykov](https://github.com/kalmykov)
- [BIT Systems](http://www.caci.com/bit-systems)
Expand Down
30 changes: 30 additions & 0 deletions packages/engine/Source/Renderer/Buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ function Buffer(options) {
this.vertexArrayDestroyable = true;
}

Buffer.createPixelBuffer = function (options) {
//>>includeStart('debug', pragmas.debug);
Check.defined("options.context", options.context);
//>>includeEnd('debug');

if (!options.context._webgl2) {
throw new DeveloperError("A WebGL 2 context is required.");
}

return new Buffer({
context: options.context,
bufferTarget: WebGLConstants.PIXEL_PACK_BUFFER,
typedArray: options.typedArray,
sizeInBytes: options.sizeInBytes,
usage: options.usage,
});
};

/**
* Creates a vertex buffer, which contains untyped vertex data in GPU-controlled memory.
* <br /><br />
Expand Down Expand Up @@ -242,6 +260,18 @@ Buffer.prototype._getBuffer = function () {
return this._buffer;
};

Buffer.prototype._bind = function () {
const gl = this._gl;
const target = this._bufferTarget;
gl.bindBuffer(target, this._buffer);
};

Buffer.prototype._unBind = function () {
const gl = this._gl;
const target = this._bufferTarget;
gl.bindBuffer(target, null);
};

Buffer.prototype.copyFromArrayView = function (arrayView, offsetInBytes) {
offsetInBytes = offsetInBytes ?? 0;

Expand Down
4 changes: 3 additions & 1 deletion packages/engine/Source/Renderer/BufferUsage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ const BufferUsage = {
STREAM_DRAW: WebGLConstants.STREAM_DRAW,
STATIC_DRAW: WebGLConstants.STATIC_DRAW,
DYNAMIC_DRAW: WebGLConstants.DYNAMIC_DRAW,
DYNAMIC_READ: WebGLConstants.DYNAMIC_READ,

validate: function (bufferUsage) {
return (
bufferUsage === BufferUsage.STREAM_DRAW ||
bufferUsage === BufferUsage.STATIC_DRAW ||
bufferUsage === BufferUsage.DYNAMIC_DRAW
bufferUsage === BufferUsage.DYNAMIC_DRAW ||
bufferUsage === BufferUsage.DYNAMIC_READ
);
},
};
Expand Down
67 changes: 52 additions & 15 deletions packages/engine/Source/Renderer/Context.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Buffer from "./Buffer.js";
import Check from "../Core/Check.js";
import Color from "../Core/Color.js";
import ComponentDatatype from "../Core/ComponentDatatype.js";
Expand Down Expand Up @@ -1449,6 +1450,7 @@ Context.prototype.endFrame = function () {
* @param {number} [readState.width=this.drawingBufferWidth] The width of the rectangle to read from.
* @param {number} [readState.height=this.drawingBufferHeight] The height of the rectangle to read from.
* @param {Framebuffer} [readState.framebuffer] The framebuffer to read from. If undefined, the read will be from the default framebuffer.
* @param {Framebuffer} [readState.pbo] If true pixel data is read to PBO instead of TypedArray.
* @returns {Uint8Array|Uint16Array|Float32Array|Uint32Array} The pixels in the specified rectangle.
*/
Context.prototype.readPixels = function (readState) {
Expand All @@ -1460,35 +1462,70 @@ Context.prototype.readPixels = function (readState) {
const width = readState.width ?? this.drawingBufferWidth;
const height = readState.height ?? this.drawingBufferHeight;
const framebuffer = readState.framebuffer;
const pbo = readState.pbo ?? false;

if (pbo && !this._webgl2) {
throw new DeveloperError("A WebGL 2 context is required.");
}

//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThan("readState.width", width, 0);
Check.typeOf.number.greaterThan("readState.height", height, 0);
//>>includeEnd('debug');

let pixelDatatype = PixelDatatype.UNSIGNED_BYTE;
let pixelFormat = PixelFormat.RGBA;
if (defined(framebuffer) && framebuffer.numberOfColorAttachments > 0) {
pixelDatatype = framebuffer.getColorTexture(0).pixelDatatype;
pixelFormat = framebuffer.getColorTexture(0).pixelFormat;
}

const pixels = PixelFormat.createTypedArray(
PixelFormat.RGBA,
pixelDatatype,
width,
height,
);
let pixels;
if (pbo) {
pixels = Buffer.createPixelBuffer({
context: this,
sizeInBytes: PixelFormat.textureSizeInBytes(
pixelFormat,
pixelDatatype,
width,
height,
),
usage: BufferUsage.DYNAMIC_READ,
});
} else {
pixels = PixelFormat.createTypedArray(
pixelFormat,
pixelDatatype,
width,
height,
);
}

bindFramebuffer(this, framebuffer);

gl.readPixels(
x,
y,
width,
height,
PixelFormat.RGBA,
PixelDatatype.toWebGLConstant(pixelDatatype, this),
pixels,
);
if (pbo) {
pixels._bind();
gl.readPixels(
x,
y,
width,
height,
pixelFormat,
PixelDatatype.toWebGLConstant(pixelDatatype, this),
0,
);
pixels._unBind();
} else {
gl.readPixels(
x,
y,
width,
height,
PixelFormat.RGBA,
PixelDatatype.toWebGLConstant(pixelDatatype, this),
pixels,
);
}

return pixels;
};
Expand Down
46 changes: 46 additions & 0 deletions packages/engine/Source/Renderer/Sync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Check from "../Core/Check.js";
import destroyObject from "../Core/destroyObject.js";
import DeveloperError from "../Core/DeveloperError.js";
import Frozen from "../Core/Frozen.js";
import WebGLConstants from "../Core/WebGLConstants.js";

/**
* @private
*/
function Sync(options) {
options = options ?? Frozen.EMPTY_OBJECT;

//>>includeStart('debug', pragmas.debug);
Check.defined("options.context", options.context);
//>>includeEnd('debug');

if (!options.context._webgl2) {
throw new DeveloperError("A WebGL 2 context is required.");
}

const context = options.context;
const gl = context._gl;

const sync = gl.fenceSync(WebGLConstants.SYNC_GPU_COMMANDS_COMPLETE, 0);

this._gl = gl;
this._sync = sync;
}
Sync.create = function (options) {
return new Sync(options);
};
Sync.prototype.getStatus = function () {
const status = this._gl.getSyncParameter(
this._sync,
WebGLConstants.SYNC_STATUS,
);
return status;
};
Sync.prototype.isDestroyed = function () {
return false;
};
Sync.prototype.destroy = function () {
this._gl.deleteSync(this._sync);
return destroyObject(this);
};
export default Sync;
Loading