PolylineCollection.js 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957
  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(/in\s+float\s+v_polylineAngle;/g) !== -1
  1173. ) {
  1174. defines.push("POLYLINE_DASH");
  1175. }
  1176. if (!FeatureDetection.isInternetExplorer()) {
  1177. defines.push("CLIP_POLYLINE");
  1178. }
  1179. const fs = new ShaderSource({
  1180. defines: defines,
  1181. sources: ["in vec4 v_pickColor;\n", this.material.shaderSource, PolylineFS],
  1182. });
  1183. const vsSource = batchTable.getVertexShaderCallback()(PolylineVS);
  1184. const vs = new ShaderSource({
  1185. defines: defines,
  1186. sources: [PolylineCommon, vsSource],
  1187. });
  1188. this.shaderProgram = ShaderProgram.fromCache({
  1189. context: context,
  1190. vertexShaderSource: vs,
  1191. fragmentShaderSource: fs,
  1192. attributeLocations: attributeLocations,
  1193. });
  1194. };
  1195. function intersectsIDL(polyline) {
  1196. return (
  1197. Cartesian3.dot(Cartesian3.UNIT_X, polyline._boundingVolume.center) < 0 ||
  1198. polyline._boundingVolume.intersectPlane(Plane.ORIGIN_ZX_PLANE) ===
  1199. Intersect.INTERSECTING
  1200. );
  1201. }
  1202. PolylineBucket.prototype.getPolylinePositionsLength = function (polyline) {
  1203. let length;
  1204. if (this.mode === SceneMode.SCENE3D || !intersectsIDL(polyline)) {
  1205. length = polyline._actualPositions.length;
  1206. return length * 4.0 - 4.0;
  1207. }
  1208. let count = 0;
  1209. const segmentLengths = polyline._segments.lengths;
  1210. length = segmentLengths.length;
  1211. for (let i = 0; i < length; ++i) {
  1212. count += segmentLengths[i] * 4.0 - 4.0;
  1213. }
  1214. return count;
  1215. };
  1216. const scratchWritePosition = new Cartesian3();
  1217. const scratchWritePrevPosition = new Cartesian3();
  1218. const scratchWriteNextPosition = new Cartesian3();
  1219. const scratchWriteVector = new Cartesian3();
  1220. const scratchPickColorCartesian = new Cartesian4();
  1221. const scratchWidthShowCartesian = new Cartesian2();
  1222. PolylineBucket.prototype.write = function (
  1223. positionArray,
  1224. texCoordExpandAndBatchIndexArray,
  1225. positionIndex,
  1226. colorIndex,
  1227. texCoordExpandAndBatchIndexIndex,
  1228. batchTable,
  1229. context,
  1230. projection
  1231. ) {
  1232. const mode = this.mode;
  1233. const maxLon = projection.ellipsoid.maximumRadius * CesiumMath.PI;
  1234. const polylines = this.polylines;
  1235. const length = polylines.length;
  1236. for (let i = 0; i < length; ++i) {
  1237. const polyline = polylines[i];
  1238. const width = polyline.width;
  1239. const show = polyline.show && width > 0.0;
  1240. const polylineBatchIndex = polyline._index;
  1241. const segments = this.getSegments(polyline, projection);
  1242. const positions = segments.positions;
  1243. const lengths = segments.lengths;
  1244. const positionsLength = positions.length;
  1245. const pickColor = polyline.getPickId(context).color;
  1246. let segmentIndex = 0;
  1247. let count = 0;
  1248. let position;
  1249. for (let j = 0; j < positionsLength; ++j) {
  1250. if (j === 0) {
  1251. if (polyline._loop) {
  1252. position = positions[positionsLength - 2];
  1253. } else {
  1254. position = scratchWriteVector;
  1255. Cartesian3.subtract(positions[0], positions[1], position);
  1256. Cartesian3.add(positions[0], position, position);
  1257. }
  1258. } else {
  1259. position = positions[j - 1];
  1260. }
  1261. Cartesian3.clone(position, scratchWritePrevPosition);
  1262. Cartesian3.clone(positions[j], scratchWritePosition);
  1263. if (j === positionsLength - 1) {
  1264. if (polyline._loop) {
  1265. position = positions[1];
  1266. } else {
  1267. position = scratchWriteVector;
  1268. Cartesian3.subtract(
  1269. positions[positionsLength - 1],
  1270. positions[positionsLength - 2],
  1271. position
  1272. );
  1273. Cartesian3.add(positions[positionsLength - 1], position, position);
  1274. }
  1275. } else {
  1276. position = positions[j + 1];
  1277. }
  1278. Cartesian3.clone(position, scratchWriteNextPosition);
  1279. const segmentLength = lengths[segmentIndex];
  1280. if (j === count + segmentLength) {
  1281. count += segmentLength;
  1282. ++segmentIndex;
  1283. }
  1284. const segmentStart = j - count === 0;
  1285. const segmentEnd = j === count + lengths[segmentIndex] - 1;
  1286. if (mode === SceneMode.SCENE2D) {
  1287. scratchWritePrevPosition.z = 0.0;
  1288. scratchWritePosition.z = 0.0;
  1289. scratchWriteNextPosition.z = 0.0;
  1290. }
  1291. if (mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) {
  1292. if (
  1293. (segmentStart || segmentEnd) &&
  1294. maxLon - Math.abs(scratchWritePosition.x) < 1.0
  1295. ) {
  1296. if (
  1297. (scratchWritePosition.x < 0.0 &&
  1298. scratchWritePrevPosition.x > 0.0) ||
  1299. (scratchWritePosition.x > 0.0 && scratchWritePrevPosition.x < 0.0)
  1300. ) {
  1301. Cartesian3.clone(scratchWritePosition, scratchWritePrevPosition);
  1302. }
  1303. if (
  1304. (scratchWritePosition.x < 0.0 &&
  1305. scratchWriteNextPosition.x > 0.0) ||
  1306. (scratchWritePosition.x > 0.0 && scratchWriteNextPosition.x < 0.0)
  1307. ) {
  1308. Cartesian3.clone(scratchWritePosition, scratchWriteNextPosition);
  1309. }
  1310. }
  1311. }
  1312. const startK = segmentStart ? 2 : 0;
  1313. const endK = segmentEnd ? 2 : 4;
  1314. for (let k = startK; k < endK; ++k) {
  1315. EncodedCartesian3.writeElements(
  1316. scratchWritePosition,
  1317. positionArray,
  1318. positionIndex
  1319. );
  1320. EncodedCartesian3.writeElements(
  1321. scratchWritePrevPosition,
  1322. positionArray,
  1323. positionIndex + 6
  1324. );
  1325. EncodedCartesian3.writeElements(
  1326. scratchWriteNextPosition,
  1327. positionArray,
  1328. positionIndex + 12
  1329. );
  1330. const direction = k - 2 < 0 ? -1.0 : 1.0;
  1331. texCoordExpandAndBatchIndexArray[texCoordExpandAndBatchIndexIndex] =
  1332. j / (positionsLength - 1); // s tex coord
  1333. texCoordExpandAndBatchIndexArray[texCoordExpandAndBatchIndexIndex + 1] =
  1334. 2 * (k % 2) - 1; // expand direction
  1335. texCoordExpandAndBatchIndexArray[
  1336. texCoordExpandAndBatchIndexIndex + 2
  1337. ] = direction;
  1338. texCoordExpandAndBatchIndexArray[
  1339. texCoordExpandAndBatchIndexIndex + 3
  1340. ] = polylineBatchIndex;
  1341. positionIndex += 6 * 3;
  1342. texCoordExpandAndBatchIndexIndex += 4;
  1343. }
  1344. }
  1345. const colorCartesian = scratchPickColorCartesian;
  1346. colorCartesian.x = Color.floatToByte(pickColor.red);
  1347. colorCartesian.y = Color.floatToByte(pickColor.green);
  1348. colorCartesian.z = Color.floatToByte(pickColor.blue);
  1349. colorCartesian.w = Color.floatToByte(pickColor.alpha);
  1350. const widthShowCartesian = scratchWidthShowCartesian;
  1351. widthShowCartesian.x = width;
  1352. widthShowCartesian.y = show ? 1.0 : 0.0;
  1353. const boundingSphere =
  1354. mode === SceneMode.SCENE2D
  1355. ? polyline._boundingVolume2D
  1356. : polyline._boundingVolumeWC;
  1357. const encodedCenter = EncodedCartesian3.fromCartesian(
  1358. boundingSphere.center,
  1359. scratchUpdatePolylineEncodedCartesian
  1360. );
  1361. const high = encodedCenter.high;
  1362. const low = Cartesian4.fromElements(
  1363. encodedCenter.low.x,
  1364. encodedCenter.low.y,
  1365. encodedCenter.low.z,
  1366. boundingSphere.radius,
  1367. scratchUpdatePolylineCartesian4
  1368. );
  1369. const nearFarCartesian = scratchNearFarCartesian2;
  1370. nearFarCartesian.x = 0.0;
  1371. nearFarCartesian.y = Number.MAX_VALUE;
  1372. const distanceDisplayCondition = polyline.distanceDisplayCondition;
  1373. if (defined(distanceDisplayCondition)) {
  1374. nearFarCartesian.x = distanceDisplayCondition.near;
  1375. nearFarCartesian.y = distanceDisplayCondition.far;
  1376. }
  1377. batchTable.setBatchedAttribute(polylineBatchIndex, 0, widthShowCartesian);
  1378. batchTable.setBatchedAttribute(polylineBatchIndex, 1, colorCartesian);
  1379. if (batchTable.attributes.length > 2) {
  1380. batchTable.setBatchedAttribute(polylineBatchIndex, 2, high);
  1381. batchTable.setBatchedAttribute(polylineBatchIndex, 3, low);
  1382. batchTable.setBatchedAttribute(polylineBatchIndex, 4, nearFarCartesian);
  1383. }
  1384. }
  1385. };
  1386. const morphPositionScratch = new Cartesian3();
  1387. const morphPrevPositionScratch = new Cartesian3();
  1388. const morphNextPositionScratch = new Cartesian3();
  1389. const morphVectorScratch = new Cartesian3();
  1390. PolylineBucket.prototype.writeForMorph = function (
  1391. positionArray,
  1392. positionIndex
  1393. ) {
  1394. const modelMatrix = this.modelMatrix;
  1395. const polylines = this.polylines;
  1396. const length = polylines.length;
  1397. for (let i = 0; i < length; ++i) {
  1398. const polyline = polylines[i];
  1399. const positions = polyline._segments.positions;
  1400. const lengths = polyline._segments.lengths;
  1401. const positionsLength = positions.length;
  1402. let segmentIndex = 0;
  1403. let count = 0;
  1404. for (let j = 0; j < positionsLength; ++j) {
  1405. let prevPosition;
  1406. if (j === 0) {
  1407. if (polyline._loop) {
  1408. prevPosition = positions[positionsLength - 2];
  1409. } else {
  1410. prevPosition = morphVectorScratch;
  1411. Cartesian3.subtract(positions[0], positions[1], prevPosition);
  1412. Cartesian3.add(positions[0], prevPosition, prevPosition);
  1413. }
  1414. } else {
  1415. prevPosition = positions[j - 1];
  1416. }
  1417. prevPosition = Matrix4.multiplyByPoint(
  1418. modelMatrix,
  1419. prevPosition,
  1420. morphPrevPositionScratch
  1421. );
  1422. const position = Matrix4.multiplyByPoint(
  1423. modelMatrix,
  1424. positions[j],
  1425. morphPositionScratch
  1426. );
  1427. let nextPosition;
  1428. if (j === positionsLength - 1) {
  1429. if (polyline._loop) {
  1430. nextPosition = positions[1];
  1431. } else {
  1432. nextPosition = morphVectorScratch;
  1433. Cartesian3.subtract(
  1434. positions[positionsLength - 1],
  1435. positions[positionsLength - 2],
  1436. nextPosition
  1437. );
  1438. Cartesian3.add(
  1439. positions[positionsLength - 1],
  1440. nextPosition,
  1441. nextPosition
  1442. );
  1443. }
  1444. } else {
  1445. nextPosition = positions[j + 1];
  1446. }
  1447. nextPosition = Matrix4.multiplyByPoint(
  1448. modelMatrix,
  1449. nextPosition,
  1450. morphNextPositionScratch
  1451. );
  1452. const segmentLength = lengths[segmentIndex];
  1453. if (j === count + segmentLength) {
  1454. count += segmentLength;
  1455. ++segmentIndex;
  1456. }
  1457. const segmentStart = j - count === 0;
  1458. const segmentEnd = j === count + lengths[segmentIndex] - 1;
  1459. const startK = segmentStart ? 2 : 0;
  1460. const endK = segmentEnd ? 2 : 4;
  1461. for (let k = startK; k < endK; ++k) {
  1462. EncodedCartesian3.writeElements(position, positionArray, positionIndex);
  1463. EncodedCartesian3.writeElements(
  1464. prevPosition,
  1465. positionArray,
  1466. positionIndex + 6
  1467. );
  1468. EncodedCartesian3.writeElements(
  1469. nextPosition,
  1470. positionArray,
  1471. positionIndex + 12
  1472. );
  1473. positionIndex += 6 * 3;
  1474. }
  1475. }
  1476. }
  1477. };
  1478. const scratchSegmentLengths = new Array(1);
  1479. PolylineBucket.prototype.updateIndices = function (
  1480. totalIndices,
  1481. vertexBufferOffset,
  1482. vertexArrayBuckets,
  1483. offset
  1484. ) {
  1485. let vaCount = vertexArrayBuckets.length - 1;
  1486. let bucketLocator = new VertexArrayBucketLocator(0, offset, this);
  1487. vertexArrayBuckets[vaCount].push(bucketLocator);
  1488. let count = 0;
  1489. let indices = totalIndices[totalIndices.length - 1];
  1490. let indicesCount = 0;
  1491. if (indices.length > 0) {
  1492. indicesCount = indices[indices.length - 1] + 1;
  1493. }
  1494. const polylines = this.polylines;
  1495. const length = polylines.length;
  1496. for (let i = 0; i < length; ++i) {
  1497. const polyline = polylines[i];
  1498. polyline._locatorBuckets = [];
  1499. let segments;
  1500. if (this.mode === SceneMode.SCENE3D) {
  1501. segments = scratchSegmentLengths;
  1502. const positionsLength = polyline._actualPositions.length;
  1503. if (positionsLength > 0) {
  1504. segments[0] = positionsLength;
  1505. } else {
  1506. continue;
  1507. }
  1508. } else {
  1509. segments = polyline._segments.lengths;
  1510. }
  1511. const numberOfSegments = segments.length;
  1512. if (numberOfSegments > 0) {
  1513. let segmentIndexCount = 0;
  1514. for (let j = 0; j < numberOfSegments; ++j) {
  1515. const segmentLength = segments[j] - 1.0;
  1516. for (let k = 0; k < segmentLength; ++k) {
  1517. if (indicesCount + 4 > CesiumMath.SIXTY_FOUR_KILOBYTES) {
  1518. polyline._locatorBuckets.push({
  1519. locator: bucketLocator,
  1520. count: segmentIndexCount,
  1521. });
  1522. segmentIndexCount = 0;
  1523. vertexBufferOffset.push(4);
  1524. indices = [];
  1525. totalIndices.push(indices);
  1526. indicesCount = 0;
  1527. bucketLocator.count = count;
  1528. count = 0;
  1529. offset = 0;
  1530. bucketLocator = new VertexArrayBucketLocator(0, 0, this);
  1531. vertexArrayBuckets[++vaCount] = [bucketLocator];
  1532. }
  1533. indices.push(indicesCount, indicesCount + 2, indicesCount + 1);
  1534. indices.push(indicesCount + 1, indicesCount + 2, indicesCount + 3);
  1535. segmentIndexCount += 6;
  1536. count += 6;
  1537. offset += 6;
  1538. indicesCount += 4;
  1539. }
  1540. }
  1541. polyline._locatorBuckets.push({
  1542. locator: bucketLocator,
  1543. count: segmentIndexCount,
  1544. });
  1545. if (indicesCount + 4 > CesiumMath.SIXTY_FOUR_KILOBYTES) {
  1546. vertexBufferOffset.push(0);
  1547. indices = [];
  1548. totalIndices.push(indices);
  1549. indicesCount = 0;
  1550. bucketLocator.count = count;
  1551. offset = 0;
  1552. count = 0;
  1553. bucketLocator = new VertexArrayBucketLocator(0, 0, this);
  1554. vertexArrayBuckets[++vaCount] = [bucketLocator];
  1555. }
  1556. }
  1557. polyline._clean();
  1558. }
  1559. bucketLocator.count = count;
  1560. return offset;
  1561. };
  1562. PolylineBucket.prototype.getPolylineStartIndex = function (polyline) {
  1563. const polylines = this.polylines;
  1564. let positionIndex = 0;
  1565. const length = polylines.length;
  1566. for (let i = 0; i < length; ++i) {
  1567. const p = polylines[i];
  1568. if (p === polyline) {
  1569. break;
  1570. }
  1571. positionIndex += p._actualLength;
  1572. }
  1573. return positionIndex;
  1574. };
  1575. const scratchSegments = {
  1576. positions: undefined,
  1577. lengths: undefined,
  1578. };
  1579. const scratchLengths = new Array(1);
  1580. const pscratch = new Cartesian3();
  1581. const scratchCartographic = new Cartographic();
  1582. PolylineBucket.prototype.getSegments = function (polyline, projection) {
  1583. let positions = polyline._actualPositions;
  1584. if (this.mode === SceneMode.SCENE3D) {
  1585. scratchLengths[0] = positions.length;
  1586. scratchSegments.positions = positions;
  1587. scratchSegments.lengths = scratchLengths;
  1588. return scratchSegments;
  1589. }
  1590. if (intersectsIDL(polyline)) {
  1591. positions = polyline._segments.positions;
  1592. }
  1593. const ellipsoid = projection.ellipsoid;
  1594. const newPositions = [];
  1595. const modelMatrix = this.modelMatrix;
  1596. const length = positions.length;
  1597. let position;
  1598. let p = pscratch;
  1599. for (let n = 0; n < length; ++n) {
  1600. position = positions[n];
  1601. p = Matrix4.multiplyByPoint(modelMatrix, position, p);
  1602. newPositions.push(
  1603. projection.project(
  1604. ellipsoid.cartesianToCartographic(p, scratchCartographic)
  1605. )
  1606. );
  1607. }
  1608. if (newPositions.length > 0) {
  1609. polyline._boundingVolume2D = BoundingSphere.fromPoints(
  1610. newPositions,
  1611. polyline._boundingVolume2D
  1612. );
  1613. const center2D = polyline._boundingVolume2D.center;
  1614. polyline._boundingVolume2D.center = new Cartesian3(
  1615. center2D.z,
  1616. center2D.x,
  1617. center2D.y
  1618. );
  1619. }
  1620. scratchSegments.positions = newPositions;
  1621. scratchSegments.lengths = polyline._segments.lengths;
  1622. return scratchSegments;
  1623. };
  1624. let scratchPositionsArray;
  1625. PolylineBucket.prototype.writeUpdate = function (
  1626. index,
  1627. polyline,
  1628. positionBuffer,
  1629. projection
  1630. ) {
  1631. const mode = this.mode;
  1632. const maxLon = projection.ellipsoid.maximumRadius * CesiumMath.PI;
  1633. let positionsLength = polyline._actualLength;
  1634. if (positionsLength) {
  1635. index += this.getPolylineStartIndex(polyline);
  1636. let positionArray = scratchPositionsArray;
  1637. const positionsArrayLength = 6 * positionsLength * 3;
  1638. if (
  1639. !defined(positionArray) ||
  1640. positionArray.length < positionsArrayLength
  1641. ) {
  1642. positionArray = scratchPositionsArray = new Float32Array(
  1643. positionsArrayLength
  1644. );
  1645. } else if (positionArray.length > positionsArrayLength) {
  1646. positionArray = new Float32Array(
  1647. positionArray.buffer,
  1648. 0,
  1649. positionsArrayLength
  1650. );
  1651. }
  1652. const segments = this.getSegments(polyline, projection);
  1653. const positions = segments.positions;
  1654. const lengths = segments.lengths;
  1655. let positionIndex = 0;
  1656. let segmentIndex = 0;
  1657. let count = 0;
  1658. let position;
  1659. positionsLength = positions.length;
  1660. for (let i = 0; i < positionsLength; ++i) {
  1661. if (i === 0) {
  1662. if (polyline._loop) {
  1663. position = positions[positionsLength - 2];
  1664. } else {
  1665. position = scratchWriteVector;
  1666. Cartesian3.subtract(positions[0], positions[1], position);
  1667. Cartesian3.add(positions[0], position, position);
  1668. }
  1669. } else {
  1670. position = positions[i - 1];
  1671. }
  1672. Cartesian3.clone(position, scratchWritePrevPosition);
  1673. Cartesian3.clone(positions[i], scratchWritePosition);
  1674. if (i === positionsLength - 1) {
  1675. if (polyline._loop) {
  1676. position = positions[1];
  1677. } else {
  1678. position = scratchWriteVector;
  1679. Cartesian3.subtract(
  1680. positions[positionsLength - 1],
  1681. positions[positionsLength - 2],
  1682. position
  1683. );
  1684. Cartesian3.add(positions[positionsLength - 1], position, position);
  1685. }
  1686. } else {
  1687. position = positions[i + 1];
  1688. }
  1689. Cartesian3.clone(position, scratchWriteNextPosition);
  1690. const segmentLength = lengths[segmentIndex];
  1691. if (i === count + segmentLength) {
  1692. count += segmentLength;
  1693. ++segmentIndex;
  1694. }
  1695. const segmentStart = i - count === 0;
  1696. const segmentEnd = i === count + lengths[segmentIndex] - 1;
  1697. if (mode === SceneMode.SCENE2D) {
  1698. scratchWritePrevPosition.z = 0.0;
  1699. scratchWritePosition.z = 0.0;
  1700. scratchWriteNextPosition.z = 0.0;
  1701. }
  1702. if (mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) {
  1703. if (
  1704. (segmentStart || segmentEnd) &&
  1705. maxLon - Math.abs(scratchWritePosition.x) < 1.0
  1706. ) {
  1707. if (
  1708. (scratchWritePosition.x < 0.0 &&
  1709. scratchWritePrevPosition.x > 0.0) ||
  1710. (scratchWritePosition.x > 0.0 && scratchWritePrevPosition.x < 0.0)
  1711. ) {
  1712. Cartesian3.clone(scratchWritePosition, scratchWritePrevPosition);
  1713. }
  1714. if (
  1715. (scratchWritePosition.x < 0.0 &&
  1716. scratchWriteNextPosition.x > 0.0) ||
  1717. (scratchWritePosition.x > 0.0 && scratchWriteNextPosition.x < 0.0)
  1718. ) {
  1719. Cartesian3.clone(scratchWritePosition, scratchWriteNextPosition);
  1720. }
  1721. }
  1722. }
  1723. const startJ = segmentStart ? 2 : 0;
  1724. const endJ = segmentEnd ? 2 : 4;
  1725. for (let j = startJ; j < endJ; ++j) {
  1726. EncodedCartesian3.writeElements(
  1727. scratchWritePosition,
  1728. positionArray,
  1729. positionIndex
  1730. );
  1731. EncodedCartesian3.writeElements(
  1732. scratchWritePrevPosition,
  1733. positionArray,
  1734. positionIndex + 6
  1735. );
  1736. EncodedCartesian3.writeElements(
  1737. scratchWriteNextPosition,
  1738. positionArray,
  1739. positionIndex + 12
  1740. );
  1741. positionIndex += 6 * 3;
  1742. }
  1743. }
  1744. positionBuffer.copyFromArrayView(
  1745. positionArray,
  1746. 6 * 3 * Float32Array.BYTES_PER_ELEMENT * index
  1747. );
  1748. }
  1749. };
  1750. export default PolylineCollection;