GeometryVisualizer.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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. var emptyArray = [];
  33. var geometryUpdaters = [BoxGeometryUpdater, CylinderGeometryUpdater, CorridorGeometryUpdater, EllipseGeometryUpdater, EllipsoidGeometryUpdater, PlaneGeometryUpdater,
  34. PolygonGeometryUpdater, PolylineVolumeGeometryUpdater, RectangleGeometryUpdater, WallGeometryUpdater];
  35. function GeometryUpdaterSet(entity, scene) {
  36. this.entity = entity;
  37. this.scene = scene;
  38. var updaters = new Array(geometryUpdaters.length);
  39. var geometryChanged = new Event();
  40. function raiseEvent(geometry) {
  41. geometryChanged.raiseEvent(geometry);
  42. }
  43. var eventHelper = new EventHelper();
  44. for (var i = 0; i < updaters.length; i++) {
  45. var updater = new geometryUpdaters[i](entity, scene);
  46. eventHelper.add(updater.geometryChanged, raiseEvent);
  47. updaters[i] = updater;
  48. }
  49. this.updaters = updaters;
  50. this.geometryChanged = geometryChanged;
  51. this.eventHelper = eventHelper;
  52. this._removeEntitySubscription = entity.definitionChanged.addEventListener(GeometryUpdaterSet.prototype._onEntityPropertyChanged, this);
  53. }
  54. GeometryUpdaterSet.prototype._onEntityPropertyChanged = function(entity, propertyName, newValue, oldValue) {
  55. var updaters = this.updaters;
  56. for (var i = 0; i < updaters.length; i++) {
  57. updaters[i]._onEntityPropertyChanged(entity, propertyName, newValue, oldValue);
  58. }
  59. };
  60. GeometryUpdaterSet.prototype.forEach = function (callback) {
  61. var updaters = this.updaters;
  62. for (var i = 0; i < updaters.length; i++) {
  63. callback(updaters[i]);
  64. }
  65. };
  66. GeometryUpdaterSet.prototype.destroy = function() {
  67. this.eventHelper.removeAll();
  68. var updaters = this.updaters;
  69. for (var i = 0; i < updaters.length; i++) {
  70. updaters[i].destroy();
  71. }
  72. this._removeEntitySubscription();
  73. destroyObject(this);
  74. };
  75. /**
  76. * A general purpose visualizer for geometry represented by {@link Primitive} instances.
  77. * @alias GeometryVisualizer
  78. * @constructor
  79. *
  80. * @param {Scene} scene The scene the primitives will be rendered in.
  81. * @param {EntityCollection} entityCollection The entityCollection to visualize.
  82. * @param {PrimitiveCollection} [primitives=scene.primitives] A collection to add primitives related to the entities
  83. * @param {PrimitiveCollection} [groundPrimitives=scene.groundPrimitives] A collection to add ground primitives related to the entities
  84. */
  85. function GeometryVisualizer(scene, entityCollection, primitives, groundPrimitives) {
  86. //>>includeStart('debug', pragmas.debug);
  87. Check.defined('scene', scene);
  88. Check.defined('entityCollection', entityCollection);
  89. //>>includeEnd('debug');
  90. primitives = defaultValue(primitives, scene.primitives);
  91. groundPrimitives = defaultValue(groundPrimitives, scene.groundPrimitives);
  92. this._scene = scene;
  93. this._primitives = primitives;
  94. this._groundPrimitives = groundPrimitives;
  95. this._entityCollection = undefined;
  96. this._addedObjects = new AssociativeArray();
  97. this._removedObjects = new AssociativeArray();
  98. this._changedObjects = new AssociativeArray();
  99. var numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES;
  100. this._outlineBatches = new Array(numberOfShadowModes*2);
  101. this._closedColorBatches = new Array(numberOfShadowModes*2);
  102. this._closedMaterialBatches = new Array(numberOfShadowModes*2);
  103. this._openColorBatches = new Array(numberOfShadowModes*2);
  104. this._openMaterialBatches = new Array(numberOfShadowModes*2);
  105. var supportsMaterialsforEntitiesOnTerrain = Entity.supportsMaterialsforEntitiesOnTerrain(scene);
  106. this._supportsMaterialsforEntitiesOnTerrain = supportsMaterialsforEntitiesOnTerrain;
  107. var i;
  108. for (i = 0; i < numberOfShadowModes; ++i) {
  109. this._outlineBatches[i] = new StaticOutlineGeometryBatch(primitives, scene, i, false);
  110. this._outlineBatches[numberOfShadowModes + i] = new StaticOutlineGeometryBatch(primitives, scene, i, true);
  111. this._closedColorBatches[i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, true, i, true);
  112. this._closedColorBatches[numberOfShadowModes + i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, true, i, false);
  113. this._closedMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, true, i, true);
  114. this._closedMaterialBatches[numberOfShadowModes + i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, true, i, false);
  115. this._openColorBatches[i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, false, i, true);
  116. this._openColorBatches[numberOfShadowModes + i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, false, i, false);
  117. this._openMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, false, i, true);
  118. this._openMaterialBatches[numberOfShadowModes + i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, false, i, false);
  119. }
  120. var numberOfClassificationTypes = ClassificationType.NUMBER_OF_CLASSIFICATION_TYPES;
  121. var groundColorBatches = new Array(numberOfClassificationTypes);
  122. var groundMaterialBatches = [];
  123. if (supportsMaterialsforEntitiesOnTerrain) {
  124. for (i = 0; i < numberOfClassificationTypes; ++i) {
  125. groundMaterialBatches.push(new StaticGroundGeometryPerMaterialBatch(groundPrimitives, i, MaterialAppearance));
  126. groundColorBatches[i] = new StaticGroundGeometryPerMaterialBatch(groundPrimitives, i, PerInstanceColorAppearance);
  127. }
  128. } else {
  129. for (i = 0; i < numberOfClassificationTypes; ++i) {
  130. groundColorBatches[i] = new StaticGroundGeometryColorBatch(groundPrimitives, i);
  131. }
  132. }
  133. this._groundColorBatches = groundColorBatches;
  134. this._groundMaterialBatches = groundMaterialBatches;
  135. this._dynamicBatch = new DynamicGeometryBatch(primitives, groundPrimitives);
  136. this._batches = this._outlineBatches.concat(this._closedColorBatches, this._closedMaterialBatches, this._openColorBatches, this._openMaterialBatches, this._groundColorBatches, this._groundMaterialBatches, this._dynamicBatch);
  137. this._subscriptions = new AssociativeArray();
  138. this._updaterSets = new AssociativeArray();
  139. this._entityCollection = entityCollection;
  140. entityCollection.collectionChanged.addEventListener(GeometryVisualizer.prototype._onCollectionChanged, this);
  141. this._onCollectionChanged(entityCollection, entityCollection.values, emptyArray);
  142. }
  143. /**
  144. * Updates all of the primitives created by this visualizer to match their
  145. * Entity counterpart at the given time.
  146. *
  147. * @param {JulianDate} time The time to update to.
  148. * @returns {Boolean} True if the visualizer successfully updated to the provided time,
  149. * false if the visualizer is waiting for asynchronous primitives to be created.
  150. */
  151. GeometryVisualizer.prototype.update = function(time) {
  152. //>>includeStart('debug', pragmas.debug);
  153. Check.defined('time', time);
  154. //>>includeEnd('debug');
  155. var addedObjects = this._addedObjects;
  156. var added = addedObjects.values;
  157. var removedObjects = this._removedObjects;
  158. var removed = removedObjects.values;
  159. var changedObjects = this._changedObjects;
  160. var changed = changedObjects.values;
  161. var i;
  162. var entity;
  163. var id;
  164. var updaterSet;
  165. var that = this;
  166. for (i = changed.length - 1; i > -1; i--) {
  167. entity = changed[i];
  168. id = entity.id;
  169. updaterSet = this._updaterSets.get(id);
  170. //If in a single update, an entity gets removed and a new instance
  171. //re-added with the same id, the updater no longer tracks the
  172. //correct entity, we need to both remove the old one and
  173. //add the new one, which is done by pushing the entity
  174. //onto the removed/added lists.
  175. if (updaterSet.entity === entity) {
  176. updaterSet.forEach(function(updater) {
  177. that._removeUpdater(updater);
  178. that._insertUpdaterIntoBatch(time, updater);
  179. });
  180. } else {
  181. removed.push(entity);
  182. added.push(entity);
  183. }
  184. }
  185. for (i = removed.length - 1; i > -1; i--) {
  186. entity = removed[i];
  187. id = entity.id;
  188. updaterSet = this._updaterSets.get(id);
  189. updaterSet.forEach(this._removeUpdater.bind(this));
  190. updaterSet.destroy();
  191. this._updaterSets.remove(id);
  192. this._subscriptions.get(id)();
  193. this._subscriptions.remove(id);
  194. }
  195. for (i = added.length - 1; i > -1; i--) {
  196. entity = added[i];
  197. id = entity.id;
  198. updaterSet = new GeometryUpdaterSet(entity, this._scene);
  199. this._updaterSets.set(id, updaterSet);
  200. updaterSet.forEach(function(updater) {
  201. that._insertUpdaterIntoBatch(time, updater);
  202. });
  203. this._subscriptions.set(id, updaterSet.geometryChanged.addEventListener(GeometryVisualizer._onGeometryChanged, this));
  204. }
  205. addedObjects.removeAll();
  206. removedObjects.removeAll();
  207. changedObjects.removeAll();
  208. var isUpdated = true;
  209. var batches = this._batches;
  210. var length = batches.length;
  211. for (i = 0; i < length; i++) {
  212. isUpdated = batches[i].update(time) && isUpdated;
  213. }
  214. return isUpdated;
  215. };
  216. var getBoundingSphereArrayScratch = [];
  217. var getBoundingSphereBoundingSphereScratch = new BoundingSphere();
  218. /**
  219. * Computes a bounding sphere which encloses the visualization produced for the specified entity.
  220. * The bounding sphere is in the fixed frame of the scene's globe.
  221. *
  222. * @param {Entity} entity The entity whose bounding sphere to compute.
  223. * @param {BoundingSphere} result The bounding sphere onto which to store the result.
  224. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere,
  225. * BoundingSphereState.PENDING if the result is still being computed, or
  226. * BoundingSphereState.FAILED if the entity has no visualization in the current scene.
  227. * @private
  228. */
  229. GeometryVisualizer.prototype.getBoundingSphere = function(entity, result) {
  230. //>>includeStart('debug', pragmas.debug);
  231. Check.defined('entity', entity);
  232. Check.defined('result', result);
  233. //>>includeEnd('debug');
  234. var boundingSpheres = getBoundingSphereArrayScratch;
  235. var tmp = getBoundingSphereBoundingSphereScratch;
  236. var count = 0;
  237. var state = BoundingSphereState.DONE;
  238. var batches = this._batches;
  239. var batchesLength = batches.length;
  240. var id = entity.id;
  241. var updaters = this._updaterSets.get(id).updaters;
  242. for (var j = 0; j < updaters.length; j++) {
  243. var updater = updaters[j];
  244. for (var i = 0; i < batchesLength; i++) {
  245. state = batches[i].getBoundingSphere(updater, tmp);
  246. if (state === BoundingSphereState.PENDING) {
  247. return BoundingSphereState.PENDING;
  248. } else if (state === BoundingSphereState.DONE) {
  249. boundingSpheres[count] = BoundingSphere.clone(tmp, boundingSpheres[count]);
  250. count++;
  251. }
  252. }
  253. }
  254. if (count === 0) {
  255. return BoundingSphereState.FAILED;
  256. }
  257. boundingSpheres.length = count;
  258. BoundingSphere.fromBoundingSpheres(boundingSpheres, result);
  259. return BoundingSphereState.DONE;
  260. };
  261. /**
  262. * Returns true if this object was destroyed; otherwise, false.
  263. *
  264. * @returns {Boolean} True if this object was destroyed; otherwise, false.
  265. */
  266. GeometryVisualizer.prototype.isDestroyed = function() {
  267. return false;
  268. };
  269. /**
  270. * Removes and destroys all primitives created by this instance.
  271. */
  272. GeometryVisualizer.prototype.destroy = function() {
  273. this._entityCollection.collectionChanged.removeEventListener(GeometryVisualizer.prototype._onCollectionChanged, this);
  274. this._addedObjects.removeAll();
  275. this._removedObjects.removeAll();
  276. var i;
  277. var batches = this._batches;
  278. var length = batches.length;
  279. for (i = 0; i < length; i++) {
  280. batches[i].removeAllPrimitives();
  281. }
  282. var subscriptions = this._subscriptions.values;
  283. length = subscriptions.length;
  284. for (i = 0; i < length; i++) {
  285. subscriptions[i]();
  286. }
  287. this._subscriptions.removeAll();
  288. var updaterSets = this._updaterSets.values;
  289. length = updaterSets.length;
  290. for (i = 0; i < length; i++) {
  291. updaterSets[i].destroy();
  292. }
  293. this._updaterSets.removeAll();
  294. return destroyObject(this);
  295. };
  296. /**
  297. * @private
  298. */
  299. GeometryVisualizer.prototype._removeUpdater = function(updater) {
  300. //We don't keep track of which batch an updater is in, so just remove it from all of them.
  301. var batches = this._batches;
  302. var length = batches.length;
  303. for (var i = 0; i < length; i++) {
  304. batches[i].remove(updater);
  305. }
  306. };
  307. /**
  308. * @private
  309. */
  310. GeometryVisualizer.prototype._insertUpdaterIntoBatch = function(time, updater) {
  311. if (updater.isDynamic) {
  312. this._dynamicBatch.add(time, updater);
  313. return;
  314. }
  315. var shadows;
  316. if (updater.outlineEnabled || updater.fillEnabled) {
  317. shadows = updater.shadowsProperty.getValue(time);
  318. }
  319. var numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES;
  320. if (updater.outlineEnabled) {
  321. if (defined(updater.terrainOffsetProperty)) {
  322. this._outlineBatches[numberOfShadowModes + shadows].add(time, updater);
  323. } else {
  324. this._outlineBatches[shadows].add(time, updater);
  325. }
  326. }
  327. if (updater.fillEnabled) {
  328. if (updater.onTerrain) {
  329. var classificationType = updater.classificationTypeProperty.getValue(time);
  330. if (updater.fillMaterialProperty instanceof ColorMaterialProperty) {
  331. this._groundColorBatches[classificationType].add(time, updater);
  332. } else {
  333. // If unsupported, updater will not be on terrain.
  334. this._groundMaterialBatches[classificationType].add(time, updater);
  335. }
  336. } else if (updater.isClosed) {
  337. if (updater.fillMaterialProperty instanceof ColorMaterialProperty) {
  338. if (defined(updater.terrainOffsetProperty)) {
  339. this._closedColorBatches[numberOfShadowModes + shadows].add(time, updater);
  340. } else {
  341. this._closedColorBatches[shadows].add(time, updater);
  342. }
  343. } else if (defined(updater.terrainOffsetProperty)) {
  344. this._closedMaterialBatches[numberOfShadowModes + shadows].add(time, updater);
  345. } else {
  346. this._closedMaterialBatches[shadows].add(time, updater);
  347. }
  348. } else if (updater.fillMaterialProperty instanceof ColorMaterialProperty) {
  349. if (defined(updater.terrainOffsetProperty)) {
  350. this._openColorBatches[numberOfShadowModes + shadows].add(time, updater);
  351. } else {
  352. this._openColorBatches[shadows].add(time, updater);
  353. }
  354. } else if (defined(updater.terrainOffsetProperty)) {
  355. this._openMaterialBatches[numberOfShadowModes + shadows].add(time, updater);
  356. } else {
  357. this._openMaterialBatches[shadows].add(time, updater);
  358. }
  359. }
  360. };
  361. /**
  362. * @private
  363. */
  364. GeometryVisualizer._onGeometryChanged = function(updater) {
  365. var removedObjects = this._removedObjects;
  366. var changedObjects = this._changedObjects;
  367. var entity = updater.entity;
  368. var id = entity.id;
  369. if (!defined(removedObjects.get(id)) && !defined(changedObjects.get(id))) {
  370. changedObjects.set(id, entity);
  371. }
  372. };
  373. /**
  374. * @private
  375. */
  376. GeometryVisualizer.prototype._onCollectionChanged = function(entityCollection, added, removed) {
  377. var addedObjects = this._addedObjects;
  378. var removedObjects = this._removedObjects;
  379. var changedObjects = this._changedObjects;
  380. var i;
  381. var id;
  382. var entity;
  383. for (i = removed.length - 1; i > -1; i--) {
  384. entity = removed[i];
  385. id = entity.id;
  386. if (!addedObjects.remove(id)) {
  387. removedObjects.set(id, entity);
  388. changedObjects.remove(id);
  389. }
  390. }
  391. for (i = added.length - 1; i > -1; i--) {
  392. entity = added[i];
  393. id = entity.id;
  394. if (removedObjects.remove(id)) {
  395. changedObjects.set(id, entity);
  396. } else {
  397. addedObjects.set(id, entity);
  398. }
  399. }
  400. };
  401. export default GeometryVisualizer;