智慧水务管理系统 - 精河县供水工程综合管理平台

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import BoundingRectangle from "../Core/BoundingRectangle.js";
  2. import Color from "../Core/Color.js";
  3. import defined from "../Core/defined.js";
  4. import destroyObject from "../Core/destroyObject.js";
  5. import FramebufferManager from "../Renderer/FramebufferManager.js";
  6. import PassState from "../Renderer/PassState.js";
  7. import PixelDatatype from "../Renderer/PixelDatatype.js";
  8. import PixelFormat from "../Core/PixelFormat.js";
  9. import Sync from "../Renderer/Sync.js";
  10. import RuntimeError from "../Core/RuntimeError.js";
  11. /**
  12. * @private
  13. */
  14. function PickFramebuffer(context) {
  15. // Override per-command states
  16. const 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 = new FramebufferManager({
  25. depthStencil: true,
  26. });
  27. this._passState = passState;
  28. this._width = 0;
  29. this._height = 0;
  30. }
  31. /**
  32. * Return the picked object(s) rendered within a given rectangle.
  33. *
  34. * @private
  35. * @param {object} context The active context.
  36. * @param {Uint8Array|Uint16Array|Float32Array|Uint32Array} pixels The pixels in the specified rectangle.
  37. * @param {number} width The rectangle width.
  38. * @param {number} height The rectangle height.
  39. * @param {number} [limit=1] If supplied, stop iterating after collecting this many objects.
  40. * @returns {object[]} A list of rendered objects, ordered by distance to the middle of the rectangle.
  41. */
  42. function pickObjectsFromPixels(context, pixels, width, height, limit = 1) {
  43. const max = Math.max(width, height);
  44. const length = max * max;
  45. const halfWidth = Math.floor(width * 0.5);
  46. const halfHeight = Math.floor(height * 0.5);
  47. let x = 0;
  48. let y = 0;
  49. let dx = 0;
  50. let dy = -1;
  51. // Spiral around the center pixel, this is a workaround until
  52. // we can access the depth buffer on all browsers.
  53. // The region does not have to square and the dimensions do not have to be odd, but
  54. // loop iterations would be wasted. Prefer square regions where the size is odd.
  55. const objects = new Set();
  56. for (let i = 0; i < length; ++i) {
  57. if (
  58. -halfWidth <= x &&
  59. x <= halfWidth &&
  60. -halfHeight <= y &&
  61. y <= halfHeight
  62. ) {
  63. const index = 4 * ((halfHeight - y) * width + x + halfWidth);
  64. const pickColor = Color.bytesToRgba(
  65. pixels[index],
  66. pixels[index + 1],
  67. pixels[index + 2],
  68. pixels[index + 3],
  69. );
  70. const object = context.getObjectByPickColor(pickColor);
  71. if (defined(object)) {
  72. objects.add(object);
  73. if (objects.size >= limit) {
  74. break;
  75. }
  76. }
  77. }
  78. // if (top right || bottom left corners) || (top left corner) || (bottom right corner + (1, 0))
  79. // change spiral direction
  80. if (x === y || (x < 0 && -x === y) || (x > 0 && x === 1 - y)) {
  81. const temp = dx;
  82. dx = -dy;
  83. dy = temp;
  84. }
  85. x += dx;
  86. y += dy;
  87. }
  88. return [...objects];
  89. }
  90. PickFramebuffer.prototype.begin = function (screenSpaceRectangle, viewport) {
  91. const context = this._context;
  92. const { width, height } = viewport;
  93. BoundingRectangle.clone(
  94. screenSpaceRectangle,
  95. this._passState.scissorTest.rectangle,
  96. );
  97. // Create or recreate renderbuffers and framebuffer used for picking
  98. this._width = width;
  99. this._height = height;
  100. this._fb.update(context, width, height);
  101. this._passState.framebuffer = this._fb.framebuffer;
  102. this._passState.viewport.width = width;
  103. this._passState.viewport.height = height;
  104. return this._passState;
  105. };
  106. /**
  107. * Return the picked objects rendered within a given rectangle using asynchronously without stalling the GPU.
  108. * Requires WebGL2.
  109. *
  110. * @param {BoundingRectangle} screenSpaceRectangle
  111. * @param {FrameState} frameState
  112. * @param {number} [limit=1] If supplied, stop iterating after collecting this many objects.
  113. * @returns {Promise<object[]>} A list of rendered objects, ordered by distance to the middle of the rectangle.
  114. *
  115. * @exception {RuntimeError} Async Picking Request Timeout.
  116. * @exception {DeveloperError} A WebGL 2 context is required.
  117. */
  118. PickFramebuffer.prototype.endAsync = async function (
  119. screenSpaceRectangle,
  120. frameState,
  121. limit = 1,
  122. ) {
  123. const width = screenSpaceRectangle.width ?? 1.0;
  124. const height = screenSpaceRectangle.height ?? 1.0;
  125. const context = this._context;
  126. const framebuffer = this._fb.framebuffer;
  127. let pixelDatatype = PixelDatatype.UNSIGNED_BYTE;
  128. let pixelFormat = PixelFormat.RGBA;
  129. if (defined(framebuffer) && framebuffer.numberOfColorAttachments > 0) {
  130. pixelDatatype = framebuffer.getColorTexture(0).pixelDatatype;
  131. pixelFormat = framebuffer.getColorTexture(0).pixelFormat;
  132. }
  133. const pbo = context.readPixelsToPBO({
  134. x: screenSpaceRectangle.x,
  135. y: screenSpaceRectangle.y,
  136. width: width,
  137. height: height,
  138. framebuffer: framebuffer,
  139. });
  140. const sync = Sync.create({
  141. context: context,
  142. });
  143. // Wait for the GPU to signal that it is ready to readback the PBO data
  144. try {
  145. await sync.waitForSignal((next) => frameState.afterRender.push(next));
  146. const pixels = PixelFormat.createTypedArray(
  147. pixelFormat,
  148. pixelDatatype,
  149. width,
  150. height,
  151. );
  152. pbo.getBufferData(pixels);
  153. const pickedObjects = pickObjectsFromPixels(
  154. context,
  155. pixels,
  156. width,
  157. height,
  158. limit,
  159. );
  160. return pickedObjects;
  161. } catch (e) {
  162. throw new RuntimeError("Async Picking Request Timeout");
  163. } finally {
  164. sync.destroy();
  165. pbo.destroy();
  166. }
  167. };
  168. /**
  169. * Return the picked objects rendered within a given rectangle.
  170. *
  171. * @param {BoundingRectangle} screenSpaceRectangle
  172. * @param {number} [limit=1] If supplied, stop iterating after collecting this many objects.
  173. * @returns {object[]} A list of rendered objects, ordered by distance to the middle of the rectangle.
  174. */
  175. PickFramebuffer.prototype.end = function (screenSpaceRectangle, limit = 1) {
  176. const width = screenSpaceRectangle.width ?? 1.0;
  177. const height = screenSpaceRectangle.height ?? 1.0;
  178. const context = this._context;
  179. const pixels = context.readPixels({
  180. x: screenSpaceRectangle.x,
  181. y: screenSpaceRectangle.y,
  182. width: width,
  183. height: height,
  184. framebuffer: this._fb.framebuffer,
  185. });
  186. return pickObjectsFromPixels(context, pixels, width, height, limit);
  187. };
  188. /**
  189. * Return a typed array containing the RGBA (byte) components of the
  190. * pixel that is at the center of the given rectangle.
  191. *
  192. * This may, for example, be voxel tile and sample information as rendered
  193. * by a pickVoxel pass, within a given rectangle. Or it may be the result
  194. * of a metadata picking rendering pass.
  195. *
  196. * @param {BoundingRectangle} screenSpaceRectangle
  197. * @returns {Uint8Array} The RGBA components
  198. */
  199. PickFramebuffer.prototype.readCenterPixel = function (screenSpaceRectangle) {
  200. const width = screenSpaceRectangle.width ?? 1.0;
  201. const height = screenSpaceRectangle.height ?? 1.0;
  202. const context = this._context;
  203. const pixels = context.readPixels({
  204. x: screenSpaceRectangle.x,
  205. y: screenSpaceRectangle.y,
  206. width: width,
  207. height: height,
  208. framebuffer: this._fb.framebuffer,
  209. });
  210. // Read the center pixel
  211. const halfWidth = Math.floor(width * 0.5);
  212. const halfHeight = Math.floor(height * 0.5);
  213. const index = 4 * (halfHeight * width + halfWidth);
  214. return pixels.slice(index, index + 4);
  215. };
  216. PickFramebuffer.prototype.isDestroyed = function () {
  217. return false;
  218. };
  219. PickFramebuffer.prototype.destroy = function () {
  220. this._fb.destroy();
  221. return destroyObject(this);
  222. };
  223. export default PickFramebuffer;