PickFramebuffer.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import BoundingRectangle from "../Core/BoundingRectangle.js";
  2. import Color from "../Core/Color.js";
  3. import defaultValue from "../Core/defaultValue.js";
  4. import defined from "../Core/defined.js";
  5. import destroyObject from "../Core/destroyObject.js";
  6. import FramebufferManager from "../Renderer/FramebufferManager.js";
  7. import PassState from "../Renderer/PassState.js";
  8. /**
  9. * @private
  10. */
  11. function PickFramebuffer(context) {
  12. // Override per-command states
  13. const passState = new PassState(context);
  14. passState.blendingEnabled = false;
  15. passState.scissorTest = {
  16. enabled: true,
  17. rectangle: new BoundingRectangle(),
  18. };
  19. passState.viewport = new BoundingRectangle();
  20. this._context = context;
  21. this._fb = new FramebufferManager({
  22. depthStencil: true,
  23. });
  24. this._passState = passState;
  25. this._width = 0;
  26. this._height = 0;
  27. }
  28. PickFramebuffer.prototype.begin = function (screenSpaceRectangle, viewport) {
  29. const context = this._context;
  30. const width = viewport.width;
  31. const height = viewport.height;
  32. BoundingRectangle.clone(
  33. screenSpaceRectangle,
  34. this._passState.scissorTest.rectangle
  35. );
  36. // Create or recreate renderbuffers and framebuffer used for picking
  37. this._width = width;
  38. this._height = height;
  39. this._fb.update(context, width, height);
  40. this._passState.framebuffer = this._fb.framebuffer;
  41. this._passState.viewport.width = width;
  42. this._passState.viewport.height = height;
  43. return this._passState;
  44. };
  45. const colorScratch = new Color();
  46. PickFramebuffer.prototype.end = function (screenSpaceRectangle) {
  47. const width = defaultValue(screenSpaceRectangle.width, 1.0);
  48. const height = defaultValue(screenSpaceRectangle.height, 1.0);
  49. const context = this._context;
  50. const pixels = context.readPixels({
  51. x: screenSpaceRectangle.x,
  52. y: screenSpaceRectangle.y,
  53. width: width,
  54. height: height,
  55. framebuffer: this._fb.framebuffer,
  56. });
  57. const max = Math.max(width, height);
  58. const length = max * max;
  59. const halfWidth = Math.floor(width * 0.5);
  60. const halfHeight = Math.floor(height * 0.5);
  61. let x = 0;
  62. let y = 0;
  63. let dx = 0;
  64. let dy = -1;
  65. // Spiral around the center pixel, this is a workaround until
  66. // we can access the depth buffer on all browsers.
  67. // The region does not have to square and the dimensions do not have to be odd, but
  68. // loop iterations would be wasted. Prefer square regions where the size is odd.
  69. for (let i = 0; i < length; ++i) {
  70. if (
  71. -halfWidth <= x &&
  72. x <= halfWidth &&
  73. -halfHeight <= y &&
  74. y <= halfHeight
  75. ) {
  76. const index = 4 * ((halfHeight - y) * width + x + halfWidth);
  77. colorScratch.red = Color.byteToFloat(pixels[index]);
  78. colorScratch.green = Color.byteToFloat(pixels[index + 1]);
  79. colorScratch.blue = Color.byteToFloat(pixels[index + 2]);
  80. colorScratch.alpha = Color.byteToFloat(pixels[index + 3]);
  81. const object = context.getObjectByPickColor(colorScratch);
  82. if (defined(object)) {
  83. return object;
  84. }
  85. }
  86. // if (top right || bottom left corners) || (top left corner) || (bottom right corner + (1, 0))
  87. // change spiral direction
  88. if (x === y || (x < 0 && -x === y) || (x > 0 && x === 1 - y)) {
  89. const temp = dx;
  90. dx = -dy;
  91. dy = temp;
  92. }
  93. x += dx;
  94. y += dy;
  95. }
  96. return undefined;
  97. };
  98. PickFramebuffer.prototype.isDestroyed = function () {
  99. return false;
  100. };
  101. PickFramebuffer.prototype.destroy = function () {
  102. this._fb.destroy();
  103. return destroyObject(this);
  104. };
  105. export default PickFramebuffer;