PolylineCollection.js 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import Cartesian2 from "../Core/Cartesian2.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import Cartesian4 from "../Core/Cartesian4.js";
  5. import Cartographic from "../Core/Cartographic.js";
  6. import Color from "../Core/Color.js";
  7. import combine from "../Core/combine.js";
  8. import ComponentDatatype from "../Core/ComponentDatatype.js";
  9. import defaultValue from "../Core/defaultValue.js";
  10. import defined from "../Core/defined.js";
  11. import destroyObject from "../Core/destroyObject.js";
  12. import DeveloperError from "../Core/DeveloperError.js";
  13. import EncodedCartesian3 from "../Core/EncodedCartesian3.js";
  14. import FeatureDetection from "../Core/FeatureDetection.js";
  15. import IndexDatatype from "../Core/IndexDatatype.js";
  16. import Intersect from "../Core/Intersect.js";
  17. import CesiumMath from "../Core/Math.js";
  18. import Matrix4 from "../Core/Matrix4.js";
  19. import Plane from "../Core/Plane.js";
  20. import RuntimeError from "../Core/RuntimeError.js";
  21. import Buffer from "../Renderer/Buffer.js";
  22. import BufferUsage from "../Renderer/BufferUsage.js";
  23. import ContextLimits from "../Renderer/ContextLimits.js";
  24. import DrawCommand from "../Renderer/DrawCommand.js";
  25. import Pass from "../Renderer/Pass.js";
  26. import RenderState from "../Renderer/RenderState.js";
  27. import ShaderProgram from "../Renderer/ShaderProgram.js";
  28. import ShaderSource from "../Renderer/ShaderSource.js";
  29. import Texture from "../Renderer/Texture.js";
  30. import VertexArray from "../Renderer/VertexArray.js";
  31. import PolylineCommon from "../Shaders/PolylineCommon.js";
  32. import PolylineFS from "../Shaders/PolylineFS.js";
  33. import PolylineVS from "../Shaders/PolylineVS.js";
  34. import BatchTable from "./BatchTable.js";
  35. import BlendingState from "./BlendingState.js";
  36. import Material from "./Material.js";
  37. import Polyline from "./Polyline.js";
  38. import SceneMode from "./SceneMode.js";
  39. const SHOW_INDEX = Polyline.SHOW_INDEX;
  40. const WIDTH_INDEX = Polyline.WIDTH_INDEX;
  41. const POSITION_INDEX = Polyline.POSITION_INDEX;
  42. const MATERIAL_INDEX = Polyline.MATERIAL_INDEX;
  43. //POSITION_SIZE_INDEX is needed for when the polyline's position array changes size.
  44. //When it does, we need to recreate the indicesBuffer.
  45. const POSITION_SIZE_INDEX = Polyline.POSITION_SIZE_INDEX;
  46. const DISTANCE_DISPLAY_CONDITION = Polyline.DISTANCE_DISPLAY_CONDITION;
  47. const NUMBER_OF_PROPERTIES = Polyline.NUMBER_OF_PROPERTIES;
  48. const attributeLocations = {
  49. texCoordExpandAndBatchIndex: 0,
  50. position3DHigh: 1,
  51. position3DLow: 2,
  52. position2DHigh: 3,
  53. position2DLow: 4,
  54. prevPosition3DHigh: 5,
  55. prevPosition3DLow: 6,
  56. prevPosition2DHigh: 7,
  57. prevPosition2DLow: 8,
  58. nextPosition3DHigh: 9,
  59. nextPosition3DLow: 10,
  60. nextPosition2DHigh: 11,
  61. nextPosition2DLow: 12,
  62. };
  63. /**
  64. * A renderable collection of polylines.
  65. * <br /><br />
  66. * <div align="center">
  67. * <img src="Images/Polyline.png" width="400" height="300" /><br />
  68. * Example polylines
  69. * </div>
  70. * <br /><br />
  71. * Polylines are added and removed from the collection using {@link PolylineCollection#add}
  72. * and {@link PolylineCollection#remove}.
  73. *
  74. * @alias PolylineCollection
  75. * @constructor
  76. *
  77. * @param {Object} [options] Object with the following properties:
  78. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms each polyline from model to world coordinates.
  79. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
  80. * @param {Boolean} [options.show=true] Determines if the polylines in the collection will be shown.
  81. *
  82. * @performance For best performance, prefer a few collections, each with many polylines, to
  83. * many collections with only a few polylines each. Organize collections so that polylines
  84. * with the same update frequency are in the same collection, i.e., polylines that do not
  85. * change should be in one collection; polylines that change every frame should be in another
  86. * collection; and so on.
  87. *
  88. * @see PolylineCollection#add
  89. * @see PolylineCollection#remove
  90. * @see Polyline
  91. * @see LabelCollection
  92. *
  93. * @example
  94. * // Create a polyline collection with two polylines
  95. * const polylines = new Cesium.PolylineCollection();
  96. * polylines.add({
  97. * positions : Cesium.Cartesian3.fromDegreesArray([
  98. * -75.10, 39.57,
  99. * -77.02, 38.53,
  100. * -80.50, 35.14,
  101. * -80.12, 25.46]),
  102. * width : 2
  103. * });
  104. *
  105. * polylines.add({
  106. * positions : Cesium.Cartesian3.fromDegreesArray([
  107. * -73.10, 37.57,
  108. * -75.02, 36.53,
  109. * -78.50, 33.14,
  110. * -78.12, 23.46]),
  111. * width : 4
  112. * });
  113. */
  114. function PolylineCollection(options) {
  115. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  116. /**
  117. * Determines if polylines in this collection will be shown.
  118. *
  119. * @type {Boolean}
  120. * @default true
  121. */
  122. this.show = defaultValue(options.show, true);
  123. /**
  124. * The 4x4 transformation matrix that transforms each polyline in this collection from model to world coordinates.
  125. * When this is the identity matrix, the polylines are drawn in world coordinates, i.e., Earth's WGS84 coordinates.
  126. * Local reference frames can be used by providing a different transformation matrix, like that returned
  127. * by {@link Transforms.eastNorthUpToFixedFrame}.
  128. *
  129. * @type {Matrix4}
  130. * @default {@link Matrix4.IDENTITY}
  131. */
  132. this.modelMatrix = Matrix4.clone(
  133. defaultValue(options.modelMatrix, Matrix4.IDENTITY)
  134. );
  135. this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY);
  136. /**
  137. * This property is for debugging only; it is not for production use nor is it optimized.
  138. * <p>
  139. * Draws the bounding sphere for each draw command in the primitive.
  140. * </p>
  141. *
  142. * @type {Boolean}
  143. *
  144. * @default false
  145. */
  146. this.debugShowBoundingVolume = defaultValue(
  147. options.debugShowBoundingVolume,
  148. false
  149. );
  150. this._opaqueRS = undefined;
  151. this._translucentRS = undefined;
  152. this._colorCommands = [];
  153. this._polylinesUpdated = false;
  154. this._polylinesRemoved = false;
  155. this._createVertexArray = false;
  156. this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES);
  157. this._polylines = [];
  158. this._polylineBuckets = {};
  159. // The buffer usage is determined based on the usage of the attribute over time.
  160. this._positionBufferUsage = {
  161. bufferUsage: BufferUsage.STATIC_DRAW,
  162. frameCount: 0,
  163. };
  164. this._mode = undefined;
  165. this._polylinesToUpdate = [];
  166. this._vertexArrays = [];
  167. this._positionBuffer = undefined;
  168. this._texCoordExpandAndBatchIndexBuffer = undefined;
  169. this._batchTable = undefined;
  170. this._createBatchTable = false;
  171. // Only used by Vector3DTilePoints
  172. this._useHighlightColor = false;
  173. this._highlightColor = Color.clone(Color.WHITE);
  174. const that = this;
  175. this._uniformMap = {
  176. u_highlightColor: function () {
  177. return that._highlightColor;
  178. },
  179. };
  180. }
  181. Object.defineProperties(PolylineCollection.prototype, {
  182. /**
  183. * Returns the number of polylines in this collection. This is commonly used with
  184. * {@link PolylineCollection#get} to iterate over all the polylines
  185. * in the collection.
  186. * @memberof PolylineCollection.prototype
  187. * @type {Number}
  188. */
  189. length: {
  190. get: function () {
  191. removePolylines(this);
  192. return this._polylines.length;
  193. },
  194. },
  195. });
  196. /**
  197. * Creates and adds a polyline with the specified initial properties to the collection.
  198. * The added polyline is returned so it can be modified or removed from the collection later.
  199. *
  200. * @param {Object}[options] A template describing the polyline's properties as shown in Example 1.
  201. * @returns {Polyline} The polyline that was added to the collection.
  202. *
  203. * @performance After calling <code>add</code>, {@link PolylineCollection#update} is called and
  204. * the collection's vertex buffer is rewritten - an <code>O(n)</code> operation that also incurs CPU to GPU overhead.
  205. * For best performance, add as many polylines as possible before calling <code>update</code>.
  206. *
  207. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  208. *
  209. *
  210. * @example
  211. * // Example 1: Add a polyline, specifying all the default values.
  212. * const p = polylines.add({
  213. * show : true,
  214. * positions : ellipsoid.cartographicArrayToCartesianArray([
  215. Cesium.Cartographic.fromDegrees(-75.10, 39.57),
  216. Cesium.Cartographic.fromDegrees(-77.02, 38.53)]),
  217. * width : 1
  218. * });
  219. *
  220. * @see PolylineCollection#remove
  221. * @see PolylineCollection#removeAll
  222. * @see PolylineCollection#update
  223. */
  224. PolylineCollection.prototype.add = function (options) {
  225. const p = new Polyline(options, this);
  226. p._index = this._polylines.length;
  227. this._polylines.push(p);
  228. this._createVertexArray = true;
  229. this._createBatchTable = true;
  230. return p;
  231. };
  232. /**
  233. * Removes a polyline from the collection.
  234. *
  235. * @param {Polyline} polyline The polyline to remove.
  236. * @returns {Boolean} <code>true</code> if the polyline was removed; <code>false</code> if the polyline was not found in the collection.
  237. *
  238. * @performance After calling <code>remove</code>, {@link PolylineCollection#update} is called and
  239. * the collection's vertex buffer is rewritten - an <code>O(n)</code> operation that also incurs CPU to GPU overhead.
  240. * For best performance, remove as many polylines as possible before calling <code>update</code>.
  241. * If you intend to temporarily hide a polyline, it is usually more efficient to call
  242. * {@link Polyline#show} instead of removing and re-adding the polyline.
  243. *
  244. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  245. *
  246. *
  247. * @example
  248. * const p = polylines.add(...);
  249. * polylines.remove(p); // Returns true
  250. *
  251. * @see PolylineCollection#add
  252. * @see PolylineCollection#removeAll
  253. * @see PolylineCollection#update
  254. * @see Polyline#show
  255. */
  256. PolylineCollection.prototype.remove = function (polyline) {
  257. if (this.contains(polyline)) {
  258. this._polylinesRemoved = true;
  259. this._createVertexArray = true;
  260. this._createBatchTable = true;
  261. if (defined(polyline._bucket)) {
  262. const bucket = polyline._bucket;
  263. bucket.shaderProgram =
  264. bucket.shaderProgram && bucket.shaderProgram.destroy();
  265. }
  266. polyline._destroy();
  267. return true;
  268. }
  269. return false;
  270. };
  271. /**
  272. * Removes all polylines from the collection.
  273. *
  274. * @performance <code>O(n)</code>. It is more efficient to remove all the polylines
  275. * from a collection and then add new ones than to create a new collection entirely.
  276. *
  277. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  278. *
  279. *
  280. * @example
  281. * polylines.add(...);
  282. * polylines.add(...);
  283. * polylines.removeAll();
  284. *
  285. * @see PolylineCollection#add
  286. * @see PolylineCollection#remove
  287. * @see PolylineCollection#update
  288. */
  289. PolylineCollection.prototype.removeAll = function () {
  290. releaseShaders(this);
  291. destroyPolylines(this);
  292. this._polylineBuckets = {};
  293. this._polylinesRemoved = false;
  294. this._polylines.length = 0;
  295. this._polylinesToUpdate.length = 0;
  296. this._createVertexArray = true;
  297. };
  298. /**
  299. * Determines if this collection contains the specified polyline.
  300. *
  301. * @param {Polyline} polyline The polyline to check for.
  302. * @returns {Boolean} true if this collection contains the polyline, false otherwise.
  303. *
  304. * @see PolylineCollection#get
  305. */
  306. PolylineCollection.prototype.contains = function (polyline) {
  307. return defined(polyline) && polyline._polylineCollection === this;
  308. };
  309. /**
  310. * Returns the polyline in the collection at the specified index. Indices are zero-based
  311. * and increase as polylines are added. Removing a polyline shifts all polylines after
  312. * it to the left, changing their indices. This function is commonly used with
  313. * {@link PolylineCollection#length} to iterate over all the polylines
  314. * in the collection.
  315. *
  316. * @param {Number} index The zero-based index of the polyline.
  317. * @returns {Polyline} The polyline at the specified index.
  318. *
  319. * @performance If polylines were removed from the collection and
  320. * {@link PolylineCollection#update} was not called, an implicit <code>O(n)</code>
  321. * operation is performed.
  322. *
  323. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  324. *
  325. * @example
  326. * // Toggle the show property of every polyline in the collection
  327. * const len = polylines.length;
  328. * for (let i = 0; i < len; ++i) {
  329. * const p = polylines.get(i);
  330. * p.show = !p.show;
  331. * }
  332. *
  333. * @see PolylineCollection#length
  334. */
  335. PolylineCollection.prototype.get = function (index) {
  336. //>>includeStart('debug', pragmas.debug);
  337. if (!defined(index)) {
  338. throw new DeveloperError("index is required.");
  339. }
  340. //>>includeEnd('debug');
  341. removePolylines(this);
  342. return this._polylines[index];
  343. };
  344. function createBatchTable(collection, context) {
  345. if (defined(collection._batchTable)) {
  346. collection._batchTable.destroy();
  347. }
  348. const attributes = [
  349. {
  350. functionName: "batchTable_getWidthAndShow",
  351. componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
  352. componentsPerAttribute: 2,
  353. },
  354. {
  355. functionName: "batchTable_getPickColor",
  356. componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
  357. componentsPerAttribute: 4,
  358. normalize: true,
  359. },
  360. {
  361. functionName: "batchTable_getCenterHigh",
  362. componentDatatype: ComponentDatatype.FLOAT,
  363. componentsPerAttribute: 3,
  364. },
  365. {
  366. functionName: "batchTable_getCenterLowAndRadius",
  367. componentDatatype: ComponentDatatype.FLOAT,
  368. componentsPerAttribute: 4,
  369. },
  370. {
  371. functionName: "batchTable_getDistanceDisplayCondition",
  372. componentDatatype: ComponentDatatype.FLOAT,
  373. componentsPerAttribute: 2,
  374. },
  375. ];
  376. collection._batchTable = new BatchTable(
  377. context,
  378. attributes,
  379. collection._polylines.length
  380. );
  381. }
  382. const scratchUpdatePolylineEncodedCartesian = new EncodedCartesian3();
  383. const scratchUpdatePolylineCartesian4 = new Cartesian4();
  384. const scratchNearFarCartesian2 = new Cartesian2();
  385. /**
  386. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  387. * get the draw commands needed to render this primitive.
  388. * <p>
  389. * Do not call this function directly. This is documented just to
  390. * list the exceptions that may be propagated when the scene is rendered:
  391. * </p>
  392. *
  393. * @exception {RuntimeError} Vertex texture fetch support is required to render primitives with per-instance attributes. The maximum number of vertex texture image units must be greater than zero.
  394. */
  395. PolylineCollection.prototype.update = function (frameState) {
  396. removePolylines(this);
  397. if (this._polylines.length === 0 || !this.show) {
  398. return;
  399. }
  400. updateMode(this, frameState);
  401. const context = frameState.context;
  402. const projection = frameState.mapProjection;
  403. let polyline;
  404. let properties = this._propertiesChanged;
  405. if (this._createBatchTable) {
  406. if (ContextLimits.maximumVertexTextureImageUnits === 0) {
  407. throw new RuntimeError(
  408. "Vertex texture fetch support is required to render polylines. The maximum number of vertex texture image units must be greater than zero."
  409. );
  410. }
  411. createBatchTable(this, context);
  412. this._createBatchTable = false;
  413. }
  414. if (this._createVertexArray || computeNewBuffersUsage(this)) {
  415. createVertexArrays(this, context, projection);
  416. } else if (this._polylinesUpdated) {
  417. // Polylines were modified, but no polylines were added or removed.
  418. const polylinesToUpdate = this._polylinesToUpdate;
  419. if (this._mode !== SceneMode.SCENE3D) {
  420. const updateLength = polylinesToUpdate.length;
  421. for (let i = 0; i < updateLength; ++i) {
  422. polyline = polylinesToUpdate[i];
  423. polyline.update();
  424. }
  425. }
  426. // if a polyline's positions size changes, we need to recreate the vertex arrays and vertex buffers because the indices will be different.
  427. // if a polyline's material changes, we need to recreate the VAOs and VBOs because they will be batched differently.
  428. if (properties[POSITION_SIZE_INDEX] || properties[MATERIAL_INDEX]) {
  429. createVertexArrays(this, context, projection);
  430. } else {
  431. const length = polylinesToUpdate.length;
  432. const polylineBuckets = this._polylineBuckets;
  433. for (let ii = 0; ii < length; ++ii) {
  434. polyline = polylinesToUpdate[ii];
  435. properties = polyline._propertiesChanged;
  436. const bucket = polyline._bucket;
  437. let index = 0;
  438. for (const x in polylineBuckets) {
  439. if (polylineBuckets.hasOwnProperty(x)) {
  440. if (polylineBuckets[x] === bucket) {
  441. if (properties[POSITION_INDEX]) {
  442. bucket.writeUpdate(
  443. index,
  444. polyline,
  445. this._positionBuffer,
  446. projection
  447. );
  448. }
  449. break;
  450. }
  451. index += polylineBuckets[x].lengthOfPositions;
  452. }
  453. }
  454. if (properties[SHOW_INDEX] || properties[WIDTH_INDEX]) {
  455. this._batchTable.setBatchedAttribute(
  456. polyline._index,
  457. 0,
  458. new Cartesian2(polyline._width, polyline._show)
  459. );
  460. }
  461. if (this._batchTable.attributes.length > 2) {
  462. if (properties[POSITION_INDEX] || properties[POSITION_SIZE_INDEX]) {
  463. const boundingSphere =
  464. frameState.mode === SceneMode.SCENE2D
  465. ? polyline._boundingVolume2D
  466. : polyline._boundingVolumeWC;
  467. const encodedCenter = EncodedCartesian3.fromCartesian(
  468. boundingSphere.center,
  469. scratchUpdatePolylineEncodedCartesian
  470. );
  471. const low = Cartesian4.fromElements(
  472. encodedCenter.low.x,
  473. encodedCenter.low.y,
  474. encodedCenter.low.z,
  475. boundingSphere.radius,
  476. scratchUpdatePolylineCartesian4
  477. );
  478. this._batchTable.setBatchedAttribute(
  479. polyline._index,
  480. 2,
  481. encodedCenter.high
  482. );
  483. this._batchTable.setBatchedAttribute(polyline._index, 3, low);
  484. }
  485. if (properties[DISTANCE_DISPLAY_CONDITION]) {
  486. const nearFarCartesian = scratchNearFarCartesian2;
  487. nearFarCartesian.x = 0.0;
  488. nearFarCartesian.y = Number.MAX_VALUE;
  489. const distanceDisplayCondition = polyline.distanceDisplayCondition;
  490. if (defined(distanceDisplayCondition)) {
  491. nearFarCartesian.x = distanceDisplayCondition.near;
  492. nearFarCartesian.y = distanceDisplayCondition.far;
  493. }
  494. this._batchTable.setBatchedAttribute(
  495. polyline._index,
  496. 4,
  497. nearFarCartesian
  498. );
  499. }
  500. }
  501. polyline._clean();
  502. }
  503. }
  504. polylinesToUpdate.length = 0;
  505. this._polylinesUpdated = false;
  506. }
  507. properties = this._propertiesChanged;
  508. for (let k = 0; k < NUMBER_OF_PROPERTIES; ++k) {
  509. properties[k] = 0;
  510. }
  511. let modelMatrix = Matrix4.IDENTITY;
  512. if (frameState.mode === SceneMode.SCENE3D) {
  513. modelMatrix = this.modelMatrix;
  514. }
  515. const pass = frameState.passes;
  516. const useDepthTest = frameState.morphTime !== 0.0;
  517. if (
  518. !defined(this._opaqueRS) ||
  519. this._opaqueRS.depthTest.enabled !== useDepthTest
  520. ) {
  521. this._opaqueRS = RenderState.fromCache({
  522. depthMask: useDepthTest,
  523. depthTest: {
  524. enabled: useDepthTest,
  525. },
  526. });
  527. }
  528. if (
  529. !defined(this._translucentRS) ||
  530. this._translucentRS.depthTest.enabled !== useDepthTest
  531. ) {
  532. this._translucentRS = RenderState.fromCache({
  533. blending: BlendingState.ALPHA_BLEND,
  534. depthMask: !useDepthTest,
  535. depthTest: {
  536. enabled: useDepthTest,
  537. },
  538. });
  539. }
  540. this._batchTable.update(frameState);
  541. if (pass.render || pass.pick) {
  542. const colorList = this._colorCommands;
  543. createCommandLists(this, frameState, colorList, modelMatrix);
  544. }
  545. };
  546. const boundingSphereScratch = new BoundingSphere();
  547. const boundingSphereScratch2 = new BoundingSphere();
  548. function createCommandLists(
  549. polylineCollection,
  550. frameState,
  551. commands,
  552. modelMatrix
  553. ) {
  554. const context = frameState.context;
  555. const commandList = frameState.commandList;
  556. const commandsLength = commands.length;
  557. let commandIndex = 0;
  558. let cloneBoundingSphere = true;
  559. const vertexArrays = polylineCollection._vertexArrays;
  560. const debugShowBoundingVolume = polylineCollection.debugShowBoundingVolume;
  561. const batchTable = polylineCollection._batchTable;
  562. const uniformCallback = batchTable.getUniformMapCallback();
  563. const length = vertexArrays.length;
  564. for (let m = 0; m < length; ++m) {
  565. const va = vertexArrays[m];
  566. const buckets = va.buckets;
  567. const bucketLength = buckets.length;
  568. for (let n = 0; n < bucketLength; ++n) {
  569. const bucketLocator = buckets[n];
  570. let offset = bucketLocator.offset;
  571. const sp = bucketLocator.bucket.shaderProgram;
  572. const polylines = bucketLocator.bucket.polylines;
  573. const polylineLength = polylines.length;
  574. let currentId;
  575. let currentMaterial;
  576. let count = 0;
  577. let command;
  578. let uniformMap;
  579. for (let s = 0; s < polylineLength; ++s) {
  580. const polyline = polylines[s];
  581. const mId = createMaterialId(polyline._material);
  582. if (mId !== currentId) {
  583. if (defined(currentId) && count > 0) {
  584. const translucent = currentMaterial.isTranslucent();
  585. if (commandIndex >= commandsLength) {
  586. command = new DrawCommand({
  587. owner: polylineCollection,
  588. });
  589. commands.push(command);
  590. } else {
  591. command = commands[commandIndex];
  592. }
  593. ++commandIndex;
  594. uniformMap = combine(
  595. uniformCallback(currentMaterial._uniforms),
  596. polylineCollection._uniformMap
  597. );
  598. command.boundingVolume = BoundingSphere.clone(
  599. boundingSphereScratch,
  600. command.boundingVolume
  601. );
  602. command.modelMatrix = modelMatrix;
  603. command.shaderProgram = sp;
  604. command.vertexArray = va.va;
  605. command.renderState = translucent
  606. ? polylineCollection._translucentRS
  607. : polylineCollection._opaqueRS;
  608. command.pass = translucent ? Pass.TRANSLUCENT : Pass.OPAQUE;
  609. command.debugShowBoundingVolume = debugShowBoundingVolume;
  610. command.pickId = "v_pickColor";
  611. command.uniformMap = uniformMap;
  612. command.count = count;
  613. command.offset = offset;
  614. offset += count;
  615. count = 0;
  616. cloneBoundingSphere = true;
  617. commandList.push(command);
  618. }
  619. currentMaterial = polyline._material;
  620. currentMaterial.update(context);
  621. currentId = mId;
  622. }
  623. const locators = polyline._locatorBuckets;
  624. const locatorLength = locators.length;
  625. for (let t = 0; t < locatorLength; ++t) {
  626. const locator = locators[t];
  627. if (locator.locator === bucketLocator) {
  628. count += locator.count;
  629. }
  630. }
  631. let boundingVolume;
  632. if (frameState.mode === SceneMode.SCENE3D) {
  633. boundingVolume = polyline._boundingVolumeWC;
  634. } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
  635. boundingVolume = polyline._boundingVolume2D;
  636. } else if (frameState.mode === SceneMode.SCENE2D) {
  637. if (defined(polyline._boundingVolume2D)) {
  638. boundingVolume = BoundingSphere.clone(
  639. polyline._boundingVolume2D,
  640. boundingSphereScratch2
  641. );
  642. boundingVolume.center.x = 0.0;
  643. }
  644. } else if (
  645. defined(polyline._boundingVolumeWC) &&
  646. defined(polyline._boundingVolume2D)
  647. ) {
  648. boundingVolume = BoundingSphere.union(
  649. polyline._boundingVolumeWC,
  650. polyline._boundingVolume2D,
  651. boundingSphereScratch2
  652. );
  653. }
  654. if (cloneBoundingSphere) {
  655. cloneBoundingSphere = false;
  656. BoundingSphere.clone(boundingVolume, boundingSphereScratch);
  657. } else {
  658. BoundingSphere.union(
  659. boundingVolume,
  660. boundingSphereScratch,
  661. boundingSphereScratch
  662. );
  663. }
  664. }
  665. if (defined(currentId) && count > 0) {
  666. if (commandIndex >= commandsLength) {
  667. command = new DrawCommand({
  668. owner: polylineCollection,
  669. });
  670. commands.push(command);
  671. } else {
  672. command = commands[commandIndex];
  673. }
  674. ++commandIndex;
  675. uniformMap = combine(
  676. uniformCallback(currentMaterial._uniforms),
  677. polylineCollection._uniformMap
  678. );
  679. command.boundingVolume = BoundingSphere.clone(
  680. boundingSphereScratch,
  681. command.boundingVolume
  682. );
  683. command.modelMatrix = modelMatrix;
  684. command.shaderProgram = sp;
  685. command.vertexArray = va.va;
  686. command.renderState = currentMaterial.isTranslucent()
  687. ? polylineCollection._translucentRS
  688. : polylineCollection._opaqueRS;
  689. command.pass = currentMaterial.isTranslucent()
  690. ? Pass.TRANSLUCENT
  691. : Pass.OPAQUE;
  692. command.debugShowBoundingVolume = debugShowBoundingVolume;
  693. command.pickId = "v_pickColor";
  694. command.uniformMap = uniformMap;
  695. command.count = count;
  696. command.offset = offset;
  697. cloneBoundingSphere = true;
  698. commandList.push(command);
  699. }
  700. currentId = undefined;
  701. }
  702. }
  703. commands.length = commandIndex;
  704. }
  705. /**
  706. * Returns true if this object was destroyed; otherwise, false.
  707. * <br /><br />
  708. * If this object was destroyed, it should not be used; calling any function other than
  709. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  710. *
  711. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  712. *
  713. * @see PolylineCollection#destroy
  714. */
  715. PolylineCollection.prototype.isDestroyed = function () {
  716. return false;
  717. };
  718. /**
  719. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  720. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  721. * <br /><br />
  722. * Once an object is destroyed, it should not be used; calling any function other than
  723. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  724. * assign the return value (<code>undefined</code>) to the object as done in the example.
  725. *
  726. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  727. *
  728. *
  729. * @example
  730. * polylines = polylines && polylines.destroy();
  731. *
  732. * @see PolylineCollection#isDestroyed
  733. */
  734. PolylineCollection.prototype.destroy = function () {
  735. destroyVertexArrays(this);
  736. releaseShaders(this);
  737. destroyPolylines(this);
  738. this._batchTable = this._batchTable && this._batchTable.destroy();
  739. return destroyObject(this);
  740. };
  741. function computeNewBuffersUsage(collection) {
  742. let usageChanged = false;
  743. const properties = collection._propertiesChanged;
  744. const bufferUsage = collection._positionBufferUsage;
  745. if (properties[POSITION_INDEX]) {
  746. if (bufferUsage.bufferUsage !== BufferUsage.STREAM_DRAW) {
  747. usageChanged = true;
  748. bufferUsage.bufferUsage = BufferUsage.STREAM_DRAW;
  749. bufferUsage.frameCount = 100;
  750. } else {
  751. bufferUsage.frameCount = 100;
  752. }
  753. } else if (bufferUsage.bufferUsage !== BufferUsage.STATIC_DRAW) {
  754. if (bufferUsage.frameCount === 0) {
  755. usageChanged = true;
  756. bufferUsage.bufferUsage = BufferUsage.STATIC_DRAW;
  757. } else {
  758. bufferUsage.frameCount--;
  759. }
  760. }
  761. return usageChanged;
  762. }
  763. const emptyVertexBuffer = [0.0, 0.0, 0.0];
  764. function createVertexArrays(collection, context, projection) {
  765. collection._createVertexArray = false;
  766. releaseShaders(collection);
  767. destroyVertexArrays(collection);
  768. sortPolylinesIntoBuckets(collection);
  769. //stores all of the individual indices arrays.
  770. const totalIndices = [[]];
  771. let indices = totalIndices[0];
  772. const batchTable = collection._batchTable;
  773. const useHighlightColor = collection._useHighlightColor;
  774. //used to determine the vertexBuffer offset if the indicesArray goes over 64k.
  775. //if it's the same polyline while it goes over 64k, the offset needs to backtrack componentsPerAttribute * componentDatatype bytes
  776. //so that the polyline looks contiguous.
  777. //if the polyline ends at the 64k mark, then the offset is just 64k * componentsPerAttribute * componentDatatype
  778. const vertexBufferOffset = [0];
  779. let offset = 0;
  780. const vertexArrayBuckets = [[]];
  781. let totalLength = 0;
  782. const polylineBuckets = collection._polylineBuckets;
  783. let x;
  784. let bucket;
  785. for (x in polylineBuckets) {
  786. if (polylineBuckets.hasOwnProperty(x)) {
  787. bucket = polylineBuckets[x];
  788. bucket.updateShader(context, batchTable, useHighlightColor);
  789. totalLength += bucket.lengthOfPositions;
  790. }
  791. }
  792. if (totalLength > 0) {
  793. const mode = collection._mode;
  794. const positionArray = new Float32Array(6 * totalLength * 3);
  795. const texCoordExpandAndBatchIndexArray = new Float32Array(totalLength * 4);
  796. let position3DArray;
  797. let positionIndex = 0;
  798. let colorIndex = 0;
  799. let texCoordExpandAndBatchIndexIndex = 0;
  800. for (x in polylineBuckets) {
  801. if (polylineBuckets.hasOwnProperty(x)) {
  802. bucket = polylineBuckets[x];
  803. bucket.write(
  804. positionArray,
  805. texCoordExpandAndBatchIndexArray,
  806. positionIndex,
  807. colorIndex,
  808. texCoordExpandAndBatchIndexIndex,
  809. batchTable,
  810. context,
  811. projection
  812. );
  813. if (mode === SceneMode.MORPHING) {
  814. if (!defined(position3DArray)) {
  815. position3DArray = new Float32Array(6 * totalLength * 3);
  816. }
  817. bucket.writeForMorph(position3DArray, positionIndex);
  818. }
  819. const bucketLength = bucket.lengthOfPositions;
  820. positionIndex += 6 * bucketLength * 3;
  821. colorIndex += bucketLength * 4;
  822. texCoordExpandAndBatchIndexIndex += bucketLength * 4;
  823. offset = bucket.updateIndices(
  824. totalIndices,
  825. vertexBufferOffset,
  826. vertexArrayBuckets,
  827. offset
  828. );
  829. }
  830. }
  831. const positionBufferUsage = collection._positionBufferUsage.bufferUsage;
  832. const texCoordExpandAndBatchIndexBufferUsage = BufferUsage.STATIC_DRAW;
  833. collection._positionBuffer = Buffer.createVertexBuffer({
  834. context: context,
  835. typedArray: positionArray,
  836. usage: positionBufferUsage,
  837. });
  838. let position3DBuffer;
  839. if (defined(position3DArray)) {
  840. position3DBuffer = Buffer.createVertexBuffer({
  841. context: context,
  842. typedArray: position3DArray,
  843. usage: positionBufferUsage,
  844. });
  845. }
  846. collection._texCoordExpandAndBatchIndexBuffer = Buffer.createVertexBuffer({
  847. context: context,
  848. typedArray: texCoordExpandAndBatchIndexArray,
  849. usage: texCoordExpandAndBatchIndexBufferUsage,
  850. });
  851. const positionSizeInBytes = 3 * Float32Array.BYTES_PER_ELEMENT;
  852. const texCoordExpandAndBatchIndexSizeInBytes =
  853. 4 * Float32Array.BYTES_PER_ELEMENT;
  854. let vbo = 0;
  855. const numberOfIndicesArrays = totalIndices.length;
  856. for (let k = 0; k < numberOfIndicesArrays; ++k) {
  857. indices = totalIndices[k];
  858. if (indices.length > 0) {
  859. const indicesArray = new Uint16Array(indices);
  860. const indexBuffer = Buffer.createIndexBuffer({
  861. context: context,
  862. typedArray: indicesArray,
  863. usage: BufferUsage.STATIC_DRAW,
  864. indexDatatype: IndexDatatype.UNSIGNED_SHORT,
  865. });
  866. vbo += vertexBufferOffset[k];
  867. const positionHighOffset =
  868. 6 *
  869. (k * (positionSizeInBytes * CesiumMath.SIXTY_FOUR_KILOBYTES) -
  870. vbo * positionSizeInBytes); //componentsPerAttribute(3) * componentDatatype(4)
  871. const positionLowOffset = positionSizeInBytes + positionHighOffset;
  872. const prevPositionHighOffset = positionSizeInBytes + positionLowOffset;
  873. const prevPositionLowOffset =
  874. positionSizeInBytes + prevPositionHighOffset;
  875. const nextPositionHighOffset =
  876. positionSizeInBytes + prevPositionLowOffset;
  877. const nextPositionLowOffset =
  878. positionSizeInBytes + nextPositionHighOffset;
  879. const vertexTexCoordExpandAndBatchIndexBufferOffset =
  880. k *
  881. (texCoordExpandAndBatchIndexSizeInBytes *
  882. CesiumMath.SIXTY_FOUR_KILOBYTES) -
  883. vbo * texCoordExpandAndBatchIndexSizeInBytes;
  884. const attributes = [
  885. {
  886. index: attributeLocations.position3DHigh,
  887. componentsPerAttribute: 3,
  888. componentDatatype: ComponentDatatype.FLOAT,
  889. offsetInBytes: positionHighOffset,
  890. strideInBytes: 6 * positionSizeInBytes,
  891. },
  892. {
  893. index: attributeLocations.position3DLow,
  894. componentsPerAttribute: 3,
  895. componentDatatype: ComponentDatatype.FLOAT,
  896. offsetInBytes: positionLowOffset,
  897. strideInBytes: 6 * positionSizeInBytes,
  898. },
  899. {
  900. index: attributeLocations.position2DHigh,
  901. componentsPerAttribute: 3,
  902. componentDatatype: ComponentDatatype.FLOAT,
  903. offsetInBytes: positionHighOffset,
  904. strideInBytes: 6 * positionSizeInBytes,
  905. },
  906. {
  907. index: attributeLocations.position2DLow,
  908. componentsPerAttribute: 3,
  909. componentDatatype: ComponentDatatype.FLOAT,
  910. offsetInBytes: positionLowOffset,
  911. strideInBytes: 6 * positionSizeInBytes,
  912. },
  913. {
  914. index: attributeLocations.prevPosition3DHigh,
  915. componentsPerAttribute: 3,
  916. componentDatatype: ComponentDatatype.FLOAT,
  917. offsetInBytes: prevPositionHighOffset,
  918. strideInBytes: 6 * positionSizeInBytes,
  919. },
  920. {
  921. index: attributeLocations.prevPosition3DLow,
  922. componentsPerAttribute: 3,
  923. componentDatatype: ComponentDatatype.FLOAT,
  924. offsetInBytes: prevPositionLowOffset,
  925. strideInBytes: 6 * positionSizeInBytes,
  926. },
  927. {
  928. index: attributeLocations.prevPosition2DHigh,
  929. componentsPerAttribute: 3,
  930. componentDatatype: ComponentDatatype.FLOAT,
  931. offsetInBytes: prevPositionHighOffset,
  932. strideInBytes: 6 * positionSizeInBytes,
  933. },
  934. {
  935. index: attributeLocations.prevPosition2DLow,
  936. componentsPerAttribute: 3,
  937. componentDatatype: ComponentDatatype.FLOAT,
  938. offsetInBytes: prevPositionLowOffset,
  939. strideInBytes: 6 * positionSizeInBytes,
  940. },
  941. {
  942. index: attributeLocations.nextPosition3DHigh,
  943. componentsPerAttribute: 3,
  944. componentDatatype: ComponentDatatype.FLOAT,
  945. offsetInBytes: nextPositionHighOffset,
  946. strideInBytes: 6 * positionSizeInBytes,
  947. },
  948. {
  949. index: attributeLocations.nextPosition3DLow,
  950. componentsPerAttribute: 3,
  951. componentDatatype: ComponentDatatype.FLOAT,
  952. offsetInBytes: nextPositionLowOffset,
  953. strideInBytes: 6 * positionSizeInBytes,
  954. },
  955. {
  956. index: attributeLocations.nextPosition2DHigh,
  957. componentsPerAttribute: 3,
  958. componentDatatype: ComponentDatatype.FLOAT,
  959. offsetInBytes: nextPositionHighOffset,
  960. strideInBytes: 6 * positionSizeInBytes,
  961. },
  962. {
  963. index: attributeLocations.nextPosition2DLow,
  964. componentsPerAttribute: 3,
  965. componentDatatype: ComponentDatatype.FLOAT,
  966. offsetInBytes: nextPositionLowOffset,
  967. strideInBytes: 6 * positionSizeInBytes,
  968. },
  969. {
  970. index: attributeLocations.texCoordExpandAndBatchIndex,
  971. componentsPerAttribute: 4,
  972. componentDatatype: ComponentDatatype.FLOAT,
  973. vertexBuffer: collection._texCoordExpandAndBatchIndexBuffer,
  974. offsetInBytes: vertexTexCoordExpandAndBatchIndexBufferOffset,
  975. },
  976. ];
  977. let bufferProperty3D;
  978. let buffer3D;
  979. let buffer2D;
  980. let bufferProperty2D;
  981. if (mode === SceneMode.SCENE3D) {
  982. buffer3D = collection._positionBuffer;
  983. bufferProperty3D = "vertexBuffer";
  984. buffer2D = emptyVertexBuffer;
  985. bufferProperty2D = "value";
  986. } else if (
  987. mode === SceneMode.SCENE2D ||
  988. mode === SceneMode.COLUMBUS_VIEW
  989. ) {
  990. buffer3D = emptyVertexBuffer;
  991. bufferProperty3D = "value";
  992. buffer2D = collection._positionBuffer;
  993. bufferProperty2D = "vertexBuffer";
  994. } else {
  995. buffer3D = position3DBuffer;
  996. bufferProperty3D = "vertexBuffer";
  997. buffer2D = collection._positionBuffer;
  998. bufferProperty2D = "vertexBuffer";
  999. }
  1000. attributes[0][bufferProperty3D] = buffer3D;
  1001. attributes[1][bufferProperty3D] = buffer3D;
  1002. attributes[2][bufferProperty2D] = buffer2D;
  1003. attributes[3][bufferProperty2D] = buffer2D;
  1004. attributes[4][bufferProperty3D] = buffer3D;
  1005. attributes[5][bufferProperty3D] = buffer3D;
  1006. attributes[6][bufferProperty2D] = buffer2D;
  1007. attributes[7][bufferProperty2D] = buffer2D;
  1008. attributes[8][bufferProperty3D] = buffer3D;
  1009. attributes[9][bufferProperty3D] = buffer3D;
  1010. attributes[10][bufferProperty2D] = buffer2D;
  1011. attributes[11][bufferProperty2D] = buffer2D;
  1012. const va = new VertexArray({
  1013. context: context,
  1014. attributes: attributes,
  1015. indexBuffer: indexBuffer,
  1016. });
  1017. collection._vertexArrays.push({
  1018. va: va,
  1019. buckets: vertexArrayBuckets[k],
  1020. });
  1021. }
  1022. }
  1023. }
  1024. }
  1025. function replacer(key, value) {
  1026. if (value instanceof Texture) {
  1027. return value.id;
  1028. }
  1029. return value;
  1030. }
  1031. const scratchUniformArray = [];
  1032. function createMaterialId(material) {
  1033. const uniforms = Material._uniformList[material.type];
  1034. const length = uniforms.length;
  1035. scratchUniformArray.length = 2.0 * length;
  1036. let index = 0;
  1037. for (let i = 0; i < length; ++i) {
  1038. const uniform = uniforms[i];
  1039. scratchUniformArray[index] = uniform;
  1040. scratchUniformArray[index + 1] = material._uniforms[uniform]();
  1041. index += 2;
  1042. }
  1043. return `${material.type}:${JSON.stringify(scratchUniformArray, replacer)}`;
  1044. }
  1045. function sortPolylinesIntoBuckets(collection) {
  1046. const mode = collection._mode;
  1047. const modelMatrix = collection._modelMatrix;
  1048. const polylineBuckets = (collection._polylineBuckets = {});
  1049. const polylines = collection._polylines;
  1050. const length = polylines.length;
  1051. for (let i = 0; i < length; ++i) {
  1052. const p = polylines[i];
  1053. if (p._actualPositions.length > 1) {
  1054. p.update();
  1055. const material = p.material;
  1056. let value = polylineBuckets[material.type];
  1057. if (!defined(value)) {
  1058. value = polylineBuckets[material.type] = new PolylineBucket(
  1059. material,
  1060. mode,
  1061. modelMatrix
  1062. );
  1063. }
  1064. value.addPolyline(p);
  1065. }
  1066. }
  1067. }
  1068. function updateMode(collection, frameState) {
  1069. const mode = frameState.mode;
  1070. if (
  1071. collection._mode !== mode ||
  1072. !Matrix4.equals(collection._modelMatrix, collection.modelMatrix)
  1073. ) {
  1074. collection._mode = mode;
  1075. collection._modelMatrix = Matrix4.clone(collection.modelMatrix);
  1076. collection._createVertexArray = true;
  1077. }
  1078. }
  1079. function removePolylines(collection) {
  1080. if (collection._polylinesRemoved) {
  1081. collection._polylinesRemoved = false;
  1082. const definedPolylines = [];
  1083. const definedPolylinesToUpdate = [];
  1084. let polyIndex = 0;
  1085. let polyline;
  1086. const length = collection._polylines.length;
  1087. for (let i = 0; i < length; ++i) {
  1088. polyline = collection._polylines[i];
  1089. if (!polyline.isDestroyed) {
  1090. polyline._index = polyIndex++;
  1091. definedPolylinesToUpdate.push(polyline);
  1092. definedPolylines.push(polyline);
  1093. }
  1094. }
  1095. collection._polylines = definedPolylines;
  1096. collection._polylinesToUpdate = definedPolylinesToUpdate;
  1097. }
  1098. }
  1099. function releaseShaders(collection) {
  1100. const polylines = collection._polylines;
  1101. const length = polylines.length;
  1102. for (let i = 0; i < length; ++i) {
  1103. if (!polylines[i].isDestroyed) {
  1104. const bucket = polylines[i]._bucket;
  1105. if (defined(bucket)) {
  1106. bucket.shaderProgram =
  1107. bucket.shaderProgram && bucket.shaderProgram.destroy();
  1108. }
  1109. }
  1110. }
  1111. }
  1112. function destroyVertexArrays(collection) {
  1113. const length = collection._vertexArrays.length;
  1114. for (let t = 0; t < length; ++t) {
  1115. collection._vertexArrays[t].va.destroy();
  1116. }
  1117. collection._vertexArrays.length = 0;
  1118. }
  1119. PolylineCollection.prototype._updatePolyline = function (
  1120. polyline,
  1121. propertyChanged
  1122. ) {
  1123. this._polylinesUpdated = true;
  1124. if (!polyline._dirty) {
  1125. this._polylinesToUpdate.push(polyline);
  1126. }
  1127. ++this._propertiesChanged[propertyChanged];
  1128. };
  1129. function destroyPolylines(collection) {
  1130. const polylines = collection._polylines;
  1131. const length = polylines.length;
  1132. for (let i = 0; i < length; ++i) {
  1133. if (!polylines[i].isDestroyed) {
  1134. polylines[i]._destroy();
  1135. }
  1136. }
  1137. }
  1138. function VertexArrayBucketLocator(count, offset, bucket) {
  1139. this.count = count;
  1140. this.offset = offset;
  1141. this.bucket = bucket;
  1142. }
  1143. function PolylineBucket(material, mode, modelMatrix) {
  1144. this.polylines = [];
  1145. this.lengthOfPositions = 0;
  1146. this.material = material;
  1147. this.shaderProgram = undefined;
  1148. this.mode = mode;
  1149. this.modelMatrix = modelMatrix;
  1150. }
  1151. PolylineBucket.prototype.addPolyline = function (p) {
  1152. const polylines = this.polylines;
  1153. polylines.push(p);
  1154. p._actualLength = this.getPolylinePositionsLength(p);
  1155. this.lengthOfPositions += p._actualLength;
  1156. p._bucket = this;
  1157. };
  1158. PolylineBucket.prototype.updateShader = function (
  1159. context,
  1160. batchTable,
  1161. useHighlightColor
  1162. ) {
  1163. if (defined(this.shaderProgram)) {
  1164. return;
  1165. }
  1166. const defines = ["DISTANCE_DISPLAY_CONDITION"];
  1167. if (useHighlightColor) {
  1168. defines.push("VECTOR_TILE");
  1169. }
  1170. // Check for use of v_polylineAngle in material shader
  1171. if (
  1172. this.material.shaderSource.search(/varying\s+float\s+v_polylineAngle;/g) !==
  1173. -1
  1174. ) {
  1175. defines.push("POLYLINE_DASH");
  1176. }
  1177. if (!FeatureDetection.isInternetExplorer()) {
  1178. defines.push("CLIP_POLYLINE");
  1179. }
  1180. const fs = new ShaderSource({
  1181. defines: defines,
  1182. sources: [
  1183. "varying vec4 v_pickColor;\n",
  1184. this.material.shaderSource,
  1185. PolylineFS,
  1186. ],
  1187. });
  1188. const vsSource = batchTable.getVertexShaderCallback()(PolylineVS);
  1189. const vs = new ShaderSource({
  1190. defines: defines,
  1191. sources: [PolylineCommon, vsSource],
  1192. });
  1193. this.shaderProgram = ShaderProgram.fromCache({
  1194. context: context,
  1195. vertexShaderSource: vs,
  1196. fragmentShaderSource: fs,
  1197. attributeLocations: attributeLocations,
  1198. });
  1199. };
  1200. function intersectsIDL(polyline) {
  1201. return (
  1202. Cartesian3.dot(Cartesian3.UNIT_X, polyline._boundingVolume.center) < 0 ||
  1203. polyline._boundingVolume.intersectPlane(Plane.ORIGIN_ZX_PLANE) ===
  1204. Intersect.INTERSECTING
  1205. );
  1206. }
  1207. PolylineBucket.prototype.getPolylinePositionsLength = function (polyline) {
  1208. let length;
  1209. if (this.mode === SceneMode.SCENE3D || !intersectsIDL(polyline)) {
  1210. length = polyline._actualPositions.length;
  1211. return length * 4.0 - 4.0;
  1212. }
  1213. let count = 0;
  1214. const segmentLengths = polyline._segments.lengths;
  1215. length = segmentLengths.length;
  1216. for (let i = 0; i < length; ++i) {
  1217. count += segmentLengths[i] * 4.0 - 4.0;
  1218. }
  1219. return count;
  1220. };
  1221. const scratchWritePosition = new Cartesian3();
  1222. const scratchWritePrevPosition = new Cartesian3();
  1223. const scratchWriteNextPosition = new Cartesian3();
  1224. const scratchWriteVector = new Cartesian3();
  1225. const scratchPickColorCartesian = new Cartesian4();
  1226. const scratchWidthShowCartesian = new Cartesian2();
  1227. PolylineBucket.prototype.write = function (
  1228. positionArray,
  1229. texCoordExpandAndBatchIndexArray,
  1230. positionIndex,
  1231. colorIndex,
  1232. texCoordExpandAndBatchIndexIndex,
  1233. batchTable,
  1234. context,
  1235. projection
  1236. ) {
  1237. const mode = this.mode;
  1238. const maxLon = projection.ellipsoid.maximumRadius * CesiumMath.PI;
  1239. const polylines = this.polylines;
  1240. const length = polylines.length;
  1241. for (let i = 0; i < length; ++i) {
  1242. const polyline = polylines[i];
  1243. const width = polyline.width;
  1244. const show = polyline.show && width > 0.0;
  1245. const polylineBatchIndex = polyline._index;
  1246. const segments = this.getSegments(polyline, projection);
  1247. const positions = segments.positions;
  1248. const lengths = segments.lengths;
  1249. const positionsLength = positions.length;
  1250. const pickColor = polyline.getPickId(context).color;
  1251. let segmentIndex = 0;
  1252. let count = 0;
  1253. let position;
  1254. for (let j = 0; j < positionsLength; ++j) {
  1255. if (j === 0) {
  1256. if (polyline._loop) {
  1257. position = positions[positionsLength - 2];
  1258. } else {
  1259. position = scratchWriteVector;
  1260. Cartesian3.subtract(positions[0], positions[1], position);
  1261. Cartesian3.add(positions[0], position, position);
  1262. }
  1263. } else {
  1264. position = positions[j - 1];
  1265. }
  1266. Cartesian3.clone(position, scratchWritePrevPosition);
  1267. Cartesian3.clone(positions[j], scratchWritePosition);
  1268. if (j === positionsLength - 1) {
  1269. if (polyline._loop) {
  1270. position = positions[1];
  1271. } else {
  1272. position = scratchWriteVector;
  1273. Cartesian3.subtract(
  1274. positions[positionsLength - 1],
  1275. positions[positionsLength - 2],
  1276. position
  1277. );
  1278. Cartesian3.add(positions[positionsLength - 1], position, position);
  1279. }
  1280. } else {
  1281. position = positions[j + 1];
  1282. }
  1283. Cartesian3.clone(position, scratchWriteNextPosition);
  1284. const segmentLength = lengths[segmentIndex];
  1285. if (j === count + segmentLength) {
  1286. count += segmentLength;
  1287. ++segmentIndex;
  1288. }
  1289. const segmentStart = j - count === 0;
  1290. const segmentEnd = j === count + lengths[segmentIndex] - 1;
  1291. if (mode === SceneMode.SCENE2D) {
  1292. scratchWritePrevPosition.z = 0.0;
  1293. scratchWritePosition.z = 0.0;
  1294. scratchWriteNextPosition.z = 0.0;
  1295. }
  1296. if (mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) {
  1297. if (
  1298. (segmentStart || segmentEnd) &&
  1299. maxLon - Math.abs(scratchWritePosition.x) < 1.0
  1300. ) {
  1301. if (
  1302. (scratchWritePosition.x < 0.0 &&
  1303. scratchWritePrevPosition.x > 0.0) ||
  1304. (scratchWritePosition.x > 0.0 && scratchWritePrevPosition.x < 0.0)
  1305. ) {
  1306. Cartesian3.clone(scratchWritePosition, scratchWritePrevPosition);
  1307. }
  1308. if (
  1309. (scratchWritePosition.x < 0.0 &&
  1310. scratchWriteNextPosition.x > 0.0) ||
  1311. (scratchWritePosition.x > 0.0 && scratchWriteNextPosition.x < 0.0)
  1312. ) {
  1313. Cartesian3.clone(scratchWritePosition, scratchWriteNextPosition);
  1314. }
  1315. }
  1316. }
  1317. const startK = segmentStart ? 2 : 0;
  1318. const endK = segmentEnd ? 2 : 4;
  1319. for (let k = startK; k < endK; ++k) {
  1320. EncodedCartesian3.writeElements(
  1321. scratchWritePosition,
  1322. positionArray,
  1323. positionIndex
  1324. );
  1325. EncodedCartesian3.writeElements(
  1326. scratchWritePrevPosition,
  1327. positionArray,
  1328. positionIndex + 6
  1329. );
  1330. EncodedCartesian3.writeElements(
  1331. scratchWriteNextPosition,
  1332. positionArray,
  1333. positionIndex + 12
  1334. );
  1335. const direction = k - 2 < 0 ? -1.0 : 1.0;
  1336. texCoordExpandAndBatchIndexArray[texCoordExpandAndBatchIndexIndex] =
  1337. j / (positionsLength - 1); // s tex coord
  1338. texCoordExpandAndBatchIndexArray[texCoordExpandAndBatchIndexIndex + 1] =
  1339. 2 * (k % 2) - 1; // expand direction
  1340. texCoordExpandAndBatchIndexArray[
  1341. texCoordExpandAndBatchIndexIndex + 2
  1342. ] = direction;
  1343. texCoordExpandAndBatchIndexArray[
  1344. texCoordExpandAndBatchIndexIndex + 3
  1345. ] = polylineBatchIndex;
  1346. positionIndex += 6 * 3;
  1347. texCoordExpandAndBatchIndexIndex += 4;
  1348. }
  1349. }
  1350. const colorCartesian = scratchPickColorCartesian;
  1351. colorCartesian.x = Color.floatToByte(pickColor.red);
  1352. colorCartesian.y = Color.floatToByte(pickColor.green);
  1353. colorCartesian.z = Color.floatToByte(pickColor.blue);
  1354. colorCartesian.w = Color.floatToByte(pickColor.alpha);
  1355. const widthShowCartesian = scratchWidthShowCartesian;
  1356. widthShowCartesian.x = width;
  1357. widthShowCartesian.y = show ? 1.0 : 0.0;
  1358. const boundingSphere =
  1359. mode === SceneMode.SCENE2D
  1360. ? polyline._boundingVolume2D
  1361. : polyline._boundingVolumeWC;
  1362. const encodedCenter = EncodedCartesian3.fromCartesian(
  1363. boundingSphere.center,
  1364. scratchUpdatePolylineEncodedCartesian
  1365. );
  1366. const high = encodedCenter.high;
  1367. const low = Cartesian4.fromElements(
  1368. encodedCenter.low.x,
  1369. encodedCenter.low.y,
  1370. encodedCenter.low.z,
  1371. boundingSphere.radius,
  1372. scratchUpdatePolylineCartesian4
  1373. );
  1374. const nearFarCartesian = scratchNearFarCartesian2;
  1375. nearFarCartesian.x = 0.0;
  1376. nearFarCartesian.y = Number.MAX_VALUE;
  1377. const distanceDisplayCondition = polyline.distanceDisplayCondition;
  1378. if (defined(distanceDisplayCondition)) {
  1379. nearFarCartesian.x = distanceDisplayCondition.near;
  1380. nearFarCartesian.y = distanceDisplayCondition.far;
  1381. }
  1382. batchTable.setBatchedAttribute(polylineBatchIndex, 0, widthShowCartesian);
  1383. batchTable.setBatchedAttribute(polylineBatchIndex, 1, colorCartesian);
  1384. if (batchTable.attributes.length > 2) {
  1385. batchTable.setBatchedAttribute(polylineBatchIndex, 2, high);
  1386. batchTable.setBatchedAttribute(polylineBatchIndex, 3, low);
  1387. batchTable.setBatchedAttribute(polylineBatchIndex, 4, nearFarCartesian);
  1388. }
  1389. }
  1390. };
  1391. const morphPositionScratch = new Cartesian3();
  1392. const morphPrevPositionScratch = new Cartesian3();
  1393. const morphNextPositionScratch = new Cartesian3();
  1394. const morphVectorScratch = new Cartesian3();
  1395. PolylineBucket.prototype.writeForMorph = function (
  1396. positionArray,
  1397. positionIndex
  1398. ) {
  1399. const modelMatrix = this.modelMatrix;
  1400. const polylines = this.polylines;
  1401. const length = polylines.length;
  1402. for (let i = 0; i < length; ++i) {
  1403. const polyline = polylines[i];
  1404. const positions = polyline._segments.positions;
  1405. const lengths = polyline._segments.lengths;
  1406. const positionsLength = positions.length;
  1407. let segmentIndex = 0;
  1408. let count = 0;
  1409. for (let j = 0; j < positionsLength; ++j) {
  1410. let prevPosition;
  1411. if (j === 0) {
  1412. if (polyline._loop) {
  1413. prevPosition = positions[positionsLength - 2];
  1414. } else {
  1415. prevPosition = morphVectorScratch;
  1416. Cartesian3.subtract(positions[0], positions[1], prevPosition);
  1417. Cartesian3.add(positions[0], prevPosition, prevPosition);
  1418. }
  1419. } else {
  1420. prevPosition = positions[j - 1];
  1421. }
  1422. prevPosition = Matrix4.multiplyByPoint(
  1423. modelMatrix,
  1424. prevPosition,
  1425. morphPrevPositionScratch
  1426. );
  1427. const position = Matrix4.multiplyByPoint(
  1428. modelMatrix,
  1429. positions[j],
  1430. morphPositionScratch
  1431. );
  1432. let nextPosition;
  1433. if (j === positionsLength - 1) {
  1434. if (polyline._loop) {
  1435. nextPosition = positions[1];
  1436. } else {
  1437. nextPosition = morphVectorScratch;
  1438. Cartesian3.subtract(
  1439. positions[positionsLength - 1],
  1440. positions[positionsLength - 2],
  1441. nextPosition
  1442. );
  1443. Cartesian3.add(
  1444. positions[positionsLength - 1],
  1445. nextPosition,
  1446. nextPosition
  1447. );
  1448. }
  1449. } else {
  1450. nextPosition = positions[j + 1];
  1451. }
  1452. nextPosition = Matrix4.multiplyByPoint(
  1453. modelMatrix,
  1454. nextPosition,
  1455. morphNextPositionScratch
  1456. );
  1457. const segmentLength = lengths[segmentIndex];
  1458. if (j === count + segmentLength) {
  1459. count += segmentLength;
  1460. ++segmentIndex;
  1461. }
  1462. const segmentStart = j - count === 0;
  1463. const segmentEnd = j === count + lengths[segmentIndex] - 1;
  1464. const startK = segmentStart ? 2 : 0;
  1465. const endK = segmentEnd ? 2 : 4;
  1466. for (let k = startK; k < endK; ++k) {
  1467. EncodedCartesian3.writeElements(position, positionArray, positionIndex);
  1468. EncodedCartesian3.writeElements(
  1469. prevPosition,
  1470. positionArray,
  1471. positionIndex + 6
  1472. );
  1473. EncodedCartesian3.writeElements(
  1474. nextPosition,
  1475. positionArray,
  1476. positionIndex + 12
  1477. );
  1478. positionIndex += 6 * 3;
  1479. }
  1480. }
  1481. }
  1482. };
  1483. const scratchSegmentLengths = new Array(1);
  1484. PolylineBucket.prototype.updateIndices = function (
  1485. totalIndices,
  1486. vertexBufferOffset,
  1487. vertexArrayBuckets,
  1488. offset
  1489. ) {
  1490. let vaCount = vertexArrayBuckets.length - 1;
  1491. let bucketLocator = new VertexArrayBucketLocator(0, offset, this);
  1492. vertexArrayBuckets[vaCount].push(bucketLocator);
  1493. let count = 0;
  1494. let indices = totalIndices[totalIndices.length - 1];
  1495. let indicesCount = 0;
  1496. if (indices.length > 0) {
  1497. indicesCount = indices[indices.length - 1] + 1;
  1498. }
  1499. const polylines = this.polylines;
  1500. const length = polylines.length;
  1501. for (let i = 0; i < length; ++i) {
  1502. const polyline = polylines[i];
  1503. polyline._locatorBuckets = [];
  1504. let segments;
  1505. if (this.mode === SceneMode.SCENE3D) {
  1506. segments = scratchSegmentLengths;
  1507. const positionsLength = polyline._actualPositions.length;
  1508. if (positionsLength > 0) {
  1509. segments[0] = positionsLength;
  1510. } else {
  1511. continue;
  1512. }
  1513. } else {
  1514. segments = polyline._segments.lengths;
  1515. }
  1516. const numberOfSegments = segments.length;
  1517. if (numberOfSegments > 0) {
  1518. let segmentIndexCount = 0;
  1519. for (let j = 0; j < numberOfSegments; ++j) {
  1520. const segmentLength = segments[j] - 1.0;
  1521. for (let k = 0; k < segmentLength; ++k) {
  1522. if (indicesCount + 4 > CesiumMath.SIXTY_FOUR_KILOBYTES) {
  1523. polyline._locatorBuckets.push({
  1524. locator: bucketLocator,
  1525. count: segmentIndexCount,
  1526. });
  1527. segmentIndexCount = 0;
  1528. vertexBufferOffset.push(4);
  1529. indices = [];
  1530. totalIndices.push(indices);
  1531. indicesCount = 0;
  1532. bucketLocator.count = count;
  1533. count = 0;
  1534. offset = 0;
  1535. bucketLocator = new VertexArrayBucketLocator(0, 0, this);
  1536. vertexArrayBuckets[++vaCount] = [bucketLocator];
  1537. }
  1538. indices.push(indicesCount, indicesCount + 2, indicesCount + 1);
  1539. indices.push(indicesCount + 1, indicesCount + 2, indicesCount + 3);
  1540. segmentIndexCount += 6;
  1541. count += 6;
  1542. offset += 6;
  1543. indicesCount += 4;
  1544. }
  1545. }
  1546. polyline._locatorBuckets.push({
  1547. locator: bucketLocator,
  1548. count: segmentIndexCount,
  1549. });
  1550. if (indicesCount + 4 > CesiumMath.SIXTY_FOUR_KILOBYTES) {
  1551. vertexBufferOffset.push(0);
  1552. indices = [];
  1553. totalIndices.push(indices);
  1554. indicesCount = 0;
  1555. bucketLocator.count = count;
  1556. offset = 0;
  1557. count = 0;
  1558. bucketLocator = new VertexArrayBucketLocator(0, 0, this);
  1559. vertexArrayBuckets[++vaCount] = [bucketLocator];
  1560. }
  1561. }
  1562. polyline._clean();
  1563. }
  1564. bucketLocator.count = count;
  1565. return offset;
  1566. };
  1567. PolylineBucket.prototype.getPolylineStartIndex = function (polyline) {
  1568. const polylines = this.polylines;
  1569. let positionIndex = 0;
  1570. const length = polylines.length;
  1571. for (let i = 0; i < length; ++i) {
  1572. const p = polylines[i];
  1573. if (p === polyline) {
  1574. break;
  1575. }
  1576. positionIndex += p._actualLength;
  1577. }
  1578. return positionIndex;
  1579. };
  1580. const scratchSegments = {
  1581. positions: undefined,
  1582. lengths: undefined,
  1583. };
  1584. const scratchLengths = new Array(1);
  1585. const pscratch = new Cartesian3();
  1586. const scratchCartographic = new Cartographic();
  1587. PolylineBucket.prototype.getSegments = function (polyline, projection) {
  1588. let positions = polyline._actualPositions;
  1589. if (this.mode === SceneMode.SCENE3D) {
  1590. scratchLengths[0] = positions.length;
  1591. scratchSegments.positions = positions;
  1592. scratchSegments.lengths = scratchLengths;
  1593. return scratchSegments;
  1594. }
  1595. if (intersectsIDL(polyline)) {
  1596. positions = polyline._segments.positions;
  1597. }
  1598. const ellipsoid = projection.ellipsoid;
  1599. const newPositions = [];
  1600. const modelMatrix = this.modelMatrix;
  1601. const length = positions.length;
  1602. let position;
  1603. let p = pscratch;
  1604. for (let n = 0; n < length; ++n) {
  1605. position = positions[n];
  1606. p = Matrix4.multiplyByPoint(modelMatrix, position, p);
  1607. newPositions.push(
  1608. projection.project(
  1609. ellipsoid.cartesianToCartographic(p, scratchCartographic)
  1610. )
  1611. );
  1612. }
  1613. if (newPositions.length > 0) {
  1614. polyline._boundingVolume2D = BoundingSphere.fromPoints(
  1615. newPositions,
  1616. polyline._boundingVolume2D
  1617. );
  1618. const center2D = polyline._boundingVolume2D.center;
  1619. polyline._boundingVolume2D.center = new Cartesian3(
  1620. center2D.z,
  1621. center2D.x,
  1622. center2D.y
  1623. );
  1624. }
  1625. scratchSegments.positions = newPositions;
  1626. scratchSegments.lengths = polyline._segments.lengths;
  1627. return scratchSegments;
  1628. };
  1629. let scratchPositionsArray;
  1630. PolylineBucket.prototype.writeUpdate = function (
  1631. index,
  1632. polyline,
  1633. positionBuffer,
  1634. projection
  1635. ) {
  1636. const mode = this.mode;
  1637. const maxLon = projection.ellipsoid.maximumRadius * CesiumMath.PI;
  1638. let positionsLength = polyline._actualLength;
  1639. if (positionsLength) {
  1640. index += this.getPolylineStartIndex(polyline);
  1641. let positionArray = scratchPositionsArray;
  1642. const positionsArrayLength = 6 * positionsLength * 3;
  1643. if (
  1644. !defined(positionArray) ||
  1645. positionArray.length < positionsArrayLength
  1646. ) {
  1647. positionArray = scratchPositionsArray = new Float32Array(
  1648. positionsArrayLength
  1649. );
  1650. } else if (positionArray.length > positionsArrayLength) {
  1651. positionArray = new Float32Array(
  1652. positionArray.buffer,
  1653. 0,
  1654. positionsArrayLength
  1655. );
  1656. }
  1657. const segments = this.getSegments(polyline, projection);
  1658. const positions = segments.positions;
  1659. const lengths = segments.lengths;
  1660. let positionIndex = 0;
  1661. let segmentIndex = 0;
  1662. let count = 0;
  1663. let position;
  1664. positionsLength = positions.length;
  1665. for (let i = 0; i < positionsLength; ++i) {
  1666. if (i === 0) {
  1667. if (polyline._loop) {
  1668. position = positions[positionsLength - 2];
  1669. } else {
  1670. position = scratchWriteVector;
  1671. Cartesian3.subtract(positions[0], positions[1], position);
  1672. Cartesian3.add(positions[0], position, position);
  1673. }
  1674. } else {
  1675. position = positions[i - 1];
  1676. }
  1677. Cartesian3.clone(position, scratchWritePrevPosition);
  1678. Cartesian3.clone(positions[i], scratchWritePosition);
  1679. if (i === positionsLength - 1) {
  1680. if (polyline._loop) {
  1681. position = positions[1];
  1682. } else {
  1683. position = scratchWriteVector;
  1684. Cartesian3.subtract(
  1685. positions[positionsLength - 1],
  1686. positions[positionsLength - 2],
  1687. position
  1688. );
  1689. Cartesian3.add(positions[positionsLength - 1], position, position);
  1690. }
  1691. } else {
  1692. position = positions[i + 1];
  1693. }
  1694. Cartesian3.clone(position, scratchWriteNextPosition);
  1695. const segmentLength = lengths[segmentIndex];
  1696. if (i === count + segmentLength) {
  1697. count += segmentLength;
  1698. ++segmentIndex;
  1699. }
  1700. const segmentStart = i - count === 0;
  1701. const segmentEnd = i === count + lengths[segmentIndex] - 1;
  1702. if (mode === SceneMode.SCENE2D) {
  1703. scratchWritePrevPosition.z = 0.0;
  1704. scratchWritePosition.z = 0.0;
  1705. scratchWriteNextPosition.z = 0.0;
  1706. }
  1707. if (mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) {
  1708. if (
  1709. (segmentStart || segmentEnd) &&
  1710. maxLon - Math.abs(scratchWritePosition.x) < 1.0
  1711. ) {
  1712. if (
  1713. (scratchWritePosition.x < 0.0 &&
  1714. scratchWritePrevPosition.x > 0.0) ||
  1715. (scratchWritePosition.x > 0.0 && scratchWritePrevPosition.x < 0.0)
  1716. ) {
  1717. Cartesian3.clone(scratchWritePosition, scratchWritePrevPosition);
  1718. }
  1719. if (
  1720. (scratchWritePosition.x < 0.0 &&
  1721. scratchWriteNextPosition.x > 0.0) ||
  1722. (scratchWritePosition.x > 0.0 && scratchWriteNextPosition.x < 0.0)
  1723. ) {
  1724. Cartesian3.clone(scratchWritePosition, scratchWriteNextPosition);
  1725. }
  1726. }
  1727. }
  1728. const startJ = segmentStart ? 2 : 0;
  1729. const endJ = segmentEnd ? 2 : 4;
  1730. for (let j = startJ; j < endJ; ++j) {
  1731. EncodedCartesian3.writeElements(
  1732. scratchWritePosition,
  1733. positionArray,
  1734. positionIndex
  1735. );
  1736. EncodedCartesian3.writeElements(
  1737. scratchWritePrevPosition,
  1738. positionArray,
  1739. positionIndex + 6
  1740. );
  1741. EncodedCartesian3.writeElements(
  1742. scratchWriteNextPosition,
  1743. positionArray,
  1744. positionIndex + 12
  1745. );
  1746. positionIndex += 6 * 3;
  1747. }
  1748. }
  1749. positionBuffer.copyFromArrayView(
  1750. positionArray,
  1751. 6 * 3 * Float32Array.BYTES_PER_ELEMENT * index
  1752. );
  1753. }
  1754. };
  1755. export default PolylineCollection;