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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136
  1. import buildVoxelCustomShader from "./buildVoxelCustomShader.js";
  2. import buildVoxelDrawCommands from "./buildVoxelDrawCommands.js";
  3. import Cartesian2 from "../Core/Cartesian2.js";
  4. import Cartesian3 from "../Core/Cartesian3.js";
  5. import Cartesian4 from "../Core/Cartesian4.js";
  6. import Cartographic from "../Core/Cartographic.js";
  7. import Cesium3DTilesetStatistics from "./Cesium3DTilesetStatistics.js";
  8. import CesiumMath from "../Core/Math.js";
  9. import Check from "../Core/Check.js";
  10. import Color from "../Core/Color.js";
  11. import ClippingPlaneCollection from "./ClippingPlaneCollection.js";
  12. import clone from "../Core/clone.js";
  13. import CustomShader from "./Model/CustomShader.js";
  14. import Frozen from "../Core/Frozen.js";
  15. import defined from "../Core/defined.js";
  16. import destroyObject from "../Core/destroyObject.js";
  17. import Ellipsoid from "../Core/Ellipsoid.js";
  18. import Event from "../Core/Event.js";
  19. import JulianDate from "../Core/JulianDate.js";
  20. import Material from "./Material.js";
  21. import Matrix3 from "../Core/Matrix3.js";
  22. import Matrix4 from "../Core/Matrix4.js";
  23. import MetadataComponentType from "./MetadataComponentType.js";
  24. import MetadataType from "./MetadataType.js";
  25. import oneTimeWarning from "../Core/oneTimeWarning.js";
  26. import PolylineCollection from "./PolylineCollection.js";
  27. import VerticalExaggeration from "../Core/VerticalExaggeration.js";
  28. import VoxelContent from "./VoxelContent.js";
  29. import VoxelShapeType from "./VoxelShapeType.js";
  30. import VoxelTraversal from "./VoxelTraversal.js";
  31. import VoxelMetadataOrder from "./VoxelMetadataOrder.js";
  32. /**
  33. * A primitive that renders voxel data from a {@link VoxelProvider}.
  34. *
  35. * @alias VoxelPrimitive
  36. * @constructor
  37. *
  38. * @param {object} [options] Object with the following properties:
  39. * @param {VoxelProvider} [options.provider] The voxel provider that supplies the primitive with tile data.
  40. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The model matrix used to transform the primitive.
  41. * @param {CustomShader} [options.customShader] The custom shader used to style the primitive.
  42. * @param {Clock} [options.clock] The clock used to control time dynamic behavior.
  43. * @param {boolean} [options.calculateStatistics] Generate statistics for performance profile.
  44. *
  45. * @see VoxelProvider
  46. * @see Cesium3DTilesVoxelProvider
  47. * @see VoxelShapeType
  48. * @see {@link https://github.com/CesiumGS/cesium/tree/main/Documentation/CustomShaderGuide|Custom Shader Guide}
  49. *
  50. * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
  51. */
  52. function VoxelPrimitive(options) {
  53. options = options ?? Frozen.EMPTY_OBJECT;
  54. const {
  55. provider = VoxelPrimitive.DefaultProvider,
  56. modelMatrix = Matrix4.IDENTITY,
  57. customShader = buildVoxelCustomShader(provider),
  58. clock,
  59. calculateStatistics = false,
  60. } = options;
  61. /**
  62. * @type {boolean}
  63. * @private
  64. */
  65. this._ready = false;
  66. /**
  67. * @type {VoxelProvider}
  68. * @private
  69. */
  70. this._provider = provider;
  71. /**
  72. * This member is not created until the first update loop.
  73. *
  74. * @type {VoxelTraversal}
  75. * @private
  76. */
  77. this._traversal = undefined;
  78. /**
  79. * @type {Cesium3DTilesetStatistics}
  80. * @private
  81. */
  82. this._statistics = new Cesium3DTilesetStatistics();
  83. /**
  84. * @type {boolean}
  85. * @private
  86. */
  87. this._calculateStatistics = calculateStatistics;
  88. const {
  89. shape: shapeType,
  90. minBounds = VoxelShapeType.getMinBounds(shapeType),
  91. maxBounds = VoxelShapeType.getMaxBounds(shapeType),
  92. dimensions,
  93. paddingBefore = Cartesian3.ZERO,
  94. paddingAfter = Cartesian3.ZERO,
  95. metadataOrder,
  96. availableLevels = 1,
  97. } = provider;
  98. /**
  99. * @type {Cartesian3}
  100. * @readonly
  101. * @constant
  102. * @private
  103. */
  104. this._dimensions = Cartesian3.clone(dimensions);
  105. /**
  106. * @type {Cartesian3}
  107. * @private
  108. */
  109. this._paddingBefore = Cartesian3.clone(paddingBefore);
  110. /**
  111. * @type {Cartesian3}
  112. * @private
  113. */
  114. this._paddingAfter = Cartesian3.clone(paddingAfter);
  115. /**
  116. * @type {Cartesian3}
  117. * @private
  118. */
  119. this._inputDimensions = computeInputDimensions(
  120. dimensions,
  121. paddingBefore,
  122. paddingAfter,
  123. metadataOrder,
  124. );
  125. /**
  126. * @type {number}
  127. * @private
  128. */
  129. this._availableLevels = availableLevels;
  130. /**
  131. * @type {Cartesian3}
  132. * @private
  133. */
  134. this._minBounds = minBounds.clone();
  135. /**
  136. * Used to detect if the shape is dirty.
  137. *
  138. * @type {Cartesian3}
  139. * @private
  140. */
  141. this._minBoundsOld = new Cartesian3();
  142. /**
  143. * @type {Cartesian3}
  144. * @private
  145. */
  146. this._maxBounds = maxBounds.clone();
  147. /**
  148. * Used to detect if the shape is dirty.
  149. *
  150. * @type {Cartesian3}
  151. * @private
  152. */
  153. this._maxBoundsOld = new Cartesian3();
  154. /**
  155. * @type {Cartesian3}
  156. * @private
  157. */
  158. this._minClippingBounds = minBounds.clone();
  159. /**
  160. * Used to detect if the clipping is dirty.
  161. *
  162. * @type {Cartesian3}
  163. * @private
  164. */
  165. this._minClippingBoundsOld = new Cartesian3();
  166. /**
  167. * @type {Cartesian3}
  168. * @private
  169. */
  170. this._maxClippingBounds = maxBounds.clone();
  171. /**
  172. * Used to detect if the clipping is dirty.
  173. *
  174. * @type {Cartesian3}
  175. * @private
  176. */
  177. this._maxClippingBoundsOld = new Cartesian3();
  178. /**
  179. * Vertical exaggeration applied to the voxel shape
  180. *
  181. * @type {number}
  182. * @private
  183. */
  184. this._verticalExaggeration = 1.0;
  185. /**
  186. * The height relative to which the shape is exaggerated.
  187. *
  188. * @type {number}
  189. * @private
  190. */
  191. this._verticalExaggerationRelativeHeight = 0.0;
  192. /**
  193. * Clipping planes on the primitive
  194. *
  195. * @type {ClippingPlaneCollection}
  196. * @private
  197. */
  198. this._clippingPlanes = undefined;
  199. /**
  200. * Keeps track of when the clipping planes change
  201. *
  202. * @type {number}
  203. * @private
  204. */
  205. this._clippingPlanesState = 0;
  206. /**
  207. * Keeps track of when the clipping planes are enabled / disabled
  208. *
  209. * @type {boolean}
  210. * @private
  211. */
  212. this._clippingPlanesEnabled = false;
  213. /**
  214. * The primitive's model matrix.
  215. *
  216. * @type {Matrix4}
  217. * @private
  218. */
  219. this._modelMatrix = Matrix4.clone(modelMatrix);
  220. /**
  221. * Used to detect if the model matrix is dirty.
  222. *
  223. * @type {Matrix4}
  224. * @private
  225. */
  226. this._modelMatrixOld = Matrix4.clone(this._modelMatrix);
  227. /**
  228. * @type {CustomShader}
  229. * @private
  230. */
  231. this._customShader = customShader ?? VoxelPrimitive.DefaultCustomShader;
  232. /**
  233. * @type {Event}
  234. * @private
  235. */
  236. this._customShaderCompilationEvent = new Event();
  237. /**
  238. * @type {boolean}
  239. * @private
  240. */
  241. this._shaderDirty = true;
  242. /**
  243. * @type {DrawCommand}
  244. * @private
  245. */
  246. this._drawCommand = undefined;
  247. /**
  248. * @type {DrawCommand}
  249. * @private
  250. */
  251. this._drawCommandPick = undefined;
  252. /**
  253. * @type {object}
  254. * @private
  255. */
  256. this._pickId = undefined;
  257. /**
  258. * @type {Clock}
  259. * @private
  260. */
  261. this._clock = clock;
  262. // Transforms and other values that are computed when the shape changes
  263. /**
  264. * @type {Matrix4}
  265. * @private
  266. */
  267. this._transformPositionLocalToWorld = new Matrix4();
  268. /**
  269. * @type {Matrix4}
  270. * @private
  271. */
  272. this._transformPositionWorldToLocal = new Matrix4();
  273. /**
  274. * Transforms a plane in Hessian normal form from local space to view space.
  275. * @type {Matrix4}
  276. * @private
  277. */
  278. this._transformPlaneLocalToView = new Matrix4();
  279. /**
  280. * @type {Matrix3}
  281. * @private
  282. */
  283. this._transformDirectionWorldToLocal = new Matrix3();
  284. // Rendering
  285. /**
  286. * @type {boolean}
  287. * @private
  288. */
  289. this._nearestSampling = false;
  290. /**
  291. * @type {number}
  292. * @private
  293. */
  294. this._levelBlendFactor = 0.0;
  295. /**
  296. * @type {number}
  297. * @private
  298. */
  299. this._stepSizeMultiplier = 1.0;
  300. /**
  301. * @type {boolean}
  302. * @private
  303. */
  304. this._depthTest = true;
  305. /**
  306. * @type {boolean}
  307. * @private
  308. */
  309. this._useLogDepth = undefined;
  310. /**
  311. * @type {number}
  312. * @private
  313. */
  314. this._screenSpaceError = 4.0; // in pixels
  315. // Debug / statistics
  316. /**
  317. * @type {PolylineCollection}
  318. * @private
  319. */
  320. this._debugPolylines = new PolylineCollection();
  321. /**
  322. * @type {boolean}
  323. * @private
  324. */
  325. this._debugDraw = false;
  326. /**
  327. * @type {boolean}
  328. * @private
  329. */
  330. this._disableRender = false;
  331. /**
  332. * @type {boolean}
  333. * @private
  334. */
  335. this._disableUpdate = false;
  336. const ShapeConstructor = VoxelShapeType.getShapeConstructor(shapeType);
  337. /**
  338. * @type {VoxelShape}
  339. * @private
  340. */
  341. this._shape = new ShapeConstructor();
  342. checkTransformAndBounds(this);
  343. /**
  344. * @type {boolean}
  345. * @private
  346. */
  347. this._shapeVisible = updateShapeAndTransforms(this);
  348. /**
  349. * @type {Object<string, any>}
  350. * @private
  351. */
  352. this._uniforms = {
  353. octreeInternalNodeTexture: undefined,
  354. octreeInternalNodeTilesPerRow: 0,
  355. octreeInternalNodeTexelSizeUv: new Cartesian2(),
  356. octreeLeafNodeTexture: undefined,
  357. octreeLeafNodeTilesPerRow: 0,
  358. octreeLeafNodeTexelSizeUv: new Cartesian2(),
  359. megatextureTextures: [],
  360. megatextureTileCounts: new Cartesian3(),
  361. dimensions: this._dimensions,
  362. inputDimensions: this._inputDimensions,
  363. paddingBefore: this._paddingBefore,
  364. paddingAfter: this._paddingAfter,
  365. transformPositionViewToLocal: new Matrix4(),
  366. transformDirectionViewToLocal: new Matrix3(),
  367. cameraPositionLocal: new Cartesian3(),
  368. cameraDirectionLocal: new Cartesian3(),
  369. cameraTileCoordinates: new Cartesian4(),
  370. cameraTileUv: new Cartesian3(),
  371. ndcSpaceAxisAlignedBoundingBox: new Cartesian4(),
  372. clippingPlanesTexture: undefined,
  373. clippingPlanesMatrix: new Matrix4(),
  374. renderBoundPlanesTexture: undefined,
  375. stepSize: this._stepSizeMultiplier,
  376. pickColor: new Color(),
  377. };
  378. /**
  379. * Shape specific shader defines from the previous shape update. Used to detect if the shader needs to be rebuilt.
  380. * @type {Object<string, any>}
  381. * @private
  382. */
  383. this._shapeDefinesOld = {};
  384. /**
  385. * Map uniform names to functions that return the uniform values.
  386. * @type {Object<string, function():any>}
  387. * @private
  388. */
  389. this._uniformMap = {};
  390. const uniforms = this._uniforms;
  391. const uniformMap = this._uniformMap;
  392. for (const key in uniforms) {
  393. if (uniforms.hasOwnProperty(key)) {
  394. const name = `u_${key}`;
  395. uniformMap[name] = function () {
  396. return uniforms[key];
  397. };
  398. }
  399. }
  400. setupShapeUniformsAndDefines(this, this._shape);
  401. /**
  402. * The event fired to indicate that a tile's content was loaded.
  403. * <p>
  404. * This event is fired during the tileset traversal while the frame is being rendered
  405. * so that updates to the tile take effect in the same frame. Do not create or modify
  406. * Cesium entities or primitives during the event listener.
  407. * </p>
  408. *
  409. * @type {Event}
  410. *
  411. * @example
  412. * voxelPrimitive.tileLoad.addEventListener(function() {
  413. * console.log('A tile was loaded.');
  414. * });
  415. */
  416. this.tileLoad = new Event();
  417. /**
  418. * This event fires once for each visible tile in a frame.
  419. * <p>
  420. * This event is fired during the traversal while the frame is being rendered.
  421. *
  422. * @type {Event}
  423. *
  424. * @example
  425. * voxelPrimitive.tileVisible.addEventListener(function() {
  426. * console.log('A tile is visible.');
  427. * });
  428. *
  429. */
  430. this.tileVisible = new Event();
  431. /**
  432. * The event fired to indicate that a tile's content failed to load.
  433. *
  434. * @type {Event}
  435. *
  436. * @example
  437. * voxelPrimitive.tileFailed.addEventListener(function() {
  438. * console.log('An error occurred loading tile.');
  439. * });
  440. */
  441. this.tileFailed = new Event();
  442. /**
  443. * The event fired to indicate that a tile's content was unloaded.
  444. *
  445. * @type {Event}
  446. *
  447. * @example
  448. * voxelPrimitive.tileUnload.addEventListener(function() {
  449. * console.log('A tile was unloaded from the cache.');
  450. * });
  451. *
  452. */
  453. this.tileUnload = new Event();
  454. /**
  455. * The event fired to indicate progress of loading new tiles. This event is fired when a new tile
  456. * is requested, when a requested tile is finished downloading, and when a downloaded tile has been
  457. * processed and is ready to render.
  458. * <p>
  459. * The number of pending tile requests, <code>numberOfPendingRequests</code>, and number of tiles
  460. * processing, <code>numberOfTilesProcessing</code> are passed to the event listener.
  461. * </p>
  462. * <p>
  463. * This event is fired at the end of the frame after the scene is rendered.
  464. * </p>
  465. *
  466. * @type {Event}
  467. *
  468. * @example
  469. * voxelPrimitive.loadProgress.addEventListener(function(numberOfPendingRequests, numberOfTilesProcessing) {
  470. * if ((numberOfPendingRequests === 0) && (numberOfTilesProcessing === 0)) {
  471. * console.log('Finished loading');
  472. * return;
  473. * }
  474. *
  475. * console.log(`Loading: requests: ${numberOfPendingRequests}, processing: ${numberOfTilesProcessing}`);
  476. * });
  477. */
  478. this.loadProgress = new Event();
  479. /**
  480. * The event fired to indicate that all tiles that meet the screen space error this frame are loaded. The voxel
  481. * primitive is completely loaded for this view.
  482. * <p>
  483. * This event is fired at the end of the frame after the scene is rendered.
  484. * </p>
  485. *
  486. * @type {Event}
  487. *
  488. * @example
  489. * voxelPrimitive.allTilesLoaded.addEventListener(function() {
  490. * console.log('All tiles are loaded');
  491. * });
  492. */
  493. this.allTilesLoaded = new Event();
  494. /**
  495. * The event fired to indicate that all tiles that meet the screen space error this frame are loaded. This event
  496. * is fired once when all tiles in the initial view are loaded.
  497. * <p>
  498. * This event is fired at the end of the frame after the scene is rendered.
  499. * </p>
  500. *
  501. * @type {Event}
  502. *
  503. * @example
  504. * voxelPrimitive.initialTilesLoaded.addEventListener(function() {
  505. * console.log('Initial tiles are loaded');
  506. * });
  507. *
  508. * @see Cesium3DTileset#allTilesLoaded
  509. */
  510. this.initialTilesLoaded = new Event();
  511. }
  512. /**
  513. * Computes the dimensions of the input voxel data, including padding and in the input orientation.
  514. *
  515. * @param {Cartesian3} dimensions The dimensions of the voxel data, not including padding, in z-up orientation.
  516. * @param {Cartesian3} paddingBefore The padding before the voxel data.
  517. * @param {Cartesian3} paddingAfter The padding after the voxel data.
  518. * @param {VoxelMetadataOrder} metadataOrder The ordering of the input metadata dimensions.
  519. *
  520. * @private
  521. */
  522. function computeInputDimensions(
  523. dimensions,
  524. paddingBefore,
  525. paddingAfter,
  526. metadataOrder,
  527. ) {
  528. const inputDimensions = Cartesian3.add(
  529. dimensions,
  530. paddingBefore,
  531. new Cartesian3(),
  532. );
  533. Cartesian3.add(inputDimensions, paddingAfter, inputDimensions);
  534. if (metadataOrder === VoxelMetadataOrder.Y_UP) {
  535. const inputDimensionsY = inputDimensions.y;
  536. inputDimensions.y = inputDimensions.z;
  537. inputDimensions.z = inputDimensionsY;
  538. }
  539. return inputDimensions;
  540. }
  541. /**
  542. * Combine uniforms from the shape with the primitive uniform map, and
  543. * setup change tracking for shape defines to know when to rebuild the shader.
  544. *
  545. * @param {VoxelPrimitive} primitive The primitive with which the shape uniforms are associated.
  546. * @param {VoxelShape} shape The shape from which to pull the shader uniforms and defines.
  547. *
  548. * @private
  549. */
  550. function setupShapeUniformsAndDefines(primitive, shape) {
  551. const uniformMap = primitive._uniformMap;
  552. const { shaderUniforms, shaderDefines } = shape;
  553. for (const uniformName in shaderUniforms) {
  554. if (shaderUniforms.hasOwnProperty(uniformName)) {
  555. const name = `u_${uniformName}`;
  556. //>>includeStart('debug', pragmas.debug);
  557. if (defined(uniformMap[name])) {
  558. oneTimeWarning(
  559. `VoxelPrimitive: Uniform name "${name}" is already defined`,
  560. );
  561. }
  562. //>>includeEnd('debug');
  563. uniformMap[name] = function () {
  564. return shaderUniforms[uniformName];
  565. };
  566. }
  567. }
  568. primitive._shapeDefinesOld = clone(shaderDefines, true);
  569. }
  570. Object.defineProperties(VoxelPrimitive.prototype, {
  571. /**
  572. * Gets a value indicating whether or not the primitive is ready for use.
  573. *
  574. * @memberof VoxelPrimitive.prototype
  575. * @type {boolean}
  576. * @readonly
  577. */
  578. ready: {
  579. get: function () {
  580. return this._ready;
  581. },
  582. },
  583. /**
  584. * Gets the {@link VoxelProvider} associated with this primitive.
  585. *
  586. * @memberof VoxelPrimitive.prototype
  587. * @type {VoxelProvider}
  588. * @readonly
  589. */
  590. provider: {
  591. get: function () {
  592. return this._provider;
  593. },
  594. },
  595. /**
  596. * Gets the bounding sphere.
  597. *
  598. * @memberof VoxelPrimitive.prototype
  599. * @type {BoundingSphere}
  600. * @readonly
  601. */
  602. boundingSphere: {
  603. get: function () {
  604. return this._shape.boundingSphere;
  605. },
  606. },
  607. /**
  608. * Gets the oriented bounding box.
  609. *
  610. * @memberof VoxelPrimitive.prototype
  611. * @type {OrientedBoundingBox}
  612. * @readonly
  613. */
  614. orientedBoundingBox: {
  615. get: function () {
  616. return this._shape.orientedBoundingBox;
  617. },
  618. },
  619. /**
  620. * Gets the model matrix.
  621. *
  622. * @memberof VoxelPrimitive.prototype
  623. * @type {Matrix4}
  624. * @readonly
  625. */
  626. modelMatrix: {
  627. get: function () {
  628. return this._modelMatrix;
  629. },
  630. set: function (modelMatrix) {
  631. //>>includeStart('debug', pragmas.debug);
  632. Check.typeOf.object("modelMatrix", modelMatrix);
  633. //>>includeEnd('debug');
  634. this._modelMatrix = Matrix4.clone(modelMatrix, this._modelMatrix);
  635. },
  636. },
  637. /**
  638. * Gets the shape type.
  639. *
  640. * @memberof VoxelPrimitive.prototype
  641. * @type {VoxelShapeType}
  642. * @readonly
  643. */
  644. shape: {
  645. get: function () {
  646. return this._provider.shape;
  647. },
  648. },
  649. /**
  650. * Gets the dimensions of each voxel tile, in z-up orientation.
  651. * Does not include padding.
  652. *
  653. * @memberof VoxelPrimitive.prototype
  654. * @type {Cartesian3}
  655. * @readonly
  656. * @constant
  657. */
  658. dimensions: {
  659. get: function () {
  660. return this._dimensions;
  661. },
  662. },
  663. /**
  664. * Gets the dimensions of one tile of the input voxel data, in the input orientation.
  665. * Includes padding.
  666. * @memberof VoxelPrimitive.prototype
  667. * @type {Cartesian3}
  668. * @readonly
  669. * @constant
  670. */
  671. inputDimensions: {
  672. get: function () {
  673. return this._inputDimensions;
  674. },
  675. },
  676. /**
  677. * Gets the padding before the voxel data.
  678. *
  679. * @memberof VoxelPrimitive.prototype
  680. * @type {Cartesian3}
  681. * @readonly
  682. * @constant
  683. */
  684. paddingBefore: {
  685. get: function () {
  686. return this._paddingBefore;
  687. },
  688. },
  689. /**
  690. * Gets the padding after the voxel data.
  691. *
  692. * @memberof VoxelPrimitive.prototype
  693. * @type {Cartesian3}
  694. * @readonly
  695. * @constant
  696. */
  697. paddingAfter: {
  698. get: function () {
  699. return this._paddingAfter;
  700. },
  701. },
  702. /**
  703. * Gets the minimum value per channel of the voxel data.
  704. *
  705. * @memberof VoxelPrimitive.prototype
  706. * @type {number[][]}
  707. * @readonly
  708. * @constant
  709. */
  710. minimumValues: {
  711. get: function () {
  712. return this._provider.minimumValues;
  713. },
  714. },
  715. /**
  716. * Gets the maximum value per channel of the voxel data.
  717. *
  718. * @memberof VoxelPrimitive.prototype
  719. * @type {number[][]}
  720. * @readonly
  721. * @constant
  722. */
  723. maximumValues: {
  724. get: function () {
  725. return this._provider.maximumValues;
  726. },
  727. },
  728. /**
  729. * Gets or sets whether or not this primitive should be displayed.
  730. *
  731. * @memberof VoxelPrimitive.prototype
  732. * @type {boolean}
  733. */
  734. show: {
  735. get: function () {
  736. return !this._disableRender;
  737. },
  738. set: function (show) {
  739. //>>includeStart('debug', pragmas.debug);
  740. Check.typeOf.bool("show", show);
  741. //>>includeEnd('debug');
  742. this._disableRender = !show;
  743. },
  744. },
  745. /**
  746. * Gets or sets whether or not the primitive should update when the view changes.
  747. *
  748. * @memberof VoxelPrimitive.prototype
  749. * @type {boolean}
  750. */
  751. disableUpdate: {
  752. get: function () {
  753. return this._disableUpdate;
  754. },
  755. set: function (disableUpdate) {
  756. //>>includeStart('debug', pragmas.debug);
  757. Check.typeOf.bool("disableUpdate", disableUpdate);
  758. //>>includeEnd('debug');
  759. this._disableUpdate = disableUpdate;
  760. },
  761. },
  762. /**
  763. * Gets or sets whether or not to render debug visualizations.
  764. *
  765. * @memberof VoxelPrimitive.prototype
  766. * @type {boolean}
  767. */
  768. debugDraw: {
  769. get: function () {
  770. return this._debugDraw;
  771. },
  772. set: function (debugDraw) {
  773. //>>includeStart('debug', pragmas.debug);
  774. Check.typeOf.bool("debugDraw", debugDraw);
  775. //>>includeEnd('debug');
  776. this._debugDraw = debugDraw;
  777. },
  778. },
  779. /**
  780. * Gets or sets whether or not to test against depth when rendering.
  781. *
  782. * @memberof VoxelPrimitive.prototype
  783. * @type {boolean}
  784. */
  785. depthTest: {
  786. get: function () {
  787. return this._depthTest;
  788. },
  789. set: function (depthTest) {
  790. //>>includeStart('debug', pragmas.debug);
  791. Check.typeOf.bool("depthTest", depthTest);
  792. //>>includeEnd('debug');
  793. if (this._depthTest !== depthTest) {
  794. this._depthTest = depthTest;
  795. this._shaderDirty = true;
  796. }
  797. },
  798. },
  799. /**
  800. * Gets or sets the nearest sampling.
  801. *
  802. * @memberof VoxelPrimitive.prototype
  803. * @type {boolean}
  804. */
  805. nearestSampling: {
  806. get: function () {
  807. return this._nearestSampling;
  808. },
  809. set: function (nearestSampling) {
  810. //>>includeStart('debug', pragmas.debug);
  811. Check.typeOf.bool("nearestSampling", nearestSampling);
  812. //>>includeEnd('debug');
  813. this._nearestSampling = nearestSampling;
  814. },
  815. },
  816. /**
  817. * Controls how quickly to blend between different levels of the tree.
  818. * 0.0 means an instantaneous pop.
  819. * 1.0 means a full linear blend.
  820. *
  821. * @memberof VoxelPrimitive.prototype
  822. * @type {number}
  823. * @private
  824. */
  825. levelBlendFactor: {
  826. get: function () {
  827. return this._levelBlendFactor;
  828. },
  829. set: function (levelBlendFactor) {
  830. //>>includeStart('debug', pragmas.debug);
  831. Check.typeOf.number("levelBlendFactor", levelBlendFactor);
  832. //>>includeEnd('debug');
  833. this._levelBlendFactor = CesiumMath.clamp(levelBlendFactor, 0.0, 1.0);
  834. },
  835. },
  836. /**
  837. * Gets or sets the screen space error in pixels. If the screen space size
  838. * of a voxel is greater than the screen space error, the tile is subdivided.
  839. * Lower screen space error corresponds with higher detail rendering, but could
  840. * result in worse performance and higher memory consumption.
  841. *
  842. * @memberof VoxelPrimitive.prototype
  843. * @type {number}
  844. */
  845. screenSpaceError: {
  846. get: function () {
  847. return this._screenSpaceError;
  848. },
  849. set: function (screenSpaceError) {
  850. //>>includeStart('debug', pragmas.debug);
  851. Check.typeOf.number("screenSpaceError", screenSpaceError);
  852. //>>includeEnd('debug');
  853. this._screenSpaceError = screenSpaceError;
  854. },
  855. },
  856. /**
  857. * Gets or sets the step size multiplier used during raymarching.
  858. * The lower the value, the higher the rendering quality, but
  859. * also the worse the performance.
  860. *
  861. * @memberof VoxelPrimitive.prototype
  862. * @type {number}
  863. */
  864. stepSize: {
  865. get: function () {
  866. return this._stepSizeMultiplier;
  867. },
  868. set: function (stepSize) {
  869. //>>includeStart('debug', pragmas.debug);
  870. Check.typeOf.number("stepSize", stepSize);
  871. //>>includeEnd('debug');
  872. this._stepSizeMultiplier = stepSize;
  873. },
  874. },
  875. /**
  876. * Gets or sets the minimum bounds in the shape's local coordinate system.
  877. * Voxel data is stretched or squashed to fit the bounds.
  878. *
  879. * @memberof VoxelPrimitive.prototype
  880. * @type {Cartesian3}
  881. */
  882. minBounds: {
  883. get: function () {
  884. return this._minBounds;
  885. },
  886. set: function (minBounds) {
  887. //>>includeStart('debug', pragmas.debug);
  888. Check.defined("minBounds", minBounds);
  889. //>>includeEnd('debug');
  890. this._minBounds = Cartesian3.clone(minBounds, this._minBounds);
  891. },
  892. },
  893. /**
  894. * Gets or sets the maximum bounds in the shape's local coordinate system.
  895. * Voxel data is stretched or squashed to fit the bounds.
  896. *
  897. * @memberof VoxelPrimitive.prototype
  898. * @type {Cartesian3}
  899. */
  900. maxBounds: {
  901. get: function () {
  902. return this._maxBounds;
  903. },
  904. set: function (maxBounds) {
  905. //>>includeStart('debug', pragmas.debug);
  906. Check.defined("maxBounds", maxBounds);
  907. //>>includeEnd('debug');
  908. this._maxBounds = Cartesian3.clone(maxBounds, this._maxBounds);
  909. },
  910. },
  911. /**
  912. * Gets or sets the minimum clipping location in the shape's local coordinate system.
  913. * Any voxel content outside the range is clipped.
  914. *
  915. * @memberof VoxelPrimitive.prototype
  916. * @type {Cartesian3}
  917. */
  918. minClippingBounds: {
  919. get: function () {
  920. return this._minClippingBounds;
  921. },
  922. set: function (minClippingBounds) {
  923. //>>includeStart('debug', pragmas.debug);
  924. Check.defined("minClippingBounds", minClippingBounds);
  925. //>>includeEnd('debug');
  926. this._minClippingBounds = Cartesian3.clone(
  927. minClippingBounds,
  928. this._minClippingBounds,
  929. );
  930. },
  931. },
  932. /**
  933. * Gets or sets the maximum clipping location in the shape's local coordinate system.
  934. * Any voxel content outside the range is clipped.
  935. *
  936. * @memberof VoxelPrimitive.prototype
  937. * @type {Cartesian3}
  938. */
  939. maxClippingBounds: {
  940. get: function () {
  941. return this._maxClippingBounds;
  942. },
  943. set: function (maxClippingBounds) {
  944. //>>includeStart('debug', pragmas.debug);
  945. Check.defined("maxClippingBounds", maxClippingBounds);
  946. //>>includeEnd('debug');
  947. this._maxClippingBounds = Cartesian3.clone(
  948. maxClippingBounds,
  949. this._maxClippingBounds,
  950. );
  951. },
  952. },
  953. /**
  954. * The {@link ClippingPlaneCollection} used to selectively disable rendering the primitive.
  955. *
  956. * @memberof VoxelPrimitive.prototype
  957. * @type {ClippingPlaneCollection}
  958. */
  959. clippingPlanes: {
  960. get: function () {
  961. return this._clippingPlanes;
  962. },
  963. set: function (clippingPlanes) {
  964. // Don't need to check if undefined, it's handled in the setOwner function
  965. ClippingPlaneCollection.setOwner(clippingPlanes, this, "_clippingPlanes");
  966. },
  967. },
  968. /**
  969. * Gets or sets the custom shader. If undefined, attempt to build a default custom shader
  970. * appropriate to the metadata type. If that fails, use {@link VoxelPrimitive.DefaultCustomShader}.
  971. *
  972. * @memberof VoxelPrimitive.prototype
  973. * @type {CustomShader}
  974. * @see {@link https://github.com/CesiumGS/cesium/tree/main/Documentation/CustomShaderGuide|Custom Shader Guide}
  975. */
  976. customShader: {
  977. get: function () {
  978. return this._customShader;
  979. },
  980. set: function (customShader) {
  981. if (customShader === this._customShader) {
  982. return;
  983. }
  984. // Delete old custom shader entries from the uniform map
  985. // (they were added in VoxelRenderResources when the shader was built)
  986. const uniformMap = this._uniformMap;
  987. const oldCustomShader = this._customShader;
  988. const oldCustomShaderUniformMap = oldCustomShader.uniformMap;
  989. for (const uniformName in oldCustomShaderUniformMap) {
  990. if (oldCustomShaderUniformMap.hasOwnProperty(uniformName)) {
  991. delete uniformMap[uniformName];
  992. }
  993. }
  994. if (!defined(customShader)) {
  995. const defaultShader = buildVoxelCustomShader(this._provider);
  996. this._customShader =
  997. defaultShader ?? VoxelPrimitive.DefaultCustomShader;
  998. } else {
  999. this._customShader = customShader;
  1000. }
  1001. this._shaderDirty = true;
  1002. },
  1003. },
  1004. /**
  1005. * Gets an event that is raised whenever a custom shader is compiled.
  1006. *
  1007. * @memberof VoxelPrimitive.prototype
  1008. * @type {Event}
  1009. * @readonly
  1010. */
  1011. customShaderCompilationEvent: {
  1012. get: function () {
  1013. return this._customShaderCompilationEvent;
  1014. },
  1015. },
  1016. /**
  1017. * Loading and rendering information for requested content.
  1018. * To use `visited` and `numberOfTilesWithContentReady` statistics, set options.calculateStatistics` to `true` in the constructor.
  1019. * @type {Cesium3DTilesetStatistics}
  1020. * @readonly
  1021. * @private
  1022. */
  1023. statistics: {
  1024. get: function () {
  1025. return this._statistics;
  1026. },
  1027. },
  1028. });
  1029. const scratchIntersect = new Cartesian4();
  1030. const scratchNdcAabb = new Cartesian4();
  1031. const scratchTransformPositionLocalToWorld = new Matrix4();
  1032. const scratchTransformPositionLocalToProjection = new Matrix4();
  1033. const scratchCameraPositionShapeUv = new Cartesian3();
  1034. const scratchCameraTileCoordinates = new Cartesian4();
  1035. /**
  1036. * Updates the voxel primitive.
  1037. *
  1038. * @param {FrameState} frameState
  1039. * @private
  1040. */
  1041. VoxelPrimitive.prototype.update = function (frameState) {
  1042. const provider = this._provider;
  1043. const uniforms = this._uniforms;
  1044. // Update the custom shader in case it has texture uniforms.
  1045. this._customShader.update(frameState);
  1046. // Initialize from the provider. This only happens once.
  1047. const context = frameState.context;
  1048. if (!this._ready) {
  1049. initializeFromContext(this, provider, context);
  1050. // Set the primitive as ready after the first frame render since
  1051. // the user might set up events subscribed to the post render event,
  1052. // and the primitive may not be ready for those past the first frame.
  1053. frameState.afterRender.push(() => {
  1054. this._ready = true;
  1055. return true;
  1056. });
  1057. // Don't render until the next frame after ready is set to true
  1058. return;
  1059. }
  1060. // Check if the shape is dirty before updating it. This needs to happen every
  1061. // frame because the member variables can be modified externally via the
  1062. // getters.
  1063. const shapeDirty = checkTransformAndBounds(this);
  1064. const exaggerationChanged = updateVerticalExaggeration(this, frameState);
  1065. if (shapeDirty || exaggerationChanged) {
  1066. this._shapeVisible = updateShapeAndTransforms(this);
  1067. if (checkShapeDefines(this)) {
  1068. this._shaderDirty = true;
  1069. }
  1070. }
  1071. if (!this._shapeVisible) {
  1072. return;
  1073. }
  1074. this._shape.updateViewTransforms(frameState);
  1075. // Update the traversal and prepare for rendering.
  1076. const keyframeLocation = getKeyframeLocation(
  1077. provider.timeIntervalCollection,
  1078. this._clock,
  1079. );
  1080. const traversal = this._traversal;
  1081. const sampleCountOld = traversal._sampleCount;
  1082. traversal.update(
  1083. frameState,
  1084. keyframeLocation,
  1085. shapeDirty, // recomputeBoundingVolumes
  1086. this._disableUpdate, // pauseUpdate
  1087. );
  1088. if (sampleCountOld !== traversal._sampleCount) {
  1089. this._shaderDirty = true;
  1090. }
  1091. if (!traversal.isRenderable(traversal.rootNode)) {
  1092. return;
  1093. }
  1094. if (this._debugDraw) {
  1095. // Debug draw bounding boxes and other things. Must go after traversal update
  1096. // because that's what updates the tile bounding boxes.
  1097. debugDraw(this, frameState);
  1098. }
  1099. if (this._disableRender) {
  1100. return;
  1101. }
  1102. // Check if log depth changed
  1103. if (this._useLogDepth !== frameState.useLogDepth) {
  1104. this._useLogDepth = frameState.useLogDepth;
  1105. this._shaderDirty = true;
  1106. }
  1107. // Check if clipping planes changed
  1108. const clippingPlanesChanged = updateClippingPlanes(this, frameState);
  1109. if (clippingPlanesChanged) {
  1110. this._shaderDirty = true;
  1111. }
  1112. const leafNodeTexture = traversal.leafNodeTexture;
  1113. if (defined(leafNodeTexture)) {
  1114. uniforms.octreeLeafNodeTexture = traversal.leafNodeTexture;
  1115. uniforms.octreeLeafNodeTexelSizeUv = Cartesian2.clone(
  1116. traversal.leafNodeTexelSizeUv,
  1117. uniforms.octreeLeafNodeTexelSizeUv,
  1118. );
  1119. uniforms.octreeLeafNodeTilesPerRow = traversal.leafNodeTilesPerRow;
  1120. }
  1121. // Rebuild shaders
  1122. if (this._shaderDirty) {
  1123. buildVoxelDrawCommands(this, context);
  1124. this._shaderDirty = false;
  1125. }
  1126. // Calculate the NDC-space AABB to "scissor" the fullscreen quad
  1127. const transformPositionWorldToProjection =
  1128. context.uniformState.viewProjection;
  1129. const { orientedBoundingBox } = this._shape;
  1130. const ndcAabb = orientedBoundingBoxToNdcAabb(
  1131. orientedBoundingBox,
  1132. transformPositionWorldToProjection,
  1133. scratchNdcAabb,
  1134. );
  1135. // If the object is offscreen, don't render it.
  1136. const offscreen =
  1137. ndcAabb.x === +1.0 ||
  1138. ndcAabb.y === +1.0 ||
  1139. ndcAabb.z === -1.0 ||
  1140. ndcAabb.w === -1.0;
  1141. if (offscreen) {
  1142. return;
  1143. }
  1144. // Prepare to render: update uniforms that can change every frame
  1145. // Using a uniform instead of going through RenderState's scissor because the viewport is not accessible here, and the scissor command needs pixel coordinates.
  1146. uniforms.ndcSpaceAxisAlignedBoundingBox = Cartesian4.clone(
  1147. ndcAabb,
  1148. uniforms.ndcSpaceAxisAlignedBoundingBox,
  1149. );
  1150. const transformPositionViewToWorld = context.uniformState.inverseView;
  1151. const transformPositionViewToLocal = Matrix4.multiplyTransformation(
  1152. this._transformPositionWorldToLocal,
  1153. transformPositionViewToWorld,
  1154. uniforms.transformPositionViewToLocal,
  1155. );
  1156. this._transformPlaneLocalToView = Matrix4.transpose(
  1157. transformPositionViewToLocal,
  1158. this._transformPlaneLocalToView,
  1159. );
  1160. const transformDirectionViewToWorld =
  1161. context.uniformState.inverseViewRotation;
  1162. uniforms.transformDirectionViewToLocal = Matrix3.multiply(
  1163. this._transformDirectionWorldToLocal,
  1164. transformDirectionViewToWorld,
  1165. uniforms.transformDirectionViewToLocal,
  1166. );
  1167. uniforms.cameraPositionLocal = Matrix4.multiplyByPoint(
  1168. this._transformPositionWorldToLocal,
  1169. frameState.camera.positionWC,
  1170. uniforms.cameraPositionLocal,
  1171. );
  1172. uniforms.cameraDirectionLocal = Matrix3.multiplyByVector(
  1173. this._transformDirectionWorldToLocal,
  1174. frameState.camera.directionWC,
  1175. uniforms.cameraDirectionLocal,
  1176. );
  1177. const cameraTileCoordinates = getTileCoordinates(
  1178. this,
  1179. uniforms.cameraPositionLocal,
  1180. scratchCameraTileCoordinates,
  1181. );
  1182. uniforms.cameraTileCoordinates = Cartesian4.fromElements(
  1183. Math.floor(cameraTileCoordinates.x),
  1184. Math.floor(cameraTileCoordinates.y),
  1185. Math.floor(cameraTileCoordinates.z),
  1186. cameraTileCoordinates.w,
  1187. uniforms.cameraTileCoordinates,
  1188. );
  1189. uniforms.cameraTileUv = Cartesian3.fromElements(
  1190. cameraTileCoordinates.x - Math.floor(cameraTileCoordinates.x),
  1191. cameraTileCoordinates.y - Math.floor(cameraTileCoordinates.y),
  1192. cameraTileCoordinates.z - Math.floor(cameraTileCoordinates.z),
  1193. uniforms.cameraTileUv,
  1194. );
  1195. uniforms.stepSize = this._stepSizeMultiplier;
  1196. updateNearestSampling(this);
  1197. updateRenderBoundPlanes(this, frameState);
  1198. // Render the primitive
  1199. const command = frameState.passes.pick
  1200. ? this._drawCommandPick
  1201. : frameState.passes.pickVoxel
  1202. ? this._drawCommandPickVoxel
  1203. : this._drawCommand;
  1204. command.boundingVolume = this._shape.boundingSphere;
  1205. frameState.commandList.push(command);
  1206. };
  1207. function updateNearestSampling(primitive) {
  1208. const { megatextures } = primitive._traversal;
  1209. for (let i = 0; i < megatextures.length; ++i) {
  1210. megatextures[i].nearestSampling = primitive._nearestSampling;
  1211. }
  1212. }
  1213. function updateRenderBoundPlanes(primitive, frameState) {
  1214. const uniforms = primitive._uniforms;
  1215. const { renderBoundPlanes } = primitive._shape;
  1216. if (!defined(renderBoundPlanes)) {
  1217. return;
  1218. }
  1219. renderBoundPlanes.update(frameState, primitive._transformPlaneLocalToView);
  1220. uniforms.renderBoundPlanesTexture = renderBoundPlanes.texture;
  1221. }
  1222. /**
  1223. * Converts a position in local space to tile coordinates.
  1224. *
  1225. * @param {VoxelPrimitive} primitive The primitive to get the tile coordinates for.
  1226. * @param {Cartesian3} positionLocal The position in local space to convert to tile coordinates.
  1227. * @param {Cartesian4} result The result object to store the tile coordinates.
  1228. * @returns {Cartesian4} The tile coordinates of the supplied position.
  1229. * @private
  1230. */
  1231. function getTileCoordinates(primitive, positionLocal, result) {
  1232. const shapeUv = primitive._shape.convertLocalToShapeUvSpace(
  1233. positionLocal,
  1234. scratchCameraPositionShapeUv,
  1235. );
  1236. const availableLevels = primitive._availableLevels;
  1237. const numTiles = 2 ** (availableLevels - 1);
  1238. return Cartesian4.fromElements(
  1239. shapeUv.x * numTiles,
  1240. shapeUv.y * numTiles,
  1241. shapeUv.z * numTiles,
  1242. availableLevels - 1,
  1243. result,
  1244. );
  1245. }
  1246. const scratchExaggerationScale = new Cartesian3();
  1247. const scratchExaggerationCenter = new Cartesian3();
  1248. const scratchCartographicCenter = new Cartographic();
  1249. /**
  1250. * Check for changes in the vertical exaggeration of the primitive
  1251. * @param {VoxelPrimitive} primitive The primitive to update
  1252. * @param {FrameState} frameState The current frame state
  1253. * @returns {boolean} <code>true</code> if the exaggeration was changed
  1254. * @private
  1255. */
  1256. function updateVerticalExaggeration(primitive, frameState) {
  1257. const { verticalExaggeration, verticalExaggerationRelativeHeight } =
  1258. frameState;
  1259. if (
  1260. primitive._verticalExaggeration === verticalExaggeration &&
  1261. primitive._verticalExaggerationRelativeHeight ===
  1262. verticalExaggerationRelativeHeight
  1263. ) {
  1264. return false;
  1265. }
  1266. primitive._verticalExaggeration = verticalExaggeration;
  1267. primitive._verticalExaggerationRelativeHeight =
  1268. verticalExaggerationRelativeHeight;
  1269. return true;
  1270. }
  1271. /**
  1272. * Initialize primitive properties that are derived from the voxel provider
  1273. * @param {VoxelPrimitive} primitive
  1274. * @param {VoxelProvider} provider
  1275. * @param {Context} context
  1276. * @private
  1277. */
  1278. function initializeFromContext(primitive, provider, context) {
  1279. const uniforms = primitive._uniforms;
  1280. primitive._pickId = context.createPickId({ primitive });
  1281. uniforms.pickColor = Color.clone(primitive._pickId.color, uniforms.pickColor);
  1282. // Create the VoxelTraversal, and set related uniforms
  1283. const keyframeCount = provider.keyframeCount ?? 1;
  1284. primitive._traversal = new VoxelTraversal(primitive, context, keyframeCount);
  1285. primitive.statistics.texturesByteLength =
  1286. primitive._traversal.textureMemoryByteLength;
  1287. setTraversalUniforms(primitive._traversal, uniforms);
  1288. }
  1289. /**
  1290. * Track changes in provider transform and primitive bounds
  1291. * @param {VoxelPrimitive} primitive
  1292. * @returns {boolean} Whether any of the transform or bounds changed
  1293. * @private
  1294. */
  1295. function checkTransformAndBounds(primitive) {
  1296. const numChanges =
  1297. updateBound(primitive, "_modelMatrix", "_modelMatrixOld") +
  1298. updateBound(primitive, "_minBounds", "_minBoundsOld") +
  1299. updateBound(primitive, "_maxBounds", "_maxBoundsOld") +
  1300. updateBound(primitive, "_minClippingBounds", "_minClippingBoundsOld") +
  1301. updateBound(primitive, "_maxClippingBounds", "_maxClippingBoundsOld");
  1302. return numChanges > 0;
  1303. }
  1304. /**
  1305. * Compare old and new values of a bound and update the old if it is different.
  1306. * @param {VoxelPrimitive} primitive The primitive with bounds properties
  1307. * @param {string} newBoundKey A key pointing to a bounds property of type Cartesian3 or Matrix4
  1308. * @param {string} oldBoundKey A key pointing to a bounds property of the same type as the property at newBoundKey
  1309. * @returns {number} 1 if the bound value changed, 0 otherwise
  1310. *
  1311. * @private
  1312. */
  1313. function updateBound(primitive, newBoundKey, oldBoundKey) {
  1314. const newBound = primitive[newBoundKey];
  1315. const oldBound = primitive[oldBoundKey];
  1316. const changed = !newBound.equals(oldBound);
  1317. if (changed) {
  1318. newBound.clone(oldBound);
  1319. }
  1320. return changed ? 1 : 0;
  1321. }
  1322. const scratchExaggeratedMinBounds = new Cartesian3();
  1323. const scratchExaggeratedMaxBounds = new Cartesian3();
  1324. const scratchExaggeratedMinClippingBounds = new Cartesian3();
  1325. const scratchExaggeratedMaxClippingBounds = new Cartesian3();
  1326. const scratchExaggeratedModelMatrix = new Matrix4();
  1327. const scratchCompoundModelMatrix = new Matrix4();
  1328. /**
  1329. * Update the shape and related transforms
  1330. * @param {VoxelPrimitive} primitive
  1331. * @returns {boolean} True if the shape is visible
  1332. * @private
  1333. */
  1334. function updateShapeAndTransforms(primitive) {
  1335. const verticalExaggeration = primitive._verticalExaggeration;
  1336. const verticalExaggerationRelativeHeight =
  1337. primitive._verticalExaggerationRelativeHeight;
  1338. const exaggeratedMinBounds = Cartesian3.clone(
  1339. primitive._minBounds,
  1340. scratchExaggeratedMinBounds,
  1341. );
  1342. const exaggeratedMaxBounds = Cartesian3.clone(
  1343. primitive._maxBounds,
  1344. scratchExaggeratedMaxBounds,
  1345. );
  1346. const exaggeratedMinClippingBounds = Cartesian3.clone(
  1347. primitive._minClippingBounds,
  1348. scratchExaggeratedMinClippingBounds,
  1349. );
  1350. const exaggeratedMaxClippingBounds = Cartesian3.clone(
  1351. primitive._maxClippingBounds,
  1352. scratchExaggeratedMaxClippingBounds,
  1353. );
  1354. const exaggeratedModelMatrix = Matrix4.clone(
  1355. primitive._modelMatrix,
  1356. scratchExaggeratedModelMatrix,
  1357. );
  1358. if (primitive.shape === VoxelShapeType.ELLIPSOID) {
  1359. // Apply the exaggeration by stretching the height bounds
  1360. exaggeratedMinBounds.z = VerticalExaggeration.getHeight(
  1361. primitive._minBounds.z,
  1362. verticalExaggeration,
  1363. verticalExaggerationRelativeHeight,
  1364. );
  1365. exaggeratedMaxBounds.z = VerticalExaggeration.getHeight(
  1366. primitive._maxBounds.z,
  1367. verticalExaggeration,
  1368. verticalExaggerationRelativeHeight,
  1369. );
  1370. exaggeratedMinClippingBounds.z = VerticalExaggeration.getHeight(
  1371. primitive._minClippingBounds.z,
  1372. verticalExaggeration,
  1373. verticalExaggerationRelativeHeight,
  1374. );
  1375. exaggeratedMaxClippingBounds.z = VerticalExaggeration.getHeight(
  1376. primitive._maxClippingBounds.z,
  1377. verticalExaggeration,
  1378. verticalExaggerationRelativeHeight,
  1379. );
  1380. } else {
  1381. // Apply the exaggeration via the model matrix
  1382. const exaggerationScale = Cartesian3.fromElements(
  1383. 1.0,
  1384. 1.0,
  1385. verticalExaggeration,
  1386. scratchExaggerationScale,
  1387. );
  1388. Matrix4.multiplyByScale(
  1389. exaggeratedModelMatrix,
  1390. exaggerationScale,
  1391. exaggeratedModelMatrix,
  1392. );
  1393. Matrix4.multiplyByTranslation(
  1394. exaggeratedModelMatrix,
  1395. computeBoxExaggerationTranslation(primitive),
  1396. exaggeratedModelMatrix,
  1397. );
  1398. }
  1399. const provider = primitive._provider;
  1400. const shapeTransform = provider.shapeTransform ?? Matrix4.IDENTITY;
  1401. const globalTransform = provider.globalTransform ?? Matrix4.IDENTITY;
  1402. // Compound model matrix = global transform * model matrix * shape transform
  1403. const compoundModelMatrix = Matrix4.multiplyTransformation(
  1404. globalTransform,
  1405. exaggeratedModelMatrix,
  1406. scratchCompoundModelMatrix,
  1407. );
  1408. Matrix4.multiplyTransformation(
  1409. compoundModelMatrix,
  1410. shapeTransform,
  1411. compoundModelMatrix,
  1412. );
  1413. const shape = primitive._shape;
  1414. const visible = shape.update(
  1415. compoundModelMatrix,
  1416. exaggeratedMinBounds,
  1417. exaggeratedMaxBounds,
  1418. exaggeratedMinClippingBounds,
  1419. exaggeratedMaxClippingBounds,
  1420. );
  1421. if (!visible) {
  1422. return false;
  1423. }
  1424. primitive._transformPositionLocalToWorld = Matrix4.clone(
  1425. shape.shapeTransform,
  1426. primitive._transformPositionLocalToWorld,
  1427. );
  1428. primitive._transformPositionWorldToLocal = Matrix4.inverse(
  1429. primitive._transformPositionLocalToWorld,
  1430. primitive._transformPositionWorldToLocal,
  1431. );
  1432. primitive._transformDirectionWorldToLocal = Matrix4.getMatrix3(
  1433. primitive._transformPositionWorldToLocal,
  1434. primitive._transformDirectionWorldToLocal,
  1435. );
  1436. return true;
  1437. }
  1438. const scratchExaggerationTranslation = new Cartesian3();
  1439. /**
  1440. * Compute the translation to apply to box shapes to account for vertical exaggeration
  1441. *
  1442. * @param {VoxelPrimitive} primitive
  1443. * @returns {Cartesian3} The translation to apply to the box to account for vertical exaggeration
  1444. * @private
  1445. */
  1446. function computeBoxExaggerationTranslation(primitive) {
  1447. const verticalExaggeration = primitive._verticalExaggeration;
  1448. const verticalExaggerationRelativeHeight =
  1449. primitive._verticalExaggerationRelativeHeight;
  1450. // Compute translation based on box center, relative height, and exaggeration
  1451. const {
  1452. shapeTransform = Matrix4.IDENTITY,
  1453. globalTransform = Matrix4.IDENTITY,
  1454. } = primitive._provider;
  1455. // Find the Cartesian position of the center of the OBB
  1456. const initialCenter = Matrix4.getTranslation(
  1457. shapeTransform,
  1458. scratchExaggerationCenter,
  1459. );
  1460. const intermediateCenter = Matrix4.multiplyByPoint(
  1461. primitive._modelMatrix,
  1462. initialCenter,
  1463. scratchExaggerationCenter,
  1464. );
  1465. const transformedCenter = Matrix4.multiplyByPoint(
  1466. globalTransform,
  1467. intermediateCenter,
  1468. scratchExaggerationCenter,
  1469. );
  1470. // Find the cartographic height
  1471. const ellipsoid = Ellipsoid.WGS84;
  1472. const centerCartographic = ellipsoid.cartesianToCartographic(
  1473. transformedCenter,
  1474. scratchCartographicCenter,
  1475. );
  1476. let centerHeight = 0.0;
  1477. if (defined(centerCartographic)) {
  1478. centerHeight = centerCartographic.height;
  1479. }
  1480. // Find the shift that will put the center in the right position relative
  1481. // to relativeHeight, after it is scaled by verticalExaggeration
  1482. const exaggeratedHeight = VerticalExaggeration.getHeight(
  1483. centerHeight,
  1484. verticalExaggeration,
  1485. verticalExaggerationRelativeHeight,
  1486. );
  1487. return Cartesian3.fromElements(
  1488. 0.0,
  1489. 0.0,
  1490. (exaggeratedHeight - centerHeight) / verticalExaggeration,
  1491. scratchExaggerationTranslation,
  1492. );
  1493. }
  1494. /**
  1495. * Set uniforms that come from the traversal.
  1496. * @param {VoxelTraversal} traversal
  1497. * @param {object} uniforms
  1498. * @private
  1499. */
  1500. function setTraversalUniforms(traversal, uniforms) {
  1501. uniforms.octreeInternalNodeTexture = traversal.internalNodeTexture;
  1502. uniforms.octreeInternalNodeTexelSizeUv = Cartesian2.clone(
  1503. traversal.internalNodeTexelSizeUv,
  1504. uniforms.octreeInternalNodeTexelSizeUv,
  1505. );
  1506. uniforms.octreeInternalNodeTilesPerRow = traversal.internalNodeTilesPerRow;
  1507. const { megatextures } = traversal;
  1508. const megatexture = megatextures[0];
  1509. uniforms.megatextureTextures = new Array(megatextures.length);
  1510. for (let i = 0; i < megatextures.length; i++) {
  1511. uniforms.megatextureTextures[i] = megatextures[i].texture;
  1512. }
  1513. uniforms.megatextureTileCounts = Cartesian3.clone(
  1514. megatexture.tileCounts,
  1515. uniforms.megatextureTileCounts,
  1516. );
  1517. }
  1518. /**
  1519. * Track changes in shape-related shader defines
  1520. * @param {VoxelPrimitive} primitive
  1521. * @returns {boolean} True if any of the shape defines changed, requiring a shader rebuild
  1522. * @private
  1523. */
  1524. function checkShapeDefines(primitive) {
  1525. const { shaderDefines } = primitive._shape;
  1526. const shapeDefinesChanged = Object.keys(shaderDefines).some(
  1527. (key) => shaderDefines[key] !== primitive._shapeDefinesOld[key],
  1528. );
  1529. if (shapeDefinesChanged) {
  1530. primitive._shapeDefinesOld = clone(shaderDefines, true);
  1531. }
  1532. return shapeDefinesChanged;
  1533. }
  1534. /**
  1535. * Find the keyframe location to render at. Doesn't need to be a whole number.
  1536. * @param {TimeIntervalCollection} timeIntervalCollection
  1537. * @param {Clock} clock
  1538. * @returns {number}
  1539. *
  1540. * @private
  1541. */
  1542. function getKeyframeLocation(timeIntervalCollection, clock) {
  1543. if (!defined(timeIntervalCollection) || !defined(clock)) {
  1544. return 0.0;
  1545. }
  1546. let date = clock.currentTime;
  1547. let timeInterval;
  1548. let timeIntervalIndex = timeIntervalCollection.indexOf(date);
  1549. if (timeIntervalIndex >= 0) {
  1550. timeInterval = timeIntervalCollection.get(timeIntervalIndex);
  1551. } else {
  1552. // Date fell outside the range
  1553. timeIntervalIndex = ~timeIntervalIndex;
  1554. if (timeIntervalIndex === timeIntervalCollection.length) {
  1555. // Date past range
  1556. timeIntervalIndex = timeIntervalCollection.length - 1;
  1557. timeInterval = timeIntervalCollection.get(timeIntervalIndex);
  1558. date = timeInterval.stop;
  1559. } else {
  1560. // Date before range
  1561. timeInterval = timeIntervalCollection.get(timeIntervalIndex);
  1562. date = timeInterval.start;
  1563. }
  1564. }
  1565. // De-lerp between the start and end of the interval
  1566. const totalSeconds = JulianDate.secondsDifference(
  1567. timeInterval.stop,
  1568. timeInterval.start,
  1569. );
  1570. const secondsDifferenceStart = JulianDate.secondsDifference(
  1571. date,
  1572. timeInterval.start,
  1573. );
  1574. const t = secondsDifferenceStart / totalSeconds;
  1575. return timeIntervalIndex + t;
  1576. }
  1577. /**
  1578. * Update the clipping planes state and associated uniforms
  1579. *
  1580. * @param {VoxelPrimitive} primitive
  1581. * @param {FrameState} frameState
  1582. * @returns {boolean} Whether the clipping planes changed, requiring a shader rebuild
  1583. * @private
  1584. */
  1585. function updateClippingPlanes(primitive, frameState) {
  1586. const clippingPlanes = primitive.clippingPlanes;
  1587. if (!defined(clippingPlanes)) {
  1588. return false;
  1589. }
  1590. clippingPlanes.update(frameState);
  1591. const { clippingPlanesState, enabled } = clippingPlanes;
  1592. if (enabled) {
  1593. const uniforms = primitive._uniforms;
  1594. uniforms.clippingPlanesTexture = clippingPlanes.texture;
  1595. // Compute the clipping plane's transformation to local space and then take the inverse
  1596. // transpose to properly transform the hessian normal form of the plane.
  1597. // transpose(inverse(worldToLocal * clippingPlaneLocalToWorld))
  1598. // transpose(inverse(clippingPlaneLocalToWorld) * inverse(worldToLocal))
  1599. // transpose(inverse(clippingPlaneLocalToWorld) * localToWorld)
  1600. uniforms.clippingPlanesMatrix = Matrix4.transpose(
  1601. Matrix4.multiplyTransformation(
  1602. Matrix4.inverse(
  1603. clippingPlanes.modelMatrix,
  1604. uniforms.clippingPlanesMatrix,
  1605. ),
  1606. primitive._transformPositionLocalToWorld,
  1607. uniforms.clippingPlanesMatrix,
  1608. ),
  1609. uniforms.clippingPlanesMatrix,
  1610. );
  1611. }
  1612. if (
  1613. primitive._clippingPlanesState === clippingPlanesState &&
  1614. primitive._clippingPlanesEnabled === enabled
  1615. ) {
  1616. return false;
  1617. }
  1618. primitive._clippingPlanesState = clippingPlanesState;
  1619. primitive._clippingPlanesEnabled = enabled;
  1620. return true;
  1621. }
  1622. /**
  1623. * Returns true if this object was destroyed; otherwise, false.
  1624. * <br /><br />
  1625. * If this object was destroyed, it should not be used; calling any function other than
  1626. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  1627. *
  1628. * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  1629. *
  1630. * @see VoxelPrimitive#destroy
  1631. */
  1632. VoxelPrimitive.prototype.isDestroyed = function () {
  1633. return false;
  1634. };
  1635. /**
  1636. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  1637. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  1638. * <br /><br />
  1639. * Once an object is destroyed, it should not be used; calling any function other than
  1640. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  1641. * assign the return value (<code>undefined</code>) to the object as done in the example.
  1642. *
  1643. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  1644. *
  1645. * @see VoxelPrimitive#isDestroyed
  1646. *
  1647. * @example
  1648. * voxelPrimitive = voxelPrimitive && voxelPrimitive.destroy();
  1649. */
  1650. VoxelPrimitive.prototype.destroy = function () {
  1651. const drawCommand = this._drawCommand;
  1652. if (defined(drawCommand)) {
  1653. drawCommand.shaderProgram =
  1654. drawCommand.shaderProgram && drawCommand.shaderProgram.destroy();
  1655. }
  1656. const drawCommandPick = this._drawCommandPick;
  1657. if (defined(drawCommandPick)) {
  1658. drawCommandPick.shaderProgram =
  1659. drawCommandPick.shaderProgram && drawCommandPick.shaderProgram.destroy();
  1660. }
  1661. this._pickId = this._pickId && this._pickId.destroy();
  1662. this._traversal = this._traversal && this._traversal.destroy();
  1663. this.statistics.texturesByteLength = 0;
  1664. this._clippingPlanes = this._clippingPlanes && this._clippingPlanes.destroy();
  1665. return destroyObject(this);
  1666. };
  1667. const corners = new Array(
  1668. new Cartesian4(-1.0, -1.0, -1.0, 1.0),
  1669. new Cartesian4(+1.0, -1.0, -1.0, 1.0),
  1670. new Cartesian4(-1.0, +1.0, -1.0, 1.0),
  1671. new Cartesian4(+1.0, +1.0, -1.0, 1.0),
  1672. new Cartesian4(-1.0, -1.0, +1.0, 1.0),
  1673. new Cartesian4(+1.0, -1.0, +1.0, 1.0),
  1674. new Cartesian4(-1.0, +1.0, +1.0, 1.0),
  1675. new Cartesian4(+1.0, +1.0, +1.0, 1.0),
  1676. );
  1677. const vertexNeighborIndices = new Array(
  1678. 1,
  1679. 2,
  1680. 4,
  1681. 0,
  1682. 3,
  1683. 5,
  1684. 0,
  1685. 3,
  1686. 6,
  1687. 1,
  1688. 2,
  1689. 7,
  1690. 0,
  1691. 5,
  1692. 6,
  1693. 1,
  1694. 4,
  1695. 7,
  1696. 2,
  1697. 4,
  1698. 7,
  1699. 3,
  1700. 5,
  1701. 6,
  1702. );
  1703. const scratchCornersClipSpace = new Array(
  1704. new Cartesian4(),
  1705. new Cartesian4(),
  1706. new Cartesian4(),
  1707. new Cartesian4(),
  1708. new Cartesian4(),
  1709. new Cartesian4(),
  1710. new Cartesian4(),
  1711. new Cartesian4(),
  1712. );
  1713. /**
  1714. * Projects all 8 corners of the oriented bounding box to NDC space and finds the
  1715. * resulting NDC axis aligned bounding box. To avoid projecting a vertex that is
  1716. * behind the near plane, it uses the intersection point of each of the vertex's
  1717. * edges against the near plane as part of the AABB calculation. This is done in
  1718. * clip space prior to perspective division.
  1719. *
  1720. * @function
  1721. *
  1722. * @param {OrientedBoundingBox} orientedBoundingBox
  1723. * @param {Matrix4} worldToProjection
  1724. * @param {Cartesian4} result
  1725. * @returns {Cartesian4}
  1726. *
  1727. * @private
  1728. */
  1729. function orientedBoundingBoxToNdcAabb(
  1730. orientedBoundingBox,
  1731. worldToProjection,
  1732. result,
  1733. ) {
  1734. const transformPositionLocalToWorld = Matrix4.fromRotationTranslation(
  1735. orientedBoundingBox.halfAxes,
  1736. orientedBoundingBox.center,
  1737. scratchTransformPositionLocalToWorld,
  1738. );
  1739. const transformPositionLocalToProjection = Matrix4.multiply(
  1740. worldToProjection,
  1741. transformPositionLocalToWorld,
  1742. scratchTransformPositionLocalToProjection,
  1743. );
  1744. let ndcMinX = +Number.MAX_VALUE;
  1745. let ndcMaxX = -Number.MAX_VALUE;
  1746. let ndcMinY = +Number.MAX_VALUE;
  1747. let ndcMaxY = -Number.MAX_VALUE;
  1748. let cornerIndex;
  1749. // Convert all points to clip space
  1750. const cornersClipSpace = scratchCornersClipSpace;
  1751. const cornersLength = corners.length;
  1752. for (cornerIndex = 0; cornerIndex < cornersLength; cornerIndex++) {
  1753. Matrix4.multiplyByVector(
  1754. transformPositionLocalToProjection,
  1755. corners[cornerIndex],
  1756. cornersClipSpace[cornerIndex],
  1757. );
  1758. }
  1759. for (cornerIndex = 0; cornerIndex < cornersLength; cornerIndex++) {
  1760. const position = cornersClipSpace[cornerIndex];
  1761. if (position.z >= -position.w) {
  1762. // Position is past near plane, so there's no need to clip.
  1763. const ndcX = position.x / position.w;
  1764. const ndcY = position.y / position.w;
  1765. ndcMinX = Math.min(ndcMinX, ndcX);
  1766. ndcMaxX = Math.max(ndcMaxX, ndcX);
  1767. ndcMinY = Math.min(ndcMinY, ndcY);
  1768. ndcMaxY = Math.max(ndcMaxY, ndcY);
  1769. } else {
  1770. for (let neighborIndex = 0; neighborIndex < 3; neighborIndex++) {
  1771. const neighborVertexIndex =
  1772. vertexNeighborIndices[cornerIndex * 3 + neighborIndex];
  1773. const neighborPosition = cornersClipSpace[neighborVertexIndex];
  1774. if (neighborPosition.z >= -neighborPosition.w) {
  1775. // Position is behind the near plane and neighbor is after, so get intersection point on the near plane.
  1776. const distanceToPlaneFromPosition = position.z + position.w;
  1777. const distanceToPlaneFromNeighbor =
  1778. neighborPosition.z + neighborPosition.w;
  1779. const t =
  1780. distanceToPlaneFromPosition /
  1781. (distanceToPlaneFromPosition - distanceToPlaneFromNeighbor);
  1782. const intersect = Cartesian4.lerp(
  1783. position,
  1784. neighborPosition,
  1785. t,
  1786. scratchIntersect,
  1787. );
  1788. const intersectNdcX = intersect.x / intersect.w;
  1789. const intersectNdcY = intersect.y / intersect.w;
  1790. ndcMinX = Math.min(ndcMinX, intersectNdcX);
  1791. ndcMaxX = Math.max(ndcMaxX, intersectNdcX);
  1792. ndcMinY = Math.min(ndcMinY, intersectNdcY);
  1793. ndcMaxY = Math.max(ndcMaxY, intersectNdcY);
  1794. }
  1795. }
  1796. }
  1797. }
  1798. // Clamp the NDC values to -1 to +1 range even if they extend much further.
  1799. ndcMinX = CesiumMath.clamp(ndcMinX, -1.0, +1.0);
  1800. ndcMinY = CesiumMath.clamp(ndcMinY, -1.0, +1.0);
  1801. ndcMaxX = CesiumMath.clamp(ndcMaxX, -1.0, +1.0);
  1802. ndcMaxY = CesiumMath.clamp(ndcMaxY, -1.0, +1.0);
  1803. result = Cartesian4.fromElements(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, result);
  1804. return result;
  1805. }
  1806. const polylineAxisDistance = 30000000.0;
  1807. const polylineXAxis = new Cartesian3(polylineAxisDistance, 0.0, 0.0);
  1808. const polylineYAxis = new Cartesian3(0.0, polylineAxisDistance, 0.0);
  1809. const polylineZAxis = new Cartesian3(0.0, 0.0, polylineAxisDistance);
  1810. /**
  1811. * Draws the tile bounding boxes and axes.
  1812. *
  1813. * @function
  1814. *
  1815. * @param {VoxelPrimitive} that
  1816. * @param {FrameState} frameState
  1817. *
  1818. * @private
  1819. */
  1820. function debugDraw(that, frameState) {
  1821. const traversal = that._traversal;
  1822. const polylines = that._debugPolylines;
  1823. polylines.removeAll();
  1824. function makePolylineLineSegment(startPos, endPos, color, thickness) {
  1825. polylines.add({
  1826. positions: [startPos, endPos],
  1827. width: thickness,
  1828. material: Material.fromType("Color", {
  1829. color: color,
  1830. }),
  1831. });
  1832. }
  1833. function makePolylineBox(orientedBoundingBox, color, thickness) {
  1834. // Normally would want to use a scratch variable to store the corners, but
  1835. // polylines don't clone the positions.
  1836. const corners = orientedBoundingBox.computeCorners();
  1837. makePolylineLineSegment(corners[0], corners[1], color, thickness);
  1838. makePolylineLineSegment(corners[2], corners[3], color, thickness);
  1839. makePolylineLineSegment(corners[4], corners[5], color, thickness);
  1840. makePolylineLineSegment(corners[6], corners[7], color, thickness);
  1841. makePolylineLineSegment(corners[0], corners[2], color, thickness);
  1842. makePolylineLineSegment(corners[4], corners[6], color, thickness);
  1843. makePolylineLineSegment(corners[1], corners[3], color, thickness);
  1844. makePolylineLineSegment(corners[5], corners[7], color, thickness);
  1845. makePolylineLineSegment(corners[0], corners[4], color, thickness);
  1846. makePolylineLineSegment(corners[2], corners[6], color, thickness);
  1847. makePolylineLineSegment(corners[1], corners[5], color, thickness);
  1848. makePolylineLineSegment(corners[3], corners[7], color, thickness);
  1849. }
  1850. function drawTile(tile) {
  1851. if (!traversal.isRenderable(tile)) {
  1852. return;
  1853. }
  1854. const level = tile.level;
  1855. const startThickness = 5.0;
  1856. const thickness = Math.max(1.0, startThickness / Math.pow(2.0, level));
  1857. const colors = [Color.RED, Color.LIME, Color.BLUE];
  1858. const color = colors[level % 3];
  1859. makePolylineBox(tile.orientedBoundingBox, color, thickness);
  1860. if (defined(tile.children)) {
  1861. for (let i = 0; i < 8; i++) {
  1862. drawTile(tile.children[i]);
  1863. }
  1864. }
  1865. }
  1866. makePolylineBox(that._shape.orientedBoundingBox, Color.WHITE, 5.0);
  1867. drawTile(traversal.rootNode);
  1868. const axisThickness = 10.0;
  1869. makePolylineLineSegment(
  1870. Cartesian3.ZERO,
  1871. polylineXAxis,
  1872. Color.RED,
  1873. axisThickness,
  1874. );
  1875. makePolylineLineSegment(
  1876. Cartesian3.ZERO,
  1877. polylineYAxis,
  1878. Color.LIME,
  1879. axisThickness,
  1880. );
  1881. makePolylineLineSegment(
  1882. Cartesian3.ZERO,
  1883. polylineZAxis,
  1884. Color.BLUE,
  1885. axisThickness,
  1886. );
  1887. polylines.update(frameState);
  1888. }
  1889. /**
  1890. * The default custom shader used by the primitive.
  1891. *
  1892. * @type {CustomShader}
  1893. * @constant
  1894. * @readonly
  1895. *
  1896. * @private
  1897. */
  1898. VoxelPrimitive.DefaultCustomShader = new CustomShader({
  1899. fragmentShaderText: `void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
  1900. {
  1901. vec3 voxelNormal = fsInput.attributes.normalEC;
  1902. float diffuse = max(0.0, dot(voxelNormal, czm_lightDirectionEC));
  1903. float lighting = 0.5 + 0.5 * diffuse;
  1904. material.diffuse = vec3(lighting);
  1905. material.alpha = 1.0;
  1906. }`,
  1907. });
  1908. function DefaultVoxelProvider() {
  1909. this.shape = VoxelShapeType.BOX;
  1910. this.dimensions = new Cartesian3(1, 1, 1);
  1911. this.names = ["data"];
  1912. this.types = [MetadataType.SCALAR];
  1913. this.componentTypes = [MetadataComponentType.FLOAT32];
  1914. this.maximumTileCount = 1;
  1915. }
  1916. DefaultVoxelProvider.prototype.requestData = function (options) {
  1917. const tileLevel = defined(options) ? (options.tileLevel ?? 0) : 0;
  1918. if (tileLevel >= 1) {
  1919. return undefined;
  1920. }
  1921. const content = new VoxelContent({ metadata: [new Float32Array(1)] });
  1922. return Promise.resolve(content);
  1923. };
  1924. VoxelPrimitive.DefaultProvider = new DefaultVoxelProvider();
  1925. export default VoxelPrimitive;