@hztxi/cesium-engine 25.0.4 → 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 (202) hide show
  1. package/Build/Workers/chunk-247MLVK2.js +124 -0
  2. package/Build/Workers/chunk-26I7TIAE.js +58 -0
  3. package/Build/Workers/chunk-26OYTC24.js +38 -0
  4. package/Build/Workers/chunk-2GJAWJJT.js +1344 -0
  5. package/Build/Workers/chunk-2IMOZG7R.js +973 -0
  6. package/Build/Workers/chunk-2J2VS5RI.js +288 -0
  7. package/Build/Workers/chunk-2L6JGSYJ.js +57 -0
  8. package/Build/Workers/chunk-32N2SB76.js +56 -0
  9. package/Build/Workers/chunk-375SHKGW.js +44 -0
  10. package/Build/Workers/chunk-37WL527K.js +1287 -0
  11. package/Build/Workers/chunk-3IGH46TC.js +1054 -0
  12. package/Build/Workers/chunk-47BSFGJC.js +837 -0
  13. package/Build/Workers/chunk-4J7PL5AM.js +454 -0
  14. package/Build/Workers/chunk-4UMO63VY.js +76 -0
  15. package/Build/Workers/chunk-5FYUSABU.js +945 -0
  16. package/Build/Workers/chunk-5WU37CUJ.js +630 -0
  17. package/Build/Workers/chunk-5YI3TQQC.js +778 -0
  18. package/Build/Workers/chunk-6EBDN5WS.js +97 -0
  19. package/Build/Workers/chunk-75HIWCEV.js +301 -0
  20. package/Build/Workers/chunk-7AMVPXZY.js +1992 -0
  21. package/Build/Workers/chunk-7BMDD6SM.js +124 -0
  22. package/Build/Workers/chunk-7OME2OKM.js +171 -0
  23. package/Build/Workers/chunk-A2BROW3D.js +352 -0
  24. package/Build/Workers/chunk-AEZQ2UWS.js +288 -0
  25. package/Build/Workers/chunk-AXE2ZIEG.js +219 -0
  26. package/Build/Workers/chunk-B4XOVZGA.js +476 -0
  27. package/Build/Workers/chunk-BDR7GL3E.js +1067 -0
  28. package/Build/Workers/chunk-C342XITR.js +44 -0
  29. package/Build/Workers/chunk-CDLPLYLV.js +1287 -0
  30. package/Build/Workers/chunk-CEKCKPFN.js +368 -0
  31. package/Build/Workers/chunk-CHMDKP5L.js +2342 -0
  32. package/Build/Workers/chunk-CWAUXQRF.js +754 -0
  33. package/Build/Workers/chunk-CWGB3D4Z.js +257 -0
  34. package/Build/Workers/chunk-DQEUGBGA.js +2719 -0
  35. package/Build/Workers/chunk-DUBE5G2H.js +497 -0
  36. package/Build/Workers/chunk-E4PBNWWK.js +58 -0
  37. package/Build/Workers/chunk-E5F7LDEP.js +3201 -0
  38. package/Build/Workers/chunk-E5MWOMVC.js +57 -0
  39. package/Build/Workers/chunk-EE3XBOQI.js +1483 -0
  40. package/Build/Workers/chunk-EHA2ZDPZ.js +415 -0
  41. package/Build/Workers/chunk-EKEKPAJF.js +454 -0
  42. package/Build/Workers/chunk-EOHKUXPX.js +198 -0
  43. package/Build/Workers/chunk-ERENMHG2.js +754 -0
  44. package/Build/Workers/chunk-ESQGFRLZ.js +163 -0
  45. package/Build/Workers/chunk-FAKYMBUA.js +1046 -0
  46. package/Build/Workers/chunk-FSK4OZOO.js +352 -0
  47. package/Build/Workers/chunk-FSLRYNZ6.js +682 -0
  48. package/Build/Workers/chunk-FV4OWDV4.js +9230 -0
  49. package/Build/Workers/chunk-FYRNNQSL.js +280 -0
  50. package/Build/Workers/chunk-GMDOGAXK.js +2719 -0
  51. package/Build/Workers/chunk-GRAFCIPA.js +455 -0
  52. package/Build/Workers/chunk-HEY4IOS3.js +140 -0
  53. package/Build/Workers/chunk-HRWFO57T.js +301 -0
  54. package/Build/Workers/chunk-HTCLVUBZ.js +82 -0
  55. package/Build/Workers/chunk-I2Q7XDRA.js +630 -0
  56. package/Build/Workers/chunk-ILPH3NF7.js +3794 -0
  57. package/Build/Workers/chunk-JMF4ATAS.js +3794 -0
  58. package/Build/Workers/chunk-JZUUODN6.js +97 -0
  59. package/Build/Workers/chunk-KEJKMITY.js +82 -0
  60. package/Build/Workers/chunk-KW2QZDUS.js +147 -0
  61. package/Build/Workers/chunk-KWVB54J6.js +241 -0
  62. package/Build/Workers/chunk-L6B5XPBL.js +73 -0
  63. package/Build/Workers/chunk-LGJXN2VP.js +352 -0
  64. package/Build/Workers/chunk-LY2UG3HV.js +38 -0
  65. package/Build/Workers/chunk-M3B3ZUU2.js +476 -0
  66. package/Build/Workers/chunk-MKR6YCMG.js +497 -0
  67. package/Build/Workers/chunk-MROMS2RL.js +162 -0
  68. package/Build/Workers/chunk-MUNAUSK7.js +280 -0
  69. package/Build/Workers/chunk-NCPJBP24.js +140 -0
  70. package/Build/Workers/chunk-NFY5V6MR.js +102 -0
  71. package/Build/Workers/chunk-NHU5JEPP.js +1344 -0
  72. package/Build/Workers/chunk-OODXPB45.js +73 -0
  73. package/Build/Workers/chunk-OXZ5J3JW.js +368 -0
  74. package/Build/Workers/chunk-OYPSUED3.js +1067 -0
  75. package/Build/Workers/chunk-QBSWSTHY.js +257 -0
  76. package/Build/Workers/chunk-QDJYG7QL.js +352 -0
  77. package/Build/Workers/chunk-QIOOTLWN.js +56 -0
  78. package/Build/Workers/chunk-QUPRALOD.js +101 -0
  79. package/Build/Workers/chunk-RBEVHHXK.js +3201 -0
  80. package/Build/Workers/chunk-RHO5HV52.js +305 -0
  81. package/Build/Workers/chunk-RX3U2BBW.js +1483 -0
  82. package/Build/Workers/chunk-S3XHL7GO.js +1992 -0
  83. package/Build/Workers/chunk-SBIC4OGS.js +171 -0
  84. package/Build/Workers/chunk-SVCZUP5R.js +837 -0
  85. package/Build/Workers/chunk-T4CYNROE.js +102 -0
  86. package/Build/Workers/chunk-T5VLPB4H.js +414 -0
  87. package/Build/Workers/chunk-TBHW5UME.js +973 -0
  88. package/Build/Workers/chunk-TPOX6IOR.js +1054 -0
  89. package/Build/Workers/chunk-TX763OWB.js +387 -0
  90. package/Build/Workers/chunk-ULNBTI6R.js +138 -0
  91. package/Build/Workers/chunk-UMUS2AQQ.js +398 -0
  92. package/Build/Workers/chunk-UTAWMCSE.js +163 -0
  93. package/Build/Workers/chunk-UZ7GTP2L.js +162 -0
  94. package/Build/Workers/chunk-VETQOADR.js +2342 -0
  95. package/Build/Workers/chunk-VXRWOM3T.js +101 -0
  96. package/Build/Workers/chunk-W3C7OHXF.js +387 -0
  97. package/Build/Workers/chunk-WB5GWYDP.js +455 -0
  98. package/Build/Workers/chunk-WN6MAL25.js +682 -0
  99. package/Build/Workers/chunk-WTR7KMQA.js +693 -0
  100. package/Build/Workers/chunk-WTTFMU3P.js +241 -0
  101. package/Build/Workers/chunk-WULONJZC.js +1800 -0
  102. package/Build/Workers/chunk-WVVFCMYF.js +147 -0
  103. package/Build/Workers/chunk-WZPMAVTV.js +511 -0
  104. package/Build/Workers/chunk-XAVNBZQ6.js +56 -0
  105. package/Build/Workers/chunk-XDSDR62Y.js +1800 -0
  106. package/Build/Workers/chunk-XDZCVNIZ.js +198 -0
  107. package/Build/Workers/chunk-XNLCW2OI.js +138 -0
  108. package/Build/Workers/chunk-XNQ2BTJD.js +693 -0
  109. package/Build/Workers/chunk-XZYBDSFP.js +305 -0
  110. package/Build/Workers/chunk-Y7KVUJEB.js +219 -0
  111. package/Build/Workers/chunk-YDYN3RPI.js +398 -0
  112. package/Build/Workers/chunk-YMMEQBEW.js +511 -0
  113. package/Build/Workers/chunk-YQFOQAXD.js +9230 -0
  114. package/Build/Workers/chunk-Z6JV4DZG.js +1046 -0
  115. package/Build/Workers/chunk-ZTK5EBUI.js +76 -0
  116. package/Build/Workers/chunk-ZZPDORJK.js +56 -0
  117. package/Build/Workers/combineGeometry.js +24 -24
  118. package/Build/Workers/createBoxGeometry.js +18 -18
  119. package/Build/Workers/createBoxOutlineGeometry.js +16 -16
  120. package/Build/Workers/createCircleGeometry.js +26 -26
  121. package/Build/Workers/createCircleOutlineGeometry.js +19 -19
  122. package/Build/Workers/createCoplanarPolygonGeometry.js +33 -33
  123. package/Build/Workers/createCoplanarPolygonOutlineGeometry.js +31 -31
  124. package/Build/Workers/createCorridorGeometry.js +29 -29
  125. package/Build/Workers/createCorridorOutlineGeometry.js +28 -28
  126. package/Build/Workers/createCylinderGeometry.js +20 -20
  127. package/Build/Workers/createCylinderOutlineGeometry.js +18 -18
  128. package/Build/Workers/createEllipseGeometry.js +26 -26
  129. package/Build/Workers/createEllipseOutlineGeometry.js +19 -19
  130. package/Build/Workers/createEllipsoidGeometry.js +19 -19
  131. package/Build/Workers/createEllipsoidOutlineGeometry.js +18 -18
  132. package/Build/Workers/createFrustumGeometry.js +18 -18
  133. package/Build/Workers/createFrustumOutlineGeometry.js +18 -18
  134. package/Build/Workers/createGeometry.js +24 -24
  135. package/Build/Workers/createGroundPolylineGeometry.js +95 -31
  136. package/Build/Workers/createPlaneGeometry.js +16 -16
  137. package/Build/Workers/createPlaneOutlineGeometry.js +15 -15
  138. package/Build/Workers/createPolygonGeometry.js +32 -32
  139. package/Build/Workers/createPolygonOutlineGeometry.js +30 -30
  140. package/Build/Workers/createPolylineGeometry.js +25 -25
  141. package/Build/Workers/createPolylineVolumeGeometry.js +31 -31
  142. package/Build/Workers/createPolylineVolumeOutlineGeometry.js +27 -27
  143. package/Build/Workers/createRectangleGeometry.js +27 -27
  144. package/Build/Workers/createRectangleOutlineGeometry.js +20 -20
  145. package/Build/Workers/createSimplePolylineGeometry.js +23 -23
  146. package/Build/Workers/createSphereGeometry.js +19 -19
  147. package/Build/Workers/createSphereOutlineGeometry.js +18 -18
  148. package/Build/Workers/createTaskProcessorWorker.js +3 -3
  149. package/Build/Workers/createVectorTileClampedPolylines.js +14 -14
  150. package/Build/Workers/createVectorTileGeometries.js +24 -24
  151. package/Build/Workers/createVectorTilePoints.js +13 -13
  152. package/Build/Workers/createVectorTilePolygons.js +22 -22
  153. package/Build/Workers/createVectorTilePolylines.js +14 -14
  154. package/Build/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -26
  155. package/Build/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +23 -23
  156. package/Build/Workers/createVerticesFromHeightmap.js +23 -23
  157. package/Build/Workers/createVerticesFromQuantizedTerrainMesh.js +21 -21
  158. package/Build/Workers/createWallGeometry.js +24 -24
  159. package/Build/Workers/createWallOutlineGeometry.js +23 -23
  160. package/Build/Workers/decodeDraco.js +10 -10
  161. package/Build/Workers/decodeGoogleEarthEnterprisePacket.js +5 -5
  162. package/Build/Workers/decodeI3S.js +10 -10
  163. package/Build/Workers/gaussianSplatSorter.js +4 -4
  164. package/Build/Workers/gaussianSplatTextureGenerator.js +4 -4
  165. package/Build/Workers/incrementallyBuildTerrainPicker.js +10 -10
  166. package/Build/Workers/transcodeKTX2.js +6 -6
  167. package/Build/Workers/transferTypedArrayTest.js +1 -1
  168. package/Build/Workers/upsampleQuantizedTerrainMesh.js +23 -23
  169. package/Build/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -26
  170. package/Source/Core/GroundPolylineGeometry.js +99 -4
  171. package/Source/Core/PolylinePipeline.js +9 -2
  172. package/Source/DataSources/PolylineGeometryUpdater.js +125 -7
  173. package/Source/Scene/Camera.js +56 -3
  174. package/Source/Scene/CameraFlightPath.js +14 -0
  175. package/Source/Scene/ClassificationPrimitive.js +63 -3
  176. package/Source/Scene/ColumbusWorlds.js +190 -0
  177. package/Source/Scene/FrameState.js +10 -0
  178. package/Source/Scene/Globe.js +89 -22
  179. package/Source/Scene/GlobeDepth.js +216 -5
  180. package/Source/Scene/GlobeSurfaceTileProvider.js +897 -678
  181. package/Source/Scene/GroundPolylinePrimitive.js +2 -1
  182. package/Source/Scene/Polyline.js +32 -5
  183. package/Source/Scene/PolylineCollection.js +41 -2
  184. package/Source/Scene/Primitive.js +12 -13
  185. package/Source/Scene/PrimitivePipeline.js +158 -28
  186. package/Source/Scene/Scene.js +428 -5
  187. package/Source/Scene/SceneTransforms.js +17 -0
  188. package/Source/Scene/ScreenSpaceCameraController.js +12 -3
  189. package/Source/Shaders/PolylineShadowVolumeFS.glsl +14 -0
  190. package/Source/Shaders/PolylineShadowVolumeFS.js +14 -0
  191. package/Source/Shaders/PolylineShadowVolumeMorphVS.glsl +14 -1
  192. package/Source/Shaders/PolylineShadowVolumeMorphVS.js +14 -1
  193. package/Source/Shaders/PolylineShadowVolumeVS.glsl +7 -0
  194. package/Source/Shaders/PolylineShadowVolumeVS.js +7 -0
  195. package/Source/Shaders/PolylineVS.glsl +16 -6
  196. package/Source/Shaders/PolylineVS.js +16 -6
  197. package/Source/Shaders/RestoreDepth.glsl +8 -0
  198. package/Source/Shaders/RestoreDepth.js +10 -0
  199. package/Source/ThirdParty/zip-module.wasm +0 -0
  200. package/index.d.ts +535 -1393
  201. package/index.js +267 -265
  202. package/package.json +10 -10
@@ -21,9 +21,16 @@ PolylinePipeline.numberOfPoints = function (p0, p1, minDistance) {
21
21
  };
22
22
 
23
23
  PolylinePipeline.numberOfPointsRhumbLine = function (p0, p1, granularity) {
24
+ // Wrap the longitude delta to (-pi, pi]: EllipsoidRhumbLine always
25
+ // interpolates along the shortest rhumb path. A segment crossing the
26
+ // +/-180 meridian has a raw delta of nearly 2*pi but spans only a tiny arc;
27
+ // counting subdivisions from the raw delta over-tessellates that segment by
28
+ // up to ~360x, which skews index-based parameterizations (the polyline `st`
29
+ // attribute used by line materials): animations appear to stall at the
30
+ // antimeridian.
31
+ const deltaLongitude = CesiumMath.negativePiToPi(p0.longitude - p1.longitude);
24
32
  const radiansDistanceSquared =
25
- Math.pow(p0.longitude - p1.longitude, 2) +
26
- Math.pow(p0.latitude - p1.latitude, 2);
33
+ Math.pow(deltaLongitude, 2) + Math.pow(p0.latitude - p1.latitude, 2);
27
34
 
28
35
  return Math.max(
29
36
  1,
@@ -649,6 +649,8 @@ function DynamicGeometryUpdater(primitives, groundPrimitives, geometryUpdater) {
649
649
  this._primitives = primitives;
650
650
  this._groundPrimitives = groundPrimitives;
651
651
  this._groundPolylinePrimitive = undefined;
652
+ this._groundGeometrySignature = undefined;
653
+ this._colorAppearance = undefined;
652
654
  this._material = undefined;
653
655
  this._geometryUpdater = geometryUpdater;
654
656
  this._positions = [];
@@ -692,6 +694,81 @@ function invalidateGroundPolylinePrimitiveGeometry(groundPolylinePrimitive) {
692
694
  groundPolylinePrimitive._spMorph = undefined;
693
695
  }
694
696
 
697
+ /**
698
+ * Compares the evaluated ground polyline geometry attributes with the ones the
699
+ * current primitive was built from and refreshes the stored signature.
700
+ * Returns true when the primitive has to be rebuilt.
701
+ *
702
+ * Entity polylines driven by a dynamic property (e.g. a CallbackProperty used
703
+ * by the editors) are evaluated every frame. Editors usually return the same
704
+ * positions frame after frame - the camera moves, not the line - so rebuilding
705
+ * the ground polyline primitive unconditionally would destroy its vertex
706
+ * arrays and shader programs on every rendered frame, which is very expensive
707
+ * and makes the line flicker while the view changes.
708
+ *
709
+ * @private
710
+ */
711
+ function updateGroundPolylineSignature(
712
+ dynamicUpdater,
713
+ positions,
714
+ width,
715
+ arcType,
716
+ granularity,
717
+ color,
718
+ ) {
719
+ const length = positions.length;
720
+ const previous = dynamicUpdater._groundGeometrySignature;
721
+ const sameLength = defined(previous) && previous.xyz.length === length * 3;
722
+ const xyz = sameLength ? previous.xyz : new Float64Array(length * 3);
723
+ const colorChanged =
724
+ !sameLength ||
725
+ !defined(previous.color) !== !defined(color) ||
726
+ (defined(color) &&
727
+ (previous.color.red !== color.red ||
728
+ previous.color.green !== color.green ||
729
+ previous.color.blue !== color.blue ||
730
+ previous.color.alpha !== color.alpha));
731
+ let changed =
732
+ !sameLength ||
733
+ colorChanged ||
734
+ previous.width !== width ||
735
+ previous.arcType !== arcType ||
736
+ previous.granularity !== granularity;
737
+
738
+ // Compare the positions by value: the editors move the nodes of the line
739
+ // while the property keeps returning an array of the same length, so only
740
+ // the coordinates tell an edit from a plain re-evaluation.
741
+ for (let i = 0, j = 0; i < length; ++i) {
742
+ const position = positions[i];
743
+ const x = position.x;
744
+ const y = position.y;
745
+ const z = position.z;
746
+ if (!sameLength || xyz[j] !== x || xyz[j + 1] !== y || xyz[j + 2] !== z) {
747
+ changed = true;
748
+ }
749
+ xyz[j++] = x;
750
+ xyz[j++] = y;
751
+ xyz[j++] = z;
752
+ }
753
+
754
+ dynamicUpdater._groundGeometrySignature = {
755
+ xyz,
756
+ width,
757
+ arcType,
758
+ granularity,
759
+ // Copy the components: colors are evaluated into shared scratch objects.
760
+ color: defined(color)
761
+ ? {
762
+ red: color.red,
763
+ green: color.green,
764
+ blue: color.blue,
765
+ alpha: color.alpha,
766
+ }
767
+ : undefined,
768
+ };
769
+ return changed;
770
+ }
771
+
695
772
  DynamicGeometryUpdater.prototype.update = function (time) {
696
773
  const geometryUpdater = this._geometryUpdater;
697
774
  const entity = geometryUpdater._entity;
@@ -754,9 +831,45 @@ DynamicGeometryUpdater.prototype.update = function (time) {
754
831
  }
755
832
 
756
833
  const fillMaterialProperty = geometryUpdater.fillMaterialProperty;
834
+ const isColorMaterial =
835
+ fillMaterialProperty instanceof ColorMaterialProperty;
836
+
837
+ // Per-instance attributes (show, distance display condition and the
838
+ // material color) are baked into the geometry instance, so any animated
839
+ // one of them requires a rebuild every frame.
840
+ const attributesAnimated =
841
+ !Property.isConstant(polyline._show) ||
842
+ !Property.isConstant(geometryUpdater._distanceDisplayConditionProperty) ||
843
+ (isColorMaterial &&
844
+ defined(fillMaterialProperty.color) &&
845
+ !Property.isConstant(fillMaterialProperty.color));
846
+
847
+ // The per-instance color is part of the geometry instance as well.
848
+ const colorAttribute =
849
+ isColorMaterial && defined(fillMaterialProperty.color)
850
+ ? fillMaterialProperty.color.getValue(time, scratchColor)
851
+ : undefined;
852
+
853
+ // Only rebuild the ground polyline primitive when the evaluated geometry
854
+ // actually changed. See updateGroundPolylineSignature.
855
+ const geometryChanged =
856
+ attributesAnimated ||
857
+ updateGroundPolylineSignature(
858
+ this,
859
+ positions,
860
+ geometryUpdater._groundGeometryOptions.width,
861
+ geometryUpdater._groundGeometryOptions.arcType,
862
+ geometryUpdater._groundGeometryOptions.granularity,
863
+ colorAttribute,
864
+ );
865
+
757
866
  let appearance;
758
- if (fillMaterialProperty instanceof ColorMaterialProperty) {
759
- appearance = new PolylineColorAppearance();
867
+ if (isColorMaterial) {
868
+ // Reuse one appearance instance: assigning a new appearance makes the
869
+ // primitive rebuild its render states and shader programs.
870
+ appearance =
871
+ this._colorAppearance ??
872
+ (this._colorAppearance = new PolylineColorAppearance());
760
873
  } else {
761
874
  const material = MaterialProperty.getValue(
762
875
  time,
@@ -770,7 +883,6 @@ DynamicGeometryUpdater.prototype.update = function (time) {
770
883
  this._material = material;
771
884
  }
772
885
 
773
- const geometryInstance = geometryUpdater.createFillGeometryInstance(time);
774
886
  const classificationType =
775
887
  geometryUpdater.classificationTypeProperty.getValue(time);
776
888
  const zIndex = Property.getValueOrUndefined(geometryUpdater.zIndex, time);
@@ -779,7 +891,7 @@ DynamicGeometryUpdater.prototype.update = function (time) {
779
891
  if (!defined(this._groundPolylinePrimitive)) {
780
892
  this._groundPolylinePrimitive = groundPrimitives.add(
781
893
  new GroundPolylinePrimitive({
782
- geometryInstances: geometryInstance,
894
+ geometryInstances: geometryUpdater.createFillGeometryInstance(time),
783
895
  appearance: appearance,
784
896
  classificationType: classificationType,
785
897
  asynchronous: false,
@@ -791,9 +903,15 @@ DynamicGeometryUpdater.prototype.update = function (time) {
791
903
  if (groundPolylinePrimitive._zIndex !== zIndexKey) {
792
904
  groundPrimitives.set(groundPolylinePrimitive, zIndexKey);
793
905
  }
794
- groundPolylinePrimitive.geometryInstances = geometryInstance;
795
- invalidateGroundPolylinePrimitiveGeometry(groundPolylinePrimitive);
796
- groundPolylinePrimitive.appearance = appearance;
906
+ if (geometryChanged) {
907
+ groundPolylinePrimitive.geometryInstances =
908
+ geometryUpdater.createFillGeometryInstance(time);
909
+ invalidateGroundPolylinePrimitiveGeometry(groundPolylinePrimitive);
910
+ }
911
+ // Animated materials still need their uniforms refreshed per frame.
912
+ if (geometryChanged || !isColorMaterial) {
913
+ groundPolylinePrimitive.appearance = appearance;
914
+ }
797
915
  groundPolylinePrimitive.classificationType = classificationType;
798
916
  }
799
917
 
@@ -26,6 +26,7 @@ import Ray from "../Core/Ray.js";
26
26
  import Rectangle from "../Core/Rectangle.js";
27
27
  import Transforms from "../Core/Transforms.js";
28
28
  import CameraFlightPath from "./CameraFlightPath.js";
29
+ import ColumbusWorlds from "./ColumbusWorlds.js";
29
30
  import MapMode2D from "./MapMode2D.js";
30
31
  import SceneMode from "./SceneMode.js";
31
32
 
@@ -537,6 +538,18 @@ function convertTransformForColumbusView(camera) {
537
538
  camera._transform,
538
539
  camera._actualTransform,
539
540
  );
541
+
542
+ if (camera._scene.columbusRepeatWorlds === true) {
543
+ // Columbus repeat worlds: anchor the converted reference frame on the
544
+ // world copy nearest the camera (element [13] is the translation's
545
+ // map-u component in the column-major matrix) so the frame, and offsets
546
+ // within it, stay continuous with the camera's current copy.
547
+ camera._actualTransform[13] = ColumbusWorlds.nearestWorldU(
548
+ camera._positionWC.y,
549
+ camera._actualTransform[13],
550
+ camera._projection,
551
+ );
552
+ }
540
553
  }
541
554
 
542
555
  const scratchCartographic = new Cartographic();
@@ -752,6 +765,18 @@ function updateMembers(camera) {
752
765
  }
753
766
 
754
767
  camera._projection.unproject(positionENU, camera._positionCartographic);
768
+
769
+ if (
770
+ mode === SceneMode.COLUMBUS_VIEW &&
771
+ camera._scene.columbusRepeatWorlds === true
772
+ ) {
773
+ // Columbus repeat worlds: the camera may sit on any world copy, which
774
+ // unprojects to a longitude outside [-180°, 180°]; wrap it so
775
+ // rectangle containment checks see the equivalent primary point.
776
+ camera._positionCartographic.longitude = CesiumMath.negativePiToPi(
777
+ camera._positionCartographic.longitude,
778
+ );
779
+ }
755
780
  }
756
781
  }
757
782
 
@@ -1315,6 +1340,15 @@ function setViewCV(camera, position, hpr, convert) {
1315
1340
  );
1316
1341
  position = projection.project(cartographic, scratchSetViewCartesian);
1317
1342
  }
1343
+ if (camera._scene.columbusRepeatWorlds === true) {
1344
+ // Columbus repeat worlds: keep the view continuous with the camera's
1345
+ // current world copy instead of snapping back to the primary world.
1346
+ position.x = ColumbusWorlds.nearestWorldU(
1347
+ camera.positionWC.y,
1348
+ position.x,
1349
+ camera._projection,
1350
+ );
1351
+ }
1318
1352
  Cartesian3.clone(position, camera.position);
1319
1353
  }
1320
1354
  hpr.heading = hpr.heading - CesiumMath.PI_OVER_TWO;
@@ -2874,13 +2908,19 @@ function pickMapColumbusView(camera, windowPosition, projection, result) {
2874
2908
 
2875
2909
  if (
2876
2910
  cart.latitude < -CesiumMath.PI_OVER_TWO ||
2877
- cart.latitude > CesiumMath.PI_OVER_TWO ||
2878
- cart.longitude < -Math.PI ||
2879
- cart.longitude > Math.PI
2911
+ cart.latitude > CesiumMath.PI_OVER_TWO
2880
2912
  ) {
2881
2913
  return undefined;
2882
2914
  }
2883
2915
 
2916
+ // The longitude may fall outside [-PI, PI] when Columbus repeat worlds are
2917
+ // enabled and the picked point is on a translated world copy. Accept it:
2918
+ // the returned ECEF position is the equivalent primary-world point, so
2919
+ // picks on world copies succeed instead of returning undefined. Callers
2920
+ // that need to know which copy was picked should use
2921
+ // Globe.pickWorldCoordinates or Scene.pickPosition, which operate in scene
2922
+ // (map-plane) world coordinates.
2923
+
2884
2924
  return projection.ellipsoid.cartographicToCartesian(cart, result);
2885
2925
  }
2886
2926
 
@@ -3190,6 +3230,19 @@ function createAnimationCV(camera, duration) {
3190
3230
 
3191
3231
  position = camera.cameraToWorldCoordinatesPoint(camera.position, posScratch);
3192
3232
 
3233
+ if (camera._scene.columbusRepeatWorlds === true) {
3234
+ // Columbus repeat worlds: the camera may sit on any world copy; wrapping
3235
+ // its map-u coordinate into the primary world is visually identical (the
3236
+ // map repeats) and keeps the bounds checks below meaningful.
3237
+ const worldWidth = ColumbusWorlds.getWorldWidth(camera._projection);
3238
+ position.y =
3239
+ CesiumMath.mod(position.y + worldWidth * 0.5, worldWidth) -
3240
+ worldWidth * 0.5;
3241
+ center.y =
3242
+ CesiumMath.mod(center.y + worldWidth * 0.5, worldWidth) -
3243
+ worldWidth * 0.5;
3244
+ }
3245
+
3193
3246
  const tanPhi = Math.tan(camera.frustum.fovy * 0.5);
3194
3247
  const tanTheta = camera.frustum.aspectRatio * tanPhi;
3195
3248
  const distToC = Cartesian3.magnitude(
@@ -8,6 +8,7 @@ import EasingFunction from "../Core/EasingFunction.js";
8
8
  import CesiumMath from "../Core/Math.js";
9
9
  import PerspectiveFrustum from "../Core/PerspectiveFrustum.js";
10
10
  import PerspectiveOffCenterFrustum from "../Core/PerspectiveOffCenterFrustum.js";
11
+ import ColumbusWorlds from "./ColumbusWorlds.js";
11
12
  import SceneMode from "./SceneMode.js";
12
13
 
13
14
  /**
@@ -436,6 +437,19 @@ CameraFlightPath.createTween = function (scene, options) {
436
437
  }
437
438
 
438
439
  const camera = scene.camera;
440
+
441
+ if (mode === SceneMode.COLUMBUS_VIEW && scene.columbusRepeatWorlds === true) {
442
+ // Columbus repeat worlds: fly to the copy of the destination nearest the
443
+ // camera's current copy so the flight pans locally instead of crossing a
444
+ // full world. In local (u, v, h) coordinates, u is the x component.
445
+ scratchDestination.x = ColumbusWorlds.nearestWorldU(
446
+ camera.position.x,
447
+ destination.x,
448
+ projection,
449
+ );
450
+ destination = scratchDestination;
451
+ }
452
+
439
453
  const transform = options.endTransform;
440
454
  if (defined(transform)) {
441
455
  camera._setTransform(transform);
@@ -12,6 +12,8 @@ import ShaderProgram from "../Renderer/ShaderProgram.js";
12
12
  import ShaderSource from "../Renderer/ShaderSource.js";
13
13
  import ShadowVolumeAppearanceVS from "../Shaders/ShadowVolumeAppearanceVS.js";
14
14
  import ShadowVolumeFS from "../Shaders/ShadowVolumeFS.js";
15
+ import BlendEquation from "./BlendEquation.js";
16
+ import BlendFunction from "./BlendFunction.js";
15
17
  import BlendingState from "./BlendingState.js";
16
18
  import ClassificationType from "./ClassificationType.js";
17
19
  import DepthFunction from "./DepthFunction.js";
@@ -366,7 +368,61 @@ function getStencilDepthRenderState(enableStencil, mask3DTiles) {
366
368
  };
367
369
  }
368
370
 
369
- function getColorRenderState(enableStencil) {
371
+ /**
372
+ * HZ patch: per-material blend modes for ground classification.
373
+ * The app encodes the mode as a suffix on the composed material type
374
+ * (e.g. ComposedPolygon_..._blendScreen). Source fragments are
375
+ * premultiplied (ShadowVolumeAppearanceFS: rgb *= alpha), so:
376
+ * screen = src + dst * (1 - src) -> (ONE, ONE_MINUS_SRC_COLOR)
377
+ * multiply = dst * mix(1, c, a) (alpha-gated) -> (DST_COLOR, ONE_MINUS_SRC_ALPHA)
378
+ * additive = src + dst -> (ONE, ONE)
379
+ * Alpha keeps standard premultiplied over except additive.
380
+ */
381
+ const classificationBlendOverrides = {
382
+ _blendScreen: {
383
+ enabled: true,
384
+ equationRgb: BlendEquation.ADD,
385
+ equationAlpha: BlendEquation.ADD,
386
+ functionSourceRgb: BlendFunction.ONE,
387
+ functionSourceAlpha: BlendFunction.ONE,
388
+ functionDestinationRgb: BlendFunction.ONE_MINUS_SOURCE_COLOR,
389
+ functionDestinationAlpha: BlendFunction.ONE_MINUS_SOURCE_ALPHA,
390
+ },
391
+ _blendMultiply: {
392
+ enabled: true,
393
+ equationRgb: BlendEquation.ADD,
394
+ equationAlpha: BlendEquation.ADD,
395
+ functionSourceRgb: BlendFunction.DESTINATION_COLOR,
396
+ functionSourceAlpha: BlendFunction.ONE,
397
+ functionDestinationRgb: BlendFunction.ONE_MINUS_SOURCE_ALPHA,
398
+ functionDestinationAlpha: BlendFunction.ONE_MINUS_SOURCE_ALPHA,
399
+ },
400
+ _blendAdditive: {
401
+ enabled: true,
402
+ equationRgb: BlendEquation.ADD,
403
+ equationAlpha: BlendEquation.ADD,
404
+ functionSourceRgb: BlendFunction.ONE,
405
+ functionSourceAlpha: BlendFunction.ONE,
406
+ functionDestinationRgb: BlendFunction.ONE,
407
+ functionDestinationAlpha: BlendFunction.ONE,
408
+ },
409
+ };
410
+
411
+ /** HZ patch: material type suffix -> blending override (undefined = stock) */
412
+ function getClassificationBlendOverride(classificationPrimitive) {
413
+ const type = classificationPrimitive.appearance?.material?.type;
414
+ if (typeof type !== "string") {
415
+ return undefined;
416
+ }
417
+ for (const suffix of Object.keys(classificationBlendOverrides)) {
418
+ if (type.endsWith(suffix)) {
419
+ return classificationBlendOverrides[suffix];
420
+ }
421
+ }
422
+ return undefined;
423
+ }
424
+
425
+ function getColorRenderState(enableStencil, blending) {
370
426
  return {
371
427
  stencilTest: {
372
428
  enabled: enableStencil,
@@ -390,7 +446,7 @@ function getColorRenderState(enableStencil) {
390
446
  enabled: false,
391
447
  },
392
448
  depthMask: false,
393
- blending: BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
449
+ blending: blending ?? BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
394
450
  };
395
451
  }
396
452
 
@@ -436,8 +492,12 @@ function createRenderStates(
436
492
  classificationPrimitive._rsStencilDepthPass3DTiles = RenderState.fromCache(
437
493
  getStencilDepthRenderState(stencilEnabled, true),
438
494
  );
495
+ // HZ patch: honor per-material blend mode encoded in the material type
439
496
  classificationPrimitive._rsColorPass = RenderState.fromCache(
440
- getColorRenderState(stencilEnabled, false),
497
+ getColorRenderState(
498
+ stencilEnabled,
499
+ getClassificationBlendOverride(classificationPrimitive),
500
+ ),
441
501
  );
442
502
  classificationPrimitive._rsPickPass = RenderState.fromCache(pickRenderState);
443
503
  }
@@ -0,0 +1,190 @@
1
+ import BoundingSphere from "../Core/BoundingSphere.js";
2
+ import Cartesian3 from "../Core/Cartesian3.js";
3
+ import Cartesian4 from "../Core/Cartesian4.js";
4
+ import Cartographic from "../Core/Cartographic.js";
5
+ import defined from "../Core/defined.js";
6
+ import IntersectionTests from "../Core/IntersectionTests.js";
7
+ import Matrix4 from "../Core/Matrix4.js";
8
+ import Rectangle from "../Core/Rectangle.js";
9
+
10
+ /**
11
+ * Shared helpers for Columbus View repeat worlds: when
12
+ * {@link Scene#columbusRepeatWorlds} is enabled, the map surface repeats
13
+ * horizontally in whole-world copies (each 360° of longitude wide), so
14
+ * positions and camera destinations must be placed on the copy nearest the
15
+ * camera instead of the primary [-180°, 180°] world.
16
+ *
17
+ * @namespace ColumbusWorlds
18
+ */
19
+
20
+ const ColumbusWorlds = {};
21
+
22
+ const worldWidthCartographic = new Cartographic();
23
+ const worldWidthCartesian = new Cartesian3();
24
+ const worldWidthCache = {
25
+ projection: undefined,
26
+ width: 0.0,
27
+ };
28
+
29
+ /**
30
+ * The projected width of one full world (360° of longitude) for the given
31
+ * map projection. Cached per projection instance.
32
+ *
33
+ * @param {object} projection The map projection.
34
+ * @returns {number} The world width in projected units (meters).
35
+ *
36
+ * @private
37
+ */
38
+ ColumbusWorlds.getWorldWidth = function (projection) {
39
+ if (worldWidthCache.projection !== projection) {
40
+ const northeast = projection.project(
41
+ Rectangle.northeast(Rectangle.MAX_VALUE, worldWidthCartographic),
42
+ worldWidthCartesian,
43
+ );
44
+ worldWidthCache.projection = projection;
45
+ worldWidthCache.width = Math.abs(northeast.x) * 2.0;
46
+ }
47
+ return worldWidthCache.width;
48
+ };
49
+
50
+ /**
51
+ * Places a projected map-u coordinate on the world copy nearest the camera.
52
+ * ECEF positions normalize longitude to the primary world, so a position
53
+ * intended for a translated copy (e.g. edited at 185° while its ECEF form
54
+ * reads as -175°) would otherwise land a full world away from the camera
55
+ * with no way to tell which copy was meant. The nearest-copy rule resolves
56
+ * that ambiguity; positions hop worlds only when the camera moves more than
57
+ * half a world away, at which point they are far off screen.
58
+ *
59
+ * @param {number} cameraU The camera's projected map-u coordinate.
60
+ * @param {number} u The projected map-u coordinate to place.
61
+ * @param {object} projection The map projection.
62
+ * @returns {number} The map-u coordinate shifted to the nearest world copy.
63
+ *
64
+ * @private
65
+ */
66
+ ColumbusWorlds.nearestWorldU = function (cameraU, u, projection) {
67
+ const worldWidth = ColumbusWorlds.getWorldWidth(projection);
68
+ return u + Math.round((cameraU - u) / worldWidth) * worldWidth;
69
+ };
70
+
71
+ const cameraOffsetDelta = new Cartesian3();
72
+ const cameraOffsetTranslation = new Matrix4();
73
+
74
+ /**
75
+ * Shifts a camera so the scene draws world copy `worldOffset` instead of the
76
+ * primary world, without duplicating any geometry. Rendering copy k at
77
+ * `p + worldOffset` is equivalent to viewing the unshifted scene from a camera
78
+ * moved by `-worldOffset`: `view' = view * translate(worldOffset)` and
79
+ * `eye' = eye - worldOffset` (map-u is the y axis of Columbus view world
80
+ * coordinates). The camera's direction, up and right are unchanged, so
81
+ * screen-space sizes and depth ordering are preserved.
82
+ *
83
+ * The camera's derived members are mutated directly (not `_position`) so
84
+ * `updateMembers` does not recompute them from the primary camera frame; the
85
+ * caller is responsible for restoring them when done.
86
+ *
87
+ * @param {Camera} camera The camera to offset.
88
+ * @param {number} worldOffset The world copy offset along map-u, in meters.
89
+ *
90
+ * @private
91
+ */
92
+ ColumbusWorlds.applyCameraWorldOffset = function (camera, worldOffset) {
93
+ Cartesian3.fromElements(0.0, worldOffset, 0.0, cameraOffsetDelta);
94
+ Matrix4.fromTranslation(cameraOffsetDelta, cameraOffsetTranslation);
95
+ Matrix4.multiply(
96
+ camera._viewMatrix,
97
+ cameraOffsetTranslation,
98
+ camera._viewMatrix,
99
+ );
100
+ Matrix4.inverseTransformation(camera._viewMatrix, camera._invViewMatrix);
101
+ camera._positionWC.y -= worldOffset;
102
+ };
103
+
104
+ /**
105
+ * Returns a culling volume translated the same way as the camera by
106
+ * {@link ColumbusWorlds.applyCameraWorldOffset}: the supplied volume is the
107
+ * primary-world frustum, the result is the frustum of the shifted camera.
108
+ * Planes are `n · p + w = 0`; translating a volume by `t` yields
109
+ * `w' = w - n · t`, and the camera offset is `t = (0, -worldOffset, 0)`.
110
+ *
111
+ * @param {CullingVolume} cullingVolume The primary-world culling volume.
112
+ * @param {number} worldOffset The world copy offset along map-u, in meters.
113
+ * @param {CullingVolume} result The culling volume to write into.
114
+ * @returns {CullingVolume} The modified result parameter.
115
+ *
116
+ * @private
117
+ */
118
+ ColumbusWorlds.translateCullingVolume = function (
119
+ cullingVolume,
120
+ worldOffset,
121
+ result,
122
+ ) {
123
+ const planes = cullingVolume.planes;
124
+ const resultPlanes = result.planes;
125
+ for (let i = 0; i < planes.length; ++i) {
126
+ const plane = planes[i];
127
+ let resultPlane = resultPlanes[i];
128
+ if (!defined(resultPlane)) {
129
+ resultPlane = resultPlanes[i] = new Cartesian4();
130
+ }
131
+ resultPlane.x = plane.x;
132
+ resultPlane.y = plane.y;
133
+ resultPlane.z = plane.z;
134
+ resultPlane.w = plane.w + plane.y * worldOffset;
135
+ }
136
+ return result;
137
+ };
138
+
139
+ const worldStripSphere = new BoundingSphere();
140
+
141
+ /**
142
+ * Collects the non-primary world copy indices worth rendering for the given
143
+ * camera. The globe surface provider uses the same camera-centered window, so
144
+ * both the basemap and the data copies cover the same worlds. A conservative
145
+ * strip sphere keeps copies that are entirely off screen (for instance when
146
+ * zoomed far in) from costing an extra visible-set build and pass.
147
+ *
148
+ * @param {number} cameraU The camera's projected map-u coordinate.
149
+ * @param {number} worldWidth The projected world width, in meters.
150
+ * @param {CullingVolume} cullingVolume The primary-world culling volume.
151
+ * @param {number} maxExtraWorlds The number of copies to try on each side.
152
+ * @param {number[]} result The array to write the world indices into.
153
+ * @returns {number[]} The modified result parameter.
154
+ *
155
+ * @private
156
+ */
157
+ ColumbusWorlds.getVisibleWorldIndices = function (
158
+ cameraU,
159
+ worldWidth,
160
+ cullingVolume,
161
+ maxExtraWorlds,
162
+ result,
163
+ ) {
164
+ result.length = 0;
165
+ if (!(worldWidth > 0.0) || !Number.isFinite(cameraU)) {
166
+ return result;
167
+ }
168
+
169
+ const sphere = worldStripSphere;
170
+ sphere.center.x = 0.0;
171
+ sphere.center.z = 0.0;
172
+ sphere.radius = worldWidth * 0.75;
173
+
174
+ const centerWorld = Math.round(cameraU / worldWidth);
175
+ for (let i = -maxExtraWorlds; i <= maxExtraWorlds; ++i) {
176
+ const worldIndex = centerWorld + i;
177
+ if (worldIndex === 0) {
178
+ // The primary world is rendered by the regular passes.
179
+ continue;
180
+ }
181
+ sphere.center.y = worldIndex * worldWidth;
182
+ if (cullingVolume.computeVisibility(sphere) === IntersectionTests.OUTSIDE) {
183
+ continue;
184
+ }
185
+ result.push(worldIndex);
186
+ }
187
+ return result;
188
+ };
189
+
190
+ export default ColumbusWorlds;
@@ -126,6 +126,16 @@ function FrameState(context, creditDisplay, jobScheduler) {
126
126
  */
127
127
  this.mapProjection = undefined;
128
128
 
129
+ /**
130
+ * Whether the map repeats horizontally in Columbus View, rendering
131
+ * translated copies of the globe surface tiles so the surface continues
132
+ * seamlessly beyond the ±180° meridian.
133
+ *
134
+ * @type {boolean}
135
+ * @default false
136
+ */
137
+ this.columbusRepeatWorlds = false;
138
+
129
139
  /**
130
140
  * The current camera.
131
141
  *