TimeDynamicPointCloud.js 29 KB

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