PickFramebuffer.js 4.3 KB

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