@cesium/engine 17.0.0 → 18.0.1-splats.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 (188) hide show
  1. package/Build/ThirdParty/Workers/z-worker-pako.js +1 -1
  2. package/Build/Workers/{chunk-UC7KH56Y.js → chunk-2AXKROJA.js} +5 -5
  3. package/Build/Workers/{chunk-IKDQX7DY.js → chunk-2J5ZZ7CY.js} +4 -4
  4. package/Build/Workers/{chunk-4TIR5UPL.js → chunk-2SYY3UBI.js} +1 -1
  5. package/Build/Workers/{chunk-CC2WKHIW.js → chunk-2WKW3BEB.js} +6 -6
  6. package/Build/Workers/{chunk-CBFJVM6I.js → chunk-2ZO6BCHN.js} +11 -11
  7. package/Build/Workers/{chunk-7PHT4JHA.js → chunk-3UKG5X7F.js} +12 -12
  8. package/Build/Workers/{chunk-PM4Q36WX.js → chunk-4GYIDPXP.js} +9 -9
  9. package/Build/Workers/{chunk-QUFN3GEO.js → chunk-6EP7ED36.js} +3 -3
  10. package/Build/Workers/{chunk-QJTIOB2Z.js → chunk-6QOBORNG.js} +6 -6
  11. package/Build/Workers/{chunk-YE6MDFDA.js → chunk-6WWHK2ER.js} +14 -14
  12. package/Build/Workers/{chunk-RX65FA7X.js → chunk-6Y6TVYHS.js} +7 -7
  13. package/Build/Workers/{chunk-P2N5IDWU.js → chunk-744VWE2U.js} +13 -13
  14. package/Build/Workers/{chunk-LVGYWPEX.js → chunk-7PQ52XCW.js} +4 -4
  15. package/Build/Workers/{chunk-PS3OKVOQ.js → chunk-A5UFJW2M.js} +12 -12
  16. package/Build/Workers/{chunk-CAKU6ZPR.js → chunk-AQKVFTWV.js} +8 -8
  17. package/Build/Workers/{chunk-236N6BJN.js → chunk-BKTEHJFD.js} +2 -2
  18. package/Build/Workers/{chunk-5PC577K7.js → chunk-CQXYKPAW.js} +16 -16
  19. package/Build/Workers/{chunk-DRMXU674.js → chunk-DI7PHKC6.js} +5 -5
  20. package/Build/Workers/{chunk-CQMXZF4A.js → chunk-DKKIQS6F.js} +6 -6
  21. package/Build/Workers/{chunk-J2HTPGC7.js → chunk-DTVWE5YL.js} +2 -2
  22. package/Build/Workers/{chunk-SSLK4U3V.js → chunk-ERPS2WZC.js} +4 -4
  23. package/Build/Workers/chunk-EYMZ5FSQ.js +280 -0
  24. package/Build/Workers/{chunk-LPGOAMYH.js → chunk-F43TQW5B.js} +2 -2
  25. package/Build/Workers/{chunk-5RPUEFSA.js → chunk-FOMRN6II.js} +6 -6
  26. package/Build/Workers/{chunk-2BJXFXD7.js → chunk-FOTXJYKS.js} +4 -4
  27. package/Build/Workers/{chunk-VXAKU6EV.js → chunk-G2Y7EA4W.js} +5 -5
  28. package/Build/Workers/{chunk-KE45DUDH.js → chunk-GY4E4ZP6.js} +10 -10
  29. package/Build/Workers/{chunk-NZSBSY5K.js → chunk-HDG3VHYL.js} +2 -2
  30. package/Build/Workers/{chunk-WP3IJYLC.js → chunk-HOEUXPBL.js} +9 -9
  31. package/Build/Workers/{chunk-5YEFZMMM.js → chunk-IWDYVJ56.js} +12 -12
  32. package/Build/Workers/{chunk-SQKTLDSD.js → chunk-JA4KPQJL.js} +12 -12
  33. package/Build/Workers/{chunk-MAIKDIBS.js → chunk-JZZQEZMA.js} +6 -6
  34. package/Build/Workers/{chunk-77MDEA47.js → chunk-KPLU3WDY.js} +5 -5
  35. package/Build/Workers/{chunk-E7SMO47Q.js → chunk-LHM6V4D6.js} +5 -5
  36. package/Build/Workers/{chunk-HBNWBMAM.js → chunk-LMTGCD6P.js} +1 -1
  37. package/Build/Workers/{chunk-XYGBWBD5.js → chunk-LWZOYSSF.js} +1 -1
  38. package/Build/Workers/{chunk-OBHLT34A.js → chunk-M6U7SV4T.js} +7 -7
  39. package/Build/Workers/{chunk-MS5NWDHH.js → chunk-O25M56DO.js} +2 -2
  40. package/Build/Workers/{chunk-UX5JWAC7.js → chunk-OE2WXO2Z.js} +10 -10
  41. package/Build/Workers/{chunk-C5DCX2YQ.js → chunk-PFD462OE.js} +6 -6
  42. package/Build/Workers/{chunk-37PA7GZW.js → chunk-PPI33PXH.js} +7 -7
  43. package/Build/Workers/{chunk-4BGKMM7J.js → chunk-PRQQHLUB.js} +1 -1
  44. package/Build/Workers/{chunk-IFIS4CVK.js → chunk-RTUZ7MJB.js} +2 -2
  45. package/Build/Workers/{chunk-G6JUXZUO.js → chunk-S6QBTP5U.js} +4 -4
  46. package/Build/Workers/{chunk-UFV3YUIN.js → chunk-SMFCIGM3.js} +5 -5
  47. package/Build/Workers/{chunk-P57PR3WM.js → chunk-TC3LEKFJ.js} +5 -5
  48. package/Build/Workers/{chunk-7IIMO7ZZ.js → chunk-UTLYCPFY.js} +16 -16
  49. package/Build/Workers/{chunk-HGEGZ67N.js → chunk-V7LEBY53.js} +6 -6
  50. package/Build/Workers/{chunk-GO3RZX2R.js → chunk-VE3UKRDB.js} +9 -9
  51. package/Build/Workers/{chunk-ZTBZMRQT.js → chunk-W3FBD4FE.js} +8 -8
  52. package/Build/Workers/{chunk-K5JTWJPE.js → chunk-YRR6CJGG.js} +4 -4
  53. package/Build/Workers/{chunk-UZ6KXWCA.js → chunk-ZVTKMLU6.js} +4 -4
  54. package/Build/Workers/combineGeometry.js +21 -21
  55. package/Build/Workers/createBoxGeometry.js +15 -15
  56. package/Build/Workers/createBoxOutlineGeometry.js +13 -13
  57. package/Build/Workers/createCircleGeometry.js +23 -23
  58. package/Build/Workers/createCircleOutlineGeometry.js +16 -16
  59. package/Build/Workers/createCoplanarPolygonGeometry.js +30 -30
  60. package/Build/Workers/createCoplanarPolygonOutlineGeometry.js +28 -28
  61. package/Build/Workers/createCorridorGeometry.js +26 -26
  62. package/Build/Workers/createCorridorOutlineGeometry.js +25 -25
  63. package/Build/Workers/createCylinderGeometry.js +17 -17
  64. package/Build/Workers/createCylinderOutlineGeometry.js +15 -15
  65. package/Build/Workers/createEllipseGeometry.js +23 -23
  66. package/Build/Workers/createEllipseOutlineGeometry.js +16 -16
  67. package/Build/Workers/createEllipsoidGeometry.js +16 -16
  68. package/Build/Workers/createEllipsoidOutlineGeometry.js +15 -15
  69. package/Build/Workers/createFrustumGeometry.js +15 -15
  70. package/Build/Workers/createFrustumOutlineGeometry.js +15 -15
  71. package/Build/Workers/createGeometry.js +23 -21
  72. package/Build/Workers/createGroundPolylineGeometry.js +19 -19
  73. package/Build/Workers/createPlaneGeometry.js +13 -13
  74. package/Build/Workers/createPlaneOutlineGeometry.js +12 -12
  75. package/Build/Workers/createPolygonGeometry.js +29 -29
  76. package/Build/Workers/createPolygonOutlineGeometry.js +27 -27
  77. package/Build/Workers/createPolylineGeometry.js +22 -22
  78. package/Build/Workers/createPolylineVolumeGeometry.js +28 -28
  79. package/Build/Workers/createPolylineVolumeOutlineGeometry.js +24 -24
  80. package/Build/Workers/createRectangleGeometry.js +24 -24
  81. package/Build/Workers/createRectangleOutlineGeometry.js +17 -17
  82. package/Build/Workers/createSimplePolylineGeometry.js +20 -20
  83. package/Build/Workers/createSphereGeometry.js +16 -16
  84. package/Build/Workers/createSphereOutlineGeometry.js +15 -15
  85. package/Build/Workers/createTaskProcessorWorker.js +3 -3
  86. package/Build/Workers/createVectorTileClampedPolylines.js +12 -12
  87. package/Build/Workers/createVectorTileGeometries.js +21 -21
  88. package/Build/Workers/createVectorTilePoints.js +11 -11
  89. package/Build/Workers/createVectorTilePolygons.js +19 -19
  90. package/Build/Workers/createVectorTilePolylines.js +12 -12
  91. package/Build/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +19 -19
  92. package/Build/Workers/createVerticesFromHeightmap.js +19 -19
  93. package/Build/Workers/createVerticesFromQuantizedTerrainMesh.js +16 -16
  94. package/Build/Workers/createWallGeometry.js +21 -21
  95. package/Build/Workers/createWallOutlineGeometry.js +20 -20
  96. package/Build/Workers/decodeDraco.js +10 -10
  97. package/Build/Workers/decodeGoogleEarthEnterprisePacket.js +5 -5
  98. package/Build/Workers/decodeI3S.js +9 -9
  99. package/Build/Workers/gaussianSplatSorter.js +62 -0
  100. package/Build/Workers/gaussianSplatTextureGenerator.js +68 -0
  101. package/Build/Workers/transcodeKTX2.js +78 -7
  102. package/Build/Workers/transferTypedArrayTest.js +1 -1
  103. package/Build/Workers/upsampleQuantizedTerrainMesh.js +19 -19
  104. package/Source/Core/Ion.js +1 -1
  105. package/Source/Core/PixelFormat.js +98 -0
  106. package/Source/Renderer/Context.js +2 -0
  107. package/Source/Renderer/Pass.js +3 -2
  108. package/Source/Renderer/Texture.js +1 -0
  109. package/Source/Renderer/Texture3D.js +568 -0
  110. package/Source/Renderer/VertexArray.js +33 -8
  111. package/Source/Renderer/createUniform.js +3 -0
  112. package/Source/Renderer/createUniformArray.js +1 -0
  113. package/Source/Scene/ArcGisMapService.js +1 -1
  114. package/Source/Scene/Cesium3DTileContentFactory.js +23 -0
  115. package/Source/Scene/Cesium3DTileset.js +197 -2
  116. package/Source/Scene/GaussianSplat3DTileContent.js +504 -0
  117. package/Source/Scene/GaussianSplatPrimitive.js +932 -0
  118. package/Source/Scene/GaussianSplatRenderResources.js +81 -0
  119. package/Source/Scene/GaussianSplatSorter.js +73 -0
  120. package/Source/Scene/GaussianSplatTextureGenerator.js +64 -0
  121. package/Source/Scene/GlobeSurfaceShaderSet.js +4 -5
  122. package/Source/Scene/GltfLoader.js +82 -4
  123. package/Source/Scene/GltfSpzLoader.js +222 -0
  124. package/Source/Scene/GltfVertexBufferLoader.js +98 -3
  125. package/Source/Scene/ImageryLayer.js +19 -4
  126. package/Source/Scene/ImageryLayerCollection.js +3 -2
  127. package/Source/Scene/Model/CartesianRectangle.js +108 -0
  128. package/Source/Scene/Model/ImageryConfiguration.js +25 -0
  129. package/Source/Scene/Model/ImageryCoverage.js +488 -0
  130. package/Source/Scene/Model/ImageryFlags.js +31 -0
  131. package/Source/Scene/Model/ImageryInput.js +60 -0
  132. package/Source/Scene/Model/ImageryPipelineStage.js +1040 -0
  133. package/Source/Scene/Model/MappedPositions.js +84 -0
  134. package/Source/Scene/Model/Model.js +38 -0
  135. package/Source/Scene/Model/ModelImagery.js +403 -0
  136. package/Source/Scene/Model/ModelImageryMapping.js +489 -0
  137. package/Source/Scene/Model/ModelPrimitiveImagery.js +972 -0
  138. package/Source/Scene/Model/ModelReader.js +810 -0
  139. package/Source/Scene/Model/ModelRenderResources.js +13 -0
  140. package/Source/Scene/Model/ModelRuntimePrimitive.js +20 -0
  141. package/Source/Scene/Model/ModelSceneGraph.js +133 -27
  142. package/Source/Scene/Model/ModelUtility.js +2 -0
  143. package/Source/Scene/Model/NodeRenderResources.js +10 -0
  144. package/Source/Scene/ModelComponents.js +10 -0
  145. package/Source/Scene/PrimitiveLoadPlan.js +59 -0
  146. package/Source/Scene/ResourceCache.js +60 -3
  147. package/Source/Scene/ResourceCacheKey.js +39 -1
  148. package/Source/Scene/Scene.js +32 -1
  149. package/Source/Scene/VertexAttributeSemantic.js +30 -0
  150. package/Source/Scene/VoxelPrimitive.js +22 -37
  151. package/Source/Scene/VoxelRenderResources.js +1 -1
  152. package/Source/Scene/processVoxelProperties.js +41 -100
  153. package/Source/Shaders/Builtin/Constants/passGaussianSplats.glsl +9 -0
  154. package/Source/Shaders/Builtin/Constants/passGaussianSplats.js +11 -0
  155. package/Source/Shaders/Builtin/Constants/passOverlay.glsl +1 -1
  156. package/Source/Shaders/Builtin/Constants/passOverlay.js +1 -1
  157. package/Source/Shaders/Builtin/CzmBuiltins.js +2 -0
  158. package/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl +0 -1
  159. package/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.js +0 -1
  160. package/Source/Shaders/GlobeFS.glsl +22 -0
  161. package/Source/Shaders/GlobeFS.js +22 -0
  162. package/Source/Shaders/Model/MaterialStageFS.glsl +4 -0
  163. package/Source/Shaders/Model/MaterialStageFS.js +4 -0
  164. package/Source/Shaders/Model/ModelFS.glsl +3 -1
  165. package/Source/Shaders/Model/ModelFS.js +3 -1
  166. package/Source/Shaders/Model/ModelVS.glsl +5 -1
  167. package/Source/Shaders/Model/ModelVS.js +5 -1
  168. package/Source/Shaders/Model/PointCloudStylingStageVS.glsl +1 -1
  169. package/Source/Shaders/Model/PointCloudStylingStageVS.js +2 -1
  170. package/Source/Shaders/PrimitiveGaussianSplatFS.glsl +18 -0
  171. package/Source/Shaders/PrimitiveGaussianSplatFS.js +20 -0
  172. package/Source/Shaders/PrimitiveGaussianSplatVS.glsl +108 -0
  173. package/Source/Shaders/PrimitiveGaussianSplatVS.js +109 -0
  174. package/Source/Shaders/Voxels/IntersectDepth.glsl +10 -3
  175. package/Source/Shaders/Voxels/IntersectDepth.js +10 -3
  176. package/Source/Shaders/Voxels/Intersection.glsl +1 -3
  177. package/Source/Shaders/Voxels/Intersection.js +1 -3
  178. package/Source/Shaders/Voxels/IntersectionUtils.glsl +1 -0
  179. package/Source/Shaders/Voxels/IntersectionUtils.js +1 -0
  180. package/Source/Shaders/Voxels/VoxelFS.glsl +37 -24
  181. package/Source/Shaders/Voxels/VoxelFS.js +37 -24
  182. package/Source/ThirdParty/Workers/z-worker-pako.js +1 -1
  183. package/Source/ThirdParty/wasm_splats_bg.wasm +0 -0
  184. package/Source/Workers/gaussianSplatSorter.js +34 -0
  185. package/Source/Workers/gaussianSplatTextureGenerator.js +39 -0
  186. package/index.d.ts +66 -2
  187. package/index.js +101 -80
  188. package/package.json +6 -4
@@ -0,0 +1,1040 @@
1
+ import defined from "../../Core/defined.js";
2
+ import Cartesian4 from "../../Core/Cartesian4.js";
3
+ import Check from "../../Core/Check.js";
4
+ import Rectangle from "../../Core/Rectangle.js";
5
+
6
+ import ShaderDestination from "../../Renderer/ShaderDestination.js";
7
+
8
+ import ImageryLayer from "../ImageryLayer.js";
9
+ import AttributeType from "../AttributeType.js";
10
+
11
+ import ImageryFlags from "./ImageryFlags.js";
12
+ import ModelPrimitiveImagery from "./ModelPrimitiveImagery.js";
13
+ import ImageryInput from "./ImageryInput.js";
14
+ import ImageryState from "../ImageryState.js";
15
+ import oneTimeWarning from "../../Core/oneTimeWarning.js";
16
+
17
+ /**
18
+ * A compile-time debugging flag to draw the boundaries of imagery tiles
19
+ * (Similar to "SHOW_TILE_BOUNDARIES" in GlobeFS.glsl)
20
+ * @private
21
+ */
22
+ const debugDrawImageryBoundaries = false;
23
+
24
+ // Scratch variable for _computeTextureTranslationAndScale
25
+ const nativeBoundingRectangleScratch = new Rectangle();
26
+
27
+ // Scratch variable for _computeTextureTranslationAndScale
28
+ const nativeImageryRectangleScratch = new Rectangle();
29
+
30
+ /**
31
+ * A pipeline stage that modifies the model shader to take into account
32
+ * imagery textures that are draped over a primitive of the model, when it
33
+ * is part a <code>Model3DTileContent</code> of a <code>Cesium3DTileset</code>
34
+ * that has <code>imageryLayers</code> associated with it.
35
+ *
36
+ * Most of what is done here tries to emulate the parts from
37
+ * https://github.com/CesiumGS/cesium/blob/6cc004aaff586bb59f07f199216ae511014cf5a9/packages/engine/Source/Shaders/GlobeFS.glsl#L166
38
+ * that are relevant for imagery, using the more modern ShaderBuilder structures.
39
+ *
40
+ * @namespace ImageryPipelineStage
41
+ *
42
+ * @private
43
+ */
44
+ class ImageryPipelineStage {
45
+ /**
46
+ * Process a primitive.
47
+ *
48
+ * This will update the render resources of the given primitive,
49
+ * depending on the imagery that is covered by the given primitive.
50
+ *
51
+ * This will obtain the <code>ModelPrimitiveImagery</code> from
52
+ * the given primitive, and use that to compute the actual
53
+ * <code>ImageryInput</code> objects that describe the information
54
+ * that has to be passed to the shader for draping the imagery over
55
+ * the primitive.
56
+ *
57
+ * After the <code>ImageryInput</code> has been computed, it will
58
+ * extend the render resources with the texture coordinate
59
+ * attribute that has to be used for the imagery, and augment
60
+ * the <code>primitiveRenderResources.shaderBuilder</code> with
61
+ * the information hat is required for the draping.
62
+ *
63
+ * @param {PrimitiveRenderResources} primitiveRenderResources The render resources for the primitive
64
+ * @param {ModelComponents.Primitive} primitive The primitive to be rendered
65
+ * @param {FrameState} frameState The frame state.
66
+ */
67
+ static process(primitiveRenderResources, primitive, frameState) {
68
+ const model = primitiveRenderResources.model;
69
+ const modelPrimitiveImagery = primitive.modelPrimitiveImagery;
70
+
71
+ // When the creation of the model primitive imagery is happening
72
+ // asynchronously, then the primitive may not yet contain it.
73
+ // Return early in this case
74
+ if (!defined(modelPrimitiveImagery)) {
75
+ return;
76
+ }
77
+
78
+ // Similarly, when the model primitive imagery is not yet ready,
79
+ // then nothing can be done here
80
+ if (!modelPrimitiveImagery.ready) {
81
+ return;
82
+ }
83
+
84
+ // Compute the arrays containing ALL projections and the array
85
+ // containing the UNIQUE projections from the imagery layers,
86
+ // to establish the mapping between the imagery index and the
87
+ // imagery texture coordinate attribute set index.
88
+ // (This should be implemented and/or documented better...)
89
+ const imageryLayers = model.imageryLayers;
90
+ const allProjections =
91
+ ModelPrimitiveImagery._extractProjections(imageryLayers);
92
+ const uniqueProjections = [...new Set(allProjections)];
93
+ const imageryTexCoordAttributeSetIndices =
94
+ ImageryPipelineStage._computeIndexMapping(
95
+ allProjections,
96
+ uniqueProjections,
97
+ );
98
+
99
+ // Create the `ImageryInput` objects that describe
100
+ // - the texture
101
+ // - texture coordinate rectangle
102
+ // - translation and scale
103
+ // - index of the imagery texture coordinate attribute
104
+ // to be passed to the actual imagery pipeline stage execution
105
+ const imageryInputs = ImageryPipelineStage._createImageryInputs(
106
+ imageryLayers,
107
+ modelPrimitiveImagery,
108
+ imageryTexCoordAttributeSetIndices,
109
+ );
110
+
111
+ // This can happen when none of the imagery textures could
112
+ // be obtained, because they had all been INVALID/FAILED.
113
+ // Bail out in this case
114
+ if (imageryInputs.length === 0) {
115
+ return;
116
+ }
117
+
118
+ // TODO_DRAPING This will have to be handled with upsampling.
119
+ // For now, just truncate the textures to not exceed the
120
+ // number of texture units
121
+ if (imageryInputs.length > 10) {
122
+ oneTimeWarning(
123
+ "imagery-texture-units",
124
+ `Warning: Draped imagery requires ${imageryInputs.length} texture units, truncating`,
125
+ );
126
+ imageryInputs.length = 10;
127
+ }
128
+
129
+ // Add the imagery texture coordinate attributes to the render
130
+ // resources
131
+ ImageryPipelineStage._addImageryTexCoordAttributesToRenderResources(
132
+ modelPrimitiveImagery,
133
+ primitiveRenderResources,
134
+ );
135
+
136
+ const imageryLayersArray = [];
137
+ for (let i = 0; i < imageryLayers.length; i++) {
138
+ imageryLayersArray.push(imageryLayers.get(i));
139
+ }
140
+
141
+ ImageryPipelineStage._processImageryInputs(
142
+ imageryLayersArray,
143
+ primitiveRenderResources,
144
+ imageryInputs,
145
+ uniqueProjections.length,
146
+ );
147
+ }
148
+
149
+ /**
150
+ * Add one attribute to the render resources, for each imagery texture
151
+ * coordinate that was computed in the given model primitive imagery
152
+ * (one for each projection)
153
+ *
154
+ * @param {ModelPrimitiveImagery} modelPrimitiveImagery The model primitive imagery
155
+ * @param {PrimitiveRenderResources} primitiveRenderResources The render resources
156
+ */
157
+ static _addImageryTexCoordAttributesToRenderResources(
158
+ modelPrimitiveImagery,
159
+ primitiveRenderResources,
160
+ ) {
161
+ const imageryTexCoordAttributes =
162
+ modelPrimitiveImagery.imageryTexCoordAttributesPerProjection();
163
+ for (const imageryTexCoordAttribute of imageryTexCoordAttributes) {
164
+ ImageryPipelineStage._addImageryTexCoordAttributeToRenderResources(
165
+ imageryTexCoordAttribute,
166
+ primitiveRenderResources,
167
+ );
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Add the given attribute to the render resources
173
+ *
174
+ * @param {ModelComponents.Attribute} imageryTexCoordAttribute The attribute
175
+ * @param {PrimitiveRenderResources} primitiveRenderResources The render resources
176
+ */
177
+ static _addImageryTexCoordAttributeToRenderResources(
178
+ imageryTexCoordAttribute,
179
+ primitiveRenderResources,
180
+ ) {
181
+ const componentsPerAttribute = AttributeType.getNumberOfComponents(
182
+ imageryTexCoordAttribute.type,
183
+ );
184
+ // Convert the given object into another object that essentially
185
+ // contains the same information, but not exactly, and with most
186
+ // properties having slightly different names. Shrug.
187
+ const renderResourcesAttribute = {
188
+ index: primitiveRenderResources.attributeIndex++,
189
+ value: defined(imageryTexCoordAttribute.buffer)
190
+ ? undefined
191
+ : imageryTexCoordAttribute.constant,
192
+ vertexBuffer: imageryTexCoordAttribute.buffer,
193
+ count: imageryTexCoordAttribute.count,
194
+ componentsPerAttribute: componentsPerAttribute,
195
+ componentDatatype: imageryTexCoordAttribute.componentDatatype,
196
+ offsetInBytes: imageryTexCoordAttribute.byteOffset,
197
+ strideInBytes: imageryTexCoordAttribute.byteStride,
198
+ normalize: imageryTexCoordAttribute.normalized,
199
+ };
200
+ primitiveRenderResources.attributes.push(renderResourcesAttribute);
201
+ }
202
+
203
+ /**
204
+ * Process the <code>ImageryInput</code> objects that have been
205
+ * created in <code>process</code>.
206
+ *
207
+ * This will build the shader, containing the attributes, uniforms,
208
+ * and "sample and blend" function that is required according to
209
+ * the given imagery inputs
210
+ *
211
+ * @param {ImageryLayer[]} imageryLayersArray The imagery layers
212
+ * @param {PrimitiveRenderResources} primitiveRenderResources The primitive render resources
213
+ * @param {ImageryInput[]} imageryInputs The imagery inputs
214
+ * @param {number} numImageryTexCoordAttributes The number of texture coordinate
215
+ * attributes that have been created for the imagery (one for each distinct
216
+ * projection that was found in the imagery layers)
217
+ */
218
+ static _processImageryInputs(
219
+ imageryLayersArray,
220
+ renderResources,
221
+ imageryInputs,
222
+ numImageryTexCoordAttributes,
223
+ ) {
224
+ const shaderBuilder = renderResources.shaderBuilder;
225
+
226
+ const imageryFlags =
227
+ ImageryPipelineStage._computeImageryFlags(imageryLayersArray);
228
+ const numTextures = imageryInputs.length;
229
+
230
+ // Set the global defines indicating the presence and number of
231
+ // imagery textures.
232
+ shaderBuilder.addDefine(`HAS_IMAGERY`);
233
+ shaderBuilder.addDefine(`IMAGERY_TEXTURE_UNITS ${numTextures}`);
234
+
235
+ ImageryPipelineStage._addAttributes(
236
+ shaderBuilder,
237
+ numImageryTexCoordAttributes,
238
+ );
239
+
240
+ ImageryPipelineStage._defineUniforms(shaderBuilder, imageryFlags);
241
+ ImageryPipelineStage._buildSampleAndBlendFunction(
242
+ shaderBuilder,
243
+ imageryFlags,
244
+ );
245
+
246
+ ImageryPipelineStage._createMainImageryShader(
247
+ shaderBuilder,
248
+ imageryInputs,
249
+ imageryFlags,
250
+ );
251
+
252
+ const uniformMap = renderResources.uniformMap;
253
+ const uniforms = ImageryPipelineStage._createImageryUniforms(imageryInputs);
254
+ ImageryPipelineStage._setImageryUniforms(uniformMap, uniforms);
255
+ }
256
+
257
+ /**
258
+ * Add the attribute- and varying definitions for the imagery texture
259
+ * coordinates to the given shader.
260
+ *
261
+ * This includes the definition of the <code>initializeImageryAttributes</code>
262
+ * function that assigns the attribute values to varyings in the vertex shader.
263
+ *
264
+ * @param {ShaderBuilder} shaderBuilder The shader builder
265
+ * @param {number} numTexCoords The number of imagery texture coordinate sets
266
+ */
267
+ static _addAttributes(shaderBuilder, numTexCoords) {
268
+ for (let i = 0; i < numTexCoords; i++) {
269
+ shaderBuilder.addAttribute("vec2", `a_imagery_texCoord_${i}`);
270
+ shaderBuilder.addVarying("vec2", `v_imagery_texCoord_${i}`);
271
+ }
272
+
273
+ const functionId = "initializeImageryAttributes";
274
+ const signature = `void ${functionId}()`;
275
+ shaderBuilder.addFunction(functionId, signature, ShaderDestination.VERTEX);
276
+
277
+ for (let i = 0; i < numTexCoords; i++) {
278
+ shaderBuilder.addFunctionLines(functionId, [
279
+ `v_imagery_texCoord_${i} = a_imagery_texCoord_${i};`,
280
+ ]);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Returns the `ImageryFlags` that indicate the features that are
286
+ * required for the given imagery layers.
287
+ *
288
+ * The resulting flags will indicate whether any of the given
289
+ * imagery layer objects did *not* have the default value for
290
+ * the respective property, as defined by `ImageryLayer.DEFAULT_...`
291
+ *
292
+ * @param {ImageryLayer[]} imageryLayers The imagery layers
293
+ * @returns {ImageryFlags} The imagery flags
294
+ */
295
+ static _computeImageryFlags(imageryLayers) {
296
+ const imageryFlags = new ImageryFlags();
297
+ for (let i = 0; i < imageryLayers.length; i++) {
298
+ const imageryLayer = imageryLayers[i];
299
+
300
+ // These are short-circuiting to `number` instead
301
+ // of `boolean` here. With "ecmaVersion: 2021", we
302
+ // could use "||=" here. Otherwise, there is no
303
+ // nice shortcut for this.
304
+ imageryFlags.alpha |= imageryLayer.alpha !== 1.0;
305
+ imageryFlags.brightness |=
306
+ imageryLayer.brightness !== ImageryLayer.DEFAULT_BRIGHTNESS;
307
+ imageryFlags.contrast |=
308
+ imageryLayer.contrast !== ImageryLayer.DEFAULT_CONTRAST;
309
+ imageryFlags.hue |= imageryLayer.hue !== ImageryLayer.DEFAULT_HUE;
310
+ imageryFlags.saturation |=
311
+ imageryLayer.saturation !== ImageryLayer.DEFAULT_SATURATION;
312
+ imageryFlags.gamma |= imageryLayer.gamma !== ImageryLayer.DEFAULT_GAMMA;
313
+ const hasColorToAlpha =
314
+ defined(imageryLayer.colorToAlpha) &&
315
+ imageryLayer.colorToAlphaThreshold > 0.0;
316
+ imageryFlags.colorToAlpha |= hasColorToAlpha;
317
+ }
318
+ return imageryFlags;
319
+ }
320
+
321
+ /**
322
+ * Adds the uniforms that are required for the imagery to the shader.
323
+ *
324
+ * This will use the given shader builder to add the uniforms to the
325
+ * shader that are always required for the imagery (e.g. the
326
+ * `sampler2D u_imageryTextures[...]`).
327
+ *
328
+ * The array size of all arrays will be `IMAGERY_TEXTURE_UNITS`,
329
+ * so this has to be added as a `define` with a positive value.
330
+ *
331
+ * Depending on the given imagery flags being `true`, it will add
332
+ * the optional uniforms, like `u_imageryTextureAlpha`.
333
+ *
334
+ * The naming pattern will be `u_imageryTexture<name>`, except for
335
+ * `gamma`: To safe that one measly division, the thane will be
336
+ * `oneOverGamma` there.
337
+ *
338
+ * @param {ShaderBuilder} shaderBuilder The shader builder
339
+ * @param {ImageryFlags} imageryFlags The imagery flags
340
+ */
341
+ static _defineUniforms(shaderBuilder, imageryFlags) {
342
+ shaderBuilder.addUniform(
343
+ "sampler2D",
344
+ "u_imageryTextures[IMAGERY_TEXTURE_UNITS]",
345
+ ShaderDestination.FRAGMENT,
346
+ );
347
+ shaderBuilder.addUniform(
348
+ "vec4",
349
+ "u_imageryTextureCoordinateRectangle[IMAGERY_TEXTURE_UNITS]",
350
+ ShaderDestination.FRAGMENT,
351
+ );
352
+ shaderBuilder.addUniform(
353
+ "vec4",
354
+ "u_imageryTextureTranslationAndScale[IMAGERY_TEXTURE_UNITS]",
355
+ ShaderDestination.FRAGMENT,
356
+ );
357
+
358
+ if (imageryFlags.alpha) {
359
+ shaderBuilder.addUniform(
360
+ "float",
361
+ "u_imageryTextureAlpha[IMAGERY_TEXTURE_UNITS]",
362
+ ShaderDestination.FRAGMENT,
363
+ );
364
+ }
365
+ if (imageryFlags.brightness) {
366
+ shaderBuilder.addUniform(
367
+ "float",
368
+ "u_imageryTextureBrightness[IMAGERY_TEXTURE_UNITS]",
369
+ ShaderDestination.FRAGMENT,
370
+ );
371
+ }
372
+ if (imageryFlags.contrast) {
373
+ shaderBuilder.addUniform(
374
+ "float",
375
+ "u_imageryTextureContrast[IMAGERY_TEXTURE_UNITS]",
376
+ ShaderDestination.FRAGMENT,
377
+ );
378
+ }
379
+ if (imageryFlags.hue) {
380
+ shaderBuilder.addUniform(
381
+ "float",
382
+ "u_imageryTextureHue[IMAGERY_TEXTURE_UNITS]",
383
+ ShaderDestination.FRAGMENT,
384
+ );
385
+ }
386
+ if (imageryFlags.saturation) {
387
+ shaderBuilder.addUniform(
388
+ "float",
389
+ "u_imageryTextureSaturation[IMAGERY_TEXTURE_UNITS]",
390
+ ShaderDestination.FRAGMENT,
391
+ );
392
+ }
393
+ if (imageryFlags.gamma) {
394
+ shaderBuilder.addUniform(
395
+ "float",
396
+ "u_imageryTextureOneOverGamma[IMAGERY_TEXTURE_UNITS]",
397
+ ShaderDestination.FRAGMENT,
398
+ );
399
+ }
400
+ if (imageryFlags.colorToAlpha) {
401
+ shaderBuilder.addUniform(
402
+ "vec4",
403
+ "u_imageryTextureColorToAlpha[IMAGERY_TEXTURE_UNITS]",
404
+ ShaderDestination.FRAGMENT,
405
+ );
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Create the function signature for the `sampleAndBlend` function,
411
+ * based on the features that are required for the imagery.
412
+ *
413
+ * For details, see `buildSampleAndBlendFunction`
414
+ *
415
+ * @param {ImageryFlags} imageryFlags The imagery flags
416
+ * @returns {string} The string
417
+ */
418
+ static _createSampleAndBlendFunctionSignature(imageryFlags) {
419
+ const functionId = "sampleAndBlend";
420
+ const parameters = [];
421
+ parameters.push(`vec4 previousColor`);
422
+ parameters.push(`sampler2D textureToSample`);
423
+ parameters.push(`vec2 textureCoordinates`);
424
+ parameters.push(`vec4 textureCoordinateRectangle`);
425
+ parameters.push(`vec4 textureCoordinateTranslationAndScale`);
426
+ if (imageryFlags.alpha) {
427
+ parameters.push(`float textureAlpha`);
428
+ }
429
+ if (imageryFlags.brightness) {
430
+ parameters.push(`float textureBrightness`);
431
+ }
432
+ if (imageryFlags.contrast) {
433
+ parameters.push(`float textureContrast`);
434
+ }
435
+ if (imageryFlags.hue) {
436
+ parameters.push(`float textureHue`);
437
+ }
438
+ if (imageryFlags.saturation) {
439
+ parameters.push(`float textureSaturation`);
440
+ }
441
+ if (imageryFlags.gamma) {
442
+ parameters.push(`float textureOneOverGamma`);
443
+ }
444
+ if (imageryFlags.colorToAlpha) {
445
+ parameters.push(`vec4 colorToAlpha`);
446
+ }
447
+ const parametersString = parameters.join(", ");
448
+
449
+ const signature = `vec4 ${functionId}(${parametersString})`;
450
+ return signature;
451
+ }
452
+
453
+ /**
454
+ * Build the `sampleAndBlend` function that will be called for each imagery,
455
+ * to combine the previous pixel color with the respective imagery input.
456
+ *
457
+ * The function that is built here resembles the function that was originally defined at
458
+ * https://github.com/CesiumGS/cesium/blob/6cc004aaff586bb59f07f199216ae511014cf5a9/packages/engine/Source/Shaders/GlobeFS.glsl#L166
459
+ *
460
+ * However, for the function that is built here, the signature will
461
+ * reflect the actual features that are required for the imagery:
462
+ * For example, the `float textureAlpha` parameter will only be
463
+ * present when `imageryFlags.alpha` is `true`.
464
+ *
465
+ * @param {ShaderBuilder} shaderBuilder The shader builder
466
+ * @param {ImageryFlags} imageryFlags The imagery flags
467
+ */
468
+ static _buildSampleAndBlendFunction(shaderBuilder, imageryFlags) {
469
+ const functionId = "sampleAndBlend";
470
+ const signature =
471
+ ImageryPipelineStage._createSampleAndBlendFunctionSignature(imageryFlags);
472
+ shaderBuilder.addFunction(
473
+ functionId,
474
+ signature,
475
+ ShaderDestination.FRAGMENT,
476
+ );
477
+
478
+ shaderBuilder.addFunctionLines(functionId, [`float effectiveAlpha = 1.0;`]);
479
+ if (imageryFlags.alpha) {
480
+ shaderBuilder.addFunctionLines(functionId, [
481
+ `effectiveAlpha = textureAlpha;`,
482
+ ]);
483
+ }
484
+ shaderBuilder.addFunctionLines(functionId, [
485
+ // This is the part that is documented as "This crazy step stuff"
486
+ // in GlobeFS.glsl. Using the if-approach here...
487
+ `if (textureCoordinates.x < textureCoordinateRectangle.x) effectiveAlpha = 0.0;`,
488
+ `if (textureCoordinates.x > textureCoordinateRectangle.z) effectiveAlpha = 0.0;`,
489
+ `if (textureCoordinates.y < textureCoordinateRectangle.y) effectiveAlpha = 0.0;`,
490
+ `if (textureCoordinates.y > textureCoordinateRectangle.w) effectiveAlpha = 0.0;`,
491
+
492
+ `vec2 translation = textureCoordinateTranslationAndScale.xy;`,
493
+ `vec2 scale = textureCoordinateTranslationAndScale.zw;`,
494
+ `vec2 effectiveTextureCoordinates = textureCoordinates * scale + translation;`,
495
+ `vec4 value = texture(textureToSample, effectiveTextureCoordinates);`,
496
+ `value = czm_srgbToLinear(value);`,
497
+
498
+ `vec3 color = value.rgb;`,
499
+ `float alpha = value.a;`,
500
+ ]);
501
+
502
+ if (imageryFlags.colorToAlpha) {
503
+ shaderBuilder.addFunctionLines(functionId, [
504
+ `vec3 colorDiff = abs(color.rgb - colorToAlpha.rgb);`,
505
+ `colorDiff.r = czm_maximumComponent(colorDiff);`,
506
+ `alpha = czm_branchFreeTernary(colorDiff.r < colorToAlpha.a, 0.0, alpha);`,
507
+ ]);
508
+ }
509
+
510
+ if (imageryFlags.gamma) {
511
+ shaderBuilder.addFunctionLines(functionId, [
512
+ `color = pow(color, vec3(textureOneOverGamma));`,
513
+ ]);
514
+ } else {
515
+ shaderBuilder.addFunctionLines(functionId, [
516
+ `vec4 tempColor = czm_gammaCorrect(vec4(color, alpha));`,
517
+ `color = tempColor.rgb;`,
518
+ `alpha = tempColor.a;`,
519
+ ]);
520
+ }
521
+
522
+ if (imageryFlags.brightness) {
523
+ shaderBuilder.addFunctionLines(functionId, [
524
+ `color = mix(vec3(0.0), color, textureBrightness);`,
525
+ ]);
526
+ }
527
+
528
+ if (imageryFlags.contrast) {
529
+ shaderBuilder.addFunctionLines(functionId, [
530
+ `color = mix(vec3(0.5), color, textureContrast);`,
531
+ ]);
532
+ }
533
+ if (imageryFlags.hue) {
534
+ shaderBuilder.addFunctionLines(functionId, [
535
+ `color = czm_hue(color, textureHue);`,
536
+ ]);
537
+ }
538
+
539
+ if (imageryFlags.saturation) {
540
+ shaderBuilder.addFunctionLines(functionId, [
541
+ `color = czm_saturation(color, textureSaturation);`,
542
+ ]);
543
+ }
544
+
545
+ shaderBuilder.addFunctionLines(functionId, [
546
+ `float sourceAlpha = alpha * effectiveAlpha;`,
547
+ `float outAlpha = mix(previousColor.a, 1.0, sourceAlpha);`,
548
+ `outAlpha += sign(outAlpha) - 1.0;`,
549
+ `vec3 outColor = mix(previousColor.rgb * previousColor.a, color, sourceAlpha) / outAlpha;`,
550
+ // See comments in https://github.com/CesiumGS/cesium/blob/6cc004aaff586bb59f07f199216ae511014cf5a9/packages/engine/Source/Shaders/GlobeFS.glsl#L166
551
+ `vec4 result = vec4(outColor, max(outAlpha, 0.0));`,
552
+ ]);
553
+
554
+ // Debug mode: Draw boundaries of imagery in red
555
+ if (debugDrawImageryBoundaries) {
556
+ shaderBuilder.addFunctionLines(functionId, [
557
+ `if (abs(textureCoordinates.x - textureCoordinateRectangle.x) < (1.0/256.0) || `,
558
+ ` abs(textureCoordinates.x - textureCoordinateRectangle.z) < (1.0/256.0) || `,
559
+ ` abs(textureCoordinates.y - textureCoordinateRectangle.y) < (1.0/256.0) || `,
560
+ ` abs(textureCoordinates.y - textureCoordinateRectangle.w) < (1.0/256.0))`,
561
+ `{`,
562
+ ` result = vec4(1.0, 0.0, 0.0, effectiveAlpha);`,
563
+ `}`,
564
+ ]);
565
+ }
566
+
567
+ shaderBuilder.addFunctionLines(functionId, [`return result;`]);
568
+ }
569
+
570
+ /**
571
+ * Creates the arguments for a call to `sampleAndBlend` for the
572
+ * specified imagery.
573
+ *
574
+ * For details, see `buildSampleAndBlendFunction`
575
+ *
576
+ * @param {ImageryFlags} imageryFlags The imagery flags
577
+ * @param {number} imageryTexCoordAttributeSetIndex The index for the texture coordinate attribute
578
+ * @param {number} i The imagery index
579
+ * @returns {string} The string
580
+ */
581
+ static _createSampleAndBlendCallArguments(
582
+ imageryFlags,
583
+ imageryTexCoordAttributeSetIndex,
584
+ i,
585
+ ) {
586
+ const textureCoordinates = `v_imagery_texCoord_${imageryTexCoordAttributeSetIndex}`;
587
+
588
+ const args = [];
589
+ args.push(`blendedBaseColor`);
590
+ args.push(`u_imageryTextures[${i}]`);
591
+ args.push(`${textureCoordinates}`);
592
+ args.push(`u_imageryTextureCoordinateRectangle[${i}]`);
593
+ args.push(`u_imageryTextureTranslationAndScale[${i}]`);
594
+ if (imageryFlags.alpha) {
595
+ args.push(`u_imageryTextureAlpha[${i}]`);
596
+ }
597
+ if (imageryFlags.brightness) {
598
+ args.push(`u_imageryTextureBrightness[${i}]`);
599
+ }
600
+ if (imageryFlags.contrast) {
601
+ args.push(`u_imageryTextureContrast[${i}]`);
602
+ }
603
+ if (imageryFlags.hue) {
604
+ args.push(`u_imageryTextureHue[${i}]`);
605
+ }
606
+ if (imageryFlags.saturation) {
607
+ args.push(`u_imageryTextureSaturation[${i}]`);
608
+ }
609
+ if (imageryFlags.gamma) {
610
+ args.push(`u_imageryTextureOneOverGamma[${i}]`);
611
+ }
612
+ if (imageryFlags.colorToAlpha) {
613
+ args.push(`u_imageryTextureColorToAlpha[${i}]`);
614
+ }
615
+
616
+ const argsString = args.join(", ");
617
+ return argsString;
618
+ }
619
+
620
+ /**
621
+ * Creates the main part of the imagery shader.
622
+ *
623
+ * It adds the `blendBaseColorWithImagery` function, which is to be
624
+ * called in the `MaterialStageFS.glsl` when the `HAS_IMAGERY`
625
+ * flag was set in the shader.
626
+ *
627
+ * The `blendBaseColorWithImagery` function will go through all imagery
628
+ * layers in the input, and call the `sampleAndBlend` function, to
629
+ * incorporate the imagery input in the resulting pixel.
630
+ *
631
+ * @param {ShaderBuilder} shaderBuilder The shader builder
632
+ * @param {ImageryInput[]} imageryInputs The imagery inputs
633
+ * @param {ImageryFlags} imageryFlags The imagery flags
634
+ */
635
+ static _createMainImageryShader(shaderBuilder, imageryInputs, imageryFlags) {
636
+ const functionId = "blendBaseColorWithImagery";
637
+ shaderBuilder.addFunction(
638
+ functionId,
639
+ `vec4 blendBaseColorWithImagery(vec4 baseColorWithAlpha)`,
640
+ ShaderDestination.FRAGMENT,
641
+ );
642
+ shaderBuilder.addFunctionLines(functionId, [
643
+ `vec4 blendedBaseColor = baseColorWithAlpha;`,
644
+ ]);
645
+
646
+ // Roughly what was done in https://github.com/CesiumGS/cesium/blob/6cc004aaff586bb59f07f199216ae511014cf5a9/packages/engine/Source/Scene/GlobeSurfaceShaderSet.js#L394
647
+ for (let i = 0; i < imageryInputs.length; i++) {
648
+ const imageryInput = imageryInputs[i];
649
+ const imageryTexCoordAttributeSetIndex =
650
+ imageryInput.imageryTexCoordAttributeSetIndex;
651
+ const argsString =
652
+ ImageryPipelineStage._createSampleAndBlendCallArguments(
653
+ imageryFlags,
654
+ imageryTexCoordAttributeSetIndex,
655
+ i,
656
+ );
657
+ shaderBuilder.addFunctionLines(functionId, [
658
+ `blendedBaseColor = sampleAndBlend(${argsString});`,
659
+ ]);
660
+ }
661
+
662
+ shaderBuilder.addFunctionLines(functionId, [`return blendedBaseColor;`]);
663
+ }
664
+
665
+ /**
666
+ * Creates an object that contains the uniform values the given imagery inputs.
667
+ *
668
+ * The result will be a structure that contains the uniform values
669
+ * that match the definitions that have been created by `defineUniforms`.
670
+ * (It will include the ones that still have their default values
671
+ * and may not be needed eventually)
672
+ *
673
+ * @param {ImageryInput[]} imageryInputs The imagery inputs
674
+ * @returns {object} The uniforms
675
+ */
676
+ static _createImageryUniforms(imageryInputs) {
677
+ const length = imageryInputs.length;
678
+ const uniforms = {};
679
+ uniforms.imageryTextures = Array(length);
680
+ uniforms.imageryTextureCoordinateRectangle = Array(length);
681
+ uniforms.imageryTextureTranslationAndScale = Array(length);
682
+ uniforms.imageryTextureAlpha = Array(length);
683
+ uniforms.imageryTextureBrightness = Array(length);
684
+ uniforms.imageryTextureContrast = Array(length);
685
+ uniforms.imageryTextureHue = Array(length);
686
+ uniforms.imageryTextureSaturation = Array(length);
687
+ uniforms.imageryTextureOneOverGamma = Array(length);
688
+ uniforms.imageryTextureColorToAlpha = Array(length);
689
+
690
+ for (let i = 0; i < length; i++) {
691
+ const imageryInput = imageryInputs[i];
692
+
693
+ const imageryLayer = imageryInput.imageryLayer;
694
+ const texture = imageryInput.texture;
695
+ const textureCoordinateRectangle =
696
+ imageryInput.textureCoordinateRectangle;
697
+ const textureTranslationAndScale =
698
+ imageryInput.textureTranslationAndScale;
699
+
700
+ uniforms.imageryTextures[i] = texture;
701
+ uniforms.imageryTextureTranslationAndScale[i] =
702
+ textureTranslationAndScale;
703
+ uniforms.imageryTextureCoordinateRectangle[i] =
704
+ textureCoordinateRectangle;
705
+
706
+ uniforms.imageryTextureAlpha[i] = imageryLayer.alpha;
707
+ uniforms.imageryTextureBrightness[i] = imageryLayer.brightness;
708
+ uniforms.imageryTextureContrast[i] = imageryLayer.contrast;
709
+ uniforms.imageryTextureHue[i] = imageryLayer.hue;
710
+ uniforms.imageryTextureSaturation[i] = imageryLayer.saturation;
711
+ uniforms.imageryTextureOneOverGamma[i] = 1.0 / imageryLayer.gamma;
712
+
713
+ let colorToAlpha = uniforms.imageryTextureColorToAlpha[i];
714
+ if (!defined(colorToAlpha)) {
715
+ colorToAlpha = new Cartesian4();
716
+ uniforms.imageryTextureColorToAlpha[i] = colorToAlpha;
717
+ }
718
+ const hasColorToAlpha =
719
+ defined(imageryLayer.colorToAlpha) &&
720
+ imageryLayer.colorToAlphaThreshold > 0.0;
721
+ if (hasColorToAlpha) {
722
+ const color = imageryLayer.colorToAlpha;
723
+ colorToAlpha.x = color.red;
724
+ colorToAlpha.y = color.green;
725
+ colorToAlpha.z = color.blue;
726
+ colorToAlpha.w = imageryLayer.colorToAlphaThreshold;
727
+ } else {
728
+ colorToAlpha.w = -1.0;
729
+ }
730
+ }
731
+ return uniforms;
732
+ }
733
+
734
+ /**
735
+ * Fill the given uniform map with functions for all properties
736
+ * in the given uniforms object.
737
+ *
738
+ * The uniform names will be created as `u_<propertyName>`, and
739
+ * their value will just be a function that returns the respective
740
+ * property value.
741
+ *
742
+ * @param {UniformMap} uniformMap The uniform map
743
+ * @param {object} uniforms The uniforms
744
+ */
745
+ static _setImageryUniforms(uniformMap, uniforms) {
746
+ for (const key in uniforms) {
747
+ if (uniforms.hasOwnProperty(key)) {
748
+ const name = `u_${key}`;
749
+ uniformMap[name] = function () {
750
+ return uniforms[key];
751
+ };
752
+ }
753
+ }
754
+ }
755
+
756
+ /**
757
+ * Create the <code>ImageryInput</code> objects that have to be fed to the imagery
758
+ * pipeline stage for draping the given imagery layers over the primitive
759
+ * that is described by the given model primitive imagery.
760
+ *
761
+ * This will obtain the <code>ImageryCoverage</code> objects that are provided by
762
+ * the given model primitive imagery (and that describe the imagery tiles
763
+ * that are covered by the primitive), and create one <code>ImageryInput</code> for
764
+ * each of them.
765
+ *
766
+ * @param {ImageryLayerCollection} imageryLayers The imagery layers
767
+ * @param {ModelPrimitiveImagery} modelPrimitiveImagery The model primitive imagery
768
+ * @param {number[]} imageryTexCoordAttributeSetIndices The array that contains,
769
+ * for each imagery layer index, the set index of the texture coordinate
770
+ * attribute that should be used for this imagery. This is the value that
771
+ * will be used to access the texture coordinate attribute
772
+ * <code>a_imagery_texCoord_${imageryTexCoordAttributeSetIndex}</code>
773
+ * in the shader.
774
+ * @returns {ImageryInput[]} The imagery inputs
775
+ */
776
+ static _createImageryInputs(
777
+ imageryLayers,
778
+ modelPrimitiveImagery,
779
+ imageryTexCoordAttributeSetIndices,
780
+ ) {
781
+ //>>includeStart('debug', pragmas.debug);
782
+ Check.defined("imageryLayers", imageryLayers);
783
+ Check.defined("modelPrimitiveImagery", modelPrimitiveImagery);
784
+ Check.defined(
785
+ "imageryTexCoordAttributeSetIndices",
786
+ imageryTexCoordAttributeSetIndices,
787
+ );
788
+ //>>includeEnd('debug');
789
+
790
+ const imageryInputs = [];
791
+
792
+ for (let i = 0; i < imageryLayers.length; i++) {
793
+ const imageryLayer = imageryLayers.get(i);
794
+ const imageryTexCoordAttributeSetIndex =
795
+ imageryTexCoordAttributeSetIndices[i];
796
+ const mappedPositions =
797
+ modelPrimitiveImagery.mappedPositionsForImageryLayer(imageryLayer);
798
+ const cartographicBoundingRectangle =
799
+ mappedPositions.cartographicBoundingRectangle;
800
+ const coverages =
801
+ modelPrimitiveImagery.coveragesForImageryLayer(imageryLayer);
802
+
803
+ for (let j = 0; j < coverages.length; j++) {
804
+ const coverage = coverages[j];
805
+ const imageryInput = ImageryPipelineStage._createImageryInput(
806
+ imageryLayer,
807
+ coverage,
808
+ cartographicBoundingRectangle,
809
+ imageryTexCoordAttributeSetIndex,
810
+ );
811
+ if (defined(imageryInput)) {
812
+ imageryInputs.push(imageryInput);
813
+ }
814
+ }
815
+ }
816
+ return imageryInputs;
817
+ }
818
+
819
+ /**
820
+ * Create the `ImageryInput` that has to be passed to the imagery pipeline
821
+ * stage, for the given `ImageryCoverage`.
822
+ *
823
+ * The `ImageryCoverage` describes on imagery tile that is covered by the
824
+ * cartographic bounding rectangle of the primitive positions. This function
825
+ * obtains the actual `Imagery` object and its texture, computes the
826
+ * required texture coordinate and scale, and assembles this information
827
+ * into an `ImageryInput`.
828
+ *
829
+ * @param {ImageryLayer} imageryLayer The imagery layer
830
+ * @param {ImageryCoverage} coverage The imagery coverage
831
+ * @param {Rectangle} cartographicBoundingRectangle The bounding rectangle
832
+ * of the cartographic primitive positions
833
+ * @param {number} imageryTexCoordAttributeSetIndex The set index of the
834
+ * texture coordinate attribute that should be used for this imagery.
835
+ * This is the value that will be used to access the texture coordinate
836
+ * attribute <code>a_imagery_texCoord_${imageryTexCoordAttributeSetIndex}</code>
837
+ * in the shader.
838
+ * @returns {ImageryInput|undefined} The imagery input, or undefined if
839
+ * the imagery for the given coverage turned out to be in the
840
+ * <code>ImageryState.INVALID/FAILED<code> state, or did not have
841
+ * a valid texture.
842
+ * @private
843
+ */
844
+ static _createImageryInput(
845
+ imageryLayer,
846
+ coverage,
847
+ cartographicBoundingRectangle,
848
+ imageryTexCoordAttributeSetIndex,
849
+ ) {
850
+ //>>includeStart('debug', pragmas.debug);
851
+ Check.defined("imageryLayer", imageryLayer);
852
+ Check.defined("coverage", coverage);
853
+ Check.defined(
854
+ "cartographicBoundingRectangle",
855
+ cartographicBoundingRectangle,
856
+ );
857
+ Check.typeOf.number.greaterThanOrEquals(
858
+ "imageryTexCoordAttributeSetIndex",
859
+ imageryTexCoordAttributeSetIndex,
860
+ 0,
861
+ );
862
+ //>>includeEnd('debug');
863
+
864
+ // Bail out early if something went wrong in the imagery
865
+ // loading state machine
866
+ const imagery = coverage.imagery;
867
+ if (
868
+ imagery.state === ImageryState.FAILED ||
869
+ imagery.state === ImageryState.INVALID
870
+ ) {
871
+ return undefined;
872
+ }
873
+
874
+ // The texture coordinates are computed for the respective
875
+ // imagery, so pick either the textureWebMercator or the
876
+ // texture here
877
+ let texture = imagery.textureWebMercator;
878
+ if (!defined(texture)) {
879
+ texture = imagery.texture;
880
+ if (!defined(texture)) {
881
+ // Print an error message only when the imagery
882
+ // SHOULD actually have a texture
883
+ if (imagery.state === ImageryState.READY) {
884
+ console.log(
885
+ `Imagery at ${coverage.x}, ${coverage.y} (level ${coverage.level}) does not have any texture - state ${imagery.state}`,
886
+ );
887
+ }
888
+ return undefined;
889
+ }
890
+ }
891
+
892
+ const textureTranslationAndScale =
893
+ ImageryPipelineStage._computeTextureTranslationAndScale(
894
+ imageryLayer,
895
+ cartographicBoundingRectangle,
896
+ imagery.rectangle,
897
+ );
898
+
899
+ // Convert the texture coordinate rectangle into a Cartesian4
900
+ // for the consumption as a uniform in the shader
901
+ const textureCoordinateCartesianRectangle =
902
+ coverage.textureCoordinateRectangle;
903
+ const textureCoordinateRectangle = new Cartesian4(
904
+ textureCoordinateCartesianRectangle.minX,
905
+ textureCoordinateCartesianRectangle.minY,
906
+ textureCoordinateCartesianRectangle.maxX,
907
+ textureCoordinateCartesianRectangle.maxY,
908
+ );
909
+
910
+ const imageryInput = new ImageryInput(
911
+ imageryLayer,
912
+ texture,
913
+ textureTranslationAndScale,
914
+ textureCoordinateRectangle,
915
+ imageryTexCoordAttributeSetIndex,
916
+ );
917
+ return imageryInput;
918
+ }
919
+
920
+ /**
921
+ * Compute the translation and scale that has to be applied to
922
+ * the texture coordinates for mapping the given imagery to
923
+ * the geometry.
924
+ *
925
+ * The given rectangles will be converted into their "native" representation,
926
+ * using the tiling scheme of the given imagery layer, and passed
927
+ * to `_computeTextureTranslationAndScaleFromNative` (see that for details).
928
+ *
929
+ * @param {ImageryLayer} imageryLayer The imagery layer
930
+ * @param {Rectangle} nonNativeBoundingRectangle The bounding
931
+ * rectangle of the geometry
932
+ * @param {Rectangle} nonNativeImageryRectangle The bounding
933
+ * rectangle of the imagery
934
+ * @returns {Cartesian4} The translation and scale
935
+ * @private
936
+ */
937
+ static _computeTextureTranslationAndScale(
938
+ imageryLayer,
939
+ nonNativeBoundingRectangle,
940
+ nonNativeImageryRectangle,
941
+ ) {
942
+ //>>includeStart('debug', pragmas.debug);
943
+ Check.defined("imageryLayer", imageryLayer);
944
+ Check.defined("nonNativeBoundingRectangle", nonNativeBoundingRectangle);
945
+ Check.defined("nonNativeImageryRectangle", nonNativeImageryRectangle);
946
+ //>>includeEnd('debug');
947
+
948
+ const tilingScheme = imageryLayer.imageryProvider.tilingScheme;
949
+
950
+ const nativeBoundingRectangle = tilingScheme.rectangleToNativeRectangle(
951
+ nonNativeBoundingRectangle,
952
+ nativeBoundingRectangleScratch,
953
+ );
954
+ const nativeImageryRectangle = tilingScheme.rectangleToNativeRectangle(
955
+ nonNativeImageryRectangle,
956
+ nativeImageryRectangleScratch,
957
+ );
958
+
959
+ const translationAndScale =
960
+ ImageryPipelineStage._computeTextureTranslationAndScaleFromNative(
961
+ nativeBoundingRectangle,
962
+ nativeImageryRectangle,
963
+ );
964
+ return translationAndScale;
965
+ }
966
+
967
+ /**
968
+ * Compute the translation and scale that has to be applied to
969
+ * the texture coordinates for mapping the given imagery rectangle
970
+ * to the geometry rectangle.
971
+ *
972
+ * This will compute a Cartesian4 containing the
973
+ * (offsetX, offsetY, scaleX, scaleY) that have to be applied to
974
+ * the texture coordinates that that have been computed with
975
+ * `ModelImageryMapping.createTextureCoordinatesAttributeForMappedPositions`.
976
+ * In the shader, this offset and scale will map the given imagery rectangle
977
+ * to the geometry * rectangle.
978
+ *
979
+ * @param {Imagery} imagery The imagery
980
+ * @param {Rectangle} nonNativeBoundingRectangle The bounding
981
+ * rectangle of the geometry
982
+ * @param {Rectangle} nonNativeImageryRectangle The bounding
983
+ * rectangle of the imagery
984
+ * @returns {Cartesian4} The translation and scale
985
+ * @private
986
+ */
987
+ static _computeTextureTranslationAndScaleFromNative(
988
+ nativeBoundingRectangle,
989
+ nativeImageryRectangle,
990
+ ) {
991
+ //>>includeStart('debug', pragmas.debug);
992
+ Check.defined("nativeBoundingRectangle", nativeBoundingRectangle);
993
+ Check.defined("nativeImageryRectangle", nativeImageryRectangle);
994
+ //>>includeEnd('debug');
995
+
996
+ const invImageryWidth = 1.0 / nativeImageryRectangle.width;
997
+ const invImageryHeight = 1.0 / nativeImageryRectangle.height;
998
+ const deltaWest =
999
+ nativeBoundingRectangle.west - nativeImageryRectangle.west;
1000
+ const deltaSouth =
1001
+ nativeBoundingRectangle.south - nativeImageryRectangle.south;
1002
+ const offsetX = deltaWest * invImageryWidth;
1003
+ const offsetY = deltaSouth * invImageryHeight;
1004
+ const scaleX = nativeBoundingRectangle.width * invImageryWidth;
1005
+ const scaleY = nativeBoundingRectangle.height * invImageryHeight;
1006
+ return new Cartesian4(offsetX, offsetY, scaleX, scaleY);
1007
+ }
1008
+
1009
+ /**
1010
+ * Computes the index mapping from the given source to the given target.
1011
+ *
1012
+ * The result will be an array that has the same length as the source,
1013
+ * and contains the indices that the source elements have in the
1014
+ * target array.
1015
+ *
1016
+ * @param {object[]} source The source array
1017
+ * @param {object[]} target The target array
1018
+ * @returns {number[]} The result
1019
+ */
1020
+ static _computeIndexMapping(source, target) {
1021
+ //>>includeStart('debug', pragmas.debug);
1022
+ Check.defined("source", source);
1023
+ Check.defined("target", target);
1024
+ //>>includeEnd('debug');
1025
+
1026
+ const result = [];
1027
+ const length = source.length;
1028
+ for (let i = 0; i < length; i++) {
1029
+ const element = source[i];
1030
+ const index = target.indexOf(element);
1031
+ result.push(index);
1032
+ }
1033
+ return result;
1034
+ }
1035
+ }
1036
+
1037
+ // Static properties (direct initialization disallowed by eslint)
1038
+ //ImageryPipelineStage.name = "ImageryPipelineStage"; // Helps with debugging, if you know where to look
1039
+
1040
+ export default ImageryPipelineStage;