@tangram.gl/tangram-renderer 0.0.0-stage → 0.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 (321) hide show
  1. package/README.md +59 -2
  2. package/dist/core.js +36923 -0
  3. package/dist/core.js.map +6 -0
  4. package/dist/index.js +32 -0
  5. package/dist/loaders-gl-worker.js +82 -0
  6. package/dist/loaders-gl-worker.js.map +6 -0
  7. package/dist/map-logic.js +2 -0
  8. package/dist/map-logic.js.map +6 -0
  9. package/dist/projected-basemaps-worker.js +3 -0
  10. package/dist/projected-basemaps-worker.js.map +6 -0
  11. package/dist/shader-modules.js +141 -0
  12. package/dist/shader-modules.js.map +6 -0
  13. package/dist/style-schema.js +126 -0
  14. package/dist/style-schema.js.map +6 -0
  15. package/dist/tangram-style.schema.json +882 -0
  16. package/dist/tangram.debug.mjs +37139 -0
  17. package/dist/tangram.debug.mjs.map +6 -0
  18. package/dist/tangram.min.mjs +4950 -0
  19. package/dist/types/builders/earcut.d.ts +15 -0
  20. package/dist/types/core.d.ts +29 -0
  21. package/dist/types/gl/constants.d.ts +2 -0
  22. package/dist/types/gl/globe_mesh.d.ts +48 -0
  23. package/dist/types/gl/glsl.d.ts +17 -0
  24. package/dist/types/gl/vertex-constants.d.ts +4 -0
  25. package/dist/types/gl/vertex-types.d.ts +34 -0
  26. package/dist/types/gl/vertex_data.d.ts +32 -0
  27. package/dist/types/gl/vertex_elements.d.ts +8 -0
  28. package/dist/types/gl/vertex_layout.d.ts +32 -0
  29. package/dist/types/glsl.d.ts +14 -0
  30. package/dist/types/gpu/luma_device_renderer.d.ts +66 -0
  31. package/dist/types/gpu/resource_lifecycle.d.ts +4 -0
  32. package/dist/types/gpu/tangram_gpu_backend.d.ts +151 -0
  33. package/dist/types/index.d.ts +155 -0
  34. package/dist/types/lights/light-definitions.d.ts +121 -0
  35. package/dist/types/map-logic/draped-terrain-bounds.d.ts +41 -0
  36. package/dist/types/map-logic/globe-terrain-preparation.d.ts +85 -0
  37. package/dist/types/map-logic/globe-terrain.d.ts +54 -0
  38. package/dist/types/map-logic/index.d.ts +33 -0
  39. package/dist/types/map-logic/label-identity.d.ts +22 -0
  40. package/dist/types/map-logic/label-placement.d.ts +32 -0
  41. package/dist/types/map-logic/screen-bounds.d.ts +15 -0
  42. package/dist/types/map-logic/terrain-data.d.ts +55 -0
  43. package/dist/types/map-logic/terrain-error.d.ts +51 -0
  44. package/dist/types/map-logic/terrain-ground-topology.d.ts +26 -0
  45. package/dist/types/map-logic/terrain-height-field.d.ts +59 -0
  46. package/dist/types/map-logic/terrain-navigation.d.ts +27 -0
  47. package/dist/types/map-logic/terrain-neighborhood.d.ts +5 -0
  48. package/dist/types/map-logic/terrain-query.d.ts +33 -0
  49. package/dist/types/map-logic/terrain-resource-budget.d.ts +51 -0
  50. package/dist/types/map-logic/terrain-transition-topology.d.ts +23 -0
  51. package/dist/types/map-logic/tile-build-queue.d.ts +51 -0
  52. package/dist/types/map-logic/tile-cache-policy.d.ts +46 -0
  53. package/dist/types/map-logic/tile-residency.d.ts +17 -0
  54. package/dist/types/map-logic/triangle-query-index.d.ts +87 -0
  55. package/dist/types/procedures/js-yaml.d.ts +15 -0
  56. package/dist/types/procedures/mesh-projector.d.ts +107 -0
  57. package/dist/types/procedures/mvt-dependencies.d.ts +20 -0
  58. package/dist/types/procedures/projected-coordinate-transform.d.ts +27 -0
  59. package/dist/types/procedures/projection-batch-executor.d.ts +53 -0
  60. package/dist/types/procedures/scene-yaml-legacy.d.ts +6 -0
  61. package/dist/types/procedures/terrain-draping.d.ts +165 -0
  62. package/dist/types/procedures/terrain-globe.d.ts +6 -0
  63. package/dist/types/procedures/terrain-styled-topology.d.ts +34 -0
  64. package/dist/types/procedures/web-mercator-legacy.d.ts +4 -0
  65. package/dist/types/procedures/web-mercator-math.d.ts +4 -0
  66. package/dist/types/scene/globals.d.ts +11 -0
  67. package/dist/types/scene/ground_footprint.d.ts +16 -0
  68. package/dist/types/scene/host_frame.d.ts +54 -0
  69. package/dist/types/scene/projection_math.d.ts +52 -0
  70. package/dist/types/scene/scene-resource-types.d.ts +177 -0
  71. package/dist/types/scene/scene_bundle.d.ts +69 -0
  72. package/dist/types/scene/visibility_adapter.d.ts +106 -0
  73. package/dist/types/selection/draped-terrain-surface.d.ts +78 -0
  74. package/dist/types/selection/terrain_surface.d.ts +149 -0
  75. package/dist/types/shader-modules/globe-horizon.d.ts +15 -0
  76. package/dist/types/shader-modules/height-decode.d.ts +13 -0
  77. package/dist/types/shader-modules/hillshade.d.ts +13 -0
  78. package/dist/types/shader-modules/index.d.ts +6 -0
  79. package/dist/types/shader-modules/planar.d.ts +12 -0
  80. package/dist/types/shader-modules/sphere-map.d.ts +13 -0
  81. package/dist/types/shader-modules/triplanar.d.ts +15 -0
  82. package/dist/types/sources/decoded_tile_store.d.ts +95 -0
  83. package/dist/types/sources/tile_source_metadata.d.ts +32 -0
  84. package/dist/types/styles/text/fontfaceobserver.d.ts +10 -0
  85. package/dist/types/tile/tile_id.d.ts +37 -0
  86. package/dist/types/tile/tile_traversal_adapter.d.ts +63 -0
  87. package/dist/types/types.d.ts +293 -0
  88. package/dist/types/utils/geo.d.ts +57 -0
  89. package/dist/types/utils/hash.d.ts +1 -0
  90. package/dist/types/utils/log.d.ts +27 -0
  91. package/dist/types/utils/props.d.ts +6 -0
  92. package/dist/types/utils/subscribe.d.ts +10 -0
  93. package/dist/types/utils/thread.d.ts +6 -0
  94. package/dist/types/utils/urls.d.ts +26 -0
  95. package/dist/types/utils/utils.d.ts +26 -0
  96. package/dist/types/utils/version.d.ts +2 -0
  97. package/dist/types/utils/worker-types.d.ts +68 -0
  98. package/dist/types/utils/worker_broker.d.ts +3 -0
  99. package/package.json +103 -3
  100. package/src/builders/common.ts +41 -0
  101. package/src/builders/earcut.d.ts +15 -0
  102. package/src/builders/geometry-types.ts +60 -0
  103. package/src/builders/points.ts +115 -0
  104. package/src/builders/polygons.ts +225 -0
  105. package/src/builders/polylines.ts +682 -0
  106. package/src/builders/wireframe.ts +31 -0
  107. package/src/core.d.ts +29 -0
  108. package/src/core.ts +33 -0
  109. package/src/experimental/loaders-gl-worker.ts +19 -0
  110. package/src/experimental/projected-basemaps-worker.ts +13 -0
  111. package/src/experimental/projected-mesh-cache.ts +137 -0
  112. package/src/experimental/projected-mesh-domain.ts +110 -0
  113. package/src/experimental/projected-mesh-refinement.ts +193 -0
  114. package/src/experimental/projected-mesh.ts +399 -0
  115. package/src/experimental/projected-normal.ts +26 -0
  116. package/src/experimental/projected-symbol-mesh.ts +74 -0
  117. package/src/gl/constants.ts +18 -0
  118. package/src/gl/context.ts +103 -0
  119. package/src/gl/extensions.ts +30 -0
  120. package/src/gl/globe_mesh.ts +285 -0
  121. package/src/gl/glsl.ts +257 -0
  122. package/src/gl/mesh-types.ts +56 -0
  123. package/src/gl/render_state.ts +111 -0
  124. package/src/gl/shader_program.ts +1310 -0
  125. package/src/gl/texture.ts +814 -0
  126. package/src/gl/uniform_buffer.ts +340 -0
  127. package/src/gl/vao.ts +154 -0
  128. package/src/gl/vbo_mesh.ts +349 -0
  129. package/src/gl/vertex-constants.ts +19 -0
  130. package/src/gl/vertex-types.ts +35 -0
  131. package/src/gl/vertex_data.ts +126 -0
  132. package/src/gl/vertex_elements.ts +54 -0
  133. package/src/gl/vertex_layout.ts +241 -0
  134. package/src/glsl.d.ts +14 -0
  135. package/src/gpu/luma_device_renderer.ts +579 -0
  136. package/src/gpu/pixel_readback.ts +51 -0
  137. package/src/gpu/resource_lifecycle.ts +32 -0
  138. package/src/gpu/tangram_gpu_backend.ts +195 -0
  139. package/src/index.d.ts +155 -0
  140. package/src/index.ts +105 -0
  141. package/src/labels/collision-types.ts +94 -0
  142. package/src/labels/collision.ts +273 -0
  143. package/src/labels/collision_grid.ts +61 -0
  144. package/src/labels/intersect.ts +30 -0
  145. package/src/labels/label-types.ts +91 -0
  146. package/src/labels/label.ts +207 -0
  147. package/src/labels/label_line.ts +801 -0
  148. package/src/labels/label_point.ts +131 -0
  149. package/src/labels/main-pass-types.ts +73 -0
  150. package/src/labels/main_pass.ts +221 -0
  151. package/src/labels/point_anchor.ts +100 -0
  152. package/src/labels/point_placement.ts +150 -0
  153. package/src/labels/projected-pass.ts +259 -0
  154. package/src/labels/repeat_group.ts +72 -0
  155. package/src/lights/ambient_light.glsl +18 -0
  156. package/src/lights/directional_light.glsl +39 -0
  157. package/src/lights/geographic-lights.ts +21 -0
  158. package/src/lights/light-definitions.ts +285 -0
  159. package/src/lights/light.ts +590 -0
  160. package/src/lights/lighting-uniforms.ts +80 -0
  161. package/src/lights/lighting-wgsl.ts +116 -0
  162. package/src/lights/material.glsl +237 -0
  163. package/src/lights/material.ts +145 -0
  164. package/src/lights/native-falloff.ts +37 -0
  165. package/src/lights/point_light.glsl +96 -0
  166. package/src/lights/spot_light.glsl +111 -0
  167. package/src/map-logic/draped-terrain-bounds.ts +71 -0
  168. package/src/map-logic/globe-terrain-preparation.ts +217 -0
  169. package/src/map-logic/globe-terrain.ts +296 -0
  170. package/src/map-logic/index.ts +37 -0
  171. package/src/map-logic/label-identity.ts +36 -0
  172. package/src/map-logic/label-placement.ts +117 -0
  173. package/src/map-logic/screen-bounds.ts +34 -0
  174. package/src/map-logic/terrain-data.ts +107 -0
  175. package/src/map-logic/terrain-error.ts +134 -0
  176. package/src/map-logic/terrain-ground-topology.ts +164 -0
  177. package/src/map-logic/terrain-height-field.ts +254 -0
  178. package/src/map-logic/terrain-navigation.ts +50 -0
  179. package/src/map-logic/terrain-neighborhood.ts +43 -0
  180. package/src/map-logic/terrain-query.ts +51 -0
  181. package/src/map-logic/terrain-resource-budget.ts +124 -0
  182. package/src/map-logic/terrain-transition-topology.ts +164 -0
  183. package/src/map-logic/tile-build-queue.ts +110 -0
  184. package/src/map-logic/tile-cache-policy.ts +89 -0
  185. package/src/map-logic/tile-residency.ts +50 -0
  186. package/src/map-logic/triangle-query-index.ts +265 -0
  187. package/src/procedures/js-yaml.d.ts +15 -0
  188. package/src/procedures/mesh-projector.ts +191 -0
  189. package/src/procedures/mlt-loaders.ts +55 -0
  190. package/src/procedures/mvt-dependencies.d.ts +20 -0
  191. package/src/procedures/mvt-legacy.ts +150 -0
  192. package/src/procedures/mvt-loaders.ts +53 -0
  193. package/src/procedures/mvt-parser.ts +48 -0
  194. package/src/procedures/mvt-properties.ts +36 -0
  195. package/src/procedures/mvt-tile-provider.ts +72 -0
  196. package/src/procedures/pmtiles-loader.ts +75 -0
  197. package/src/procedures/projected-coordinate-transform.ts +79 -0
  198. package/src/procedures/projection-batch-executor.ts +140 -0
  199. package/src/procedures/scene-yaml-legacy.ts +16 -0
  200. package/src/procedures/scene-yaml-loaders.ts +10 -0
  201. package/src/procedures/terrain-draping.ts +713 -0
  202. package/src/procedures/terrain-globe.ts +32 -0
  203. package/src/procedures/terrain-styled-topology.ts +184 -0
  204. package/src/procedures/web-mercator-legacy.ts +24 -0
  205. package/src/procedures/web-mercator-math.ts +50 -0
  206. package/src/scene/camera.ts +298 -0
  207. package/src/scene/camera_base.ts +116 -0
  208. package/src/scene/classic_scene.ts +20 -0
  209. package/src/scene/external_camera.ts +101 -0
  210. package/src/scene/globals.ts +120 -0
  211. package/src/scene/ground_footprint.ts +139 -0
  212. package/src/scene/host_frame.ts +326 -0
  213. package/src/scene/projected_tile_lod.ts +125 -0
  214. package/src/scene/projection_math.ts +142 -0
  215. package/src/scene/projection_shaders.ts +183 -0
  216. package/src/scene/renderer.ts +516 -0
  217. package/src/scene/scene-resource-types.ts +174 -0
  218. package/src/scene/scene.ts +2378 -0
  219. package/src/scene/scene_bundle.ts +304 -0
  220. package/src/scene/scene_debug.ts +129 -0
  221. package/src/scene/scene_loader.ts +402 -0
  222. package/src/scene/scene_worker.ts +528 -0
  223. package/src/scene/view.ts +683 -0
  224. package/src/scene/visibility_adapter.ts +319 -0
  225. package/src/scene/worker_url.ts +15 -0
  226. package/src/selection/draped-terrain-surface.ts +377 -0
  227. package/src/selection/selection-types.ts +79 -0
  228. package/src/selection/selection.ts +542 -0
  229. package/src/selection/selection_fragment.glsl +18 -0
  230. package/src/selection/selection_globals.glsl +9 -0
  231. package/src/selection/selection_pixels.ts +59 -0
  232. package/src/selection/selection_vertex.glsl +15 -0
  233. package/src/selection/terrain_surface.ts +298 -0
  234. package/src/shader-modules/globe-horizon.ts +52 -0
  235. package/src/shader-modules/height-decode.ts +41 -0
  236. package/src/shader-modules/hillshade.ts +70 -0
  237. package/src/shader-modules/index.ts +10 -0
  238. package/src/shader-modules/planar.ts +24 -0
  239. package/src/shader-modules/sphere-map.ts +38 -0
  240. package/src/shader-modules/triplanar.ts +45 -0
  241. package/src/sources/aligned_tile_source.ts +122 -0
  242. package/src/sources/data_source.ts +655 -0
  243. package/src/sources/decoded_tile_store.ts +245 -0
  244. package/src/sources/geojson.ts +300 -0
  245. package/src/sources/mvt.ts +247 -0
  246. package/src/sources/raster.ts +341 -0
  247. package/src/sources/shared_tile_source_adapter.ts +117 -0
  248. package/src/sources/sources.ts +10 -0
  249. package/src/sources/tile_source_adapter.ts +84 -0
  250. package/src/sources/tile_source_metadata.ts +65 -0
  251. package/src/sources/tile_source_state.ts +112 -0
  252. package/src/sources/topojson.ts +90 -0
  253. package/src/styles/feature_annotations.ts +16 -0
  254. package/src/styles/filter.ts +190 -0
  255. package/src/styles/geometry-style-types.ts +209 -0
  256. package/src/styles/globe_visibility_wgsl.ts +22 -0
  257. package/src/styles/layer-types.ts +45 -0
  258. package/src/styles/layer.ts +586 -0
  259. package/src/styles/lines/dasharray.ts +49 -0
  260. package/src/styles/lines/lines.ts +707 -0
  261. package/src/styles/lines/lines_wgsl.ts +224 -0
  262. package/src/styles/points/point-runtime-types.ts +125 -0
  263. package/src/styles/points/points.ts +1079 -0
  264. package/src/styles/points/points_fragment.glsl +126 -0
  265. package/src/styles/points/points_vertex.glsl +230 -0
  266. package/src/styles/points/points_wgsl.ts +189 -0
  267. package/src/styles/polygons/polygons.ts +268 -0
  268. package/src/styles/polygons/polygons_fragment.glsl +173 -0
  269. package/src/styles/polygons/polygons_vertex.glsl +213 -0
  270. package/src/styles/polygons/polygons_wgsl.ts +127 -0
  271. package/src/styles/property-types.ts +125 -0
  272. package/src/styles/raster/raster.ts +33 -0
  273. package/src/styles/raster/raster_globals.glsl +46 -0
  274. package/src/styles/style-mixing-types.ts +101 -0
  275. package/src/styles/style-runtime-types.ts +220 -0
  276. package/src/styles/style-schema.ts +133 -0
  277. package/src/styles/style.ts +1033 -0
  278. package/src/styles/style_globals.glsl +69 -0
  279. package/src/styles/style_manager.ts +464 -0
  280. package/src/styles/style_parser.ts +620 -0
  281. package/src/styles/text/font_manager.ts +180 -0
  282. package/src/styles/text/fontfaceobserver.d.ts +10 -0
  283. package/src/styles/text/text-types.ts +220 -0
  284. package/src/styles/text/text.ts +378 -0
  285. package/src/styles/text/text_canvas.ts +773 -0
  286. package/src/styles/text/text_labels.ts +436 -0
  287. package/src/styles/text/text_segments.ts +142 -0
  288. package/src/styles/text/text_settings.ts +228 -0
  289. package/src/styles/text/text_wgsl.ts +159 -0
  290. package/src/styles/text/text_wrap.ts +194 -0
  291. package/src/tile/globe_tile_preload.ts +40 -0
  292. package/src/tile/tangram_tileset_2d.ts +108 -0
  293. package/src/tile/tile-build-types.ts +155 -0
  294. package/src/tile/tile.ts +807 -0
  295. package/src/tile/tile_id.ts +172 -0
  296. package/src/tile/tile_manager.ts +889 -0
  297. package/src/tile/tile_pyramid.ts +119 -0
  298. package/src/tile/tile_resource_cache.ts +91 -0
  299. package/src/tile/tile_traversal_adapter.ts +132 -0
  300. package/src/types.ts +332 -0
  301. package/src/utils/debounce.ts +17 -0
  302. package/src/utils/debug_settings.ts +43 -0
  303. package/src/utils/errors.ts +12 -0
  304. package/src/utils/functions.ts +95 -0
  305. package/src/utils/geo.ts +364 -0
  306. package/src/utils/hash.ts +14 -0
  307. package/src/utils/log.ts +108 -0
  308. package/src/utils/media_capture.ts +193 -0
  309. package/src/utils/merge.ts +44 -0
  310. package/src/utils/obb.ts +134 -0
  311. package/src/utils/props.ts +43 -0
  312. package/src/utils/slice.ts +14 -0
  313. package/src/utils/subscribe.ts +58 -0
  314. package/src/utils/task.ts +154 -0
  315. package/src/utils/thread.ts +26 -0
  316. package/src/utils/urls.ts +223 -0
  317. package/src/utils/utils.ts +310 -0
  318. package/src/utils/vector.ts +197 -0
  319. package/src/utils/version.ts +8 -0
  320. package/src/utils/worker-types.ts +79 -0
  321. package/src/utils/worker_broker.ts +523 -0
@@ -0,0 +1,1033 @@
1
+ // Tangram
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) 2013-2016 Brett Camper and Mapzen
4
+ // Copyright (c) 2026 vis.gl contributors
5
+
6
+ // Rendering styles
7
+
8
+
9
+ import StyleParser from './style_parser';
10
+ import type {StyleRuntime, StyleMethods, StyleInitOptions, StyleGLOptions, BuildFeature, BuildContext, BuildDraw, BuildGeometry, BuildFeatureStyle, BuildTileData, BuildMesh, LayerDebugTree, CountedLayerTree, RasterTextureInfo, RasterTextureTile} from './style-runtime-types';
11
+ import type {WorkerBuildTile, TileStylePayload, TileMeshVariant} from '../tile/tile-build-types';
12
+ import type {GeometryMeshVariant} from './geometry-style-types';
13
+ import type {PropertyCache, PropertyContext} from './property-types';
14
+ import type VertexLayout from '../gl/vertex_layout';
15
+ import VertexData from '../gl/vertex_data';
16
+ import type {VertexAttribute} from '../gl/vertex-types';
17
+ import type UniformBuffer from '../gl/uniform_buffer';
18
+ import type {MeshElementData, MeshRenderOptions} from '../gl/mesh-types';
19
+ import {markFeatureRendered} from './feature_annotations';
20
+ import FeatureSelection from '../selection/selection';
21
+ import gl from '../gl/constants'; // import GL constants since workers can't access GL context
22
+ import ShaderProgram from '../gl/shader_program';
23
+ import VBOMesh from '../gl/vbo_mesh';
24
+ import Texture from '../gl/texture';
25
+ import Material from '../lights/material';
26
+ import {PORTABLE_MATERIAL_UNIFORMS, getPortableMaterialUniforms} from '../lights/lighting-uniforms';
27
+ import Light from '../lights/light';
28
+ import {RasterTileSource} from '../sources/raster';
29
+ import log from '../utils/log';
30
+ import mergeObjects from '../utils/merge';
31
+ import Thread from '../utils/thread';
32
+ import WorkerBroker from '../utils/worker_broker';
33
+ import makeWireframeForTriangleElementData from '../builders/wireframe';
34
+ import {drapeTerrainMesh, getTerrainVertexLayout, extractDrapedTerrainQueryMesh} from '../procedures/terrain-draping';
35
+ import debugSettings from '../utils/debug_settings';
36
+ import Geo from '../utils/geo';
37
+ import {normalizeProjectedBasemapOptions, projectTileMesh, validateProjectedLights} from '../procedures/mesh-projector';
38
+
39
+ import selection_fragment_source from '../selection/selection_fragment.glsl';
40
+ import rasters_source from './raster/raster_globals.glsl';
41
+ import {heightDecode} from '../shader-modules/height-decode';
42
+
43
+ // Base class
44
+
45
+ export var Style: StyleMethods = {
46
+ init (this: StyleRuntime, { generation, styles, sources = {}, introspection, shader_language = 'glsl', config, projection_target, terrain, terrainRibbonZoom }: StyleInitOptions = {}) {
47
+ this.setGeneration(generation);
48
+ this.styles = styles; // styles for scene
49
+ this.sources = sources; // data sources for scene
50
+ this.shader_language = shader_language; // keeps worker-built vertex layouts aligned with the renderer
51
+ this.projection_target = projection_target;
52
+ this.terrain = terrain;
53
+ this.terrainRibbonZoom = terrainRibbonZoom;
54
+ if (terrain && (config?.scene?.cpu_projection || this.shaders?.blocks?.position || this.shaders?.blocks?.width)) throw new Error('Terrain draping does not support CPU-projected scenes or procedural position/width blocks');
55
+ this.cpu_projection = config?.scene?.cpu_projection === undefined ? undefined :
56
+ normalizeProjectedBasemapOptions(config.scene.cpu_projection);
57
+ this.screen_space_labels = Boolean(this.cpu_projection) || config?.scene?._host_screen_space_labels === true;
58
+ if (this.cpu_projection) {
59
+ validateProjectedLights(config?.lights);
60
+ if (this.lighting && !(['polygons', 'raster'].includes(this.baseStyle()) &&
61
+ ['vertex', 'fragment'].includes(String(this.lighting)))) throw new Error('CPU projection lighting requires polygon/raster vertex or fragment mode');
62
+ const specularChannel = this.material?.specular;
63
+ const specular = specularChannel !== null && typeof specularChannel === 'object' && 'amount' in specularChannel ? specularChannel.amount ?? specularChannel : specularChannel;
64
+ const zeroSpecular = specular === undefined || specular === 0 ||
65
+ (Array.isArray(specular) && specular.slice(0, 3).every(value => value === 0));
66
+ if (this.material?.normal || !zeroSpecular) {
67
+ throw new Error('CPU projection supports diffuse materials without normal maps or specular terms');
68
+ }
69
+ }
70
+ this.defines = (Object.prototype.hasOwnProperty.call(this, 'defines') && this.defines) || {}; // #defines to be injected into the shaders
71
+ this.defines.TANGRAM_TERRAIN_DRAPED = Boolean(terrain);
72
+ this.shaders = (Object.prototype.hasOwnProperty.call(this, 'shaders') && this.shaders) || {}; // shader customization (uniforms, defines, blocks, etc.)
73
+ this.introspection = introspection || false;
74
+ this.selection = this.selection || this.introspection || false; // flag indicating if this style supports feature selection
75
+ this.compile_setup = false; // one-time setup steps for program compilation
76
+ this.program = null; // GL program reference (for main render pass)
77
+ this.selection_program = null; // GL program reference for feature selection render pass
78
+ this.feature_style = {}; // style for feature currently being parsed, shared to lessen GC/memory thrash
79
+ this.vertex_template = []; // shared single-vertex template, filled out by each style
80
+ this.tile_data = {};
81
+ this.stencil_proxy_tiles = true; // applied to proxy tiles w/non-opaque blend mode to avoid compounding alpha
82
+
83
+ this.variants = {}; // mesh variants by variant key
84
+ this.vertex_layouts = {}; // vertex layouts by variant key
85
+
86
+ // Default world coords to wrap every 100,000 meters, can turn off by setting this to 'false'
87
+ this.defines.TANGRAM_WORLD_POSITION_WRAP = 100000;
88
+
89
+ // Blending
90
+ // `opaque` styles are drawn first/under other styles, without alpha blending
91
+ this.blend = this.blend || 'opaque'; // default to opaque
92
+ if (this.blend !== 'opaque') {
93
+ // non-opaque styles can customize their blend order, which will determine which features
94
+ // are drawn first/under each other
95
+ if (this.blend_order == null) {
96
+ this.blend_order = this.default_blend_orders[this.blend];
97
+ }
98
+ }
99
+ else {
100
+ // `opaque` ignores blend order, always render first/under
101
+ this.blend_order = this.default_blend_orders[this.blend];
102
+ }
103
+
104
+ this.defines[`TANGRAM_BLEND_${this.blend.toUpperCase()}`] = true;
105
+
106
+ this.removeShaderBlock('setup'); // clear before material injection
107
+
108
+ // If the style defines its own material, replace the inherited material instance
109
+ if (!(this.material instanceof Material)) {
110
+ if (!Material.isValid(this.material)) {
111
+ this.material = StyleParser.defaults.material;
112
+ }
113
+ this.material = new Material(this.material);
114
+ }
115
+ this.material.inject(this);
116
+
117
+ // Set lighting mode: fragment, vertex, or none (specified as 'false')
118
+ Light.setMode(this.cpu_projection ? this.lighting ?? false : this.lighting, this);
119
+
120
+ // Setup raster samplers if needed
121
+ this.setupRasters();
122
+
123
+ // Setup shader definitions for custom attributes
124
+ this.setupCustomAttributes();
125
+
126
+ this.initialized = true;
127
+ },
128
+
129
+ destroy (this: StyleRuntime) {
130
+ if (Object.prototype.hasOwnProperty.call(this, 'portable_material_buffer')) {
131
+ this.portable_material_buffer?.destroy();
132
+ }
133
+ this.portable_material_buffer = null;
134
+ if (Object.prototype.hasOwnProperty.call(this, 'program') && this.program) {
135
+ this.program.destroy();
136
+ this.program = null;
137
+ }
138
+
139
+ if (Object.prototype.hasOwnProperty.call(this, 'selection_program') && this.selection_program) {
140
+ this.selection_program.destroy();
141
+ this.selection_program = null;
142
+ }
143
+
144
+ WorkerBroker.removeTarget(this.main_thread_target);
145
+ this.gl = null;
146
+ this.resource_context = null;
147
+ this.uniform_blocks = null;
148
+ this.uniform_block_factory = null;
149
+ this.initialized = false;
150
+ },
151
+
152
+ reset () {
153
+ },
154
+
155
+ baseStyle (this: StyleRuntime): string {
156
+ return this.base || this.name;
157
+ },
158
+
159
+ setGeneration (this: StyleRuntime, generation: number | undefined) {
160
+ // Scene generation id this style was created for
161
+ this.generation = generation;
162
+
163
+ // Provide a hook for this object to be called from worker threads
164
+ this.main_thread_target = ['Style', this.name, this.generation].join('_');
165
+ if (Thread.is_main) {
166
+ WorkerBroker.addTarget(this.main_thread_target, this);
167
+ }
168
+ },
169
+
170
+ /*** Style parsing and geometry construction ***/
171
+
172
+ // Returns an object to hold feature data (for a tile or other object)
173
+ startData (this: StyleRuntime, tile: Pick<WorkerBuildTile, 'id'>) {
174
+ this.tile_data[tile.id] = this.tile_data[tile.id] || {
175
+ meshes: {},
176
+ uniforms: {},
177
+ textures: []
178
+ };
179
+ },
180
+
181
+ // Finalizes an object holding feature data (for a tile or other object)
182
+ async endData (this: StyleRuntime, tile: import('./style-runtime-types').StyleFinalizeTile) {
183
+ var tile_data = this.tile_data[tile.id];
184
+ this.tile_data[tile.id] = null;
185
+
186
+ if (tile_data && Object.keys(tile_data.meshes).length > 0) {
187
+ const transferredMeshes: Record<string, BuildMesh<Uint8Array>> = {};
188
+ for (let variant in tile_data.meshes) {
189
+ const buildingMesh = tile_data.meshes[variant];
190
+ const vertexData = buildingMesh.vertex_data;
191
+ if (vertexData instanceof Uint8Array) throw new Error('Style mesh has already been finalized');
192
+
193
+ // Remove empty mesh variants
194
+ if (vertexData.vertex_count === 0) {
195
+ delete tile_data.meshes[variant];
196
+ continue;
197
+ }
198
+
199
+ // Only keep final byte buffer
200
+ vertexData.end();
201
+ let mesh: BuildMesh<Uint8Array> = Object.assign(buildingMesh, {vertex_elements: vertexData.element_buffer, vertex_data: vertexData.vertex_buffer});
202
+ transferredMeshes[variant] = mesh;
203
+ if ((this.terrain || this.cpu_projection) && (!tile.min || !tile.coords)) throw new Error('Projected tile has no address');
204
+ if (this.terrain) {
205
+ const originalLayout = this.vertexLayoutForMeshVariant(mesh.variant);
206
+ if (!originalLayout) throw new Error('Style has no terrain vertex layout');
207
+ const draped = drapeTerrainMesh({vertices: mesh.vertex_data, indices: mesh.vertex_elements || false,
208
+ layout: originalLayout, origin: [tile.min!.x, tile.min!.y], tileZoom: tile.coords!.z, styleZoom: tile.style_z, ribbonZoom: this.terrainRibbonZoom,
209
+ annotations: ['points', 'text'].includes(this.baseStyle()),
210
+ styled: ['polygons', 'lines'].includes(this.baseStyle()),
211
+ ground: this.baseStyle() === 'raster' && this.terrain.querySurfaceStyles.includes(this.name), terrain: this.terrain});
212
+ mesh.vertex_data = draped.vertices; mesh.vertex_elements = draped.indices;
213
+ if (mesh.labels) {
214
+ const stride = getTerrainVertexLayout(originalLayout).stride;
215
+ for (const label of Object.values(mesh.labels)) label.ranges = label.ranges.map(([offset, count]) => [offset / originalLayout.stride * stride, count]);
216
+ }
217
+ mesh.variant = {...mesh.variant, terrain: true, terrainStatistics: draped.statistics, terrainTransitionDuration: this.terrain.transitionDuration,
218
+ terrainGeneration: this.terrain.transitionGeneration, terrainGroundClip: draped.groundClip};
219
+ if (!draped.indices.length) {delete tile_data.meshes[variant]; delete transferredMeshes[variant]; continue;}
220
+ if (this.terrain.querySurfaceStyles.includes(this.name)) {
221
+ if (this.baseStyle() !== 'raster') throw new Error('Terrain query surfaces require a ground raster style');
222
+ mesh.terrain_surface = extractDrapedTerrainQueryMesh(draped.vertices, draped.indices, getTerrainVertexLayout(originalLayout), [tile.min!.x, tile.min!.y], tile.coords!.z, this.terrain.maxQueryMeshBytes, draped.groundPositions);
223
+ }
224
+ }
225
+ if (this.cpu_projection) {
226
+ if (!['polygons', 'raster', 'lines', 'points', 'text'].includes(this.baseStyle())) throw new Error('CPU projection requires ground meshes or screen-facing annotations');
227
+ const layout = this.vertexLayoutForMeshVariant(mesh.variant);
228
+ if (!layout) throw new Error('Style has no projected vertex layout');
229
+ const geometry = this.baseStyle();
230
+ if (geometry !== 'polygons' && geometry !== 'raster' && geometry !== 'lines' && geometry !== 'points' && geometry !== 'text') throw new Error('Unsupported projected style geometry');
231
+ const projection = this.cpu_projection;
232
+ const projected = await projectTileMesh({vertices: mesh.vertex_data, indices: mesh.vertex_elements || false,
233
+ layout, tile: {...tile, min: tile.min!, coords: tile.coords!}, geometry, projection,
234
+ projectionCacheKey: this.projection_target,
235
+ projectPositions: this.projection_target ? coordinates => WorkerBroker.postMessage(
236
+ `${this.projection_target}.projectPositions`, WorkerBroker.withTransferables(coordinates, projection.type)) : undefined});
237
+ mesh.vertex_data = projected.vertices;
238
+ mesh.vertex_elements = projected.indices;
239
+ if (projected.indices.length === 0) {delete tile_data.meshes[variant]; delete transferredMeshes[variant];}
240
+ }
241
+ }
242
+
243
+ // Fully clipped meshes need neither GPU buffers nor raster texture acquisition.
244
+ if ((this.cpu_projection || this.terrain) && Object.keys(tile_data.meshes).length === 0) return null;
245
+
246
+ // Load raster tiles passed from data source
247
+ // Blocks mesh completion to avoid flickering
248
+ return this.buildRasterTextures(tile, Object.assign(tile_data, {meshes: Object.assign(tile_data.meshes, transferredMeshes)}));
249
+ }
250
+ else {
251
+ return null; // don't send tile data back if doesn't have geometry
252
+ }
253
+ },
254
+
255
+ // Has mesh data for a given tile?
256
+ hasDataForTile (this: StyleRuntime, tile: Pick<WorkerBuildTile, 'id'>) {
257
+ return this.tile_data[tile.id] != null;
258
+ },
259
+
260
+ getTileMesh (this: StyleRuntime, tile: Pick<WorkerBuildTile, 'id'>, variant: GeometryMeshVariant & TileMeshVariant) {
261
+ const data = this.tile_data[tile.id];
262
+ if (!data) throw new Error('Style tile geometry has not started');
263
+ const meshes = data.meshes;
264
+ if (meshes[variant.key] == null) {
265
+ const vertex_layout = this.vertexLayoutForMeshVariant(variant);
266
+ if (!vertex_layout) throw new Error('Style has no vertex layout');
267
+ meshes[variant.key] = {
268
+ variant,
269
+ vertex_data: vertex_layout.createVertexData()
270
+ };
271
+ }
272
+ return meshes[variant.key];
273
+ },
274
+
275
+ vertexLayoutForMeshVariant (_variant: TileMeshVariant | undefined): VertexLayout | undefined {
276
+ // styles must implement
277
+ return undefined;
278
+ },
279
+
280
+ meshVariantTypeForDraw (_draw: BuildDraw): (GeometryMeshVariant & TileMeshVariant) | undefined {
281
+ // styles must implement
282
+ return undefined;
283
+ },
284
+
285
+ addFeature (this: StyleRuntime, feature: BuildFeature, draw: BuildDraw, context: BuildContext) {
286
+ let tile = context.tile;
287
+ if (tile.generation !== this.generation) {
288
+ return;
289
+ }
290
+
291
+ if (!this.tile_data[tile.id]) {
292
+ this.startData(tile);
293
+ }
294
+
295
+ let style = this.parseFeature(feature, draw, context);
296
+ if (!style) {
297
+ return; // skip feature
298
+ }
299
+
300
+ if (Number(this.buildGeometry(feature.geometry, style, context)) > 0 && this.generation !== undefined) {
301
+ markFeatureRendered(feature, this.generation); // bookkeeping must not mutate shared decoded content
302
+ }
303
+ },
304
+
305
+ buildGeometry (this: StyleRuntime, geometry: BuildGeometry, style: BuildFeatureStyle, context: BuildContext) {
306
+ let geom_count: number | undefined;
307
+ if (geometry.type === 'Polygon') {
308
+ geom_count = this.buildPolygons([geometry.coordinates], style, context);
309
+ }
310
+ else if (geometry.type === 'MultiPolygon') {
311
+ geom_count = this.buildPolygons(geometry.coordinates, style, context);
312
+ }
313
+ else if (geometry.type === 'LineString') {
314
+ geom_count = this.buildLines([geometry.coordinates], style, context);
315
+ }
316
+ else if (geometry.type === 'MultiLineString') {
317
+ geom_count = this.buildLines(geometry.coordinates, style, context);
318
+ }
319
+ else if (geometry.type === 'Point') {
320
+ geom_count = this.buildPoints([geometry.coordinates], style, context);
321
+ }
322
+ else if (geometry.type === 'MultiPoint') {
323
+ geom_count = this.buildPoints(geometry.coordinates, style, context);
324
+ }
325
+
326
+ // Optionally collect per-layer stats
327
+ if (geom_count !== undefined && geom_count > 0 && debugSettings.layer_stats) {
328
+ let tile = context.tile;
329
+ if (!tile.debug) throw new Error('Style debug tile has no counters');
330
+ tile.debug.layers = tile.debug.layers || { list: {}, tree: {} };
331
+ let list = tile.debug.layers.list;
332
+ let tree = tile.debug.layers.tree;
333
+ let ftree: CountedLayerTree = {}; // tree of layers for this feature
334
+ context.layers?.forEach(layer => {
335
+ addLayerDebugEntry(list, layer, 1, geom_count, {[this.name]: geom_count}, {[this.baseStyle()]: geom_count});
336
+
337
+ let node = tree;
338
+ let fnode = ftree;
339
+ let levels = layer.split(':');
340
+ for (let i=0; i < levels.length; i++) {
341
+ let level = levels[i];
342
+ node[level] = node[level] || { features: 0, geoms: 0, styles: {}, base: {} };
343
+
344
+ if (fnode[level] == null) { // only count each layer level once per feature
345
+ fnode[level] = {};
346
+ addLayerDebugEntry(node, level, 1, geom_count, {[this.name]: geom_count}, {[this.baseStyle()]: geom_count});
347
+ }
348
+
349
+ if (i < levels.length - 1) {
350
+ node[level].layers = node[level].layers || {};
351
+ }
352
+ node = node[level].layers ?? {};
353
+ fnode = fnode[level];
354
+ }
355
+ });
356
+ }
357
+
358
+ return geom_count;
359
+ },
360
+
361
+ parseFeature (this: StyleRuntime, feature: BuildFeature, draw: BuildDraw, context: BuildContext): BuildFeatureStyle | undefined {
362
+ try {
363
+ var style: BuildFeatureStyle | undefined = this.feature_style;
364
+
365
+ // Calculate order
366
+ style.order = this.parseOrder(draw.order, context);
367
+ if (style.order == null && this.blend !== 'overlay') {
368
+ let msg = `Layer '${draw.layers?.join(', ')}', draw group '${draw.group}': `;
369
+ msg += '\'order\' parameter is required unless blend mode is \'overlay\'';
370
+ if (draw.order != null) {
371
+ msg += '; \'order\' was set to a dynamic value (e.g. string tied to feature property, ';
372
+ msg += 'or JS function), but evaluated to null for one or more features';
373
+ }
374
+ log({ level: 'warn', once: true }, msg);
375
+ return;
376
+ }
377
+
378
+ // Subclass implementation
379
+ style = this._parseFeature(feature, draw, context);
380
+ if (!style) {
381
+ return; // skip feature
382
+ }
383
+
384
+ // Custom attributes
385
+ if (this.shaders.attributes) {
386
+ style.attributes = style.attributes || {};
387
+ for (const aname in this.shaders.attributes) {
388
+ const attribute = draw.attributes?.[aname];
389
+ if (attribute !== undefined && !isPropertyCache(attribute)) throw new Error('Custom attribute is not preprocessed');
390
+ style.attributes[aname] = StyleParser.evalCachedProperty(attribute, context);
391
+ // set attribute value to zero for null/undefined/non-numeric values
392
+ if (typeof style.attributes[aname] !== 'number') {
393
+ style.attributes[aname] = 0;
394
+ }
395
+ }
396
+ }
397
+
398
+ // Feature selection (only if feature is marked as interactive, and style supports it)
399
+ if (this.selection) {
400
+ style.interactive = StyleParser.evalProperty(draw.interactive, context);
401
+ }
402
+ else {
403
+ style.interactive = false;
404
+ }
405
+
406
+ if (style.interactive === true) {
407
+ const tile = context.tile;
408
+ if (tile.generation == null || !tile.key || !tile.coords || tile.style_z === undefined || !tile.source) throw new Error('Interactive style tile is incomplete');
409
+ if (typeof context.source !== 'string' || (context.layer !== undefined && typeof context.layer !== 'string')) throw new Error('Interactive style context has invalid source metadata');
410
+ style.selection_color = FeatureSelection.makeColor(feature, {...tile, key: tile.key, coords: tile.coords, style_z: tile.style_z, source: tile.source, generation: tile.generation}, {...context, layers: context.layers ?? [], source: context.source, layer: context.layer});
411
+ }
412
+ else {
413
+ style.selection_color = FeatureSelection.defaultColor;
414
+ }
415
+
416
+ return style;
417
+ }
418
+ catch (error) {
419
+ log('error', 'Style.parseFeature: style parsing error', feature, style, error instanceof Error ? error.stack : undefined);
420
+ }
421
+ },
422
+
423
+ _parseFeature (this: StyleRuntime, _feature: BuildFeature, _draw: BuildDraw, _context: BuildContext): BuildFeatureStyle | undefined {
424
+ return this.feature_style;
425
+ },
426
+
427
+ preprocess (this: StyleRuntime, draw: BuildDraw) {
428
+ // Preprocess first time
429
+ if (!draw.preprocessed) {
430
+ // Apply draw defaults
431
+ if (this.draw) {
432
+ // Merge each property separately to avoid modifying `draw` instance identity
433
+ for (let param in this.draw) {
434
+ let val = this.draw[param];
435
+ if (typeof val === 'object' && !Array.isArray(val)) { // nested param (e.g. `outline`)
436
+ Reflect.set(draw, param, mergeObjects({}, val, Object(Reflect.get(draw, param))));
437
+ }
438
+ else if (Reflect.get(draw, param) == null) { // simple param (single scalar value or array)
439
+ Reflect.set(draw, param, val);
440
+ }
441
+ }
442
+ }
443
+
444
+ if (!this.selection) {
445
+ draw.interactive = false; // always disable feature selection for when style doesn't support it
446
+ }
447
+ else if (this.introspection) {
448
+ draw.interactive = true; // always enable feature selection for introspection
449
+ }
450
+
451
+ const preprocessed = this._preprocess(draw); // optional subclass implementation
452
+ if (!preprocessed) {
453
+ return;
454
+ }
455
+ draw = preprocessed;
456
+
457
+ // Custom attributes
458
+ if (this.shaders.attributes) {
459
+ draw.attributes = draw.attributes || {};
460
+ for (const aname in this.shaders.attributes) {
461
+ draw.attributes[aname] = StyleParser.createPropertyCache(
462
+ draw.attributes[aname] != null ? draw.attributes[aname] : 0);
463
+ }
464
+ }
465
+
466
+ draw.preprocessed = true;
467
+ }
468
+ return draw;
469
+ },
470
+
471
+ // optionally implemented by subclass
472
+ _preprocess (draw: BuildDraw): BuildDraw | undefined {
473
+ return draw;
474
+ },
475
+
476
+ // Parse an order value
477
+ parseOrder (order: unknown, context: PropertyContext): unknown {
478
+ // Calculate order if it was not cached
479
+ if (typeof order !== 'number') {
480
+ return StyleParser.calculateOrder(order, context);
481
+ }
482
+ return order;
483
+ },
484
+
485
+ // Expand final precision for half-layers (for outlines)
486
+ scaleOrder (order: number) {
487
+ return order * 2;
488
+ },
489
+
490
+ // Parse a color of choose a default if acceptable, return undefined if color missing
491
+ parseColor(this: StyleRuntime, color: PropertyCache | null | undefined, context: PropertyContext) {
492
+ // Need either a color, or a shader block for 'color' or 'filter'
493
+ if (color) {
494
+ return StyleParser.evalCachedColorProperty(color, context);
495
+ }
496
+ else if (this.shaders.blocks?.color || this.shaders.blocks?.filter) {
497
+ return StyleParser.defaults.color;
498
+ }
499
+ },
500
+
501
+ // Build functions are no-ops until overriden
502
+ buildPolygons (_polygons: Extract<BuildGeometry, {type: 'MultiPolygon'}>['coordinates'], _style: BuildFeatureStyle, _context: BuildContext) { return 0; },
503
+ buildLines (_lines: number[][][], _style: BuildFeatureStyle, _context: BuildContext) { return 0; },
504
+ buildPoints (_points: number[][], _style: BuildFeatureStyle, _context: BuildContext) { return 0; },
505
+
506
+
507
+ /*** GL state and rendering ***/
508
+
509
+ setGL (this: StyleRuntime, gl: WebGLRenderingContext | null, uniform_blocks: Record<string, UniformBuffer> = {}, options: StyleGLOptions = {}) {
510
+ this.gl = gl;
511
+ this.resource_context = options.resourceContext || gl;
512
+ this.uniform_blocks = uniform_blocks;
513
+ this.shader_language = options.shaderLanguage || 'glsl';
514
+ this.shader_factory = options.shaderFactory;
515
+ this.shader_program_validator = options.shaderProgramValidator;
516
+ this.device_shader_compilation = options.deviceShaderCompilation === true;
517
+ this.uniform_block_factory = options.uniformBlockFactory;
518
+ this.mesh_buffer_factory = options.meshBufferFactory;
519
+ this.texture_factory = options.textureFactory;
520
+ this.defer_uniform_blocks = options.deferUniformBlocks === true;
521
+ this.defer_texture_bindings = options.deferTextureBindings === true;
522
+ this.defer_uniform_updates = options.deferUniformUpdates === true;
523
+ this.max_texture_size = options.maxTextureSize || Texture.getMaxTextureSize(this.gl);
524
+ if (Object.prototype.hasOwnProperty.call(this, 'portable_material_buffer')) {
525
+ this.portable_material_buffer?.destroy();
526
+ }
527
+ this.portable_material_buffer = null;
528
+ this.portable_lighting_mode = undefined;
529
+ this.portable_light_count = options.portableLightCount;
530
+ if (options.portableLighting && ['polygons', 'lines', 'raster'].includes(this.baseStyle())) {
531
+ this.portable_lighting_mode = this.defines.TANGRAM_LIGHTING_VERTEX ? 'vertex' :
532
+ this.defines.TANGRAM_LIGHTING_FRAGMENT ? 'fragment' : false;
533
+ if (this.portable_lighting_mode) {
534
+ if (!(this.material instanceof Material)) throw new Error('Style material has not been initialized');
535
+ if (!this.uniform_block_factory) throw new Error('Style has no uniform block factory');
536
+ getPortableMaterialUniforms(this.material);
537
+ this.portable_material_buffer = this.uniform_block_factory({
538
+ name: 'TangramMaterial', binding: 7, uniforms: PORTABLE_MATERIAL_UNIFORMS
539
+ });
540
+ this.uniform_blocks = {...this.uniform_blocks, TangramMaterial: this.portable_material_buffer};
541
+ }
542
+ }
543
+ },
544
+
545
+ makeMesh (this: StyleRuntime, vertex_data: Uint8Array, vertex_elements: MeshElementData, options: Partial<TileStylePayload> = {}) {
546
+ options = { ...options, bufferFactory: this.mesh_buffer_factory };
547
+ if (['polygons', 'lines', 'raster'].includes(this.baseStyle()) &&
548
+ Number.isInteger(options.tileZoom)) {
549
+ options.globeRefinement = {tileZoom: Number(options.tileZoom), tileScale: Geo.tile_scale};
550
+ if (this.shaders?.blocks?.position) {
551
+ options.globeRefinementError = `Globe projection does not support style '${this.name}' with a position shader block`;
552
+ }
553
+ }
554
+ // Custom layout hooks may intentionally ignore absent variant metadata.
555
+ let vertex_layout = this.vertexLayoutForMeshVariant(options.variant);
556
+ if (!vertex_layout) throw new Error('Style has no mesh vertex layout');
557
+ if (options.variant?.terrain) {
558
+ vertex_layout = getTerrainVertexLayout(vertex_layout);
559
+ // The worker already refines terrain topology; do not rebuild a second main-thread globe mesh.
560
+ options.globeRefinement = undefined;
561
+ }
562
+
563
+ if (debugSettings.wireframe) {
564
+ // In wireframe debug mode, transform mesh into lines
565
+ vertex_elements = makeWireframeForTriangleElementData(vertex_elements || new Uint16Array());
566
+ return new VBOMesh(this.gl, vertex_data, vertex_elements, vertex_layout,
567
+ { ...options, globeRefinement: undefined, draw_mode: 0x0001 });
568
+ }
569
+
570
+ return new VBOMesh(this.gl, vertex_data, vertex_elements, vertex_layout, options);
571
+ },
572
+
573
+ render (mesh: VBOMesh, options: MeshRenderOptions) {
574
+ return mesh.render(options);
575
+ },
576
+
577
+ // Get a specific program, compiling if necessary
578
+ getProgram (this: StyleRuntime, key: 'program' | 'selection_program' = 'program') {
579
+ this.compileSetup();
580
+
581
+ const program = this[key];
582
+ if (!program || program.error) {
583
+ return;
584
+ }
585
+
586
+ if (!program.compiled) {
587
+ log('debug', `Compiling style '${this.name}', program key '${key}'`);
588
+ try {
589
+ program.compile();
590
+ }
591
+ catch (e) {
592
+ log('error', `Style: error compiling program for style '${this.name}' (program key '${key}')`,
593
+ this, e instanceof Error ? e.stack : undefined, Reflect.get(Object(e), 'type'), Reflect.get(Object(e), 'shader_errors'));
594
+ throw e; // re-throw so users can be notified via event subscriptions
595
+ }
596
+ }
597
+ return program;
598
+ },
599
+
600
+ // One-time setup for compiling style's programs
601
+ compileSetup (this: StyleRuntime) {
602
+ if (this.compile_setup) {
603
+ return;
604
+ }
605
+
606
+ if (!this.gl && !this.shader_factory) {
607
+ throw(new Error(`style.compile(): skipping for ${this.name} because no rendering backend is configured`));
608
+ }
609
+
610
+ // Build defines & for selection (need to create a new object since the first is stored as a reference by the program)
611
+ var defines = this.buildDefineList();
612
+ var selection_defines: Record<string, unknown> | undefined;
613
+ if (this.selection) {
614
+ selection_defines = Object.assign({}, defines);
615
+ selection_defines.TANGRAM_FEATURE_SELECTION = true;
616
+ }
617
+
618
+ // Shader blocks
619
+ const blocks = this.shaders.blocks ?? {};
620
+ const block_scopes = this.shaders.block_scopes ?? {};
621
+
622
+ // Uniforms
623
+ var uniforms = Object.assign({}, this.shaders && this.shaders.uniforms);
624
+ for (let u in uniforms) { // validate uniforms
625
+ if (uniforms[u] == null) {
626
+ log({ level: 'warn', once: true }, `Style '${this.name}' has invalid uniform '${u}': uniform values must be non-null`);
627
+ }
628
+ }
629
+
630
+ // Accept a single extension, or an array of extensions
631
+ var extensions = (this.shaders && this.shaders.extensions);
632
+ if (typeof extensions === 'string') {
633
+ extensions = [extensions];
634
+ }
635
+
636
+ let vertex_shader_src = this.vertex_shader_src;
637
+ let fragment_shader_src = this.fragment_shader_src;
638
+ if (this.shader_language === 'wgsl') {
639
+ if (typeof this.getWGSLShaderSource !== 'function') {
640
+ throw new Error(`style '${this.name}' does not have a portable WGSL shader`);
641
+ }
642
+ vertex_shader_src = fragment_shader_src = this.getWGSLShaderSource();
643
+ }
644
+
645
+ // Create shaders
646
+ this.program = new ShaderProgram(
647
+ this.gl,
648
+ vertex_shader_src,
649
+ fragment_shader_src,
650
+ {
651
+ name: this.name,
652
+ defines,
653
+ uniforms,
654
+ uniform_blocks: this.uniform_blocks ?? undefined,
655
+ shaderLanguage: this.shader_language,
656
+ deviceShaderCompilation: this.device_shader_compilation,
657
+ shaderProgramValidator: this.shader_program_validator,
658
+ deferUniformBlocks: this.defer_uniform_blocks,
659
+ deferTextureBindings: this.defer_texture_bindings,
660
+ deferUniformUpdates: this.defer_uniform_updates,
661
+ shaderFactory: this.shader_factory,
662
+ blocks,
663
+ block_scopes,
664
+ extensions
665
+ }
666
+ );
667
+
668
+ if (this.selection) {
669
+ const selection_source = this.shader_language === 'wgsl' ? this.getWGSLShaderSource?.(true) : null;
670
+ this.selection_program = new ShaderProgram(
671
+ this.gl,
672
+ selection_source ?? this.vertex_shader_src,
673
+ selection_source ?? selection_fragment_source,
674
+ {
675
+ name: (this.name + ' (selection)'),
676
+ defines: selection_defines,
677
+ uniforms,
678
+ uniform_blocks: this.uniform_blocks ?? undefined,
679
+ shaderLanguage: this.shader_language,
680
+ deviceShaderCompilation: this.device_shader_compilation,
681
+ shaderProgramValidator: this.shader_program_validator,
682
+ deferUniformBlocks: this.defer_uniform_blocks,
683
+ deferTextureBindings: this.defer_texture_bindings,
684
+ deferUniformUpdates: this.defer_uniform_updates,
685
+ shaderFactory: this.shader_factory,
686
+ blocks,
687
+ block_scopes,
688
+ extensions
689
+ }
690
+ );
691
+ }
692
+ else {
693
+ this.selection_program = null;
694
+ }
695
+
696
+ this.compile_setup = true;
697
+ },
698
+
699
+ // Add a shader block
700
+ addShaderBlock (this: StyleRuntime, key: string, block: string, scope: string | null = null) {
701
+ this.shaders.blocks = this.shaders.blocks || {};
702
+ this.shaders.blocks[key] = this.shaders.blocks[key] || [];
703
+ this.shaders.blocks[key].push(block);
704
+
705
+ this.shaders.block_scopes = this.shaders.block_scopes || {};
706
+ this.shaders.block_scopes[key] = this.shaders.block_scopes[key] || [];
707
+ this.shaders.block_scopes[key].push(scope);
708
+ },
709
+
710
+ // Remove all shader blocks for key
711
+ removeShaderBlock (this: StyleRuntime, key: string) {
712
+ if (this.shaders.blocks) {
713
+ this.shaders.blocks[key] = null;
714
+ }
715
+
716
+ if (this.shaders.block_scopes) {
717
+ this.shaders.block_scopes[key] = null;
718
+ }
719
+ },
720
+
721
+ replaceShaderBlock (this: StyleRuntime, key: string, block: string, scope: string | null = null) {
722
+ this.removeShaderBlock(key);
723
+ this.addShaderBlock(key, block, scope);
724
+ },
725
+
726
+ /** TODO: could probably combine and generalize this with similar method in ShaderProgram
727
+ * (list of define objects that inherit from each other)
728
+ */
729
+ buildDefineList (this: StyleRuntime) {
730
+ // Add any custom defines to built-in style defines
731
+ var defines: Record<string, unknown> = {}; // create a new object to avoid mutating a prototype value that may be shared with other styles
732
+ if (this.defines != null) {
733
+ for (var d in this.defines) {
734
+ defines[d] = this.defines[d];
735
+ }
736
+ }
737
+ if (this.shaders != null && this.shaders.defines != null) {
738
+ for (d in this.shaders.defines) {
739
+ defines[d] = this.shaders.defines[d];
740
+ }
741
+ }
742
+ return defines;
743
+
744
+ },
745
+
746
+ // Determines if 'raster' parameter is set to a valid value
747
+ hasRasters (this: StyleRuntime) {
748
+ return (['color', 'normal', 'custom'].indexOf(String(this.raster)) > -1);
749
+ },
750
+
751
+ // Setup raster access in shaders
752
+ setupRasters (this: StyleRuntime) {
753
+ if (!this.hasRasters()) {
754
+ return;
755
+ }
756
+
757
+ // Enable raster textures and configure how first raster is applied
758
+ if (this.raster === 'color') {
759
+ this.defines.TANGRAM_RASTER_TEXTURE_COLOR = true;
760
+ }
761
+ else if (this.raster === 'normal') {
762
+ this.defines.TANGRAM_RASTER_TEXTURE_NORMAL = true;
763
+ }
764
+ // else custom raster (samplers will be made available but not automatically applied)
765
+
766
+ // A given style may be built with multiple data sources, each of which may attach
767
+ // a variable number of raster sources (0 to N, where N is the max number of raster sources
768
+ // defined for the scene). This means we don't know *which* or *how many* rasters will be
769
+ // bound now, at initial compile-time; we only know this at geometry build-time. To ensure
770
+ // that we can bind as many raster sources as needed, we declare our uniform arrays to hold
771
+ // the maximum number of possible sources. At render time, only the necessary number of rasters
772
+ // are bound (the remaining slots aren't intended to be accessed).
773
+ let num_raster_sources =
774
+ Object.keys(this.sources)
775
+ .filter(s => this.sources[s] instanceof RasterTileSource)
776
+ .length;
777
+
778
+ this.defines.TANGRAM_NUM_RASTER_SOURCES = `${num_raster_sources}`; // force to string to avoid auto-float conversion
779
+ if (num_raster_sources > 0) {
780
+ // Track how many raster sources have alpha masking (used for handling transparency outside raster image)
781
+ const num_masked_rasters = Object.keys(this.sources)
782
+ .filter(s => 'mask_alpha' in this.sources[s] && this.sources[s].mask_alpha)
783
+ .length;
784
+ this.defines.TANGRAM_HAS_MASKED_RASTERS = (num_masked_rasters > 0);
785
+ this.defines.TANGRAM_ALL_MASKED_RASTERS = (num_masked_rasters === num_raster_sources);
786
+
787
+ // Use model position of tile's coordinate zoom for raster tile texture UVs
788
+ this.defines.TANGRAM_MODEL_POSITION_BASE_ZOOM_VARYING = true;
789
+
790
+ // Uniforms and samplers
791
+ // Global style blocks are compiled in both stages, even when only
792
+ // the fragment stage samples rasters. Decoding itself is binding-free.
793
+ this.replaceShaderBlock('raster', `${heightDecode.vs}\n${rasters_source}`, 'Raster');
794
+ }
795
+ },
796
+
797
+ // Load raster tile textures and set uniforms
798
+ async buildRasterTextures (this: StyleRuntime, tile: import('./style-runtime-types').StyleFinalizeTile, tile_data: BuildTileData<Uint8Array>) {
799
+ // skip if style doesn't support rasters
800
+ if (!this.hasRasters()) {
801
+ return tile_data;
802
+ }
803
+
804
+ // skip if source didn't attach any rasters to tile
805
+ if (!tile.rasters?.length) {
806
+ return tile_data;
807
+ }
808
+ const coords = tile.coords;
809
+ if (!coords) throw new Error('Raster tile has no address');
810
+
811
+ // Load textures on main thread and return when done
812
+ // We want to block the building of a raster tile mesh until its texture is loaded,
813
+ // to avoid flickering while loading (texture will render as black)
814
+ let textures;
815
+ try {
816
+ textures = await WorkerBroker.postMessage<RasterTextureInfo[] | undefined>(
817
+ `${this.main_thread_target}.loadTextures`,
818
+ { coords: tile.coords, source: tile.source, rasters: tile.rasters, min: tile.min, max: tile.max }
819
+ );
820
+ }
821
+ catch (e) { // error thrown if style has been removed from main thread
822
+ return tile_data;
823
+ }
824
+
825
+ if (!textures || textures.length < 1) { // no textures found (unexpected)
826
+ // TODO: warning
827
+ return tile_data;
828
+ }
829
+ else if (textures.some(t => !t.loaded)) { // some textures failed, throw out style for this tile
830
+ return null;
831
+ }
832
+
833
+ // Enable alpha masking if needed (for transparency outside raster image, on first raster only)
834
+ const firstRaster = this.sources[tile.rasters[0]];
835
+ tile_data.uniforms['u_raster_mask_alpha'] = ('mask_alpha' in firstRaster && firstRaster.mask_alpha === true);
836
+
837
+ // Set texture uniforms (returned after loading from main thread)
838
+ const u_samplers: string[] = tile_data.uniforms['u_rasters'] = [];
839
+ const u_sizes: number[][] = tile_data.uniforms['u_raster_sizes'] = [];
840
+ const u_offsets: number[][] = tile_data.uniforms['u_raster_offsets'] = [];
841
+
842
+ textures.forEach(t => {
843
+ const i = t.index;
844
+ u_samplers[i] = t.name;
845
+ tile_data.textures.push(t.name);
846
+
847
+ u_sizes[i] = [t.width, t.height];
848
+
849
+ // Tile geometry may be at a higher zoom than the raster tile texture,
850
+ // (e.g. an overzoomed raster tile), in which case we need to adjust the
851
+ // raster texture UVs to offset to the appropriate starting point for
852
+ // this geometry tile.
853
+ if (coords.z > t.coords.z) {
854
+ let dz = coords.z - t.coords.z; // # of levels raster source is overzoomed
855
+ let dz2 = Math.pow(2, dz);
856
+ u_offsets[i] = [
857
+ (((coords.x % dz2) + dz2) % dz2) / dz2, // double-modulo to handle negative (wrapped) tile coords
858
+ (dz2 - 1 - (coords.y % dz2)) / dz2, // GL texture coords are +Y up
859
+ 1 / dz2
860
+ ];
861
+ }
862
+ else {
863
+ u_offsets[i] = [0, 0, 1];
864
+ }
865
+ });
866
+
867
+ return tile_data;
868
+ },
869
+
870
+ // Determine which raster tile textures need to load for this tile, load them and return metadata to worker
871
+ // Called on main thread
872
+ async loadTextures (this: StyleRuntime, tile: RasterTextureTile) {
873
+ let configs: Record<string, import('../gl/texture').TextureOptions & {name: string; coords: RasterTextureInfo['coords']}> = {}; // texture configs to pass to texture builder, keyed by texture name
874
+ let index: Record<string, number> = {}; // index into raster sampler array, keyed by texture name
875
+ let queue: Promise<void>[] = [];
876
+
877
+ // Find raster textures that need to be loaded
878
+ tile.rasters.map(r => this.sources[r]).filter(x => x).forEach((source, i) => {
879
+ if (source instanceof RasterTileSource) {
880
+ queue.push(source.tileTexture(tile, this).then(config => {
881
+ configs[config.name] = config;
882
+ index[config.name] = i;
883
+ }));
884
+ }
885
+ });
886
+ await Promise.all(queue);
887
+
888
+ // Create and load raster textures
889
+ await Texture.createFromObject(this.resource_context, configs);
890
+ let textures = await Promise.all(Object.keys(configs)
891
+ .flatMap(name => {const texture = Texture.textures[name]; return texture instanceof Texture ? [texture.load()] : [];})
892
+ );
893
+ textures.forEach(t => t.retain());
894
+
895
+ // Take a subset of texture metadata, and decorate with raster-specific info
896
+ // NB: only return name and size of textures loaded, because we can't send actual texture objects to worker
897
+ return textures.map(t => ({
898
+ name: t.name,
899
+ width: t.width,
900
+ height: t.height,
901
+ loaded: t.loaded,
902
+ index: index[t.name], // raster sampler index
903
+ coords: configs[t.name].coords // tile coords of raster tile
904
+ }));
905
+ },
906
+
907
+ // Setup shader definitions for custom attributes
908
+ setupCustomAttributes(this: StyleRuntime) {
909
+ if (this.shaders.attributes) {
910
+ for (const [aname, attrib] of Object.entries(this.shaders.attributes)) {
911
+ // alias each custom attribute to the internal attribute name in vertex shader,
912
+ // and internal varying name in fragment shader (if varying is enabled)
913
+ if (attrib.type === 'float') {
914
+ if (attrib.varying !== false) {
915
+ this.addShaderBlock('attributes', `
916
+ #ifdef TANGRAM_VERTEX_SHADER
917
+ attribute float a_${aname};
918
+ varying float v_${aname};
919
+ #define ${aname} a_${aname}
920
+ #else
921
+ varying float v_${aname};
922
+ #define ${aname} v_${aname}
923
+ #endif
924
+ `);
925
+ this.addShaderBlock('setup', `#ifdef TANGRAM_VERTEX_SHADER\nv_${aname} = a_${aname};\n#endif`);
926
+ }
927
+ else {
928
+ this.addShaderBlock('attributes', `
929
+ #ifdef TANGRAM_VERTEX_SHADER
930
+ attribute float a_${aname};
931
+ #define ${aname} a_${aname}
932
+ #endif
933
+ `);
934
+ }
935
+ }
936
+ }
937
+ }
938
+ },
939
+
940
+ // Add custom attributes to a list of attributes for initializing a vertex layout
941
+ addCustomAttributesToAttributeList(this: StyleRuntime, attribs: VertexAttribute[]) {
942
+ if (this.shaders.attributes) {
943
+ for (const [aname, attrib] of Object.entries(this.shaders.attributes)) {
944
+ if (attrib.type === 'float') {
945
+ attribs.push({ name: `a_${aname}`, size: 1, type: gl.FLOAT, normalized: false });
946
+ }
947
+ }
948
+ }
949
+ return attribs;
950
+ },
951
+
952
+ // Add current feature values for custom attributes to vertex template
953
+ addCustomAttributesToVertexTemplate(this: StyleRuntime, draw: BuildFeatureStyle, index: number) {
954
+ if (this.shaders.attributes) {
955
+ for (let aname in this.shaders.attributes) {
956
+ const value = draw.attributes?.[aname];
957
+ this.vertex_template[index++] = typeof value === 'number' ? value : 0;
958
+ }
959
+ }
960
+ },
961
+
962
+ // Setup any GL state for rendering
963
+ setup (this: StyleRuntime) {
964
+ this.setUniforms();
965
+ if (this.portable_material_buffer) {
966
+ if (!(this.material instanceof Material)) throw new Error('Style material has not been initialized');
967
+ this.portable_material_buffer.setUniforms(getPortableMaterialUniforms(this.material));
968
+ } else {
969
+ if (!(this.material instanceof Material)) throw new Error('Style material has not been initialized');
970
+ this.material.setupProgram(ShaderProgram.current);
971
+ }
972
+ },
973
+
974
+ // Set style uniforms on currently bound program
975
+ setUniforms (this: StyleRuntime) {
976
+ var program = ShaderProgram.current;
977
+ if (!program) {
978
+ return;
979
+ }
980
+
981
+ program.setUniforms(this.shaders?.uniforms ?? {}, true); // reset texture unit to 0
982
+ },
983
+
984
+ // Render state settings by blend mode
985
+ render_states: {
986
+ opaque: { depth_test: true, depth_write: true },
987
+ translucent: { depth_test: true, depth_write: true },
988
+ add: { depth_test: true, depth_write: false },
989
+ multiply: { depth_test: true, depth_write: false },
990
+ inlay: { depth_test: true, depth_write: false },
991
+ overlay: { depth_test: false, depth_write: false }
992
+ },
993
+
994
+ // Default sort order for blend modes
995
+ default_blend_orders: {
996
+ opaque: Number.MIN_SAFE_INTEGER,
997
+ add: 1,
998
+ multiply: 2,
999
+ inlay: 3,
1000
+ translucent: 4,
1001
+ overlay: 5
1002
+ },
1003
+
1004
+ getBlendOrderForDraw (this: StyleRuntime, draw: {blend_order?: number}): number | undefined {
1005
+ // Allow draw block to override blend_order for non-opaque blend styles
1006
+ return ((this.blend !== 'opaque' && draw.blend_order != null) ? draw.blend_order : this.blend_order);
1007
+ }
1008
+
1009
+ };
1010
+
1011
+ // add feature and geometry counts for a single layer
1012
+ export function addLayerDebugEntry (target: LayerDebugTree, layer: string, faeture_count: number, geom_count: number, styles: Record<string, number>, bases: Record<string, number>) {
1013
+ target[layer] = target[layer] || { features: 0, geoms: 0, styles: {}, base: {} };
1014
+ target[layer].features += faeture_count; // feature count
1015
+ target[layer].geoms += geom_count; // geometry count
1016
+
1017
+ // geometry count by style
1018
+ for (let style in styles) {
1019
+ target[layer].styles[style] = target[layer].styles[style] || 0;
1020
+ target[layer].styles[style] += styles[style];
1021
+ }
1022
+
1023
+ // geometry count by base style
1024
+ for (let style in bases) {
1025
+ target[layer].base[style] = target[layer].base[style] || 0;
1026
+ target[layer].base[style] += bases[style];
1027
+ }
1028
+ }
1029
+
1030
+ /** Cache normalization installs the value slot before feature evaluation. */
1031
+ function isPropertyCache(value: unknown): value is PropertyCache {
1032
+ return value !== null && typeof value === 'object' && 'value' in value;
1033
+ }