@cesium/engine 25.0.0 → 26.0.0

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 (187) hide show
  1. package/Build/Workers/{chunk-GPIGURCG.js → chunk-3VFEWOBI.js} +7 -7
  2. package/Build/Workers/{chunk-BTX2NL3P.js → chunk-424RNO3D.js} +18 -18
  3. package/Build/Workers/{chunk-HSVQATK2.js → chunk-4MGMYHOO.js} +6 -6
  4. package/Build/Workers/{chunk-FIBS5S7R.js → chunk-4S7IE3LB.js} +2 -2
  5. package/Build/Workers/{chunk-A7ZBMVFS.js → chunk-52JDLOEM.js} +14 -14
  6. package/Build/Workers/{chunk-ZCCIIBZT.js → chunk-54I6Y66F.js} +6 -6
  7. package/Build/Workers/{chunk-J647NNZW.js → chunk-6YEE6JDR.js} +6 -6
  8. package/Build/Workers/{chunk-HVMG6AG4.js → chunk-7J2HKPRH.js} +9 -9
  9. package/Build/Workers/{chunk-K6NFIRFB.js → chunk-7WM2PX4V.js} +5 -5
  10. package/Build/Workers/{chunk-3LSD7OOA.js → chunk-ACXQXN46.js} +16 -16
  11. package/Build/Workers/{chunk-CZ23Y3RM.js → chunk-AYKR4VBR.js} +2 -2
  12. package/Build/Workers/{chunk-4MTRQFJP.js → chunk-C6J67V5A.js} +1 -1
  13. package/Build/Workers/{chunk-WTQGE7Q6.js → chunk-C6KNZ7T4.js} +17 -17
  14. package/Build/Workers/{chunk-AWFZHD2B.js → chunk-CFQP6LYM.js} +4 -4
  15. package/Build/Workers/{chunk-ST2UWDUU.js → chunk-CXOYE2WM.js} +5 -5
  16. package/Build/Workers/{chunk-H2A4USJR.js → chunk-CZEYZU4N.js} +10 -10
  17. package/Build/Workers/{chunk-DFEI5MAV.js → chunk-DBOEKL43.js} +7 -7
  18. package/Build/Workers/{chunk-A7A433MD.js → chunk-DDWSZVMY.js} +5 -5
  19. package/Build/Workers/{chunk-J7KQEKPK.js → chunk-DOBXCNC3.js} +13 -13
  20. package/Build/Workers/{chunk-URUCCIPS.js → chunk-E7VQ76KE.js} +6 -6
  21. package/Build/Workers/{chunk-ZAPOS52G.js → chunk-EBSIOBXY.js} +8 -8
  22. package/Build/Workers/{chunk-3VDDRF2E.js → chunk-EF2QYXGA.js} +14 -14
  23. package/Build/Workers/{chunk-WN6P5UDH.js → chunk-EPYXRCDB.js} +12 -12
  24. package/Build/Workers/{chunk-EZARA5FJ.js → chunk-F6QDY6HU.js} +13 -13
  25. package/Build/Workers/{chunk-GL7DNVUS.js → chunk-FRQZECAO.js} +5 -5
  26. package/Build/Workers/{chunk-VQKLRBIK.js → chunk-G7PXZRVF.js} +7 -7
  27. package/Build/Workers/{chunk-4QQXGTCE.js → chunk-INUEEEQA.js} +8 -8
  28. package/Build/Workers/{chunk-ITD2IN7N.js → chunk-IRBKFCEJ.js} +2 -2
  29. package/Build/Workers/{chunk-6SDGQI27.js → chunk-IYOUHCZB.js} +1 -1
  30. package/Build/Workers/{chunk-DYT5NR6P.js → chunk-J6BWOHUF.js} +3 -3
  31. package/Build/Workers/{chunk-4GF4MVPY.js → chunk-KFSTQKNM.js} +7 -7
  32. package/Build/Workers/{chunk-4XXHJI6Y.js → chunk-KKOSKAZB.js} +17 -17
  33. package/Build/Workers/{chunk-WC57YYBN.js → chunk-KSSWJIZW.js} +2 -2
  34. package/Build/Workers/{chunk-5T6QQAJW.js → chunk-M3B5JKSM.js} +1 -1
  35. package/Build/Workers/{chunk-QO2CGXMP.js → chunk-MPZHGZU6.js} +6 -6
  36. package/Build/Workers/{chunk-WUBKZFF6.js → chunk-MYP774IS.js} +9 -9
  37. package/Build/Workers/{chunk-WNN5D4W3.js → chunk-O3EA5HS7.js} +10 -10
  38. package/Build/Workers/{chunk-77RL3MBD.js → chunk-P3CK7MCY.js} +25 -4
  39. package/Build/Workers/{chunk-QXNQQYPV.js → chunk-Q3PY526V.js} +8 -8
  40. package/Build/Workers/{chunk-CKQP3DSS.js → chunk-QN67VG36.js} +6 -6
  41. package/Build/Workers/{chunk-ACFFCHHZ.js → chunk-QRWGUWI6.js} +11 -11
  42. package/Build/Workers/{chunk-LH5SF3XV.js → chunk-S4MCOY6I.js} +5 -5
  43. package/Build/Workers/{chunk-FVLVVOBQ.js → chunk-SMQZZOW6.js} +10 -10
  44. package/Build/Workers/{chunk-TA777BGS.js → chunk-TKKIREQ6.js} +1 -1
  45. package/Build/Workers/{chunk-AEAVLYPS.js → chunk-U3ZA64U7.js} +2 -2
  46. package/Build/Workers/{chunk-7LSFAEWF.js → chunk-UA25D5MX.js} +10 -10
  47. package/Build/Workers/{chunk-J3OFS2RD.js → chunk-UOOIQ3KX.js} +12 -12
  48. package/Build/Workers/{chunk-ND3NTTOF.js → chunk-V7VQKN6N.js} +6 -6
  49. package/Build/Workers/{chunk-I3A6DXYZ.js → chunk-VQYETPWC.js} +4 -4
  50. package/Build/Workers/{chunk-35H3UBIO.js → chunk-VWV2IJRB.js} +11 -11
  51. package/Build/Workers/{chunk-QSDY4NS7.js → chunk-XZMBNNDX.js} +9 -9
  52. package/Build/Workers/{chunk-TAI4HLII.js → chunk-Y57HREW5.js} +4 -4
  53. package/Build/Workers/{chunk-3TKLDHV3.js → chunk-Y5BF6AFU.js} +2 -2
  54. package/Build/Workers/{chunk-3XY3MVPG.js → chunk-YLY7NKAB.js} +4 -4
  55. package/Build/Workers/{chunk-BVAIWR7O.js → chunk-ZK3CF7ZD.js} +12 -12
  56. package/Build/Workers/{chunk-DH26SNAB.js → chunk-ZP7JMQV4.js} +1 -1
  57. package/Build/Workers/{chunk-3RFFPH4M.js → chunk-ZZ46LWL6.js} +1 -1
  58. package/Build/Workers/combineGeometry.js +24 -24
  59. package/Build/Workers/createBoxGeometry.js +18 -18
  60. package/Build/Workers/createBoxOutlineGeometry.js +16 -16
  61. package/Build/Workers/createCircleGeometry.js +26 -26
  62. package/Build/Workers/createCircleOutlineGeometry.js +19 -19
  63. package/Build/Workers/createCoplanarPolygonGeometry.js +33 -33
  64. package/Build/Workers/createCoplanarPolygonOutlineGeometry.js +31 -31
  65. package/Build/Workers/createCorridorGeometry.js +29 -29
  66. package/Build/Workers/createCorridorOutlineGeometry.js +28 -28
  67. package/Build/Workers/createCylinderGeometry.js +20 -20
  68. package/Build/Workers/createCylinderOutlineGeometry.js +18 -18
  69. package/Build/Workers/createEllipseGeometry.js +26 -26
  70. package/Build/Workers/createEllipseOutlineGeometry.js +19 -19
  71. package/Build/Workers/createEllipsoidGeometry.js +19 -19
  72. package/Build/Workers/createEllipsoidOutlineGeometry.js +18 -18
  73. package/Build/Workers/createFrustumGeometry.js +18 -18
  74. package/Build/Workers/createFrustumOutlineGeometry.js +18 -18
  75. package/Build/Workers/createGeometry.js +24 -24
  76. package/Build/Workers/createGroundPolylineGeometry.js +22 -22
  77. package/Build/Workers/createPlaneGeometry.js +16 -16
  78. package/Build/Workers/createPlaneOutlineGeometry.js +15 -15
  79. package/Build/Workers/createPolygonGeometry.js +32 -32
  80. package/Build/Workers/createPolygonOutlineGeometry.js +30 -30
  81. package/Build/Workers/createPolylineGeometry.js +25 -25
  82. package/Build/Workers/createPolylineVolumeGeometry.js +31 -31
  83. package/Build/Workers/createPolylineVolumeOutlineGeometry.js +27 -27
  84. package/Build/Workers/createRectangleGeometry.js +27 -27
  85. package/Build/Workers/createRectangleOutlineGeometry.js +20 -20
  86. package/Build/Workers/createSimplePolylineGeometry.js +23 -23
  87. package/Build/Workers/createSphereGeometry.js +19 -19
  88. package/Build/Workers/createSphereOutlineGeometry.js +18 -18
  89. package/Build/Workers/createTaskProcessorWorker.js +3 -3
  90. package/Build/Workers/createVectorTileClampedPolylines.js +14 -14
  91. package/Build/Workers/createVectorTileGeometries.js +24 -24
  92. package/Build/Workers/createVectorTilePoints.js +13 -13
  93. package/Build/Workers/createVectorTilePolygons.js +22 -22
  94. package/Build/Workers/createVectorTilePolylines.js +14 -14
  95. package/Build/Workers/createVerticesFromCesium3DTilesTerrain.js +25 -25
  96. package/Build/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +22 -22
  97. package/Build/Workers/createVerticesFromHeightmap.js +22 -22
  98. package/Build/Workers/createVerticesFromQuantizedTerrainMesh.js +20 -20
  99. package/Build/Workers/createWallGeometry.js +24 -24
  100. package/Build/Workers/createWallOutlineGeometry.js +23 -23
  101. package/Build/Workers/decodeDraco.js +10 -10
  102. package/Build/Workers/decodeGoogleEarthEnterprisePacket.js +5 -5
  103. package/Build/Workers/decodeI3S.js +10 -10
  104. package/Build/Workers/gaussianSplatSorter.js +4 -4
  105. package/Build/Workers/gaussianSplatTextureGenerator.js +4 -4
  106. package/Build/Workers/incrementallyBuildTerrainPicker.js +10 -10
  107. package/Build/Workers/transcodeKTX2.js +6 -6
  108. package/Build/Workers/transferTypedArrayTest.js +1 -1
  109. package/Build/Workers/upsampleQuantizedTerrainMesh.js +22 -22
  110. package/Build/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +25 -25
  111. package/Source/Core/Cartesian2.js +1 -1
  112. package/Source/Core/Cartesian3.js +1 -1
  113. package/Source/Core/Cartesian4.js +1 -1
  114. package/Source/Core/ComponentDatatype.js +35 -0
  115. package/Source/Core/Ion.js +1 -1
  116. package/Source/Core/MapProjection.js +1 -0
  117. package/Source/Core/Matrix2.js +1 -1
  118. package/Source/Core/Matrix3.js +1 -1
  119. package/Source/Core/Matrix4.js +1 -1
  120. package/Source/Core/Resource.js +1 -1
  121. package/Source/Core/ScreenSpaceEventHandler.js +51 -41
  122. package/Source/Core/createColorRamp.js +37 -0
  123. package/Source/Core/globalTypes.js +25 -0
  124. package/Source/Renderer/Pass.js +3 -2
  125. package/Source/Renderer/createUniform.js +346 -239
  126. package/Source/Renderer/createUniformArray.js +494 -381
  127. package/Source/Scene/ArcGisMapService.js +1 -1
  128. package/Source/Scene/BufferPoint.js +1 -0
  129. package/Source/Scene/BufferPointCollection.js +14 -1
  130. package/Source/Scene/BufferPolygon.js +3 -2
  131. package/Source/Scene/BufferPolygonCollection.js +10 -0
  132. package/Source/Scene/BufferPolyline.js +1 -0
  133. package/Source/Scene/BufferPolylineCollection.js +2 -0
  134. package/Source/Scene/BufferPrimitiveCollection.js +79 -28
  135. package/Source/Scene/CameraEventAggregator.js +30 -18
  136. package/Source/Scene/Cesium3DTile.js +54 -1
  137. package/Source/Scene/Cesium3DTileContent.js +195 -254
  138. package/Source/Scene/Cesium3DTileContentFactory.js +10 -2
  139. package/Source/Scene/Cesium3DTileset.js +45 -5
  140. package/Source/Scene/Composite3DTileContent.js +254 -292
  141. package/Source/Scene/EdgeDisplayMode.js +50 -0
  142. package/Source/Scene/Empty3DTileContent.js +100 -132
  143. package/Source/Scene/GaussianSplat3DTileContent.js +436 -478
  144. package/Source/Scene/GeoJsonPrimitive.js +874 -0
  145. package/Source/Scene/Geometry3DTileContent.js +151 -185
  146. package/Source/Scene/GltfLoader.js +109 -0
  147. package/Source/Scene/Implicit3DTileContent.js +179 -214
  148. package/Source/Scene/MVTDataProvider.js +147 -0
  149. package/Source/Scene/Model/EdgeVisibilityPipelineStage.js +79 -311
  150. package/Source/Scene/Model/Model.js +34 -0
  151. package/Source/Scene/Model/Model3DTileContent.js +424 -458
  152. package/Source/Scene/Model/ModelDrawCommand.js +31 -3
  153. package/Source/Scene/Model/ModelUtility.js +4 -0
  154. package/Source/Scene/Model/createVectorTileBuffersFromModelComponents.js +466 -150
  155. package/Source/Scene/ModelComponents.js +39 -1
  156. package/Source/Scene/Multiple3DTileContent.js +291 -334
  157. package/Source/Scene/Scene.js +26 -0
  158. package/Source/Scene/Tileset3DTileContent.js +113 -146
  159. package/Source/Scene/UrlTemplate3DTilesDataProvider.js +499 -0
  160. package/Source/Scene/Vector3DTileContent.js +205 -234
  161. package/Source/Scene/VectorGltf3DTileContent.js +85 -36
  162. package/Source/Scene/VoxelPrimitive.js +159 -175
  163. package/Source/Scene/VoxelProvider.js +64 -91
  164. package/Source/Scene/buildVectorGltfFromMVT.js +1059 -0
  165. package/Source/Scene/buildVoxelCustomShader.js +199 -0
  166. package/Source/Scene/decodeMVT.js +634 -0
  167. package/Source/Scene/renderBufferPointCollection.js +110 -64
  168. package/Source/Scene/renderBufferPolygonCollection.js +102 -55
  169. package/Source/Scene/renderBufferPolylineCollection.js +365 -166
  170. package/Source/Shaders/BufferPointMaterialFS.glsl +1 -0
  171. package/Source/Shaders/BufferPointMaterialFS.js +1 -0
  172. package/Source/Shaders/BufferPointMaterialVS.glsl +19 -9
  173. package/Source/Shaders/BufferPointMaterialVS.js +20 -10
  174. package/Source/Shaders/BufferPolygonMaterialVS.glsl +13 -4
  175. package/Source/Shaders/BufferPolygonMaterialVS.js +14 -5
  176. package/Source/Shaders/BufferPolylineMaterialVS.glsl +16 -2
  177. package/Source/Shaders/BufferPolylineMaterialVS.js +17 -3
  178. package/Source/Shaders/Builtin/Constants/passCesium3DTileEdgesDirect.glsl +9 -0
  179. package/Source/Shaders/Builtin/Constants/passCesium3DTileEdgesDirect.js +11 -0
  180. package/Source/Shaders/Builtin/Constants/passOverlay.glsl +1 -1
  181. package/Source/Shaders/Builtin/Constants/passOverlay.js +1 -1
  182. package/Source/Shaders/Builtin/CzmBuiltins.js +2 -0
  183. package/Source/Shaders/Model/EdgeVisibilityStageVS.glsl +0 -1
  184. package/Source/Shaders/Model/EdgeVisibilityStageVS.js +0 -1
  185. package/index.d.ts +253 -35
  186. package/index.js +71 -62
  187. package/package.json +2 -2
@@ -15,10 +15,22 @@ import ModelReader from "./ModelReader.js";
15
15
  import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
16
16
  import AttributeType from "../AttributeType.js";
17
17
 
18
+ /**
19
+ * 2-bit edge visibility values as defined by EXT_mesh_primitive_edge_visibility.
20
+ * @private
21
+ */
22
+ const EdgeVisibilityType = Object.freeze({
23
+ HIDDEN: 0,
24
+ SILHOUETTE: 1,
25
+ HARD: 2,
26
+ // REPEATED_HARD (3) is a secondary spec encoding for a hard edge; normalized to HARD at decode time.
27
+ REPEATED_HARD: 3,
28
+ });
29
+
18
30
  /**
19
31
  * Builds derived line geometry for model edges using EXT_mesh_primitive_edge_visibility data.
20
32
  * It parses the encoded edge visibility bits, creates a separate edge-domain vertex array with
21
- * per-edge attributes (edge type, optional feature ID, silhouette normal, adjacent face normals),
33
+ * per-edge attributes (edge type, optional feature ID, adjacent face normals),
22
34
  * sets up the required shader defines / varyings, and stores the resulting line list geometry on
23
35
  * the render resources for a later edge rendering pass.
24
36
  *
@@ -93,13 +105,6 @@ EdgeVisibilityPipelineStage.process = function (
93
105
  "a_edgeFeatureId",
94
106
  );
95
107
 
96
- // Add silhouette normal attribute and varying for silhouette edges
97
- const silhouetteNormalLocation = shaderBuilder.addAttribute(
98
- "vec3",
99
- "a_silhouetteNormal",
100
- );
101
- shaderBuilder.addVarying("vec3", "v_silhouetteNormalView", "flat");
102
-
103
108
  // Add face normal attributes for silhouette detection
104
109
  const faceNormalALocation = shaderBuilder.addAttribute(
105
110
  "vec3",
@@ -126,11 +131,6 @@ EdgeVisibilityPipelineStage.process = function (
126
131
  );
127
132
  shaderBuilder.addVarying("float", "v_edgeOffset");
128
133
 
129
- // Add varying for view space position for perspective-correct silhouette detection
130
-
131
- // Build triangle adjacency (mapping edges to adjacent triangles) and compute per-triangle face normals.
132
- const adjacencyData = buildTriangleAdjacency(primitive);
133
-
134
134
  const edgeResult = extractVisibleEdges(primitive);
135
135
 
136
136
  if (
@@ -183,7 +183,6 @@ EdgeVisibilityPipelineStage.process = function (
183
183
 
184
184
  // Generate paired face normals for each unique edge (used to classify silhouette edges in the shader).
185
185
  const edgeFaceNormals = generateEdgeFaceNormals(
186
- adjacencyData,
187
186
  edgeResult.edgeIndices,
188
187
  edgeResult.edgeData,
189
188
  primitive.edgeVisibility,
@@ -196,7 +195,6 @@ EdgeVisibilityPipelineStage.process = function (
196
195
  renderResources,
197
196
  frameState.context,
198
197
  edgeTypeLocation,
199
- silhouetteNormalLocation,
200
198
  faceNormalALocation,
201
199
  faceNormalBLocation,
202
200
  edgeFeatureIdLocation,
@@ -259,258 +257,70 @@ EdgeVisibilityPipelineStage.process = function (
259
257
  };
260
258
 
261
259
  /**
262
- * Build triangle adjacency information and per-triangle face normals in model space.
263
- * The adjacency map associates an undirected edge (minIndex,maxIndex) with the indices
264
- * of up to two adjacent triangles. Face normals are normalized and stored sequentially.
260
+ * Generate face normal pairs for each silhouette edge from the GLB silhouetteNormals
261
+ * accessor. Per the spec, the accessor MUST be present for any primitive that encodes
262
+ * at least one silhouette edge.
265
263
  *
266
- * @param {ModelComponents.Primitive} primitive The primitive containing triangle index + position data
267
- * @returns {{edgeMap:Map<string, number[]>, faceNormals:Float32Array, triangleCount:number}}
268
- * @private
269
- */
270
- function buildTriangleAdjacency(primitive) {
271
- const indices = primitive.indices;
272
- if (!defined(indices)) {
273
- return {
274
- edgeMap: new Map(),
275
- faceNormals: new Float32Array(0),
276
- triangleCount: 0,
277
- };
278
- }
279
-
280
- const triangleIndexArray = indices.typedArray;
281
- const triangleCount = Math.floor(triangleIndexArray.length / 3);
282
-
283
- // Get vertex positions for face normal calculation
284
- const positionAttribute = ModelUtility.getAttributeBySemantic(
285
- primitive,
286
- VertexAttributeSemantic.POSITION,
287
- );
288
-
289
- // Retrieve raw (possibly quantized) position data. If the attribute is quantized
290
- // we must dequantize on the CPU here because we compute face normals and silhouette
291
- // classification data before the vertex shader's dequantization stage runs.
292
- let positions = defined(positionAttribute.typedArray)
293
- ? positionAttribute.typedArray
294
- : ModelReader.readAttributeAsTypedArray(positionAttribute);
295
-
296
- const quantization = positionAttribute.quantization;
297
- if (defined(quantization) && !quantization.octEncoded) {
298
- const count = positions.length; // length is 3 * vertexCount
299
- const dequantized = new Float32Array(count);
300
- const offset = quantization.quantizedVolumeOffset;
301
- const step = quantization.quantizedVolumeStepSize;
302
- for (let i = 0; i < count; i += 3) {
303
- dequantized[i] = offset.x + positions[i] * step.x;
304
- dequantized[i + 1] = offset.y + positions[i + 1] * step.y;
305
- dequantized[i + 2] = offset.z + positions[i + 2] * step.z;
306
- }
307
- positions = dequantized;
308
- }
309
-
310
- // Build edge map: key = "min,max", value = [triangleA, triangleB?]
311
- const edgeMap = new Map();
312
-
313
- // Calculate face normals for each triangle (model space)
314
- const faceNormals = new Float32Array(triangleCount * 3);
315
-
316
- // Scratch vectors to avoid heap allocations per triangle
317
- const scratchP0 = new Cartesian3();
318
- const scratchP1 = new Cartesian3();
319
- const scratchP2 = new Cartesian3();
320
- const scratchE1 = new Cartesian3();
321
- const scratchE2 = new Cartesian3();
322
- const scratchCross = new Cartesian3();
323
-
324
- function processEdge(a, b, triIndex) {
325
- const edgeKey = `${a < b ? a : b},${a < b ? b : a}`;
326
- let list = edgeMap.get(edgeKey);
327
- if (!defined(list)) {
328
- list = [];
329
- edgeMap.set(edgeKey, list);
330
- }
331
- if (list.length < 2) {
332
- list.push(triIndex);
333
- }
334
- }
335
-
336
- for (let t = 0; t < triangleCount; t++) {
337
- const base = t * 3;
338
- const i0 = triangleIndexArray[base];
339
- const i1 = triangleIndexArray[base + 1];
340
- const i2 = triangleIndexArray[base + 2];
341
-
342
- const i0o = i0 * 3;
343
- const i1o = i1 * 3;
344
- const i2o = i2 * 3;
345
-
346
- scratchP0.x = positions[i0o];
347
- scratchP0.y = positions[i0o + 1];
348
- scratchP0.z = positions[i0o + 2];
349
- scratchP1.x = positions[i1o];
350
- scratchP1.y = positions[i1o + 1];
351
- scratchP1.z = positions[i1o + 2];
352
- scratchP2.x = positions[i2o];
353
- scratchP2.y = positions[i2o + 1];
354
- scratchP2.z = positions[i2o + 2];
355
-
356
- Cartesian3.subtract(scratchP1, scratchP0, scratchE1);
357
- Cartesian3.subtract(scratchP2, scratchP0, scratchE2);
358
- Cartesian3.cross(scratchE1, scratchE2, scratchCross);
359
-
360
- // Skip degenerate triangles: a zero or non-finite cross product cannot be
361
- // safely normalized and would produce invalid face normals.
362
- const crossMagnitudeSquared = Cartesian3.magnitudeSquared(scratchCross);
363
- if (
364
- crossMagnitudeSquared === 0.0 ||
365
- !Number.isFinite(crossMagnitudeSquared)
366
- ) {
367
- continue;
368
- }
369
-
370
- Cartesian3.multiplyByScalar(
371
- scratchCross,
372
- 1.0 / Math.sqrt(crossMagnitudeSquared),
373
- scratchCross,
374
- );
375
-
376
- faceNormals[base] = scratchCross.x;
377
- faceNormals[base + 1] = scratchCross.y;
378
- faceNormals[base + 2] = scratchCross.z;
379
-
380
- // Register edges
381
- processEdge(i0, i1, t);
382
- processEdge(i1, i2, t);
383
- processEdge(i2, i0, t);
384
- }
385
-
386
- return { edgeMap, faceNormals, triangleCount };
387
- }
388
-
389
- /**
390
- * Generate face normal pairs for each edge. Silhouette edges use normals from the GLB
391
- * silhouetteNormals accessor if available. Boundary edges synthesize the opposite normal
392
- * as the negation of the first triangle's normal.
393
- *
394
- * @param {{edgeMap:Map<string,number[]>, faceNormals:Float32Array}} adjacencyData Triangle adjacency data
395
264
  * @param {number[]} edgeIndices Packed array of 2 vertex indices per edge
396
- * @param {Object[]} edgeData Array of edge metadata (edgeType, mateVertexIndex)
265
+ * @param {Object[]} edgeData Array of edge metadata (edgeType, silhouetteEdgeIndex)
397
266
  * @param {Object} edgeVisibility Edge visibility extension data
398
267
  * @returns {Float32Array} Packed array: 6 floats per edge (normalA.xyz, normalB.xyz)
399
268
  * @private
400
269
  */
401
- function generateEdgeFaceNormals(
402
- adjacencyData,
403
- edgeIndices,
404
- edgeData,
405
- edgeVisibility,
406
- ) {
407
- const { edgeMap, faceNormals } = adjacencyData;
270
+ function generateEdgeFaceNormals(edgeIndices, edgeData, edgeVisibility) {
408
271
  const numEdges = edgeIndices.length / 2;
409
272
  const edgeFaceNormals = new Float32Array(numEdges * 6);
410
273
 
411
- const hasGLBSilhouetteNormals =
412
- defined(edgeVisibility) && defined(edgeVisibility.silhouetteNormals);
413
-
414
- // Decode GLB silhouette normals from signed-byte VEC3 to a flat Float32Array
415
- // (3 floats per normal) to avoid per-normal object allocation and GC pressure.
416
- let silhouetteNormalsFloat = null;
417
-
418
- if (hasGLBSilhouetteNormals) {
419
- const raw = edgeVisibility.silhouetteNormals;
420
- silhouetteNormalsFloat = new Float32Array(raw.length * 3);
421
- const scratchNormal = new Cartesian3();
422
-
423
- for (let i = 0; i < raw.length; i++) {
424
- const vec3 = raw[i];
425
- // Signed byte → float: map [-128,127] → [-1,1] via [0,255]
426
- scratchNormal.x = 2 * ((vec3.x + 128) / 255) - 1;
427
- scratchNormal.y = 2 * ((vec3.y + 128) / 255) - 1;
428
- scratchNormal.z = 2 * ((vec3.z + 128) / 255) - 1;
429
-
430
- if (Cartesian3.magnitudeSquared(scratchNormal) > 0) {
431
- Cartesian3.normalize(scratchNormal, scratchNormal);
432
- } else {
433
- scratchNormal.x = 0;
434
- scratchNormal.y = 0;
435
- scratchNormal.z = 1;
436
- }
437
-
438
- silhouetteNormalsFloat[i * 3] = scratchNormal.x;
439
- silhouetteNormalsFloat[i * 3 + 1] = scratchNormal.y;
440
- silhouetteNormalsFloat[i * 3 + 2] = scratchNormal.z;
274
+ const raw = edgeVisibility.silhouetteNormals;
275
+ if (!defined(raw)) {
276
+ // No silhouette edges in this primitive, the shader only checks normals for SILHOUETTE edges.
277
+ return edgeFaceNormals;
278
+ }
279
+ const silhouetteNormalsFloat = new Float32Array(raw.length * 3);
280
+ const scratchNormal = new Cartesian3();
281
+
282
+ for (let i = 0; i < raw.length; i++) {
283
+ const vec3 = raw[i];
284
+ // Signed byte float: map [-128,127] → [-1,1]
285
+ scratchNormal.x = 2 * ((vec3.x + 128) / 255) - 1;
286
+ scratchNormal.y = 2 * ((vec3.y + 128) / 255) - 1;
287
+ scratchNormal.z = 2 * ((vec3.z + 128) / 255) - 1;
288
+
289
+ if (Cartesian3.magnitudeSquared(scratchNormal) > 0) {
290
+ Cartesian3.normalize(scratchNormal, scratchNormal);
291
+ } else {
292
+ scratchNormal.x = 0;
293
+ scratchNormal.y = 0;
294
+ scratchNormal.z = 1;
441
295
  }
296
+
297
+ silhouetteNormalsFloat[i * 3] = scratchNormal.x;
298
+ silhouetteNormalsFloat[i * 3 + 1] = scratchNormal.y;
299
+ silhouetteNormalsFloat[i * 3 + 2] = scratchNormal.z;
442
300
  }
443
301
 
444
302
  for (let i = 0; i < numEdges; i++) {
445
- const a = edgeIndices[i * 2];
446
- const b = edgeIndices[i * 2 + 1];
447
- const edgeKey = `${a < b ? a : b},${a < b ? b : a}`;
448
- const triangleList = edgeMap.get(edgeKey);
449
-
450
- const { edgeType, mateVertexIndex } = edgeData[i];
451
-
452
- let nAx, nAy, nAz, nBx, nBy, nBz;
453
- let usedGLBNormals = false;
454
-
455
- // Use GLB silhouetteNormals for type=1 (SILHOUETTE) edges if available
456
- if (
457
- hasGLBSilhouetteNormals &&
458
- silhouetteNormalsFloat &&
459
- edgeType === 1 &&
460
- mateVertexIndex >= 0
461
- ) {
462
- const pairBase = mateVertexIndex * 2;
463
- if ((pairBase + 1) * 3 + 2 < silhouetteNormalsFloat.length) {
464
- const baseA = pairBase * 3;
465
- const baseB = (pairBase + 1) * 3;
466
-
467
- nAx = silhouetteNormalsFloat[baseA];
468
- nAy = silhouetteNormalsFloat[baseA + 1];
469
- nAz = silhouetteNormalsFloat[baseA + 2];
470
- nBx = silhouetteNormalsFloat[baseB];
471
- nBy = silhouetteNormalsFloat[baseB + 1];
472
- nBz = silhouetteNormalsFloat[baseB + 2];
473
-
474
- usedGLBNormals = true;
475
- }
476
- }
303
+ const { edgeType, silhouetteEdgeIndex } = edgeData[i];
477
304
 
478
- // Fallback to triangle adjacency if GLB normals not used
479
- if (!usedGLBNormals) {
480
- // Expect at least one triangle; silently skip if not found (defensive)
481
- if (!defined(triangleList) || triangleList.length === 0) {
482
- continue;
483
- }
305
+ if (edgeType !== EdgeVisibilityType.SILHOUETTE || silhouetteEdgeIndex < 0) {
306
+ continue;
307
+ }
484
308
 
485
- const tA = triangleList[0];
486
- const aBase = tA * 3;
487
- nAx = faceNormals[aBase];
488
- nAy = faceNormals[aBase + 1];
489
- nAz = faceNormals[aBase + 2];
490
-
491
- const isManifold = triangleList.length > 1;
492
-
493
- if (isManifold) {
494
- const tB = triangleList[1];
495
- const bBase = tB * 3;
496
- nBx = faceNormals[bBase];
497
- nBy = faceNormals[bBase + 1];
498
- nBz = faceNormals[bBase + 2];
499
- } else {
500
- // Boundary edge – synthesize opposite normal
501
- nBx = -nAx;
502
- nBy = -nAy;
503
- nBz = -nAz;
504
- }
309
+ const pairBase = silhouetteEdgeIndex * 2;
310
+ if ((pairBase + 1) * 3 + 2 >= silhouetteNormalsFloat.length) {
311
+ continue;
505
312
  }
506
313
 
314
+ const baseA = pairBase * 3;
315
+ const baseB = (pairBase + 1) * 3;
507
316
  const baseIdx = i * 6;
508
- edgeFaceNormals[baseIdx] = nAx;
509
- edgeFaceNormals[baseIdx + 1] = nAy;
510
- edgeFaceNormals[baseIdx + 2] = nAz;
511
- edgeFaceNormals[baseIdx + 3] = nBx;
512
- edgeFaceNormals[baseIdx + 4] = nBy;
513
- edgeFaceNormals[baseIdx + 5] = nBz;
317
+
318
+ edgeFaceNormals[baseIdx] = silhouetteNormalsFloat[baseA];
319
+ edgeFaceNormals[baseIdx + 1] = silhouetteNormalsFloat[baseA + 1];
320
+ edgeFaceNormals[baseIdx + 2] = silhouetteNormalsFloat[baseA + 2];
321
+ edgeFaceNormals[baseIdx + 3] = silhouetteNormalsFloat[baseB];
322
+ edgeFaceNormals[baseIdx + 4] = silhouetteNormalsFloat[baseB + 1];
323
+ edgeFaceNormals[baseIdx + 5] = silhouetteNormalsFloat[baseB + 2];
514
324
  }
515
325
 
516
326
  return edgeFaceNormals;
@@ -520,10 +330,10 @@ function generateEdgeFaceNormals(
520
330
  * Parse the EXT_mesh_primitive_edge_visibility 2-bit edge encoding and extract
521
331
  * a unique set of edges that should be considered for rendering. Edge types:
522
332
  * <ul>
523
- * <li>0 HIDDEN - skipped</li>
524
- * <li>1 SILHOUETTE - candidates for conditional display based on facing</li>
525
- * <li>2 HARD - always displayed</li>
526
- * <li>3 REPEATED - secondary encoding for a hard edge (treated same as 2)</li>
333
+ * <li><code>EdgeVisibilityType.HIDDEN</code> - skipped</li>
334
+ * <li><code>EdgeVisibilityType.SILHOUETTE</code> - candidates for conditional display based on facing</li>
335
+ * <li><code>EdgeVisibilityType.HARD</code> - always displayed</li>
336
+ * <li><code>EdgeVisibilityType.REPEATED_HARD</code> - secondary encoding for a hard edge (treated same as HARD)</li>
527
337
  * </ul>
528
338
  * Deduplicates edges shared by adjacent triangles and records per-edge metadata.
529
339
  *
@@ -597,7 +407,7 @@ function extractVisibleEdges(primitive) {
597
407
  const byte = visibilityArray[byteIndex];
598
408
  const visibility2Bit = (byte >> bitPairOffset) & 0x3;
599
409
 
600
- if (visibility2Bit === 0) {
410
+ if (visibility2Bit === EdgeVisibilityType.HIDDEN) {
601
411
  continue;
602
412
  }
603
413
 
@@ -606,25 +416,28 @@ function extractVisibleEdges(primitive) {
606
416
  const edgeKey = `${small},${big}`;
607
417
 
608
418
  if (seenEdgeHashes.has(edgeKey)) {
419
+ // Deduplicate edges shared by adjacent triangles. This also handles
420
+ // REPEATED_HARD edges (type=3): their first occurrence is recorded as
421
+ // HARD, and any subsequent REPEATED_HARD occurrence is skipped here.
609
422
  continue;
610
423
  }
611
424
  seenEdgeHashes.add(edgeKey);
612
425
  edgeIndices.push(a, b);
613
426
 
614
- // Only process silhouette edges (type=1) as marked in GLB
615
- // Use computed edge index for boundary edges
616
- let mateVertexIndex = -1;
617
- if (visibility2Bit === 1) {
618
- mateVertexIndex = silhouetteEdgeCount;
427
+ // Assign a sequential index into the silhouetteNormals accessor for SILHOUETTE edges.
428
+ let silhouetteEdgeIndex = -1;
429
+ if (visibility2Bit === EdgeVisibilityType.SILHOUETTE) {
430
+ silhouetteEdgeIndex = silhouetteEdgeCount;
619
431
  silhouetteEdgeCount++;
620
432
  }
621
433
 
622
434
  edgeData.push({
623
- edgeType: visibility2Bit, // Use original GLB edge type
624
- triangleIndex: Math.floor(i / 3),
625
- edgeIndex: e,
626
- mateVertexIndex: mateVertexIndex,
627
- currentTriangleVertices: [v0, v1, v2],
435
+ // Normalize REPEATED_HARD to HARD both render identically in the shader.
436
+ edgeType:
437
+ visibility2Bit === EdgeVisibilityType.REPEATED_HARD
438
+ ? EdgeVisibilityType.HARD
439
+ : visibility2Bit,
440
+ silhouetteEdgeIndex: silhouetteEdgeIndex,
628
441
  color: globalColor,
629
442
  });
630
443
  }
@@ -687,11 +500,8 @@ function extractVisibleEdges(primitive) {
687
500
  seenEdgeHashes.add(edgeKey);
688
501
  edgeIndices.push(a, b);
689
502
  edgeData.push({
690
- edgeType: 2,
691
- triangleIndex: -1,
692
- edgeIndex: -1,
693
- mateVertexIndex: -1,
694
- currentTriangleVertices: undefined,
503
+ edgeType: EdgeVisibilityType.HARD,
504
+ silhouetteEdgeIndex: -1,
695
505
  color: defined(lineColor) ? lineColor : undefined,
696
506
  });
697
507
  }
@@ -792,7 +602,6 @@ function collectVertexColors(runtimePrimitive) {
792
602
  * @param {PrimitiveRenderResources} renderResources The render resources for the primitive
793
603
  * @param {Context} context The WebGL rendering context
794
604
  * @param {number} edgeTypeLocation Shader attribute location for the edge type
795
- * @param {number} silhouetteNormalLocation Shader attribute location for input silhouette normal
796
605
  * @param {number} faceNormalALocation Shader attribute location for face normal A
797
606
  * @param {number} faceNormalBLocation Shader attribute location for face normal B
798
607
  * @param {number} edgeFeatureIdLocation Shader attribute location for optional edge feature ID
@@ -813,7 +622,6 @@ function createQuadEdgeGeometry(
813
622
  renderResources,
814
623
  context,
815
624
  edgeTypeLocation,
816
- silhouetteNormalLocation,
817
625
  faceNormalALocation,
818
626
  faceNormalBLocation,
819
627
  edgeFeatureIdLocation,
@@ -849,7 +657,6 @@ function createQuadEdgeGeometry(
849
657
  // Create arrays for quad vertices
850
658
  const edgePosArray = new Float32Array(totalVerts * 3);
851
659
  const edgeTypeArray = new Float32Array(totalVerts);
852
- const silhouetteNormalArray = new Float32Array(totalVerts * 3);
853
660
  const faceNormalAArray = new Float32Array(totalVerts * 3);
854
661
  const faceNormalBArray = new Float32Array(totalVerts * 3);
855
662
  const edgeOtherPosArray = new Float32Array(totalVerts * 3);
@@ -1002,32 +809,6 @@ function createQuadEdgeGeometry(
1002
809
  edgeCumDistArray[baseVertexIndex + 3] = cumDistB;
1003
810
  }
1004
811
 
1005
- // Set silhouette normal (same for all 4 vertices)
1006
- let normalX = 0,
1007
- normalY = 0,
1008
- normalZ = 1;
1009
-
1010
- if (rawType === 1 && defined(edgeVisibility.silhouetteNormals)) {
1011
- const mateVertexIndex = edgeData[i].mateVertexIndex;
1012
- if (
1013
- mateVertexIndex >= 0 &&
1014
- mateVertexIndex < edgeVisibility.silhouetteNormals.length
1015
- ) {
1016
- const normal = edgeVisibility.silhouetteNormals[mateVertexIndex];
1017
- if (defined(normal)) {
1018
- normalX = normal.x;
1019
- normalY = normal.y;
1020
- normalZ = normal.z;
1021
- }
1022
- }
1023
- }
1024
-
1025
- for (let v = 0; v < 4; v++) {
1026
- silhouetteNormalArray[(baseVertexIndex + v) * 3] = normalX;
1027
- silhouetteNormalArray[(baseVertexIndex + v) * 3 + 1] = normalY;
1028
- silhouetteNormalArray[(baseVertexIndex + v) * 3 + 2] = normalZ;
1029
- }
1030
-
1031
812
  // Set face normals (same for all 4 vertices)
1032
813
  const faceNormalIdx = i * 6;
1033
814
  const normalAX = edgeFaceNormals[faceNormalIdx];
@@ -1060,12 +841,6 @@ function createQuadEdgeGeometry(
1060
841
  usage: BufferUsage.STATIC_DRAW,
1061
842
  });
1062
843
 
1063
- const silhouetteNormalBuffer = Buffer.createVertexBuffer({
1064
- context,
1065
- typedArray: silhouetteNormalArray,
1066
- usage: BufferUsage.STATIC_DRAW,
1067
- });
1068
-
1069
844
  const faceNormalABuffer = Buffer.createVertexBuffer({
1070
845
  context,
1071
846
  typedArray: faceNormalAArray,
@@ -1154,13 +929,6 @@ function createQuadEdgeGeometry(
1154
929
  componentDatatype: ComponentDatatype.FLOAT,
1155
930
  normalize: false,
1156
931
  },
1157
- {
1158
- index: silhouetteNormalLocation,
1159
- vertexBuffer: silhouetteNormalBuffer,
1160
- componentsPerAttribute: 3,
1161
- componentDatatype: ComponentDatatype.FLOAT,
1162
- normalize: false,
1163
- },
1164
932
  {
1165
933
  index: faceNormalALocation,
1166
934
  vertexBuffer: faceNormalABuffer,
@@ -20,6 +20,7 @@ import ClippingPlaneCollection from "../ClippingPlaneCollection.js";
20
20
  import ClippingPolygonCollection from "../ClippingPolygonCollection.js";
21
21
  import DynamicEnvironmentMapManager from "../DynamicEnvironmentMapManager.js";
22
22
  import ColorBlendMode from "../ColorBlendMode.js";
23
+ import EdgeDisplayMode from "../EdgeDisplayMode.js";
23
24
  import GltfLoader from "../GltfLoader.js";
24
25
  import HeightReference, {
25
26
  isHeightReferenceRelative,
@@ -75,6 +76,11 @@ import ModelImagery from "./ModelImagery.js";
75
76
  * {@link https://github.com/KhronosGroup/glTF/pull/2514|EXT_mesh_primitive_restart}
76
77
  * </li>
77
78
  * <li>
79
+ * {@link https://github.com/KhronosGroup/glTF/pull/2479|EXT_mesh_primitive_edge_visibility}
80
+ * (edges are hidden by default; set {@link EdgeDisplayMode} via
81
+ * {@link Model#edgeDisplayMode} or {@link Cesium3DTileset#edgeDisplayMode} to display them)
82
+ * </li>
83
+ * <li>
78
84
  * {@link https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/EXT_meshopt_compression|EXT_meshopt_compression}
79
85
  * </li>
80
86
  * <li>
@@ -151,6 +157,7 @@ import ModelImagery from "./ModelImagery.js";
151
157
  * @privateParam {Color} [options.color] A color that blends with the model's rendered color.
152
158
  * @privateParam {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model.
153
159
  * @privateParam {number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two.
160
+ * @privateParam {EdgeDisplayMode} [options.edgeDisplayMode=EdgeDisplayMode.SURFACES_ONLY] Controls how edges from the {@link https://github.com/KhronosGroup/glTF/pull/2479|EXT_mesh_primitive_edge_visibility} extension are rendered relative to surface geometry.
154
161
  * @privateParam {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts.
155
162
  * @privateParam {number} [options.silhouetteSize=0.0] The size of the silhouette in pixels.
156
163
  * @privateParam {boolean} [options.enableShowOutline=true] Whether to enable outlines for models using the {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension. This can be set to false to avoid the additional processing of geometry at load time. When false, the showOutlines and outlineColor options are ignored.
@@ -417,6 +424,8 @@ function Model(options) {
417
424
  this._enableDebugWireframe = options.enableDebugWireframe ?? false;
418
425
  this._enableShowOutline = options.enableShowOutline ?? true;
419
426
  this._debugWireframe = options.debugWireframe ?? false;
427
+ this._edgeDisplayMode =
428
+ options.edgeDisplayMode ?? EdgeDisplayMode.SURFACES_ONLY;
420
429
 
421
430
  // Warning for improper setup of debug wireframe
422
431
  if (
@@ -1204,6 +1213,29 @@ Object.defineProperties(Model.prototype, {
1204
1213
  },
1205
1214
  },
1206
1215
 
1216
+ /**
1217
+ * Controls how edges from the
1218
+ * {@link https://github.com/KhronosGroup/glTF/pull/2479|EXT_mesh_primitive_edge_visibility}
1219
+ * glTF extension are rendered relative to surface geometry. Primitives that
1220
+ * do not declare the extension are unaffected by this setting.
1221
+ *
1222
+ * @memberof Model.prototype
1223
+ *
1224
+ * @type {EdgeDisplayMode}
1225
+ *
1226
+ * @default EdgeDisplayMode.SURFACES_ONLY
1227
+ *
1228
+ * @experimental This feature is using part of the glTF spec that is not yet final and is subject to change without Cesium's standard deprecation policy.
1229
+ */
1230
+ edgeDisplayMode: {
1231
+ get: function () {
1232
+ return this._edgeDisplayMode;
1233
+ },
1234
+ set: function (value) {
1235
+ this._edgeDisplayMode = value;
1236
+ },
1237
+ },
1238
+
1207
1239
  /**
1208
1240
  * Whether or not to render the model.
1209
1241
  *
@@ -2941,6 +2973,7 @@ Model.prototype.destroyModelResources = function () {
2941
2973
  * @param {Color} [options.color] A color that blends with the model's rendered color.
2942
2974
  * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model.
2943
2975
  * @param {number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two.
2976
+ * @param {EdgeDisplayMode} [options.edgeDisplayMode=EdgeDisplayMode.SURFACES_ONLY] Controls how edges from the {@link https://github.com/KhronosGroup/glTF/pull/2479|EXT_mesh_primitive_edge_visibility} extension are rendered relative to surface geometry.
2944
2977
  * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts.
2945
2978
  * @param {number} [options.silhouetteSize=0.0] The size of the silhouette in pixels.
2946
2979
  * @param {boolean} [options.enableShowOutline=true] Whether to enable outlines for models using the {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension. This can be set false to avoid post-processing geometry at load time. When false, the showOutlines and outlineColor options are ignored.
@@ -3307,6 +3340,7 @@ function makeModelOptions(loader, modelType, options) {
3307
3340
  color: options.color,
3308
3341
  colorBlendAmount: options.colorBlendAmount,
3309
3342
  colorBlendMode: options.colorBlendMode,
3343
+ edgeDisplayMode: options.edgeDisplayMode,
3310
3344
  silhouetteColor: options.silhouetteColor,
3311
3345
  silhouetteSize: options.silhouetteSize,
3312
3346
  enableShowOutline: options.enableShowOutline,