@hztxi/cesium-engine 25.0.6 → 25.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/Build/Workers/chunk-2GJAWJJT.js +1344 -0
  2. package/Build/Workers/chunk-2J2VS5RI.js +288 -0
  3. package/Build/Workers/chunk-375SHKGW.js +44 -0
  4. package/Build/Workers/chunk-5FYUSABU.js +945 -0
  5. package/Build/Workers/chunk-5WU37CUJ.js +630 -0
  6. package/Build/Workers/chunk-6EBDN5WS.js +97 -0
  7. package/Build/Workers/chunk-75HIWCEV.js +301 -0
  8. package/Build/Workers/chunk-7AMVPXZY.js +1992 -0
  9. package/Build/Workers/chunk-7BMDD6SM.js +124 -0
  10. package/Build/Workers/chunk-7OME2OKM.js +171 -0
  11. package/Build/Workers/chunk-A2BROW3D.js +352 -0
  12. package/Build/Workers/chunk-B4XOVZGA.js +476 -0
  13. package/Build/Workers/chunk-CDLPLYLV.js +1287 -0
  14. package/Build/Workers/chunk-CEKCKPFN.js +368 -0
  15. package/Build/Workers/chunk-CHMDKP5L.js +2342 -0
  16. package/Build/Workers/chunk-CWGB3D4Z.js +257 -0
  17. package/Build/Workers/chunk-DQEUGBGA.js +2719 -0
  18. package/Build/Workers/chunk-DUBE5G2H.js +497 -0
  19. package/Build/Workers/chunk-E4PBNWWK.js +58 -0
  20. package/Build/Workers/chunk-E5MWOMVC.js +57 -0
  21. package/Build/Workers/chunk-EE3XBOQI.js +1483 -0
  22. package/Build/Workers/chunk-EHA2ZDPZ.js +415 -0
  23. package/Build/Workers/chunk-EKEKPAJF.js +454 -0
  24. package/Build/Workers/chunk-EOHKUXPX.js +198 -0
  25. package/Build/Workers/chunk-ERENMHG2.js +754 -0
  26. package/Build/Workers/chunk-ESQGFRLZ.js +163 -0
  27. package/Build/Workers/chunk-FV4OWDV4.js +9230 -0
  28. package/Build/Workers/chunk-FYRNNQSL.js +280 -0
  29. package/Build/Workers/chunk-JMF4ATAS.js +3794 -0
  30. package/Build/Workers/chunk-KEJKMITY.js +82 -0
  31. package/Build/Workers/chunk-KW2QZDUS.js +147 -0
  32. package/Build/Workers/chunk-L6B5XPBL.js +73 -0
  33. package/Build/Workers/chunk-LGJXN2VP.js +352 -0
  34. package/Build/Workers/chunk-LY2UG3HV.js +38 -0
  35. package/Build/Workers/chunk-NCPJBP24.js +140 -0
  36. package/Build/Workers/chunk-NFY5V6MR.js +102 -0
  37. package/Build/Workers/chunk-OYPSUED3.js +1067 -0
  38. package/Build/Workers/chunk-QIOOTLWN.js +56 -0
  39. package/Build/Workers/chunk-QUPRALOD.js +101 -0
  40. package/Build/Workers/chunk-RBEVHHXK.js +3201 -0
  41. package/Build/Workers/chunk-RHO5HV52.js +305 -0
  42. package/Build/Workers/chunk-SVCZUP5R.js +837 -0
  43. package/Build/Workers/chunk-TBHW5UME.js +973 -0
  44. package/Build/Workers/chunk-TPOX6IOR.js +1054 -0
  45. package/Build/Workers/chunk-ULNBTI6R.js +138 -0
  46. package/Build/Workers/chunk-UMUS2AQQ.js +398 -0
  47. package/Build/Workers/chunk-UZ7GTP2L.js +162 -0
  48. package/Build/Workers/chunk-W3C7OHXF.js +387 -0
  49. package/Build/Workers/chunk-WB5GWYDP.js +455 -0
  50. package/Build/Workers/chunk-WN6MAL25.js +682 -0
  51. package/Build/Workers/chunk-WTR7KMQA.js +693 -0
  52. package/Build/Workers/chunk-WTTFMU3P.js +241 -0
  53. package/Build/Workers/chunk-WULONJZC.js +1800 -0
  54. package/Build/Workers/chunk-WZPMAVTV.js +511 -0
  55. package/Build/Workers/chunk-Y7KVUJEB.js +219 -0
  56. package/Build/Workers/chunk-Z6JV4DZG.js +1046 -0
  57. package/Build/Workers/chunk-ZTK5EBUI.js +76 -0
  58. package/Build/Workers/chunk-ZZPDORJK.js +56 -0
  59. package/Build/Workers/combineGeometry.js +24 -24
  60. package/Build/Workers/createBoxGeometry.js +18 -18
  61. package/Build/Workers/createBoxOutlineGeometry.js +16 -16
  62. package/Build/Workers/createCircleGeometry.js +26 -26
  63. package/Build/Workers/createCircleOutlineGeometry.js +19 -19
  64. package/Build/Workers/createCoplanarPolygonGeometry.js +33 -33
  65. package/Build/Workers/createCoplanarPolygonOutlineGeometry.js +31 -31
  66. package/Build/Workers/createCorridorGeometry.js +29 -29
  67. package/Build/Workers/createCorridorOutlineGeometry.js +28 -28
  68. package/Build/Workers/createCylinderGeometry.js +20 -20
  69. package/Build/Workers/createCylinderOutlineGeometry.js +18 -18
  70. package/Build/Workers/createEllipseGeometry.js +26 -26
  71. package/Build/Workers/createEllipseOutlineGeometry.js +19 -19
  72. package/Build/Workers/createEllipsoidGeometry.js +19 -19
  73. package/Build/Workers/createEllipsoidOutlineGeometry.js +18 -18
  74. package/Build/Workers/createFrustumGeometry.js +18 -18
  75. package/Build/Workers/createFrustumOutlineGeometry.js +18 -18
  76. package/Build/Workers/createGeometry.js +24 -24
  77. package/Build/Workers/createGroundPolylineGeometry.js +95 -31
  78. package/Build/Workers/createPlaneGeometry.js +16 -16
  79. package/Build/Workers/createPlaneOutlineGeometry.js +15 -15
  80. package/Build/Workers/createPolygonGeometry.js +32 -32
  81. package/Build/Workers/createPolygonOutlineGeometry.js +30 -30
  82. package/Build/Workers/createPolylineGeometry.js +25 -25
  83. package/Build/Workers/createPolylineVolumeGeometry.js +31 -31
  84. package/Build/Workers/createPolylineVolumeOutlineGeometry.js +27 -27
  85. package/Build/Workers/createRectangleGeometry.js +27 -27
  86. package/Build/Workers/createRectangleOutlineGeometry.js +20 -20
  87. package/Build/Workers/createSimplePolylineGeometry.js +23 -23
  88. package/Build/Workers/createSphereGeometry.js +19 -19
  89. package/Build/Workers/createSphereOutlineGeometry.js +18 -18
  90. package/Build/Workers/createTaskProcessorWorker.js +3 -3
  91. package/Build/Workers/createVectorTileClampedPolylines.js +14 -14
  92. package/Build/Workers/createVectorTileGeometries.js +24 -24
  93. package/Build/Workers/createVectorTilePoints.js +13 -13
  94. package/Build/Workers/createVectorTilePolygons.js +22 -22
  95. package/Build/Workers/createVectorTilePolylines.js +14 -14
  96. package/Build/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -26
  97. package/Build/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +23 -23
  98. package/Build/Workers/createVerticesFromHeightmap.js +23 -23
  99. package/Build/Workers/createVerticesFromQuantizedTerrainMesh.js +21 -21
  100. package/Build/Workers/createWallGeometry.js +24 -24
  101. package/Build/Workers/createWallOutlineGeometry.js +23 -23
  102. package/Build/Workers/decodeDraco.js +10 -10
  103. package/Build/Workers/decodeGoogleEarthEnterprisePacket.js +5 -5
  104. package/Build/Workers/decodeI3S.js +10 -10
  105. package/Build/Workers/gaussianSplatSorter.js +4 -4
  106. package/Build/Workers/gaussianSplatTextureGenerator.js +4 -4
  107. package/Build/Workers/incrementallyBuildTerrainPicker.js +10 -10
  108. package/Build/Workers/transcodeKTX2.js +6 -6
  109. package/Build/Workers/transferTypedArrayTest.js +1 -1
  110. package/Build/Workers/upsampleQuantizedTerrainMesh.js +23 -23
  111. package/Build/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -26
  112. package/Source/Core/GroundPolylineGeometry.js +99 -4
  113. package/Source/Core/PolylinePipeline.js +9 -2
  114. package/Source/DataSources/PolylineGeometryUpdater.js +125 -7
  115. package/Source/Scene/Camera.js +56 -3
  116. package/Source/Scene/CameraFlightPath.js +14 -0
  117. package/Source/Scene/ColumbusWorlds.js +190 -0
  118. package/Source/Scene/FrameState.js +10 -0
  119. package/Source/Scene/Globe.js +89 -22
  120. package/Source/Scene/GlobeDepth.js +216 -5
  121. package/Source/Scene/GlobeSurfaceTileProvider.js +897 -678
  122. package/Source/Scene/GroundPolylinePrimitive.js +2 -1
  123. package/Source/Scene/Polyline.js +32 -5
  124. package/Source/Scene/PolylineCollection.js +37 -0
  125. package/Source/Scene/Primitive.js +12 -13
  126. package/Source/Scene/PrimitivePipeline.js +158 -28
  127. package/Source/Scene/Scene.js +428 -5
  128. package/Source/Scene/SceneTransforms.js +17 -0
  129. package/Source/Scene/ScreenSpaceCameraController.js +12 -3
  130. package/Source/Shaders/PolylineShadowVolumeFS.glsl +14 -0
  131. package/Source/Shaders/PolylineShadowVolumeFS.js +14 -0
  132. package/Source/Shaders/RestoreDepth.glsl +8 -0
  133. package/Source/Shaders/RestoreDepth.js +10 -0
  134. package/index.d.ts +535 -1393
  135. package/index.js +138 -136
  136. package/package.json +1 -1
@@ -182,7 +182,7 @@ function GroundPolylinePrimitive(options) {
182
182
 
183
183
  this._retainedGeometryInstances = undefined;
184
184
 
185
- /** @private Root {@link GroundPolylineGeometry} ref from the last inner {@link Primitive} build. */
185
+ /** Root {@link GroundPolylineGeometry} ref from the last inner {@link Primitive} build. @private */
186
186
  this._lastGroundPolylineRootGeometry = undefined;
187
187
 
188
188
  this._ready = false;
@@ -816,6 +816,7 @@ GroundPolylinePrimitive.prototype.update = function (frameState) {
816
816
  GroundPolylineGeometry.setProjectionAndEllipsoid(
817
817
  geometryInstance.geometry,
818
818
  frameState.mapProjection,
819
+ frameState.columbusRepeatWorlds,
819
820
  );
820
821
  geometryInstance.geometry._verticalExaggeration =
821
822
  frameState.verticalExaggeration;
@@ -34,6 +34,20 @@ import Material from "./Material.js";
34
34
  * @see PolylineCollection
35
35
  *
36
36
  */
37
+ function makePolylineSegments(polyline, modelMatrix, columbusRepeatWorlds) {
38
+ if (columbusRepeatWorlds) {
39
+ // Columbus repeat worlds: keep the polyline continuous through the
40
+ // ±180° meridian instead of splitting it at the IDL.
41
+ // PolylineCollection's getSegments lifts the projected positions onto
42
+ // adjacent world copies so the line draws through the seam.
43
+ return {
44
+ positions: polyline._actualPositions,
45
+ lengths: [polyline._actualPositions.length],
46
+ };
47
+ }
48
+ return PolylinePipeline.wrapLongitude(polyline._actualPositions, modelMatrix);
49
+ }
50
+
37
51
  function Polyline(options, polylineCollection) {
38
52
  options = options ?? Frozen.EMPTY_OBJECT;
39
53
 
@@ -76,9 +90,13 @@ function Polyline(options, polylineCollection) {
76
90
  }
77
91
 
78
92
  this._modelMatrix = modelMatrix;
79
- this._segments = PolylinePipeline.wrapLongitude(
80
- this._actualPositions,
93
+ this._columbusRepeatWorlds =
94
+ defined(polylineCollection) &&
95
+ polylineCollection._columbusRepeatWorlds === true;
96
+ this._segments = makePolylineSegments(
97
+ this,
81
98
  modelMatrix,
99
+ this._columbusRepeatWorlds,
82
100
  );
83
101
 
84
102
  this._actualLength = undefined;
@@ -371,10 +389,19 @@ Polyline.prototype.update = function () {
371
389
  const positionsChanged =
372
390
  this._propertiesChanged[POSITION_INDEX] > 0 ||
373
391
  this._propertiesChanged[POSITION_SIZE_INDEX] > 0;
374
- if (!Matrix4.equals(modelMatrix, this._modelMatrix) || positionsChanged) {
375
- this._segments = PolylinePipeline.wrapLongitude(
376
- this._actualPositions,
392
+ const columbusRepeatWorlds =
393
+ defined(this._polylineCollection) &&
394
+ this._polylineCollection._columbusRepeatWorlds === true;
395
+ if (
396
+ !Matrix4.equals(modelMatrix, this._modelMatrix) ||
397
+ positionsChanged ||
398
+ columbusRepeatWorlds !== this._columbusRepeatWorlds
399
+ ) {
400
+ this._columbusRepeatWorlds = columbusRepeatWorlds;
401
+ this._segments = makePolylineSegments(
402
+ this,
377
403
  modelMatrix,
404
+ columbusRepeatWorlds,
378
405
  );
379
406
  this._boundingVolumeWC = BoundingSphere.transform(
380
407
  this._boundingVolume,
@@ -32,6 +32,7 @@ import PolylineFS from "../Shaders/PolylineFS.js";
32
32
  import PolylineVS from "../Shaders/PolylineVS.js";
33
33
  import BatchTable from "./BatchTable.js";
34
34
  import BlendingState from "./BlendingState.js";
35
+ import ColumbusWorlds from "./ColumbusWorlds.js";
35
36
  import Material from "./Material.js";
36
37
  import Polyline from "./Polyline.js";
37
38
  import SceneMode from "./SceneMode.js";
@@ -168,6 +169,11 @@ function PolylineCollection(options) {
168
169
 
169
170
  this._mode = undefined;
170
171
 
172
+ // Whether Columbus repeat worlds are enabled; when true, polylines are
173
+ // kept continuous through the ±180° meridian instead of being split at the
174
+ // IDL. Set each update() from the frame state.
175
+ this._columbusRepeatWorlds = false;
176
+
171
177
  this._polylinesToUpdate = [];
172
178
  this._vertexArrays = [];
173
179
  this._positionBuffer = undefined;
@@ -423,6 +429,16 @@ PolylineCollection.prototype.update = function (frameState) {
423
429
 
424
430
  updateMode(this, frameState);
425
431
 
432
+ const columbusRepeatWorlds =
433
+ frameState.columbusRepeatWorlds === true &&
434
+ frameState.mode === SceneMode.COLUMBUS_VIEW;
435
+ if (this._columbusRepeatWorlds !== columbusRepeatWorlds) {
436
+ // Toggling repeat worlds changes whether polylines are split at the IDL,
437
+ // which changes vertex buffer sizes; rebuild the vertex arrays.
438
+ this._columbusRepeatWorlds = columbusRepeatWorlds;
439
+ this._createVertexArray = true;
440
+ }
441
+
426
442
  const context = frameState.context;
427
443
  const projection = frameState.mapProjection;
428
444
  let polyline;
@@ -1784,6 +1800,27 @@ PolylineBucket.prototype.getSegments = function (polyline, projection) {
1784
1800
  );
1785
1801
  }
1786
1802
 
1803
+ if (
1804
+ newPositions.length > 0 &&
1805
+ this.mode === SceneMode.COLUMBUS_VIEW &&
1806
+ polyline._polylineCollection._columbusRepeatWorlds === true
1807
+ ) {
1808
+ // Columbus repeat worlds: the IDL split is disabled in this mode, so lift
1809
+ // consecutive projected u values onto the same world copy. The line stays
1810
+ // in the primary world; the scene renders the visible world copies by
1811
+ // shifting the camera, so a line crossing the seam needs no per-copy
1812
+ // placement of its own.
1813
+ const worldWidth = ColumbusWorlds.getWorldWidth(projection);
1814
+ for (let n = 1; n < newPositions.length; ++n) {
1815
+ const du = newPositions[n].x - newPositions[n - 1].x;
1816
+ if (du > worldWidth * 0.5) {
1817
+ newPositions[n].x -= worldWidth;
1818
+ } else if (du < -worldWidth * 0.5) {
1819
+ newPositions[n].x += worldWidth;
1820
+ }
1821
+ }
1822
+ }
1823
+
1787
1824
  if (newPositions.length > 0) {
1788
1825
  polyline._boundingVolume2D = BoundingSphere.fromPoints(
1789
1826
  newPositions,
@@ -683,18 +683,20 @@ function createBatchTable(primitive, context) {
683
683
  primitive._batchTableOffsetAttribute2DIndex = offset2DIndex;
684
684
  }
685
685
 
686
- function cloneAttribute(attribute) {
687
- let clonedValues;
688
- if (Array.isArray(attribute.values)) {
689
- clonedValues = attribute.values.slice(0);
690
- } else {
691
- clonedValues = new attribute.values.constructor(attribute.values);
686
+ function cloneValues(values) {
687
+ if (Array.isArray(values)) {
688
+ return values.slice(0);
692
689
  }
690
+ const ValuesConstructor = values.constructor;
691
+ return new ValuesConstructor(values);
692
+ }
693
+
694
+ function cloneAttribute(attribute) {
693
695
  return new GeometryAttribute({
694
696
  componentDatatype: attribute.componentDatatype,
695
697
  componentsPerAttribute: attribute.componentsPerAttribute,
696
698
  normalize: attribute.normalize,
697
- values: clonedValues,
699
+ values: cloneValues(attribute.values),
698
700
  });
699
701
  }
700
702
 
@@ -709,12 +711,7 @@ function cloneGeometry(geometry) {
709
711
 
710
712
  let indices;
711
713
  if (defined(geometry.indices)) {
712
- const sourceValues = geometry.indices;
713
- if (Array.isArray(sourceValues)) {
714
- indices = sourceValues.slice(0);
715
- } else {
716
- indices = new sourceValues.constructor(sourceValues);
717
- }
714
+ indices = cloneValues(geometry.indices);
718
715
  }
719
716
 
720
717
  return new Geometry({
@@ -1278,6 +1275,7 @@ function loadAsynchronous(primitive, frameState) {
1278
1275
  projection: projection,
1279
1276
  elementIndexUintSupported: frameState.context.elementIndexUint,
1280
1277
  scene3DOnly: scene3DOnly,
1278
+ columbusRepeatWorlds: frameState.columbusRepeatWorlds === true,
1281
1279
  vertexCacheOptimize: primitive.vertexCacheOptimize,
1282
1280
  compressVertices: primitive.compressVertices,
1283
1281
  modelMatrix: primitive.modelMatrix,
@@ -1360,6 +1358,7 @@ function loadSynchronous(primitive, frameState) {
1360
1358
  projection: projection,
1361
1359
  elementIndexUintSupported: frameState.context.elementIndexUint,
1362
1360
  scene3DOnly: scene3DOnly,
1361
+ columbusRepeatWorlds: frameState.columbusRepeatWorlds === true,
1363
1362
  vertexCacheOptimize: primitive.vertexCacheOptimize,
1364
1363
  compressVertices: primitive.compressVertices,
1365
1364
  modelMatrix: primitive.modelMatrix,
@@ -12,6 +12,7 @@ import IndexDatatype from "../Core/IndexDatatype.js";
12
12
  import Matrix4 from "../Core/Matrix4.js";
13
13
  import OffsetGeometryInstanceAttribute from "../Core/OffsetGeometryInstanceAttribute.js";
14
14
  import WebMercatorProjection from "../Core/WebMercatorProjection.js";
15
+ import ColumbusWorlds from "./ColumbusWorlds.js";
15
16
 
16
17
  function transformToWorldCoordinates(
17
18
  instances,
@@ -84,11 +85,120 @@ function addBatchIds(instances) {
84
85
  }
85
86
  }
86
87
 
88
+ /**
89
+ * Keeps a polyline's projected 2D attributes on one contiguous world copy in
90
+ * Columbus repeat-worlds mode. PolylineGeometry subdivides along the arc and
91
+ * projects every vertex independently, so a line crossing the ±180° meridian
92
+ * ends up with consecutive vertices on opposite sides of the primary world,
93
+ * connected by an edge drawn across the whole map. Consecutive vertices whose
94
+ * projected u jumps by roughly a full world width are seam crossings: lift
95
+ * them onto the adjacent copy (the same rule PolylineCollection.getSegments
96
+ * applies). The per-vertex lift is computed from `position2D` and applied
97
+ * identically to `prevPosition2D` and `nextPosition2D` so each vertex's
98
+ * extrusion references stay on the copy the vertex itself was lifted to.
99
+ *
100
+ * Other primitive types (no prev/next position attributes) keep the stock
101
+ * per-side projection; their own IDL splitting still applies.
102
+ *
103
+ * @private
104
+ */
105
+ function liftPolyline2DToWorldCopy(geometry, projection) {
106
+ const attributes = geometry.attributes;
107
+ const position2D = attributes.position2D;
108
+ const prevPosition2D = attributes.prevPosition2D;
109
+ const nextPosition2D = attributes.nextPosition2D;
110
+ if (
111
+ !defined(position2D) ||
112
+ !defined(prevPosition2D) ||
113
+ !defined(nextPosition2D)
114
+ ) {
115
+ return;
116
+ }
117
+
118
+ const worldWidth = ColumbusWorlds.getWorldWidth(projection);
119
+ const halfWorldWidth = worldWidth * 0.5;
120
+ const positions = position2D.values;
121
+ const vertexCount = positions.length / 3;
122
+ const lifts = new Float64Array(vertexCount);
123
+
124
+ let lift = 0.0;
125
+ let lifted = false;
126
+ for (let i = 1; i < vertexCount; ++i) {
127
+ const du = positions[i * 3] - (positions[(i - 1) * 3] + lift);
128
+ if (du > halfWorldWidth) {
129
+ lift -= worldWidth;
130
+ lifted = true;
131
+ } else if (du < -halfWorldWidth) {
132
+ lift += worldWidth;
133
+ lifted = true;
134
+ }
135
+ lifts[i] = lift;
136
+ }
137
+ if (!lifted) {
138
+ return;
139
+ }
140
+
141
+ const prevPositions = prevPosition2D.values;
142
+ const nextPositions = nextPosition2D.values;
143
+ for (let i = 0; i < vertexCount; ++i) {
144
+ const vertexLift = lifts[i];
145
+ if (vertexLift !== 0.0) {
146
+ positions[i * 3] += vertexLift;
147
+ prevPositions[i * 3] += vertexLift;
148
+ nextPositions[i * 3] += vertexLift;
149
+ }
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Replays the world-copy shift that {@link GroundPolylineGeometry} baked into
155
+ * its 2D shadow-volume attributes onto the projected volume positions. The
156
+ * volume positions are projected here, after the geometry was built, so a
157
+ * ground polyline crossing the antimeridian would otherwise keep one half on
158
+ * the neighbouring world copy while its plane tests sit on the other, and the
159
+ * shifted half is discarded by the fragment shader.
160
+ *
161
+ * @private
162
+ */
163
+ function liftGroundPolyline2DToWorldCopy(geometry, projection) {
164
+ const attributes = geometry.attributes;
165
+ const startHiLo2D = attributes.startHiLo2D;
166
+ const position2D = attributes.position2D;
167
+ if (!defined(startHiLo2D) || !defined(position2D)) {
168
+ return;
169
+ }
170
+
171
+ const worldWidth = ColumbusWorlds.getWorldWidth(projection);
172
+ const halfWorldWidth = worldWidth * 0.5;
173
+ if (!(halfWorldWidth > 0.0)) {
174
+ return;
175
+ }
176
+
177
+ const starts = startHiLo2D.values;
178
+ const positions = position2D.values;
179
+ const count = Math.min(starts.length / 4, positions.length / 3);
180
+ for (let i = 0; i < count; ++i) {
181
+ // startHiLo2D holds the segment start in the unwrapped projection space
182
+ // (GroundPolylineGeometry nudges and lifts the 2D attributes so a line
183
+ // crossing the antimeridian stays on one copy), while the volume position
184
+ // is projected from the ECEF position and therefore wraps. Snap the
185
+ // position onto the world copy of its own segment: the two can only
186
+ // differ by whole worlds.
187
+ const startU = starts[i * 4] + starts[i * 4 + 2];
188
+ const u = positions[i * 3];
189
+ const delta = startU - u;
190
+ if (delta > halfWorldWidth || delta < -halfWorldWidth) {
191
+ positions[i * 3] = u + Math.round(delta / worldWidth) * worldWidth;
192
+ }
193
+ }
194
+ }
195
+
87
196
  function geometryPipeline(parameters) {
88
197
  const instances = parameters.instances;
89
198
  const projection = parameters.projection;
90
199
  const uintIndexSupport = parameters.elementIndexUintSupported;
91
200
  const scene3DOnly = parameters.scene3DOnly;
201
+ const columbusRepeatWorlds = parameters.columbusRepeatWorlds === true;
92
202
  const vertexCacheOptimize = parameters.vertexCacheOptimize;
93
203
  const compressVertices = parameters.compressVertices;
94
204
  const modelMatrix = parameters.modelMatrix;
@@ -170,41 +280,59 @@ function geometryPipeline(parameters) {
170
280
  // Split positions for GPU RTE
171
281
  const attributes = geometry.attributes;
172
282
  if (!scene3DOnly) {
283
+ // Collect DOUBLE attribute names up front: projectTo2D renames the
284
+ // attributes it touches, so projecting while iterating them risks
285
+ // reprocessing the freshly added *3D attributes.
286
+ const doubleAttributeNames = [];
173
287
  for (const name in attributes) {
174
288
  if (
175
289
  attributes.hasOwnProperty(name) &&
176
290
  attributes[name].componentDatatype === ComponentDatatype.DOUBLE
177
291
  ) {
178
- const name3D = `${name}3D`;
179
- const name2D = `${name}2D`;
292
+ doubleAttributeNames.push(name);
293
+ }
294
+ }
180
295
 
181
- // Compute 2D positions
182
- GeometryPipeline.projectTo2D(
183
- geometry,
184
- name,
185
- name3D,
186
- name2D,
187
- projection,
188
- );
189
- if (defined(geometry.boundingSphere) && name === "position") {
190
- geometry.boundingSphereCV = BoundingSphere.fromVertices(
191
- geometry.attributes.position2D.values,
192
- );
193
- }
296
+ for (let a = 0; a < doubleAttributeNames.length; ++a) {
297
+ const name = doubleAttributeNames[a];
298
+ GeometryPipeline.projectTo2D(
299
+ geometry,
300
+ name,
301
+ `${name}3D`,
302
+ `${name}2D`,
303
+ projection,
304
+ );
305
+ }
194
306
 
195
- GeometryPipeline.encodeAttribute(
196
- geometry,
197
- name3D,
198
- `${name3D}High`,
199
- `${name3D}Low`,
200
- );
201
- GeometryPipeline.encodeAttribute(
202
- geometry,
203
- name2D,
204
- `${name2D}High`,
205
- `${name2D}Low`,
206
- );
207
- }
307
+ if (columbusRepeatWorlds) {
308
+ liftPolyline2DToWorldCopy(geometry, projection);
309
+ liftGroundPolyline2DToWorldCopy(geometry, projection);
310
+ }
311
+
312
+ // Compute the Columbus-view bounding sphere before encodeAttribute
313
+ // renames position2D into position2DHigh/Low.
314
+ if (defined(geometry.boundingSphere) && defined(attributes.position2D)) {
315
+ geometry.boundingSphereCV = BoundingSphere.fromVertices(
316
+ attributes.position2D.values,
317
+ );
318
+ }
319
+
320
+ for (let a = 0; a < doubleAttributeNames.length; ++a) {
321
+ const name3D = `${doubleAttributeNames[a]}3D`;
322
+ const name2D = `${doubleAttributeNames[a]}2D`;
323
+
324
+ GeometryPipeline.encodeAttribute(
325
+ geometry,
326
+ name3D,
327
+ `${name3D}High`,
328
+ `${name3D}Low`,
329
+ );
330
+ GeometryPipeline.encodeAttribute(
331
+ geometry,
332
+ name2D,
333
+ `${name2D}High`,
334
+ `${name2D}Low`,
335
+ );
208
336
  }
209
337
  } else {
210
338
  for (const name in attributes) {
@@ -715,6 +843,7 @@ PrimitivePipeline.packCombineGeometryParameters = function (
715
843
  isGeographic: parameters.projection instanceof GeographicProjection,
716
844
  elementIndexUintSupported: parameters.elementIndexUintSupported,
717
845
  scene3DOnly: parameters.scene3DOnly,
846
+ columbusRepeatWorlds: parameters.columbusRepeatWorlds === true,
718
847
  vertexCacheOptimize: parameters.vertexCacheOptimize,
719
848
  compressVertices: parameters.compressVertices,
720
849
  modelMatrix: parameters.modelMatrix,
@@ -761,6 +890,7 @@ PrimitivePipeline.unpackCombineGeometryParameters = function (
761
890
  projection: projection,
762
891
  elementIndexUintSupported: packedParameters.elementIndexUintSupported,
763
892
  scene3DOnly: packedParameters.scene3DOnly,
893
+ columbusRepeatWorlds: packedParameters.columbusRepeatWorlds === true,
764
894
  vertexCacheOptimize: packedParameters.vertexCacheOptimize,
765
895
  compressVertices: packedParameters.compressVertices,
766
896
  modelMatrix: Matrix4.clone(packedParameters.modelMatrix),