GeometryVisualizer.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. import AssociativeArray from "../Core/AssociativeArray.js";
  2. import BoundingSphere from "../Core/BoundingSphere.js";
  3. import Check from "../Core/Check.js";
  4. import defaultValue from "../Core/defaultValue.js";
  5. import defined from "../Core/defined.js";
  6. import destroyObject from "../Core/destroyObject.js";
  7. import Event from "../Core/Event.js";
  8. import EventHelper from "../Core/EventHelper.js";
  9. import ClassificationType from "../Scene/ClassificationType.js";
  10. import MaterialAppearance from "../Scene/MaterialAppearance.js";
  11. import PerInstanceColorAppearance from "../Scene/PerInstanceColorAppearance.js";
  12. import ShadowMode from "../Scene/ShadowMode.js";
  13. import BoundingSphereState from "./BoundingSphereState.js";
  14. import BoxGeometryUpdater from "./BoxGeometryUpdater.js";
  15. import ColorMaterialProperty from "./ColorMaterialProperty.js";
  16. import CorridorGeometryUpdater from "./CorridorGeometryUpdater.js";
  17. import CylinderGeometryUpdater from "./CylinderGeometryUpdater.js";
  18. import DynamicGeometryBatch from "./DynamicGeometryBatch.js";
  19. import EllipseGeometryUpdater from "./EllipseGeometryUpdater.js";
  20. import EllipsoidGeometryUpdater from "./EllipsoidGeometryUpdater.js";
  21. import Entity from "./Entity.js";
  22. import PlaneGeometryUpdater from "./PlaneGeometryUpdater.js";
  23. import PolygonGeometryUpdater from "./PolygonGeometryUpdater.js";
  24. import PolylineVolumeGeometryUpdater from "./PolylineVolumeGeometryUpdater.js";
  25. import RectangleGeometryUpdater from "./RectangleGeometryUpdater.js";
  26. import StaticGeometryColorBatch from "./StaticGeometryColorBatch.js";
  27. import StaticGeometryPerMaterialBatch from "./StaticGeometryPerMaterialBatch.js";
  28. import StaticGroundGeometryColorBatch from "./StaticGroundGeometryColorBatch.js";
  29. import StaticGroundGeometryPerMaterialBatch from "./StaticGroundGeometryPerMaterialBatch.js";
  30. import StaticOutlineGeometryBatch from "./StaticOutlineGeometryBatch.js";
  31. import WallGeometryUpdater from "./WallGeometryUpdater.js";
  32. const emptyArray = [];
  33. const geometryUpdaters = [
  34. BoxGeometryUpdater,
  35. CylinderGeometryUpdater,
  36. CorridorGeometryUpdater,
  37. EllipseGeometryUpdater,
  38. EllipsoidGeometryUpdater,
  39. PlaneGeometryUpdater,
  40. PolygonGeometryUpdater,
  41. PolylineVolumeGeometryUpdater,
  42. RectangleGeometryUpdater,
  43. WallGeometryUpdater,
  44. ];
  45. function GeometryUpdaterSet(entity, scene) {
  46. this.entity = entity;
  47. this.scene = scene;
  48. const updaters = new Array(geometryUpdaters.length);
  49. const geometryChanged = new Event();
  50. function raiseEvent(geometry) {
  51. geometryChanged.raiseEvent(geometry);
  52. }
  53. const eventHelper = new EventHelper();
  54. for (let i = 0; i < updaters.length; i++) {
  55. const updater = new geometryUpdaters[i](entity, scene);
  56. eventHelper.add(updater.geometryChanged, raiseEvent);
  57. updaters[i] = updater;
  58. }
  59. this.updaters = updaters;
  60. this.geometryChanged = geometryChanged;
  61. this.eventHelper = eventHelper;
  62. this._removeEntitySubscription = entity.definitionChanged.addEventListener(
  63. GeometryUpdaterSet.prototype._onEntityPropertyChanged,
  64. this
  65. );
  66. }
  67. GeometryUpdaterSet.prototype._onEntityPropertyChanged = function (
  68. entity,
  69. propertyName,
  70. newValue,
  71. oldValue
  72. ) {
  73. const updaters = this.updaters;
  74. for (let i = 0; i < updaters.length; i++) {
  75. updaters[i]._onEntityPropertyChanged(
  76. entity,
  77. propertyName,
  78. newValue,
  79. oldValue
  80. );
  81. }
  82. };
  83. GeometryUpdaterSet.prototype.forEach = function (callback) {
  84. const updaters = this.updaters;
  85. for (let i = 0; i < updaters.length; i++) {
  86. callback(updaters[i]);
  87. }
  88. };
  89. GeometryUpdaterSet.prototype.destroy = function () {
  90. this.eventHelper.removeAll();
  91. const updaters = this.updaters;
  92. for (let i = 0; i < updaters.length; i++) {
  93. updaters[i].destroy();
  94. }
  95. this._removeEntitySubscription();
  96. destroyObject(this);
  97. };
  98. /**
  99. * A general purpose visualizer for geometry represented by {@link Primitive} instances.
  100. * @alias GeometryVisualizer
  101. * @constructor
  102. *
  103. * @param {Scene} scene The scene the primitives will be rendered in.
  104. * @param {EntityCollection} entityCollection The entityCollection to visualize.
  105. * @param {PrimitiveCollection} [primitives=scene.primitives] A collection to add primitives related to the entities
  106. * @param {PrimitiveCollection} [groundPrimitives=scene.groundPrimitives] A collection to add ground primitives related to the entities
  107. */
  108. function GeometryVisualizer(
  109. scene,
  110. entityCollection,
  111. primitives,
  112. groundPrimitives
  113. ) {
  114. //>>includeStart('debug', pragmas.debug);
  115. Check.defined("scene", scene);
  116. Check.defined("entityCollection", entityCollection);
  117. //>>includeEnd('debug');
  118. primitives = defaultValue(primitives, scene.primitives);
  119. groundPrimitives = defaultValue(groundPrimitives, scene.groundPrimitives);
  120. this._scene = scene;
  121. this._primitives = primitives;
  122. this._groundPrimitives = groundPrimitives;
  123. this._entityCollection = undefined;
  124. this._addedObjects = new AssociativeArray();
  125. this._removedObjects = new AssociativeArray();
  126. this._changedObjects = new AssociativeArray();
  127. const numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES;
  128. this._outlineBatches = new Array(numberOfShadowModes * 2);
  129. this._closedColorBatches = new Array(numberOfShadowModes * 2);
  130. this._closedMaterialBatches = new Array(numberOfShadowModes * 2);
  131. this._openColorBatches = new Array(numberOfShadowModes * 2);
  132. this._openMaterialBatches = new Array(numberOfShadowModes * 2);
  133. const supportsMaterialsforEntitiesOnTerrain = Entity.supportsMaterialsforEntitiesOnTerrain(
  134. scene
  135. );
  136. this._supportsMaterialsforEntitiesOnTerrain = supportsMaterialsforEntitiesOnTerrain;
  137. let i;
  138. for (i = 0; i < numberOfShadowModes; ++i) {
  139. this._outlineBatches[i] = new StaticOutlineGeometryBatch(
  140. primitives,
  141. scene,
  142. i,
  143. false
  144. );
  145. this._outlineBatches[
  146. numberOfShadowModes + i
  147. ] = new StaticOutlineGeometryBatch(primitives, scene, i, true);
  148. this._closedColorBatches[i] = new StaticGeometryColorBatch(
  149. primitives,
  150. PerInstanceColorAppearance,
  151. undefined,
  152. true,
  153. i,
  154. true
  155. );
  156. this._closedColorBatches[
  157. numberOfShadowModes + i
  158. ] = new StaticGeometryColorBatch(
  159. primitives,
  160. PerInstanceColorAppearance,
  161. undefined,
  162. true,
  163. i,
  164. false
  165. );
  166. this._closedMaterialBatches[i] = new StaticGeometryPerMaterialBatch(
  167. primitives,
  168. MaterialAppearance,
  169. undefined,
  170. true,
  171. i,
  172. true
  173. );
  174. this._closedMaterialBatches[
  175. numberOfShadowModes + i
  176. ] = new StaticGeometryPerMaterialBatch(
  177. primitives,
  178. MaterialAppearance,
  179. undefined,
  180. true,
  181. i,
  182. false
  183. );
  184. this._openColorBatches[i] = new StaticGeometryColorBatch(
  185. primitives,
  186. PerInstanceColorAppearance,
  187. undefined,
  188. false,
  189. i,
  190. true
  191. );
  192. this._openColorBatches[
  193. numberOfShadowModes + i
  194. ] = new StaticGeometryColorBatch(
  195. primitives,
  196. PerInstanceColorAppearance,
  197. undefined,
  198. false,
  199. i,
  200. false
  201. );
  202. this._openMaterialBatches[i] = new StaticGeometryPerMaterialBatch(
  203. primitives,
  204. MaterialAppearance,
  205. undefined,
  206. false,
  207. i,
  208. true
  209. );
  210. this._openMaterialBatches[
  211. numberOfShadowModes + i
  212. ] = new StaticGeometryPerMaterialBatch(
  213. primitives,
  214. MaterialAppearance,
  215. undefined,
  216. false,
  217. i,
  218. false
  219. );
  220. }
  221. const numberOfClassificationTypes =
  222. ClassificationType.NUMBER_OF_CLASSIFICATION_TYPES;
  223. const groundColorBatches = new Array(numberOfClassificationTypes);
  224. const groundMaterialBatches = [];
  225. if (supportsMaterialsforEntitiesOnTerrain) {
  226. for (i = 0; i < numberOfClassificationTypes; ++i) {
  227. groundMaterialBatches.push(
  228. new StaticGroundGeometryPerMaterialBatch(
  229. groundPrimitives,
  230. i,
  231. MaterialAppearance
  232. )
  233. );
  234. groundColorBatches[i] = new StaticGroundGeometryColorBatch(
  235. groundPrimitives,
  236. i
  237. );
  238. }
  239. } else {
  240. for (i = 0; i < numberOfClassificationTypes; ++i) {
  241. groundColorBatches[i] = new StaticGroundGeometryColorBatch(
  242. groundPrimitives,
  243. i
  244. );
  245. }
  246. }
  247. this._groundColorBatches = groundColorBatches;
  248. this._groundMaterialBatches = groundMaterialBatches;
  249. this._dynamicBatch = new DynamicGeometryBatch(primitives, groundPrimitives);
  250. this._batches = this._outlineBatches.concat(
  251. this._closedColorBatches,
  252. this._closedMaterialBatches,
  253. this._openColorBatches,
  254. this._openMaterialBatches,
  255. this._groundColorBatches,
  256. this._groundMaterialBatches,
  257. this._dynamicBatch
  258. );
  259. this._subscriptions = new AssociativeArray();
  260. this._updaterSets = new AssociativeArray();
  261. this._entityCollection = entityCollection;
  262. entityCollection.collectionChanged.addEventListener(
  263. GeometryVisualizer.prototype._onCollectionChanged,
  264. this
  265. );
  266. this._onCollectionChanged(
  267. entityCollection,
  268. entityCollection.values,
  269. emptyArray
  270. );
  271. }
  272. /**
  273. * Updates all of the primitives created by this visualizer to match their
  274. * Entity counterpart at the given time.
  275. *
  276. * @param {JulianDate} time The time to update to.
  277. * @returns {boolean} True if the visualizer successfully updated to the provided time,
  278. * false if the visualizer is waiting for asynchronous primitives to be created.
  279. */
  280. GeometryVisualizer.prototype.update = function (time) {
  281. //>>includeStart('debug', pragmas.debug);
  282. Check.defined("time", time);
  283. //>>includeEnd('debug');
  284. const addedObjects = this._addedObjects;
  285. const added = addedObjects.values;
  286. const removedObjects = this._removedObjects;
  287. const removed = removedObjects.values;
  288. const changedObjects = this._changedObjects;
  289. const changed = changedObjects.values;
  290. let i;
  291. let entity;
  292. let id;
  293. let updaterSet;
  294. const that = this;
  295. for (i = changed.length - 1; i > -1; i--) {
  296. entity = changed[i];
  297. id = entity.id;
  298. updaterSet = this._updaterSets.get(id);
  299. //If in a single update, an entity gets removed and a new instance
  300. //re-added with the same id, the updater no longer tracks the
  301. //correct entity, we need to both remove the old one and
  302. //add the new one, which is done by pushing the entity
  303. //onto the removed/added lists.
  304. if (updaterSet.entity === entity) {
  305. updaterSet.forEach(function (updater) {
  306. that._removeUpdater(updater);
  307. that._insertUpdaterIntoBatch(time, updater);
  308. });
  309. } else {
  310. removed.push(entity);
  311. added.push(entity);
  312. }
  313. }
  314. for (i = removed.length - 1; i > -1; i--) {
  315. entity = removed[i];
  316. id = entity.id;
  317. updaterSet = this._updaterSets.get(id);
  318. updaterSet.forEach(this._removeUpdater.bind(this));
  319. updaterSet.destroy();
  320. this._updaterSets.remove(id);
  321. this._subscriptions.get(id)();
  322. this._subscriptions.remove(id);
  323. }
  324. for (i = added.length - 1; i > -1; i--) {
  325. entity = added[i];
  326. id = entity.id;
  327. updaterSet = new GeometryUpdaterSet(entity, this._scene);
  328. this._updaterSets.set(id, updaterSet);
  329. updaterSet.forEach(function (updater) {
  330. that._insertUpdaterIntoBatch(time, updater);
  331. });
  332. this._subscriptions.set(
  333. id,
  334. updaterSet.geometryChanged.addEventListener(
  335. GeometryVisualizer._onGeometryChanged,
  336. this
  337. )
  338. );
  339. }
  340. addedObjects.removeAll();
  341. removedObjects.removeAll();
  342. changedObjects.removeAll();
  343. let isUpdated = true;
  344. const batches = this._batches;
  345. const length = batches.length;
  346. for (i = 0; i < length; i++) {
  347. isUpdated = batches[i].update(time) && isUpdated;
  348. }
  349. return isUpdated;
  350. };
  351. const getBoundingSphereArrayScratch = [];
  352. const getBoundingSphereBoundingSphereScratch = new BoundingSphere();
  353. /**
  354. * Computes a bounding sphere which encloses the visualization produced for the specified entity.
  355. * The bounding sphere is in the fixed frame of the scene's globe.
  356. *
  357. * @param {Entity} entity The entity whose bounding sphere to compute.
  358. * @param {BoundingSphere} result The bounding sphere onto which to store the result.
  359. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere,
  360. * BoundingSphereState.PENDING if the result is still being computed, or
  361. * BoundingSphereState.FAILED if the entity has no visualization in the current scene.
  362. * @private
  363. */
  364. GeometryVisualizer.prototype.getBoundingSphere = function (entity, result) {
  365. //>>includeStart('debug', pragmas.debug);
  366. Check.defined("entity", entity);
  367. Check.defined("result", result);
  368. //>>includeEnd('debug');
  369. const boundingSpheres = getBoundingSphereArrayScratch;
  370. const tmp = getBoundingSphereBoundingSphereScratch;
  371. let count = 0;
  372. let state = BoundingSphereState.DONE;
  373. const batches = this._batches;
  374. const batchesLength = batches.length;
  375. const id = entity.id;
  376. const updaters = this._updaterSets.get(id).updaters;
  377. for (let j = 0; j < updaters.length; j++) {
  378. const updater = updaters[j];
  379. for (let i = 0; i < batchesLength; i++) {
  380. state = batches[i].getBoundingSphere(updater, tmp);
  381. if (state === BoundingSphereState.PENDING) {
  382. return BoundingSphereState.PENDING;
  383. } else if (state === BoundingSphereState.DONE) {
  384. boundingSpheres[count] = BoundingSphere.clone(
  385. tmp,
  386. boundingSpheres[count]
  387. );
  388. count++;
  389. }
  390. }
  391. }
  392. if (count === 0) {
  393. return BoundingSphereState.FAILED;
  394. }
  395. boundingSpheres.length = count;
  396. BoundingSphere.fromBoundingSpheres(boundingSpheres, result);
  397. return BoundingSphereState.DONE;
  398. };
  399. /**
  400. * Returns true if this object was destroyed; otherwise, false.
  401. *
  402. * @returns {boolean} True if this object was destroyed; otherwise, false.
  403. */
  404. GeometryVisualizer.prototype.isDestroyed = function () {
  405. return false;
  406. };
  407. /**
  408. * Removes and destroys all primitives created by this instance.
  409. */
  410. GeometryVisualizer.prototype.destroy = function () {
  411. this._entityCollection.collectionChanged.removeEventListener(
  412. GeometryVisualizer.prototype._onCollectionChanged,
  413. this
  414. );
  415. this._addedObjects.removeAll();
  416. this._removedObjects.removeAll();
  417. let i;
  418. const batches = this._batches;
  419. let length = batches.length;
  420. for (i = 0; i < length; i++) {
  421. batches[i].removeAllPrimitives();
  422. }
  423. const subscriptions = this._subscriptions.values;
  424. length = subscriptions.length;
  425. for (i = 0; i < length; i++) {
  426. subscriptions[i]();
  427. }
  428. this._subscriptions.removeAll();
  429. const updaterSets = this._updaterSets.values;
  430. length = updaterSets.length;
  431. for (i = 0; i < length; i++) {
  432. updaterSets[i].destroy();
  433. }
  434. this._updaterSets.removeAll();
  435. return destroyObject(this);
  436. };
  437. /**
  438. * @private
  439. */
  440. GeometryVisualizer.prototype._removeUpdater = function (updater) {
  441. //We don't keep track of which batch an updater is in, so just remove it from all of them.
  442. const batches = this._batches;
  443. const length = batches.length;
  444. for (let i = 0; i < length; i++) {
  445. batches[i].remove(updater);
  446. }
  447. };
  448. /**
  449. * @private
  450. */
  451. GeometryVisualizer.prototype._insertUpdaterIntoBatch = function (
  452. time,
  453. updater
  454. ) {
  455. if (updater.isDynamic) {
  456. this._dynamicBatch.add(time, updater);
  457. return;
  458. }
  459. let shadows;
  460. if (updater.outlineEnabled || updater.fillEnabled) {
  461. shadows = updater.shadowsProperty.getValue(time);
  462. }
  463. const numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES;
  464. if (updater.outlineEnabled) {
  465. if (defined(updater.terrainOffsetProperty)) {
  466. this._outlineBatches[numberOfShadowModes + shadows].add(time, updater);
  467. } else {
  468. this._outlineBatches[shadows].add(time, updater);
  469. }
  470. }
  471. if (updater.fillEnabled) {
  472. if (updater.onTerrain) {
  473. const classificationType = updater.classificationTypeProperty.getValue(
  474. time
  475. );
  476. if (updater.fillMaterialProperty instanceof ColorMaterialProperty) {
  477. this._groundColorBatches[classificationType].add(time, updater);
  478. } else {
  479. // If unsupported, updater will not be on terrain.
  480. this._groundMaterialBatches[classificationType].add(time, updater);
  481. }
  482. } else if (updater.isClosed) {
  483. if (updater.fillMaterialProperty instanceof ColorMaterialProperty) {
  484. if (defined(updater.terrainOffsetProperty)) {
  485. this._closedColorBatches[numberOfShadowModes + shadows].add(
  486. time,
  487. updater
  488. );
  489. } else {
  490. this._closedColorBatches[shadows].add(time, updater);
  491. }
  492. } else if (defined(updater.terrainOffsetProperty)) {
  493. this._closedMaterialBatches[numberOfShadowModes + shadows].add(
  494. time,
  495. updater
  496. );
  497. } else {
  498. this._closedMaterialBatches[shadows].add(time, updater);
  499. }
  500. } else if (updater.fillMaterialProperty instanceof ColorMaterialProperty) {
  501. if (defined(updater.terrainOffsetProperty)) {
  502. this._openColorBatches[numberOfShadowModes + shadows].add(
  503. time,
  504. updater
  505. );
  506. } else {
  507. this._openColorBatches[shadows].add(time, updater);
  508. }
  509. } else if (defined(updater.terrainOffsetProperty)) {
  510. this._openMaterialBatches[numberOfShadowModes + shadows].add(
  511. time,
  512. updater
  513. );
  514. } else {
  515. this._openMaterialBatches[shadows].add(time, updater);
  516. }
  517. }
  518. };
  519. /**
  520. * @private
  521. */
  522. GeometryVisualizer._onGeometryChanged = function (updater) {
  523. const removedObjects = this._removedObjects;
  524. const changedObjects = this._changedObjects;
  525. const entity = updater.entity;
  526. const id = entity.id;
  527. if (!defined(removedObjects.get(id)) && !defined(changedObjects.get(id))) {
  528. changedObjects.set(id, entity);
  529. }
  530. };
  531. /**
  532. * @private
  533. */
  534. GeometryVisualizer.prototype._onCollectionChanged = function (
  535. entityCollection,
  536. added,
  537. removed
  538. ) {
  539. const addedObjects = this._addedObjects;
  540. const removedObjects = this._removedObjects;
  541. const changedObjects = this._changedObjects;
  542. let i;
  543. let id;
  544. let entity;
  545. for (i = removed.length - 1; i > -1; i--) {
  546. entity = removed[i];
  547. id = entity.id;
  548. if (!addedObjects.remove(id)) {
  549. removedObjects.set(id, entity);
  550. changedObjects.remove(id);
  551. }
  552. }
  553. for (i = added.length - 1; i > -1; i--) {
  554. entity = added[i];
  555. id = entity.id;
  556. if (removedObjects.remove(id)) {
  557. changedObjects.set(id, entity);
  558. } else {
  559. addedObjects.set(id, entity);
  560. }
  561. }
  562. };
  563. export default GeometryVisualizer;