pick.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { WebGLContext } from '../../mol-gl/webgl/context';
  7. import { RenderTarget } from '../../mol-gl/webgl/render-target';
  8. import Renderer from '../../mol-gl/renderer';
  9. import Scene from '../../mol-gl/scene';
  10. import { PickingId } from '../../mol-geo/geometry/picking';
  11. import { decodeFloatRGB } from '../../mol-util/float-packing';
  12. import { Camera } from '../camera';
  13. import { HandleHelper } from '../helper/handle-helper';
  14. const NullId = Math.pow(2, 24) - 2;
  15. export class PickPass {
  16. pickDirty = true
  17. objectPickTarget: RenderTarget
  18. instancePickTarget: RenderTarget
  19. groupPickTarget: RenderTarget
  20. private objectBuffer: Uint8Array
  21. private instanceBuffer: Uint8Array
  22. private groupBuffer: Uint8Array
  23. private pickScale: number
  24. private pickWidth: number
  25. private pickHeight: number
  26. constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private handleHelper: HandleHelper, private pickBaseScale: number) {
  27. const { gl } = webgl;
  28. const width = gl.drawingBufferWidth;
  29. const height = gl.drawingBufferHeight;
  30. this.pickScale = pickBaseScale / webgl.pixelRatio;
  31. this.pickWidth = Math.round(width * this.pickScale);
  32. this.pickHeight = Math.round(height * this.pickScale);
  33. this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
  34. this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
  35. this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
  36. this.setupBuffers();
  37. }
  38. private setupBuffers() {
  39. const bufferSize = this.pickWidth * this.pickHeight * 4;
  40. if (!this.objectBuffer || this.objectBuffer.length !== bufferSize) {
  41. this.objectBuffer = new Uint8Array(bufferSize);
  42. this.instanceBuffer = new Uint8Array(bufferSize);
  43. this.groupBuffer = new Uint8Array(bufferSize);
  44. }
  45. }
  46. setSize(width: number, height: number) {
  47. this.pickScale = this.pickBaseScale / this.webgl.pixelRatio;
  48. this.pickWidth = Math.round(width * this.pickScale);
  49. this.pickHeight = Math.round(height * this.pickScale);
  50. this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
  51. this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
  52. this.groupPickTarget.setSize(this.pickWidth, this.pickHeight);
  53. this.setupBuffers();
  54. }
  55. render() {
  56. const { renderer, scene, camera, handleHelper: { scene: handleScene } } = this;
  57. renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
  58. this.objectPickTarget.bind();
  59. renderer.render(scene, camera, 'pickObject', true, false);
  60. renderer.render(handleScene, camera, 'pickObject', false, false);
  61. this.instancePickTarget.bind();
  62. renderer.render(scene, camera, 'pickInstance', true, false);
  63. renderer.render(handleScene, camera, 'pickInstance', false, false);
  64. this.groupPickTarget.bind();
  65. renderer.render(scene, camera, 'pickGroup', true, false);
  66. renderer.render(handleScene, camera, 'pickGroup', false, false);
  67. this.pickDirty = false;
  68. }
  69. private syncBuffers() {
  70. const { webgl } = this;
  71. this.objectPickTarget.bind();
  72. webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.objectBuffer);
  73. this.instancePickTarget.bind();
  74. webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.instanceBuffer);
  75. this.groupPickTarget.bind();
  76. webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.groupBuffer);
  77. }
  78. private getId(x: number, y: number, buffer: Uint8Array) {
  79. const idx = (y * this.pickWidth + x) * 4;
  80. return decodeFloatRGB(buffer[idx], buffer[idx + 1], buffer[idx + 2]);
  81. }
  82. identify(x: number, y: number): PickingId | undefined {
  83. const { webgl, pickScale } = this;
  84. if (webgl.isContextLost) return;
  85. const { gl } = webgl;
  86. if (this.pickDirty) {
  87. this.render();
  88. this.syncBuffers();
  89. }
  90. x *= webgl.pixelRatio;
  91. y *= webgl.pixelRatio;
  92. y = gl.drawingBufferHeight - y; // flip y
  93. const xp = Math.round(x * pickScale);
  94. const yp = Math.round(y * pickScale);
  95. const objectId = this.getId(xp, yp, this.objectBuffer);
  96. if (objectId === -1 || objectId === NullId) return;
  97. const instanceId = this.getId(xp, yp, this.instanceBuffer);
  98. if (instanceId === -1 || instanceId === NullId) return;
  99. const groupId = this.getId(xp, yp, this.groupBuffer);
  100. if (groupId === -1 || groupId === NullId) return;
  101. return { objectId, instanceId, groupId };
  102. }
  103. }