|
@@ -7,8 +7,12 @@
|
|
|
import { PickingId } from '../../mol-geo/geometry/picking';
|
|
|
import { PickType, Renderer } from '../../mol-gl/renderer';
|
|
|
import { Scene } from '../../mol-gl/scene';
|
|
|
+import { isWebGL2 } from '../../mol-gl/webgl/compat';
|
|
|
import { WebGLContext } from '../../mol-gl/webgl/context';
|
|
|
+import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
|
|
|
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
|
|
+import { Renderbuffer } from '../../mol-gl/webgl/renderbuffer';
|
|
|
+import { Texture } from '../../mol-gl/webgl/texture';
|
|
|
import { Vec3 } from '../../mol-math/linear-algebra';
|
|
|
import { spiral2d } from '../../mol-math/misc';
|
|
|
import { unpackRGBToInt, unpackRGBAToDepth } from '../../mol-util/number-packing';
|
|
@@ -24,10 +28,24 @@ const NullId = Math.pow(2, 24) - 2;
|
|
|
export type PickData = { id: PickingId, position: Vec3 }
|
|
|
|
|
|
export class PickPass {
|
|
|
- readonly objectPickTarget: RenderTarget;
|
|
|
- readonly instancePickTarget: RenderTarget;
|
|
|
- readonly groupPickTarget: RenderTarget;
|
|
|
- readonly depthPickTarget: RenderTarget;
|
|
|
+ private readonly objectPickTarget: RenderTarget;
|
|
|
+ private readonly instancePickTarget: RenderTarget;
|
|
|
+ private readonly groupPickTarget: RenderTarget;
|
|
|
+ private readonly depthPickTarget: RenderTarget;
|
|
|
+
|
|
|
+ private readonly framebuffer: Framebuffer;
|
|
|
+
|
|
|
+ private readonly objectPickTexture: Texture;
|
|
|
+ private readonly instancePickTexture: Texture;
|
|
|
+ private readonly groupPickTexture: Texture;
|
|
|
+ private readonly depthPickTexture: Texture;
|
|
|
+
|
|
|
+ private readonly objectPickFramebuffer: Framebuffer;
|
|
|
+ private readonly instancePickFramebuffer: Framebuffer;
|
|
|
+ private readonly groupPickFramebuffer: Framebuffer;
|
|
|
+ private readonly depthPickFramebuffer: Framebuffer;
|
|
|
+
|
|
|
+ private readonly depthRenderbuffer: Renderbuffer;
|
|
|
|
|
|
private pickWidth: number;
|
|
|
private pickHeight: number;
|
|
@@ -37,10 +55,89 @@ export class PickPass {
|
|
|
this.pickWidth = Math.ceil(drawPass.colorTarget.getWidth() * pickScale);
|
|
|
this.pickHeight = Math.ceil(drawPass.colorTarget.getHeight() * pickScale);
|
|
|
|
|
|
- this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
|
|
- this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
|
|
- this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
|
|
- this.depthPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
|
|
+ const { resources, extensions: { drawBuffers }, gl } = webgl;
|
|
|
+
|
|
|
+ if (drawBuffers) {
|
|
|
+ this.objectPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
|
|
+ this.objectPickTexture.define(this.pickWidth, this.pickHeight);
|
|
|
+
|
|
|
+ this.instancePickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
|
|
+ this.instancePickTexture.define(this.pickWidth, this.pickHeight);
|
|
|
+
|
|
|
+ this.groupPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
|
|
+ this.groupPickTexture.define(this.pickWidth, this.pickHeight);
|
|
|
+
|
|
|
+ this.depthPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
|
|
+ this.depthPickTexture.define(this.pickWidth, this.pickHeight);
|
|
|
+
|
|
|
+ this.framebuffer = resources.framebuffer();
|
|
|
+
|
|
|
+ this.objectPickFramebuffer = resources.framebuffer();
|
|
|
+ this.instancePickFramebuffer = resources.framebuffer();
|
|
|
+ this.groupPickFramebuffer = resources.framebuffer();
|
|
|
+ this.depthPickFramebuffer = resources.framebuffer();
|
|
|
+
|
|
|
+ this.framebuffer.bind();
|
|
|
+ drawBuffers!.drawBuffers([
|
|
|
+ drawBuffers!.COLOR_ATTACHMENT0,
|
|
|
+ drawBuffers!.COLOR_ATTACHMENT1,
|
|
|
+ drawBuffers!.COLOR_ATTACHMENT2,
|
|
|
+ drawBuffers!.COLOR_ATTACHMENT3,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ this.objectPickTexture.attachFramebuffer(this.framebuffer, 'color0');
|
|
|
+ this.instancePickTexture.attachFramebuffer(this.framebuffer, 'color1');
|
|
|
+ this.groupPickTexture.attachFramebuffer(this.framebuffer, 'color2');
|
|
|
+ this.depthPickTexture.attachFramebuffer(this.framebuffer, 'color3');
|
|
|
+
|
|
|
+ this.depthRenderbuffer = isWebGL2(gl)
|
|
|
+ ? resources.renderbuffer('depth32f', 'depth', this.pickWidth, this.pickHeight)
|
|
|
+ : resources.renderbuffer('depth16', 'depth', this.pickWidth, this.pickHeight);
|
|
|
+
|
|
|
+ this.depthRenderbuffer.attachFramebuffer(this.framebuffer);
|
|
|
+
|
|
|
+ this.objectPickTexture.attachFramebuffer(this.objectPickFramebuffer, 'color0');
|
|
|
+ this.instancePickTexture.attachFramebuffer(this.instancePickFramebuffer, 'color0');
|
|
|
+ this.groupPickTexture.attachFramebuffer(this.groupPickFramebuffer, 'color0');
|
|
|
+ this.depthPickTexture.attachFramebuffer(this.depthPickFramebuffer, 'color0');
|
|
|
+ } else {
|
|
|
+ this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
|
|
+ this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
|
|
+ this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
|
|
+ this.depthPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bindObject() {
|
|
|
+ if (this.webgl.extensions.drawBuffers) {
|
|
|
+ this.objectPickFramebuffer.bind();
|
|
|
+ } else {
|
|
|
+ this.objectPickTarget.bind();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bindInstance() {
|
|
|
+ if (this.webgl.extensions.drawBuffers) {
|
|
|
+ this.instancePickFramebuffer.bind();
|
|
|
+ } else {
|
|
|
+ this.instancePickTarget.bind();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bindGroup() {
|
|
|
+ if (this.webgl.extensions.drawBuffers) {
|
|
|
+ this.groupPickFramebuffer.bind();
|
|
|
+ } else {
|
|
|
+ this.groupPickTarget.bind();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bindDepth() {
|
|
|
+ if (this.webgl.extensions.drawBuffers) {
|
|
|
+ this.depthPickFramebuffer.bind();
|
|
|
+ } else {
|
|
|
+ this.depthPickTarget.bind();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
get drawingBufferHeight() {
|
|
@@ -56,19 +153,30 @@ export class PickPass {
|
|
|
this.pickWidth = pickWidth;
|
|
|
this.pickHeight = pickHeight;
|
|
|
|
|
|
- this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
|
|
|
- this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
|
|
|
- this.groupPickTarget.setSize(this.pickWidth, this.pickHeight);
|
|
|
- this.depthPickTarget.setSize(this.pickWidth, this.pickHeight);
|
|
|
+ if (this.webgl.extensions.drawBuffers) {
|
|
|
+ this.objectPickTexture.define(this.pickWidth, this.pickHeight);
|
|
|
+ this.instancePickTexture.define(this.pickWidth, this.pickHeight);
|
|
|
+ this.groupPickTexture.define(this.pickWidth, this.pickHeight);
|
|
|
+ this.depthPickTexture.define(this.pickWidth, this.pickHeight);
|
|
|
+
|
|
|
+ this.depthRenderbuffer.setSize(this.pickWidth, this.pickHeight);
|
|
|
+ } else {
|
|
|
+ this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
|
|
|
+ this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
|
|
|
+ this.groupPickTarget.setSize(this.pickWidth, this.pickHeight);
|
|
|
+ this.depthPickTarget.setSize(this.pickWidth, this.pickHeight);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: 'pick' | 'depth', pickType: number) {
|
|
|
renderer.clear(false);
|
|
|
-
|
|
|
renderer.update(camera);
|
|
|
renderer.renderPick(scene.primitives, camera, variant, null, pickType);
|
|
|
- renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
|
|
|
+
|
|
|
+ if (helper.handle.isEnabled) {
|
|
|
+ renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
|
|
|
+ }
|
|
|
|
|
|
if (helper.camera.isEnabled) {
|
|
|
helper.camera.update(camera);
|
|
@@ -78,18 +186,23 @@ export class PickPass {
|
|
|
}
|
|
|
|
|
|
render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper) {
|
|
|
- this.objectPickTarget.bind();
|
|
|
- this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Object);
|
|
|
+ if (this.webgl.extensions.drawBuffers) {
|
|
|
+ this.framebuffer.bind();
|
|
|
+ this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.None);
|
|
|
+ } else {
|
|
|
+ this.objectPickTarget.bind();
|
|
|
+ this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Object);
|
|
|
|
|
|
- this.instancePickTarget.bind();
|
|
|
- this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Instance);
|
|
|
+ this.instancePickTarget.bind();
|
|
|
+ this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Instance);
|
|
|
|
|
|
- this.groupPickTarget.bind();
|
|
|
- this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Group);
|
|
|
- // printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
|
|
|
+ this.groupPickTarget.bind();
|
|
|
+ this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Group);
|
|
|
+ // printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
|
|
|
|
|
|
- this.depthPickTarget.bind();
|
|
|
- this.renderVariant(renderer, camera, scene, helper, 'depth', PickType.None);
|
|
|
+ this.depthPickTarget.bind();
|
|
|
+ this.renderVariant(renderer, camera, scene, helper, 'depth', PickType.None);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -146,16 +259,16 @@ export class PickHelper {
|
|
|
private syncBuffers() {
|
|
|
const { pickX, pickY, pickWidth, pickHeight } = this;
|
|
|
|
|
|
- this.pickPass.objectPickTarget.bind();
|
|
|
+ this.pickPass.bindObject();
|
|
|
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.objectBuffer);
|
|
|
|
|
|
- this.pickPass.instancePickTarget.bind();
|
|
|
+ this.pickPass.bindInstance();
|
|
|
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.instanceBuffer);
|
|
|
|
|
|
- this.pickPass.groupPickTarget.bind();
|
|
|
+ this.pickPass.bindGroup();
|
|
|
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.groupBuffer);
|
|
|
|
|
|
- this.pickPass.depthPickTarget.bind();
|
|
|
+ this.pickPass.bindDepth();
|
|
|
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.depthBuffer);
|
|
|
}
|
|
|
|
|
@@ -179,7 +292,7 @@ export class PickHelper {
|
|
|
const { renderer, scene, helper } = this;
|
|
|
|
|
|
renderer.setTransparentBackground(false);
|
|
|
- renderer.setDrawingBufferSize(this.pickPass.objectPickTarget.getWidth(), this.pickPass.objectPickTarget.getHeight());
|
|
|
+ renderer.setDrawingBufferSize(pickWidth, pickHeight);
|
|
|
renderer.setPixelRatio(this.pickScale);
|
|
|
|
|
|
if (StereoCamera.is(camera)) {
|
|
@@ -237,6 +350,7 @@ export class PickHelper {
|
|
|
if (groupId === -1 || groupId === NullId) return;
|
|
|
|
|
|
const z = this.getDepth(xp, yp);
|
|
|
+ // console.log('z', z);
|
|
|
const position = Vec3.create(x, viewport.height - y, z);
|
|
|
if (StereoCamera.is(camera)) {
|
|
|
const halfWidth = Math.floor(viewport.width / 2);
|
|
@@ -251,7 +365,7 @@ export class PickHelper {
|
|
|
cameraUnproject(position, position, viewport, camera.inverseProjectionView);
|
|
|
}
|
|
|
|
|
|
- // console.log({ { objectId, instanceId, groupId }, position} );
|
|
|
+ // console.log({ id: { objectId, instanceId, groupId }, position });
|
|
|
return { id: { objectId, instanceId, groupId }, position };
|
|
|
}
|
|
|
|