VoxelPrimitive.js 51 KB

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