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

TimeDynamicPointCloud.js 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. import Check from "../Core/Check.js";
  2. import combine from "../Core/combine.js";
  3. import Frozen from "../Core/Frozen.js";
  4. import defined from "../Core/defined.js";
  5. import destroyObject from "../Core/destroyObject.js";
  6. import Event from "../Core/Event.js";
  7. import getTimestamp from "../Core/getTimestamp.js";
  8. import JulianDate from "../Core/JulianDate.js";
  9. import CesiumMath from "../Core/Math.js";
  10. import Matrix4 from "../Core/Matrix4.js";
  11. import Resource from "../Core/Resource.js";
  12. import ClippingPlaneCollection from "./ClippingPlaneCollection.js";
  13. import PointCloud from "./PointCloud.js";
  14. import PointCloudEyeDomeLighting from "./PointCloudEyeDomeLighting.js";
  15. import PointCloudShading from "./PointCloudShading.js";
  16. import SceneMode from "./SceneMode.js";
  17. import ShadowMode from "./ShadowMode.js";
  18. /**
  19. * Provides playback of time-dynamic point cloud data.
  20. * <p>
  21. * Point cloud frames are prefetched in intervals determined by the average frame load time and the current clock speed.
  22. * If intermediate frames cannot be loaded in time to meet playback speed, they will be skipped. If frames are sufficiently
  23. * small or the clock is sufficiently slow then no frames will be skipped.
  24. * </p>
  25. *
  26. * @alias TimeDynamicPointCloud
  27. * @constructor
  28. *
  29. * @param {object} options Object with the following properties:
  30. * @param {Clock} options.clock A {@link Clock} instance that is used when determining the value for the time dimension.
  31. * @param {TimeIntervalCollection} options.intervals A {@link TimeIntervalCollection} with its data property being an object containing a <code>uri</code> to a 3D Tiles Point Cloud tile and an optional <code>transform</code>.
  32. * @param {boolean} [options.show=true] Determines if the point cloud will be shown.
  33. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] A 4x4 transformation matrix that transforms the point cloud.
  34. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the point cloud casts or receives shadows from light sources.
  35. * @param {number} [options.maximumMemoryUsage=256] The maximum amount of memory in MB that can be used by the point cloud.
  36. * @param {object} [options.shading] Options for constructing a {@link PointCloudShading} object to control point attenuation and eye dome lighting.
  37. * @param {Cesium3DTileStyle} [options.style] The style, defined using the {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/Styling|3D Tiles Styling language}, applied to each point in the point cloud.
  38. * @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the point cloud.
  39. */
  40. function TimeDynamicPointCloud(options) {
  41. options = options ?? Frozen.EMPTY_OBJECT;
  42. //>>includeStart('debug', pragmas.debug);
  43. Check.typeOf.object("options.clock", options.clock);
  44. Check.typeOf.object("options.intervals", options.intervals);
  45. //>>includeEnd('debug');
  46. /**
  47. * Determines if the point cloud will be shown.
  48. *
  49. * @type {boolean}
  50. * @default true
  51. */
  52. this.show = options.show ?? true;
  53. /**
  54. * A 4x4 transformation matrix that transforms the point cloud.
  55. *
  56. * @type {Matrix4}
  57. * @default Matrix4.IDENTITY
  58. */
  59. this.modelMatrix = Matrix4.clone(options.modelMatrix ?? Matrix4.IDENTITY);
  60. /**
  61. * Determines whether the point cloud casts or receives shadows from light sources.
  62. * <p>
  63. * Enabling shadows has a performance impact. A point cloud that casts shadows must be rendered twice, once from the camera and again from the light's point of view.
  64. * </p>
  65. * <p>
  66. * Shadows are rendered only when {@link Viewer#shadows} is <code>true</code>.
  67. * </p>
  68. *
  69. * @type {ShadowMode}
  70. * @default ShadowMode.ENABLED
  71. */
  72. this.shadows = options.shadows ?? ShadowMode.ENABLED;
  73. /**
  74. * The maximum amount of GPU memory (in MB) that may be used to cache point cloud frames.
  75. * <p>
  76. * Frames that are not being loaded or rendered are unloaded to enforce this.
  77. * </p>
  78. * <p>
  79. * If decreasing this value results in unloading tiles, the tiles are unloaded the next frame.
  80. * </p>
  81. *
  82. * @type {number}
  83. * @default 256
  84. *
  85. * @see TimeDynamicPointCloud#totalMemoryUsageInBytes
  86. */
  87. this.maximumMemoryUsage = options.maximumMemoryUsage ?? 256;
  88. /**
  89. * Options for controlling point size based on geometric error and eye dome lighting.
  90. * @type {PointCloudShading}
  91. */
  92. this.shading = new PointCloudShading(options.shading);
  93. /**
  94. * The style, defined using the
  95. * {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/Styling|3D Tiles Styling language},
  96. * applied to each point in the point cloud.
  97. * <p>
  98. * Assign <code>undefined</code> to remove the style, which will restore the visual
  99. * appearance of the point cloud to its default when no style was applied.
  100. * </p>
  101. *
  102. * @type {Cesium3DTileStyle}
  103. *
  104. * @example
  105. * pointCloud.style = new Cesium.Cesium3DTileStyle({
  106. * color : {
  107. * conditions : [
  108. * ['${Classification} === 0', 'color("purple", 0.5)'],
  109. * ['${Classification} === 1', 'color("red")'],
  110. * ['true', '${COLOR}']
  111. * ]
  112. * },
  113. * show : '${Classification} !== 2'
  114. * });
  115. *
  116. * @see {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/Styling|3D Tiles Styling language}
  117. */
  118. this.style = options.style;
  119. /**
  120. * The event fired to indicate that a frame failed to load. A frame may fail to load if the
  121. * request for its uri fails or processing fails due to invalid content.
  122. * <p>
  123. * If there are no event listeners, error messages will be logged to the console.
  124. * </p>
  125. * <p>
  126. * The error object passed to the listener contains two properties:
  127. * <ul>
  128. * <li><code>uri</code>: the uri of the failed frame.</li>
  129. * <li><code>message</code>: the error message.</li>
  130. * </ul>
  131. *
  132. * @type {Event}
  133. * @default new Event()
  134. *
  135. * @example
  136. * pointCloud.frameFailed.addEventListener(function(error) {
  137. * console.log(`An error occurred loading frame: ${error.uri}`);
  138. * console.log(`Error: ${error.message}`);
  139. * });
  140. */
  141. this.frameFailed = new Event();
  142. /**
  143. * The event fired to indicate that a new frame was rendered.
  144. * <p>
  145. * The time dynamic point cloud {@link TimeDynamicPointCloud} is passed to the event listener.
  146. * </p>
  147. * @type {Event}
  148. * @default new Event()
  149. *
  150. * @example
  151. * pointCloud.frameChanged.addEventListener(function(timeDynamicPointCloud) {
  152. * viewer.camera.viewBoundingSphere(timeDynamicPointCloud.boundingSphere);
  153. * });
  154. */
  155. this.frameChanged = new Event();
  156. this._clock = options.clock;
  157. this._intervals = options.intervals;
  158. this._clippingPlanes = undefined;
  159. this.clippingPlanes = options.clippingPlanes; // Call setter
  160. this._pointCloudEyeDomeLighting = new PointCloudEyeDomeLighting();
  161. this._loadTimestamp = undefined;
  162. this._clippingPlanesState = 0;
  163. this._styleDirty = false;
  164. this._pickId = undefined;
  165. this._totalMemoryUsageInBytes = 0;
  166. this._frames = [];
  167. this._previousInterval = undefined;
  168. this._nextInterval = undefined;
  169. this._lastRenderedFrame = undefined;
  170. this._clockMultiplier = 0.0;
  171. // For calculating average load time of the last N frames
  172. this._runningSum = 0.0;
  173. this._runningLength = 0;
  174. this._runningIndex = 0;
  175. this._runningSamples = new Array(5).fill(0.0);
  176. this._runningAverage = 0.0;
  177. }
  178. Object.defineProperties(TimeDynamicPointCloud.prototype, {
  179. /**
  180. * The {@link ClippingPlaneCollection} used to selectively disable rendering the point cloud.
  181. *
  182. * @memberof TimeDynamicPointCloud.prototype
  183. *
  184. * @type {ClippingPlaneCollection}
  185. */
  186. clippingPlanes: {
  187. get: function () {
  188. return this._clippingPlanes;
  189. },
  190. set: function (value) {
  191. ClippingPlaneCollection.setOwner(value, this, "_clippingPlanes");
  192. },
  193. },
  194. /**
  195. * The total amount of GPU memory in bytes used by the point cloud.
  196. *
  197. * @memberof TimeDynamicPointCloud.prototype
  198. *
  199. * @type {number}
  200. * @readonly
  201. *
  202. * @see TimeDynamicPointCloud#maximumMemoryUsage
  203. */
  204. totalMemoryUsageInBytes: {
  205. get: function () {
  206. return this._totalMemoryUsageInBytes;
  207. },
  208. },
  209. /**
  210. * The bounding sphere of the frame being rendered. Returns <code>undefined</code> if no frame is being rendered.
  211. *
  212. * @memberof TimeDynamicPointCloud.prototype
  213. *
  214. * @type {BoundingSphere}
  215. * @readonly
  216. */
  217. boundingSphere: {
  218. get: function () {
  219. if (defined(this._lastRenderedFrame)) {
  220. return this._lastRenderedFrame.pointCloud.boundingSphere;
  221. }
  222. return undefined;
  223. },
  224. },
  225. });
  226. function getFragmentShaderLoaded(fs) {
  227. return `uniform vec4 czm_pickColor;\n${fs}`;
  228. }
  229. function getUniformMapLoaded(stream) {
  230. return function (uniformMap) {
  231. return combine(uniformMap, {
  232. czm_pickColor: function () {
  233. return stream._pickId.color;
  234. },
  235. });
  236. };
  237. }
  238. function getPickIdLoaded() {
  239. return "czm_pickColor";
  240. }
  241. /**
  242. * Marks the point cloud's {@link TimeDynamicPointCloud#style} as dirty, which forces all
  243. * points to re-evaluate the style in the next frame.
  244. */
  245. TimeDynamicPointCloud.prototype.makeStyleDirty = function () {
  246. this._styleDirty = true;
  247. };
  248. /**
  249. * Exposed for testing.
  250. *
  251. * @private
  252. */
  253. TimeDynamicPointCloud.prototype._getAverageLoadTime = function () {
  254. if (this._runningLength === 0) {
  255. // Before any frames have loaded make a best guess about the average load time
  256. return 0.05;
  257. }
  258. return this._runningAverage;
  259. };
  260. const scratchDate = new JulianDate();
  261. function getClockMultiplier(that) {
  262. const clock = that._clock;
  263. const isAnimating = clock.canAnimate && clock.shouldAnimate;
  264. const multiplier = clock.multiplier;
  265. return isAnimating ? multiplier : 0.0;
  266. }
  267. function getIntervalIndex(that, interval) {
  268. return that._intervals.indexOf(interval.start);
  269. }
  270. function getNextInterval(that, currentInterval) {
  271. const intervals = that._intervals;
  272. const clock = that._clock;
  273. const multiplier = getClockMultiplier(that);
  274. if (multiplier === 0.0) {
  275. return undefined;
  276. }
  277. const averageLoadTime = that._getAverageLoadTime();
  278. const time = JulianDate.addSeconds(
  279. clock.currentTime,
  280. averageLoadTime * multiplier,
  281. scratchDate,
  282. );
  283. let index = intervals.indexOf(time);
  284. const currentIndex = getIntervalIndex(that, currentInterval);
  285. if (index === currentIndex) {
  286. if (multiplier >= 0) {
  287. ++index;
  288. } else {
  289. --index;
  290. }
  291. }
  292. // Returns undefined if not in range
  293. return intervals.get(index);
  294. }
  295. function getCurrentInterval(that) {
  296. const intervals = that._intervals;
  297. const clock = that._clock;
  298. const time = clock.currentTime;
  299. const index = intervals.indexOf(time);
  300. // Returns undefined if not in range
  301. return intervals.get(index);
  302. }
  303. function reachedInterval(that, currentInterval, nextInterval) {
  304. const multiplier = getClockMultiplier(that);
  305. const currentIndex = getIntervalIndex(that, currentInterval);
  306. const nextIndex = getIntervalIndex(that, nextInterval);
  307. if (multiplier >= 0) {
  308. return currentIndex >= nextIndex;
  309. }
  310. return currentIndex <= nextIndex;
  311. }
  312. function handleFrameFailure(that, uri) {
  313. return function (error) {
  314. const message = defined(error.message) ? error.message : error.toString();
  315. if (that.frameFailed.numberOfListeners > 0) {
  316. that.frameFailed.raiseEvent({
  317. uri: uri,
  318. message: message,
  319. });
  320. } else {
  321. console.log(`A frame failed to load: ${uri}`);
  322. console.log(`Error: ${message}`);
  323. }
  324. };
  325. }
  326. function requestFrame(that, interval, frameState) {
  327. const index = getIntervalIndex(that, interval);
  328. const frames = that._frames;
  329. let frame = frames[index];
  330. if (!defined(frame)) {
  331. const transformArray = interval.data.transform;
  332. const transform = defined(transformArray)
  333. ? Matrix4.fromArray(transformArray)
  334. : undefined;
  335. const uri = interval.data.uri;
  336. frame = {
  337. pointCloud: undefined,
  338. transform: transform,
  339. timestamp: getTimestamp(),
  340. sequential: true,
  341. ready: false,
  342. touchedFrameNumber: frameState.frameNumber,
  343. uri: uri,
  344. };
  345. frames[index] = frame;
  346. Resource.fetchArrayBuffer({
  347. url: uri,
  348. })
  349. .then(function (arrayBuffer) {
  350. // PERFORMANCE_IDEA: share a memory pool, render states, shaders, and other resources among all
  351. // frames. Each frame just needs an index/offset into the pool.
  352. frame.pointCloud = new PointCloud({
  353. arrayBuffer: arrayBuffer,
  354. cull: true,
  355. fragmentShaderLoaded: getFragmentShaderLoaded,
  356. uniformMapLoaded: getUniformMapLoaded(that),
  357. pickIdLoaded: getPickIdLoaded,
  358. });
  359. })
  360. .catch(handleFrameFailure(that, uri));
  361. }
  362. return frame;
  363. }
  364. function updateAverageLoadTime(that, loadTime) {
  365. that._runningSum += loadTime;
  366. that._runningSum -= that._runningSamples[that._runningIndex];
  367. that._runningSamples[that._runningIndex] = loadTime;
  368. that._runningLength = Math.min(
  369. that._runningLength + 1,
  370. that._runningSamples.length,
  371. );
  372. that._runningIndex = (that._runningIndex + 1) % that._runningSamples.length;
  373. that._runningAverage = that._runningSum / that._runningLength;
  374. }
  375. function prepareFrame(that, frame, updateState, frameState) {
  376. if (frame.touchedFrameNumber < frameState.frameNumber - 1) {
  377. // If this frame was not loaded in sequential updates then it can't be used it for calculating the average load time.
  378. // For example: selecting a frame on the timeline, selecting another frame before the request finishes, then selecting this frame later.
  379. frame.sequential = false;
  380. }
  381. const pointCloud = frame.pointCloud;
  382. if (defined(pointCloud) && !frame.ready) {
  383. // Call update to prepare renderer resources. Don't render anything yet.
  384. const commandList = frameState.commandList;
  385. const lengthBeforeUpdate = commandList.length;
  386. renderFrame(that, frame, updateState, frameState);
  387. if (pointCloud.ready) {
  388. // Point cloud became ready this update
  389. frame.ready = true;
  390. that._totalMemoryUsageInBytes += pointCloud.geometryByteLength;
  391. commandList.length = lengthBeforeUpdate; // Don't allow preparing frame to insert commands.
  392. if (frame.sequential) {
  393. // Update the values used to calculate average load time
  394. const loadTime = (getTimestamp() - frame.timestamp) / 1000.0;
  395. updateAverageLoadTime(that, loadTime);
  396. }
  397. }
  398. }
  399. frame.touchedFrameNumber = frameState.frameNumber;
  400. }
  401. const scratchModelMatrix = new Matrix4();
  402. function getGeometricError(that, pointCloud) {
  403. const shading = that.shading;
  404. if (defined(shading) && defined(shading.baseResolution)) {
  405. return shading.baseResolution;
  406. } else if (defined(pointCloud.boundingSphere)) {
  407. return CesiumMath.cbrt(
  408. pointCloud.boundingSphere.volume() / pointCloud.pointsLength,
  409. );
  410. }
  411. return 0.0;
  412. }
  413. function getMaximumAttenuation(that) {
  414. const shading = that.shading;
  415. if (defined(shading) && defined(shading.maximumAttenuation)) {
  416. return shading.maximumAttenuation;
  417. }
  418. // Return a hardcoded maximum attenuation. For a tileset this would instead be the maximum screen space error.
  419. return 10.0;
  420. }
  421. const defaultShading = new PointCloudShading();
  422. function renderFrame(that, frame, updateState, frameState) {
  423. const shading = that.shading ?? defaultShading;
  424. const pointCloud = frame.pointCloud;
  425. const transform = frame.transform ?? Matrix4.IDENTITY;
  426. pointCloud.modelMatrix = Matrix4.multiplyTransformation(
  427. that.modelMatrix,
  428. transform,
  429. scratchModelMatrix,
  430. );
  431. pointCloud.style = that.style;
  432. pointCloud.time = updateState.timeSinceLoad;
  433. pointCloud.shadows = that.shadows;
  434. pointCloud.clippingPlanes = that._clippingPlanes;
  435. pointCloud.isClipped = updateState.isClipped;
  436. pointCloud.attenuation = shading.attenuation;
  437. pointCloud.backFaceCulling = shading.backFaceCulling;
  438. pointCloud.normalShading = shading.normalShading;
  439. pointCloud.geometricError = getGeometricError(that, pointCloud);
  440. pointCloud.geometricErrorScale = shading.geometricErrorScale;
  441. pointCloud.maximumAttenuation = getMaximumAttenuation(that);
  442. try {
  443. pointCloud.update(frameState);
  444. } catch (error) {
  445. handleFrameFailure(that, frame.uri)(error);
  446. }
  447. frame.touchedFrameNumber = frameState.frameNumber;
  448. }
  449. function loadFrame(that, interval, updateState, frameState) {
  450. const frame = requestFrame(that, interval, frameState);
  451. prepareFrame(that, frame, updateState, frameState);
  452. }
  453. function getUnloadCondition(frameState) {
  454. return function (frame) {
  455. // Unload all frames that aren't currently being loaded or rendered
  456. return frame.touchedFrameNumber < frameState.frameNumber;
  457. };
  458. }
  459. function unloadFrames(that, unloadCondition) {
  460. const frames = that._frames;
  461. const length = frames.length;
  462. for (let i = 0; i < length; ++i) {
  463. const frame = frames[i];
  464. if (defined(frame)) {
  465. if (!defined(unloadCondition) || unloadCondition(frame)) {
  466. const pointCloud = frame.pointCloud;
  467. if (frame.ready) {
  468. that._totalMemoryUsageInBytes -= pointCloud.geometryByteLength;
  469. }
  470. if (defined(pointCloud)) {
  471. pointCloud.destroy();
  472. }
  473. if (frame === that._lastRenderedFrame) {
  474. that._lastRenderedFrame = undefined;
  475. }
  476. frames[i] = undefined;
  477. }
  478. }
  479. }
  480. }
  481. function getFrame(that, interval) {
  482. const index = getIntervalIndex(that, interval);
  483. const frame = that._frames[index];
  484. if (defined(frame) && frame.ready) {
  485. return frame;
  486. }
  487. }
  488. function updateInterval(that, interval, frame, updateState, frameState) {
  489. if (defined(frame)) {
  490. if (frame.ready) {
  491. return true;
  492. }
  493. loadFrame(that, interval, updateState, frameState);
  494. return frame.ready;
  495. }
  496. return false;
  497. }
  498. function getNearestReadyInterval(
  499. that,
  500. previousInterval,
  501. currentInterval,
  502. updateState,
  503. frameState,
  504. ) {
  505. let i;
  506. let interval;
  507. let frame;
  508. const intervals = that._intervals;
  509. const frames = that._frames;
  510. const currentIndex = getIntervalIndex(that, currentInterval);
  511. const previousIndex = getIntervalIndex(that, previousInterval);
  512. if (currentIndex >= previousIndex) {
  513. // look backwards
  514. for (i = currentIndex; i >= previousIndex; --i) {
  515. interval = intervals.get(i);
  516. frame = frames[i];
  517. if (updateInterval(that, interval, frame, updateState, frameState)) {
  518. return interval;
  519. }
  520. }
  521. } else {
  522. // look forwards
  523. for (i = currentIndex; i <= previousIndex; ++i) {
  524. interval = intervals.get(i);
  525. frame = frames[i];
  526. if (updateInterval(that, interval, frame, updateState, frameState)) {
  527. return interval;
  528. }
  529. }
  530. }
  531. // If no intervals are ready return the previous interval
  532. return previousInterval;
  533. }
  534. function setFramesDirty(that, clippingPlanesDirty, styleDirty) {
  535. const frames = that._frames;
  536. const framesLength = frames.length;
  537. for (let i = 0; i < framesLength; ++i) {
  538. const frame = frames[i];
  539. if (defined(frame) && defined(frame.pointCloud)) {
  540. frame.pointCloud.clippingPlanesDirty = clippingPlanesDirty;
  541. frame.pointCloud.styleDirty = styleDirty;
  542. }
  543. }
  544. }
  545. const updateState = {
  546. timeSinceLoad: 0,
  547. isClipped: false,
  548. clippingPlanesDirty: false,
  549. };
  550. /**
  551. * @private
  552. */
  553. TimeDynamicPointCloud.prototype.update = function (frameState) {
  554. if (frameState.mode === SceneMode.MORPHING) {
  555. return;
  556. }
  557. if (!this.show) {
  558. return;
  559. }
  560. if (!defined(this._pickId)) {
  561. this._pickId = frameState.context.createPickId({
  562. primitive: this,
  563. });
  564. }
  565. if (!defined(this._loadTimestamp)) {
  566. this._loadTimestamp = JulianDate.clone(frameState.time);
  567. }
  568. // For styling
  569. const timeSinceLoad = Math.max(
  570. JulianDate.secondsDifference(frameState.time, this._loadTimestamp) * 1000,
  571. 0.0,
  572. );
  573. // Update clipping planes
  574. const clippingPlanes = this._clippingPlanes;
  575. let clippingPlanesState = 0;
  576. let clippingPlanesDirty = false;
  577. const isClipped = defined(clippingPlanes) && clippingPlanes.enabled;
  578. if (isClipped) {
  579. clippingPlanes.update(frameState);
  580. clippingPlanesState = clippingPlanes.clippingPlanesState;
  581. }
  582. if (this._clippingPlanesState !== clippingPlanesState) {
  583. this._clippingPlanesState = clippingPlanesState;
  584. clippingPlanesDirty = true;
  585. }
  586. const styleDirty = this._styleDirty;
  587. this._styleDirty = false;
  588. if (clippingPlanesDirty || styleDirty) {
  589. setFramesDirty(this, clippingPlanesDirty, styleDirty);
  590. }
  591. updateState.timeSinceLoad = timeSinceLoad;
  592. updateState.isClipped = isClipped;
  593. const shading = this.shading;
  594. const eyeDomeLighting = this._pointCloudEyeDomeLighting;
  595. const commandList = frameState.commandList;
  596. const lengthBeforeUpdate = commandList.length;
  597. let previousInterval = this._previousInterval;
  598. let nextInterval = this._nextInterval;
  599. const currentInterval = getCurrentInterval(this);
  600. if (!defined(currentInterval)) {
  601. return;
  602. }
  603. let clockMultiplierChanged = false;
  604. const clockMultiplier = getClockMultiplier(this);
  605. const clockPaused = clockMultiplier === 0;
  606. if (clockMultiplier !== this._clockMultiplier) {
  607. clockMultiplierChanged = true;
  608. this._clockMultiplier = clockMultiplier;
  609. }
  610. if (!defined(previousInterval) || clockPaused) {
  611. previousInterval = currentInterval;
  612. }
  613. if (
  614. !defined(nextInterval) ||
  615. clockMultiplierChanged ||
  616. reachedInterval(this, currentInterval, nextInterval)
  617. ) {
  618. nextInterval = getNextInterval(this, currentInterval);
  619. }
  620. previousInterval = getNearestReadyInterval(
  621. this,
  622. previousInterval,
  623. currentInterval,
  624. updateState,
  625. frameState,
  626. );
  627. let frame = getFrame(this, previousInterval);
  628. if (!defined(frame)) {
  629. // The frame is not ready to render. This can happen when the simulation starts or when scrubbing the timeline
  630. // to a frame that hasn't loaded yet. Just render the last rendered frame in its place until it finishes loading.
  631. loadFrame(this, previousInterval, updateState, frameState);
  632. frame = this._lastRenderedFrame;
  633. }
  634. if (defined(frame)) {
  635. renderFrame(this, frame, updateState, frameState);
  636. }
  637. if (defined(nextInterval)) {
  638. // Start loading the next frame
  639. loadFrame(this, nextInterval, updateState, frameState);
  640. }
  641. const that = this;
  642. if (defined(frame) && !defined(this._lastRenderedFrame)) {
  643. frameState.afterRender.push(function () {
  644. return true;
  645. });
  646. }
  647. if (defined(frame) && frame !== this._lastRenderedFrame) {
  648. if (that.frameChanged.numberOfListeners > 0) {
  649. frameState.afterRender.push(function () {
  650. that.frameChanged.raiseEvent(that);
  651. return true;
  652. });
  653. }
  654. }
  655. this._previousInterval = previousInterval;
  656. this._nextInterval = nextInterval;
  657. this._lastRenderedFrame = frame;
  658. const totalMemoryUsageInBytes = this._totalMemoryUsageInBytes;
  659. const maximumMemoryUsageInBytes = this.maximumMemoryUsage * 1024 * 1024;
  660. if (totalMemoryUsageInBytes > maximumMemoryUsageInBytes) {
  661. unloadFrames(this, getUnloadCondition(frameState));
  662. }
  663. const lengthAfterUpdate = commandList.length;
  664. const addedCommandsLength = lengthAfterUpdate - lengthBeforeUpdate;
  665. if (
  666. defined(shading) &&
  667. shading.attenuation &&
  668. shading.eyeDomeLighting &&
  669. addedCommandsLength > 0
  670. ) {
  671. eyeDomeLighting.update(
  672. frameState,
  673. lengthBeforeUpdate,
  674. shading,
  675. this.boundingSphere,
  676. );
  677. }
  678. };
  679. /**
  680. * Returns true if this object was destroyed; otherwise, false.
  681. * <br /><br />
  682. * If this object was destroyed, it should not be used; calling any function other than
  683. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  684. *
  685. * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  686. *
  687. * @see TimeDynamicPointCloud#destroy
  688. */
  689. TimeDynamicPointCloud.prototype.isDestroyed = function () {
  690. return false;
  691. };
  692. /**
  693. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  694. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  695. * <br /><br />
  696. * Once an object is destroyed, it should not be used; calling any function other than
  697. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  698. * assign the return value (<code>undefined</code>) to the object as done in the example.
  699. *
  700. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  701. *
  702. * @example
  703. * pointCloud = pointCloud && pointCloud.destroy();
  704. *
  705. * @see TimeDynamicPointCloud#isDestroyed
  706. */
  707. TimeDynamicPointCloud.prototype.destroy = function () {
  708. unloadFrames(this);
  709. this._clippingPlanes = this._clippingPlanes && this._clippingPlanes.destroy();
  710. this._pickId = this._pickId && this._pickId.destroy();
  711. return destroyObject(this);
  712. };
  713. export default TimeDynamicPointCloud;