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

VoxelBoxShape.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import CesiumMath from "../Core/Math.js";
  4. import Check from "../Core/Check.js";
  5. import Matrix3 from "../Core/Matrix3.js";
  6. import Matrix4 from "../Core/Matrix4.js";
  7. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  8. import VoxelBoundsCollection from "./VoxelBoundsCollection.js";
  9. import ClippingPlane from "./ClippingPlane.js";
  10. /**
  11. * A box {@link VoxelShape}.
  12. *
  13. * @alias VoxelBoxShape
  14. * @constructor
  15. *
  16. * @see VoxelShape
  17. * @see VoxelEllipsoidShape
  18. * @see VoxelCylinderShape
  19. * @see VoxelShapeType
  20. *
  21. * @private
  22. */
  23. function VoxelBoxShape() {
  24. this._orientedBoundingBox = new OrientedBoundingBox();
  25. this._boundingSphere = new BoundingSphere();
  26. this._boundTransform = new Matrix4();
  27. this._shapeTransform = new Matrix4();
  28. /**
  29. * The minimum bounds of the shape.
  30. * @type {Cartesian3}
  31. * @private
  32. */
  33. this._minBounds = VoxelBoxShape.DefaultMinBounds.clone();
  34. /**
  35. * The maximum bounds of the shape.
  36. * @type {Cartesian3}
  37. * @private
  38. */
  39. this._maxBounds = VoxelBoxShape.DefaultMaxBounds.clone();
  40. /**
  41. * The minimum render bounds of the shape.
  42. * @type {Cartesian3}
  43. * @private
  44. */
  45. this._renderMinBounds = VoxelBoxShape.DefaultMinBounds.clone();
  46. /**
  47. * The maximum render bounds of the shape.
  48. * @type {Cartesian3}
  49. * @private
  50. */
  51. this._renderMaxBounds = VoxelBoxShape.DefaultMaxBounds.clone();
  52. const { DefaultMinBounds, DefaultMaxBounds } = VoxelBoxShape;
  53. const boundPlanes = [
  54. new ClippingPlane(
  55. Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()),
  56. DefaultMinBounds.x,
  57. ),
  58. new ClippingPlane(
  59. Cartesian3.negate(Cartesian3.UNIT_Y, new Cartesian3()),
  60. DefaultMinBounds.y,
  61. ),
  62. new ClippingPlane(
  63. Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()),
  64. DefaultMinBounds.z,
  65. ),
  66. new ClippingPlane(Cartesian3.UNIT_X, -DefaultMaxBounds.x),
  67. new ClippingPlane(Cartesian3.UNIT_Y, -DefaultMaxBounds.y),
  68. new ClippingPlane(Cartesian3.UNIT_Z, -DefaultMaxBounds.z),
  69. ];
  70. this._renderBoundPlanes = new VoxelBoundsCollection({ planes: boundPlanes });
  71. /**
  72. * UV space transformation translation (JS-only, not a shader uniform)
  73. * @type {Cartesian3}
  74. * @private
  75. */
  76. this._localToShapeUvTranslate = new Cartesian3();
  77. this._shaderUniforms = {
  78. boxEcToXyz: new Matrix3(),
  79. boxLocalToShapeUvScale: new Cartesian3(),
  80. };
  81. this._shaderDefines = {
  82. BOX_INTERSECTION_INDEX: undefined,
  83. };
  84. this._shaderMaximumIntersectionsLength = 0; // not known until update
  85. }
  86. Object.defineProperties(VoxelBoxShape.prototype, {
  87. /**
  88. * An oriented bounding box containing the bounded shape.
  89. *
  90. * @memberof VoxelBoxShape.prototype
  91. * @type {OrientedBoundingBox}
  92. * @readonly
  93. * @private
  94. */
  95. orientedBoundingBox: {
  96. get: function () {
  97. return this._orientedBoundingBox;
  98. },
  99. },
  100. /**
  101. * A collection of planes used for the render bounds
  102. * @memberof VoxelBoxShape.prototype
  103. * @type {VoxelBoundsCollection}
  104. * @readonly
  105. * @private
  106. */
  107. renderBoundPlanes: {
  108. get: function () {
  109. return this._renderBoundPlanes;
  110. },
  111. },
  112. /**
  113. * A bounding sphere containing the bounded shape.
  114. *
  115. * @memberof VoxelBoxShape.prototype
  116. * @type {BoundingSphere}
  117. * @readonly
  118. * @private
  119. */
  120. boundingSphere: {
  121. get: function () {
  122. return this._boundingSphere;
  123. },
  124. },
  125. /**
  126. * A transformation matrix containing the bounded shape.
  127. *
  128. * @memberof VoxelBoxShape.prototype
  129. * @type {Matrix4}
  130. * @readonly
  131. * @private
  132. */
  133. boundTransform: {
  134. get: function () {
  135. return this._boundTransform;
  136. },
  137. },
  138. /**
  139. * A transformation matrix containing the shape, ignoring the bounds.
  140. *
  141. * @memberof VoxelBoxShape.prototype
  142. * @type {Matrix4}
  143. * @readonly
  144. * @private
  145. */
  146. shapeTransform: {
  147. get: function () {
  148. return this._shapeTransform;
  149. },
  150. },
  151. /**
  152. * @memberof VoxelBoxShape.prototype
  153. * @type {Object<string, any>}
  154. * @readonly
  155. * @private
  156. */
  157. shaderUniforms: {
  158. get: function () {
  159. return this._shaderUniforms;
  160. },
  161. },
  162. /**
  163. * @memberof VoxelBoxShape.prototype
  164. * @type {Object<string, any>}
  165. * @readonly
  166. * @private
  167. */
  168. shaderDefines: {
  169. get: function () {
  170. return this._shaderDefines;
  171. },
  172. },
  173. /**
  174. * The maximum number of intersections against the shape for any ray direction.
  175. * @memberof VoxelBoxShape.prototype
  176. * @type {number}
  177. * @readonly
  178. * @private
  179. */
  180. shaderMaximumIntersectionsLength: {
  181. get: function () {
  182. return this._shaderMaximumIntersectionsLength;
  183. },
  184. },
  185. });
  186. const scratchCenter = new Cartesian3();
  187. const scratchScale = new Cartesian3();
  188. const scratchRotation = new Matrix3();
  189. const scratchClipMinBounds = new Cartesian3();
  190. const scratchClipMaxBounds = new Cartesian3();
  191. /**
  192. * Update the shape's state.
  193. * @private
  194. * @param {Matrix4} modelMatrix The model matrix.
  195. * @param {Cartesian3} minBounds The minimum bounds.
  196. * @param {Cartesian3} maxBounds The maximum bounds.
  197. * @param {Cartesian3} [clipMinBounds] The minimum clip bounds.
  198. * @param {Cartesian3} [clipMaxBounds] The maximum clip bounds.
  199. * @returns {boolean} Whether the shape is visible.
  200. */
  201. VoxelBoxShape.prototype.update = function (
  202. modelMatrix,
  203. minBounds,
  204. maxBounds,
  205. clipMinBounds,
  206. clipMaxBounds,
  207. ) {
  208. //>>includeStart('debug', pragmas.debug);
  209. Check.typeOf.object("modelMatrix", modelMatrix);
  210. Check.typeOf.object("minBounds", minBounds);
  211. Check.typeOf.object("maxBounds", maxBounds);
  212. //>>includeEnd('debug');
  213. clipMinBounds = clipMinBounds ?? minBounds.clone(scratchClipMinBounds);
  214. clipMaxBounds = clipMaxBounds ?? maxBounds.clone(scratchClipMaxBounds);
  215. minBounds = Cartesian3.clone(minBounds, this._minBounds);
  216. maxBounds = Cartesian3.clone(maxBounds, this._maxBounds);
  217. const renderMinBounds = Cartesian3.clamp(
  218. minBounds,
  219. clipMinBounds,
  220. clipMaxBounds,
  221. this._renderMinBounds,
  222. );
  223. const renderMaxBounds = Cartesian3.clamp(
  224. maxBounds,
  225. clipMinBounds,
  226. clipMaxBounds,
  227. this._renderMaxBounds,
  228. );
  229. // Box is not visible if:
  230. // - any of the min render bounds exceed the max render bounds
  231. // - two or more of the min bounds equal the max bounds (line / point)
  232. // - scale is 0 for any component (too annoying to reconstruct rotation matrix)
  233. const scale = Matrix4.getScale(modelMatrix, scratchScale);
  234. if (
  235. renderMinBounds.x > renderMaxBounds.x ||
  236. renderMinBounds.y > renderMaxBounds.y ||
  237. renderMinBounds.z > renderMaxBounds.z ||
  238. (renderMinBounds.x === renderMaxBounds.x) +
  239. (renderMinBounds.y === renderMaxBounds.y) +
  240. (renderMinBounds.z === renderMaxBounds.z) >=
  241. 2 ||
  242. scale.x === 0.0 ||
  243. scale.y === 0.0 ||
  244. scale.z === 0.0
  245. ) {
  246. return false;
  247. }
  248. // Update the render bounds planes
  249. const renderBoundPlanes = this._renderBoundPlanes;
  250. renderBoundPlanes.get(0).distance = renderMinBounds.x;
  251. renderBoundPlanes.get(1).distance = renderMinBounds.y;
  252. renderBoundPlanes.get(2).distance = renderMinBounds.z;
  253. renderBoundPlanes.get(3).distance = -renderMaxBounds.x;
  254. renderBoundPlanes.get(4).distance = -renderMaxBounds.y;
  255. renderBoundPlanes.get(5).distance = -renderMaxBounds.z;
  256. this._shapeTransform = Matrix4.clone(modelMatrix, this._shapeTransform);
  257. this._orientedBoundingBox = getBoxChunkObb(
  258. renderMinBounds,
  259. renderMaxBounds,
  260. this._shapeTransform,
  261. this._orientedBoundingBox,
  262. );
  263. // All of the box bounds go from -1 to +1, so the model matrix scale can be
  264. // used as the oriented bounding box half axes.
  265. this._boundTransform = Matrix4.fromRotationTranslation(
  266. this._orientedBoundingBox.halfAxes,
  267. this._orientedBoundingBox.center,
  268. this._boundTransform,
  269. );
  270. this._boundingSphere = BoundingSphere.fromOrientedBoundingBox(
  271. this._orientedBoundingBox,
  272. this._boundingSphere,
  273. );
  274. const shaderUniforms = this._shaderUniforms;
  275. const shaderDefines = this._shaderDefines;
  276. // To keep things simple, clear the defines every time
  277. for (const key in shaderDefines) {
  278. if (shaderDefines.hasOwnProperty(key)) {
  279. shaderDefines[key] = undefined;
  280. }
  281. }
  282. // Keep track of how many intersections there are going to be.
  283. let intersectionCount = 0;
  284. shaderDefines["BOX_INTERSECTION_INDEX"] = intersectionCount;
  285. intersectionCount += 1;
  286. // Compute scale and translation to transform from UV space to bounded UV space
  287. const min = minBounds;
  288. const max = maxBounds;
  289. const boxLocalToShapeUvScale = Cartesian3.fromElements(
  290. boundScale(min.x, max.x),
  291. boundScale(min.y, max.y),
  292. boundScale(min.z, max.z),
  293. shaderUniforms.boxLocalToShapeUvScale,
  294. );
  295. this._localToShapeUvTranslate = Cartesian3.negate(
  296. Cartesian3.multiplyComponents(
  297. boxLocalToShapeUvScale,
  298. min,
  299. this._localToShapeUvTranslate,
  300. ),
  301. this._localToShapeUvTranslate,
  302. );
  303. this._shaderMaximumIntersectionsLength = intersectionCount;
  304. return true;
  305. };
  306. function boundScale(minBound, maxBound) {
  307. return CesiumMath.equalsEpsilon(minBound, maxBound, CesiumMath.EPSILON7)
  308. ? 1.0
  309. : 1.0 / (maxBound - minBound);
  310. }
  311. const scratchTransformPositionWorldToLocal = new Matrix4();
  312. /**
  313. * Update any view-dependent transforms.
  314. * @private
  315. * @param {FrameState} frameState The frame state.
  316. */
  317. VoxelBoxShape.prototype.updateViewTransforms = function (frameState) {
  318. const shaderUniforms = this._shaderUniforms;
  319. const transformPositionWorldToLocal = Matrix4.inverse(
  320. this._shapeTransform,
  321. scratchTransformPositionWorldToLocal,
  322. );
  323. const transformDirectionWorldToLocal = Matrix4.getMatrix3(
  324. transformPositionWorldToLocal,
  325. shaderUniforms.boxEcToXyz,
  326. );
  327. const rotateViewToWorld = frameState.context.uniformState.inverseViewRotation;
  328. Matrix3.multiply(
  329. transformDirectionWorldToLocal,
  330. rotateViewToWorld,
  331. shaderUniforms.boxEcToXyz,
  332. );
  333. };
  334. /**
  335. * Convert a local coordinate to the shape's UV space.
  336. * @private
  337. * @param {Cartesian3} positionLocal The local coordinate to convert.
  338. * @param {Cartesian3} result The Cartesian3 to store the result in.
  339. * @returns {Cartesian3} The converted UV coordinate.
  340. */
  341. VoxelBoxShape.prototype.convertLocalToShapeUvSpace = function (
  342. positionLocal,
  343. result,
  344. ) {
  345. //>>includeStart('debug', pragmas.debug);
  346. Check.typeOf.object("positionLocal", positionLocal);
  347. Check.typeOf.object("result", result);
  348. //>>includeEnd('debug');
  349. const { boxLocalToShapeUvScale } = this._shaderUniforms;
  350. const boxLocalToShapeUvTranslate = this._localToShapeUvTranslate;
  351. return Cartesian3.add(
  352. Cartesian3.multiplyComponents(
  353. positionLocal,
  354. boxLocalToShapeUvScale,
  355. result,
  356. ),
  357. boxLocalToShapeUvTranslate,
  358. result,
  359. );
  360. };
  361. const scratchTileMinBounds = new Cartesian3();
  362. const scratchTileMaxBounds = new Cartesian3();
  363. /**
  364. * Computes an oriented bounding box for a specified tile.
  365. * @private
  366. * @param {number} tileLevel The tile's level.
  367. * @param {number} tileX The tile's x coordinate.
  368. * @param {number} tileY The tile's y coordinate.
  369. * @param {number} tileZ The tile's z coordinate.
  370. * @param {OrientedBoundingBox} result The oriented bounding box that will be set to enclose the specified tile
  371. * @returns {OrientedBoundingBox} The oriented bounding box.
  372. */
  373. VoxelBoxShape.prototype.computeOrientedBoundingBoxForTile = function (
  374. tileLevel,
  375. tileX,
  376. tileY,
  377. tileZ,
  378. result,
  379. ) {
  380. //>>includeStart('debug', pragmas.debug);
  381. Check.typeOf.number("tileLevel", tileLevel);
  382. Check.typeOf.number("tileX", tileX);
  383. Check.typeOf.number("tileY", tileY);
  384. Check.typeOf.number("tileZ", tileZ);
  385. Check.typeOf.object("result", result);
  386. //>>includeEnd('debug');
  387. const minBounds = this._minBounds;
  388. const maxBounds = this._maxBounds;
  389. const sizeAtLevel = 1.0 / Math.pow(2, tileLevel);
  390. const tileMinBounds = Cartesian3.fromElements(
  391. CesiumMath.lerp(minBounds.x, maxBounds.x, sizeAtLevel * tileX),
  392. CesiumMath.lerp(minBounds.y, maxBounds.y, sizeAtLevel * tileY),
  393. CesiumMath.lerp(minBounds.z, maxBounds.z, sizeAtLevel * tileZ),
  394. scratchTileMinBounds,
  395. );
  396. const tileMaxBounds = Cartesian3.fromElements(
  397. CesiumMath.lerp(minBounds.x, maxBounds.x, sizeAtLevel * (tileX + 1)),
  398. CesiumMath.lerp(minBounds.y, maxBounds.y, sizeAtLevel * (tileY + 1)),
  399. CesiumMath.lerp(minBounds.z, maxBounds.z, sizeAtLevel * (tileZ + 1)),
  400. scratchTileMaxBounds,
  401. );
  402. return getBoxChunkObb(
  403. tileMinBounds,
  404. tileMaxBounds,
  405. this._shapeTransform,
  406. result,
  407. );
  408. };
  409. const sampleSizeScratch = new Cartesian3();
  410. /**
  411. * Computes an oriented bounding box for a specified sample within a specified tile.
  412. * @private
  413. * @param {SpatialNode} spatialNode The spatial node containing the sample
  414. * @param {Cartesian3} tileDimensions The size of the tile in number of samples, before padding
  415. * @param {Cartesian3} tileUv The sample coordinate within the tile
  416. * @param {OrientedBoundingBox} result The oriented bounding box that will be set to enclose the specified sample
  417. * @returns {OrientedBoundingBox} The oriented bounding box.
  418. */
  419. VoxelBoxShape.prototype.computeOrientedBoundingBoxForSample = function (
  420. spatialNode,
  421. tileDimensions,
  422. tileUv,
  423. result,
  424. ) {
  425. //>>includeStart('debug', pragmas.debug);
  426. Check.typeOf.object("spatialNode", spatialNode);
  427. Check.typeOf.object("tileDimensions", tileDimensions);
  428. Check.typeOf.object("tileUv", tileUv);
  429. Check.typeOf.object("result", result);
  430. //>>includeEnd('debug');
  431. const tileSizeAtLevel = 1.0 / Math.pow(2, spatialNode.level);
  432. const sampleSize = Cartesian3.divideComponents(
  433. Cartesian3.ONE,
  434. tileDimensions,
  435. sampleSizeScratch,
  436. );
  437. const sampleSizeAtLevel = Cartesian3.multiplyByScalar(
  438. sampleSize,
  439. tileSizeAtLevel,
  440. sampleSizeScratch,
  441. );
  442. const minLerp = Cartesian3.multiplyByScalar(
  443. Cartesian3.fromElements(
  444. spatialNode.x + tileUv.x,
  445. spatialNode.y + tileUv.y,
  446. spatialNode.z + tileUv.z,
  447. scratchTileMinBounds,
  448. ),
  449. tileSizeAtLevel,
  450. scratchTileMinBounds,
  451. );
  452. const maxLerp = Cartesian3.add(
  453. minLerp,
  454. sampleSizeAtLevel,
  455. scratchTileMaxBounds,
  456. );
  457. const minBounds = this._minBounds;
  458. const maxBounds = this._maxBounds;
  459. const sampleMinBounds = Cartesian3.fromElements(
  460. CesiumMath.lerp(minBounds.x, maxBounds.x, minLerp.x),
  461. CesiumMath.lerp(minBounds.y, maxBounds.y, minLerp.y),
  462. CesiumMath.lerp(minBounds.z, maxBounds.z, minLerp.z),
  463. scratchTileMinBounds,
  464. );
  465. const sampleMaxBounds = Cartesian3.fromElements(
  466. CesiumMath.lerp(minBounds.x, maxBounds.x, maxLerp.x),
  467. CesiumMath.lerp(minBounds.y, maxBounds.y, maxLerp.y),
  468. CesiumMath.lerp(minBounds.z, maxBounds.z, maxLerp.z),
  469. scratchTileMaxBounds,
  470. );
  471. return getBoxChunkObb(
  472. sampleMinBounds,
  473. sampleMaxBounds,
  474. this._shapeTransform,
  475. result,
  476. );
  477. };
  478. /**
  479. * Defines the minimum bounds of the shape. Corresponds to minimum X, Y, Z.
  480. * @private
  481. * @type {Cartesian3}
  482. * @constant
  483. * @readonly
  484. */
  485. VoxelBoxShape.DefaultMinBounds = Object.freeze(
  486. new Cartesian3(-1.0, -1.0, -1.0),
  487. );
  488. /**
  489. * Defines the maximum bounds of the shape. Corresponds to maximum X, Y, Z.
  490. * @private
  491. * @type {Cartesian3}
  492. * @constant
  493. * @readonly
  494. */
  495. VoxelBoxShape.DefaultMaxBounds = Object.freeze(
  496. new Cartesian3(+1.0, +1.0, +1.0),
  497. );
  498. const scratchBoxScale = new Cartesian3();
  499. /**
  500. * Computes an {@link OrientedBoundingBox} for a subregion of the shape.
  501. *
  502. * @function
  503. *
  504. * @param {Cartesian3} minimumBounds The minimum bounds, in the local coordinates of the shape.
  505. * @param {Cartesian3} maximumBounds The maximum bounds, in the local coordinates of the shape.
  506. * @param {Matrix4} matrix The matrix to transform the points.
  507. * @param {OrientedBoundingBox} result The object onto which to store the result.
  508. * @returns {OrientedBoundingBox} The oriented bounding box that contains this subregion.
  509. *
  510. * @private
  511. */
  512. function getBoxChunkObb(minimumBounds, maximumBounds, matrix, result) {
  513. const defaultMinBounds = VoxelBoxShape.DefaultMinBounds;
  514. const defaultMaxBounds = VoxelBoxShape.DefaultMaxBounds;
  515. const isDefaultBounds =
  516. Cartesian3.equals(minimumBounds, defaultMinBounds) &&
  517. Cartesian3.equals(maximumBounds, defaultMaxBounds);
  518. if (isDefaultBounds) {
  519. result.center = Matrix4.getTranslation(matrix, result.center);
  520. result.halfAxes = Matrix4.getMatrix3(matrix, result.halfAxes);
  521. } else {
  522. let scale = Matrix4.getScale(matrix, scratchBoxScale);
  523. const localCenter = Cartesian3.midpoint(
  524. minimumBounds,
  525. maximumBounds,
  526. scratchCenter,
  527. );
  528. result.center = Matrix4.multiplyByPoint(matrix, localCenter, result.center);
  529. scale = Cartesian3.fromElements(
  530. scale.x * 0.5 * (maximumBounds.x - minimumBounds.x),
  531. scale.y * 0.5 * (maximumBounds.y - minimumBounds.y),
  532. scale.z * 0.5 * (maximumBounds.z - minimumBounds.z),
  533. scratchBoxScale,
  534. );
  535. const rotation = Matrix4.getRotation(matrix, scratchRotation);
  536. result.halfAxes = Matrix3.setScale(rotation, scale, result.halfAxes);
  537. }
  538. return result;
  539. }
  540. export default VoxelBoxShape;