diff --git a/src/mol-canvas3d/passes/pick.ts b/src/mol-canvas3d/passes/pick.ts index 46e8ef8d7204db3b06716f1cd9831f06e1611ea4..59d332b8f155dcb4ff7b97a10374bb7975d374f9 100644 --- a/src/mol-canvas3d/passes/pick.ts +++ b/src/mol-canvas3d/passes/pick.ts @@ -11,14 +11,17 @@ import Scene from '../../mol-gl/scene'; import { PickingId } from '../../mol-geo/geometry/picking'; import { decodeFloatRGB } from '../../mol-util/float-packing'; -const readBuffer = new Uint8Array(4) - export class PickPass { pickDirty = true + objectPickTarget: RenderTarget instancePickTarget: RenderTarget groupPickTarget: RenderTarget + private objectBuffer: Uint8Array + private instanceBuffer: Uint8Array + private groupBuffer: Uint8Array + private pickScale: number private pickWidth: number private pickHeight: number @@ -31,18 +34,33 @@ export class PickPass { this.pickScale = pickBaseScale / webgl.pixelRatio this.pickWidth = Math.round(width * this.pickScale) this.pickHeight = Math.round(height * this.pickScale) + this.objectPickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight) this.instancePickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight) this.groupPickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight) + + this.setupBuffers() + } + + private setupBuffers() { + const bufferSize = this.pickWidth * this.pickHeight * 4 + if (!this.objectBuffer || this.objectBuffer.length !== bufferSize) { + this.objectBuffer = new Uint8Array(bufferSize) + this.instanceBuffer = new Uint8Array(bufferSize) + this.groupBuffer = new Uint8Array(bufferSize) + } } setSize(width: number, height: number) { this.pickScale = this.pickBaseScale / this.webgl.pixelRatio this.pickWidth = Math.round(width * this.pickScale) this.pickHeight = Math.round(height * this.pickScale) + this.objectPickTarget.setSize(this.pickWidth, this.pickHeight) this.instancePickTarget.setSize(this.pickWidth, this.pickHeight) this.groupPickTarget.setSize(this.pickWidth, this.pickHeight) + + this.setupBuffers() } render() { @@ -54,12 +72,35 @@ export class PickPass { renderer.render(scene, 'pickInstance', true); this.groupPickTarget.bind(); renderer.render(scene, 'pickGroup', true); + + this.pickDirty = false + } + + private syncBuffers() { + const { webgl } = this + + this.objectPickTarget.bind() + webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.objectBuffer) + + this.instancePickTarget.bind() + webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.instanceBuffer) + + this.groupPickTarget.bind() + webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.groupBuffer) + } + + private getId(x: number, y: number, buffer: Uint8Array) { + const idx = (y * this.pickWidth + x) * 4 + return decodeFloatRGB(buffer[idx], buffer[idx + 1], buffer[idx + 2]) } identify(x: number, y: number): PickingId | undefined { const { webgl, pickScale } = this const { gl } = webgl - if (this.pickDirty) this.render() + if (this.pickDirty) { + this.render() + this.syncBuffers() + } x *= webgl.pixelRatio y *= webgl.pixelRatio @@ -68,19 +109,13 @@ export class PickPass { const xp = Math.round(x * pickScale) const yp = Math.round(y * pickScale) - this.objectPickTarget.bind() - webgl.readPixels(xp, yp, 1, 1, readBuffer) - const objectId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2]) + const objectId = this.getId(xp, yp, this.objectBuffer) if (objectId === -1) return - this.instancePickTarget.bind() - webgl.readPixels(xp, yp, 1, 1, readBuffer) - const instanceId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2]) + const instanceId = this.getId(xp, yp, this.instanceBuffer) if (instanceId === -1) return - this.groupPickTarget.bind() - webgl.readPixels(xp, yp, 1, 1, readBuffer) - const groupId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2]) + const groupId = this.getId(xp, yp, this.groupBuffer) if (groupId === -1) return return { objectId, instanceId, groupId }