diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index 24618f9df640a0042d256e66a3357b81c8b56210..c0e5731df5f8f60ba1c07ad84981255f471730e6 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -14,7 +14,6 @@ import { TrackballControls, TrackballControlsParams } from './controls/trackball import { Viewport } from './camera/util' import { createContext, WebGLContext, getGLContext } from 'mol-gl/webgl/context'; import { Representation } from 'mol-repr/representation'; -import { createRenderTarget } from 'mol-gl/webgl/render-target'; import Scene from 'mol-gl/scene'; import { GraphicsRenderVariant } from 'mol-gl/webgl/render-item'; import { PickingId } from 'mol-geo/geometry/picking'; @@ -23,7 +22,6 @@ import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci'; import { Camera } from './camera'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { BoundingSphereHelper, DebugHelperParams } from './helper/bounding-sphere-helper'; -import { decodeFloatRGB } from 'mol-util/float-packing'; import { SetUtils } from 'mol-util/set'; import { Canvas3dInteractionHelper } from './helper/interaction-events'; import { PostprocessingParams, PostprocessingPass } from './passes/postprocessing'; @@ -32,6 +30,7 @@ import { GLRenderingContext } from 'mol-gl/webgl/compat'; import { PixelData } from 'mol-util/image'; import { readTexture } from 'mol-gl/compute/util'; import { DrawPass } from './passes/draw'; +import { PickPass } from './passes/pick'; export const Canvas3DParams = { // TODO: FPS cap? @@ -62,7 +61,6 @@ interface Canvas3D { // draw: (force?: boolean) => void requestDraw: (force?: boolean) => void animate: () => void - pick: () => void identify: (x: number, y: number) => PickingId | undefined mark: (loci: Representation.Loci, action: MarkerAction) => void getLoci: (pickingId: PickingId) => Representation.Loci @@ -133,19 +131,10 @@ namespace Canvas3D { const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input); const drawPass = new DrawPass(webgl, renderer, scene, debugHelper) + const pickPass = new PickPass(webgl, renderer, scene, 0.5) const postprocessing = new PostprocessingPass(webgl, drawPass, p.postprocessing) const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample) - const pickBaseScale = 0.5 - let pickScale = pickBaseScale / webgl.pixelRatio - let pickWidth = Math.round(width * pickScale) - let pickHeight = Math.round(height * pickScale) - const objectPickTarget = createRenderTarget(webgl, pickWidth, pickHeight) - const instancePickTarget = createRenderTarget(webgl, pickWidth, pickHeight) - const groupPickTarget = createRenderTarget(webgl, pickWidth, pickHeight) - - let pickDirty = true - let isIdentifying = false let isUpdating = false let drawPending = false @@ -173,9 +162,9 @@ namespace Canvas3D { } if (changed) { scene.update(void 0, true) - const prevPickDirty = pickDirty + const prevPickDirty = pickPass.pickDirty draw(true) - pickDirty = prevPickDirty // marking does not change picking buffers + pickPass.pickDirty = prevPickDirty // marking does not change picking buffers } } @@ -212,7 +201,7 @@ namespace Canvas3D { } function render(variant: 'pick' | 'draw', force: boolean) { - if (isIdentifying || isUpdating) return false + if (isUpdating) return false let didRender = false controls.update(currentTime); @@ -224,13 +213,7 @@ namespace Canvas3D { if (force || cameraChanged || multiSample.enabled) { switch (variant) { case 'pick': - renderer.setViewport(0, 0, pickWidth, pickHeight); - objectPickTarget.bind(); - renderer.render(scene, 'pickObject', true); - instancePickTarget.bind(); - renderer.render(scene, 'pickInstance', true); - groupPickTarget.bind(); - renderer.render(scene, 'pickGroup', true); + pickPass.render() break; case 'draw': renderer.setViewport(0, 0, width, height); @@ -240,7 +223,7 @@ namespace Canvas3D { drawPass.render(!postprocessing.enabled) if (postprocessing.enabled) postprocessing.render(true) } - pickDirty = true + pickPass.pickDirty = true break; } didRender = true @@ -274,45 +257,8 @@ namespace Canvas3D { requestAnimationFrame(animate) } - function pick() { - if (pickDirty) { - render('pick', true) - pickDirty = false - } - } - - const readBuffer = new Uint8Array(4) function identify(x: number, y: number): PickingId | undefined { - if (isIdentifying) return - - pick() // must be called before setting `isIdentifying = true` - isIdentifying = true - - x *= webgl.pixelRatio - y *= webgl.pixelRatio - y = height - y // flip y - - const xp = Math.round(x * pickScale) - const yp = Math.round(y * pickScale) - - objectPickTarget.bind() - webgl.readPixels(xp, yp, 1, 1, readBuffer) - const objectId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2]) - if (objectId === -1) { isIdentifying = false; return; } - - instancePickTarget.bind() - webgl.readPixels(xp, yp, 1, 1, readBuffer) - const instanceId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2]) - if (instanceId === -1) { isIdentifying = false; return; } - - groupPickTarget.bind() - webgl.readPixels(xp, yp, 1, 1, readBuffer) - const groupId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2]) - if (groupId === -1) { isIdentifying = false; return; } - - isIdentifying = false - - return { objectId, instanceId, groupId } + return pickPass.identify(x, y) } function add(repr: Representation.Any) { @@ -382,7 +328,6 @@ namespace Canvas3D { // draw, requestDraw, animate, - pick, identify, mark, getLoci, @@ -399,9 +344,9 @@ namespace Canvas3D { getPixelData: (variant: GraphicsRenderVariant) => { switch (variant) { case 'color': return webgl.getDrawingBufferPixelData() - case 'pickObject': return objectPickTarget.getPixelData() - case 'pickInstance': return instancePickTarget.getPixelData() - case 'pickGroup': return groupPickTarget.getPixelData() + case 'pickObject': return pickPass.objectPickTarget.getPixelData() + case 'pickInstance': return pickPass.instancePickTarget.getPixelData() + case 'pickGroup': return pickPass.groupPickTarget.getPixelData() case 'depth': return readTexture(webgl, drawPass.depthTexture) as PixelData } }, @@ -465,16 +410,10 @@ namespace Canvas3D { Viewport.set(controls.viewport, 0, 0, width, height) drawPass.setSize(width, height) + pickPass.setSize(width, height) postprocessing.setSize(width, height) multiSample.setSize(width, height) - pickScale = pickBaseScale / webgl.pixelRatio - pickWidth = Math.round(width * pickScale) - pickHeight = Math.round(height * pickScale) - objectPickTarget.setSize(pickWidth, pickHeight) - instancePickTarget.setSize(pickWidth, pickHeight) - groupPickTarget.setSize(pickWidth, pickHeight) - requestDraw(true) } } diff --git a/src/mol-canvas3d/passes/pick.ts b/src/mol-canvas3d/passes/pick.ts new file mode 100644 index 0000000000000000000000000000000000000000..13238d720b5b544cab8cbb86f760e27ed2adf6e5 --- /dev/null +++ b/src/mol-canvas3d/passes/pick.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { WebGLContext } from 'mol-gl/webgl/context'; +import { createRenderTarget, RenderTarget } from 'mol-gl/webgl/render-target'; +import Renderer from 'mol-gl/renderer'; +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 pickScale: number + private pickWidth: number + private pickHeight: number + + constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private pickBaseScale: number) { + const { gl } = webgl + const width = gl.drawingBufferWidth + const height = gl.drawingBufferHeight + + 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) + } + + 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) + } + + render() { + const { renderer, scene } = this + renderer.setViewport(0, 0, this.pickWidth, this.pickHeight); + this.objectPickTarget.bind(); + renderer.render(scene, 'pickObject', true); + this.instancePickTarget.bind(); + renderer.render(scene, 'pickInstance', true); + this.groupPickTarget.bind(); + renderer.render(scene, 'pickGroup', true); + } + + identify(x: number, y: number): PickingId | undefined { + const { webgl, pickScale } = this + const { gl } = webgl + if (this.pickDirty) this.render() + + x *= webgl.pixelRatio + y *= webgl.pixelRatio + y = gl.drawingBufferHeight - y // flip y + + 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]) + if (objectId === -1) return + + this.instancePickTarget.bind() + webgl.readPixels(xp, yp, 1, 1, readBuffer) + const instanceId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2]) + if (instanceId === -1) return + + this.groupPickTarget.bind() + webgl.readPixels(xp, yp, 1, 1, readBuffer) + const groupId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2]) + if (groupId === -1) return + + return { objectId, instanceId, groupId } + } +} \ No newline at end of file