PickFramebuffer.js 4.0 KB

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