@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,590 @@
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
+ import ShaderProgram from '../gl/shader_program';
7
+ import GLSL from '../gl/glsl';
8
+ import Geo from '../utils/geo';
9
+ import StyleParser from '../styles/style_parser';
10
+ import {Vector3} from '@math.gl/core';
11
+ import {convertLumaLight, mapTangramLight} from './light-definitions';
12
+ import type {TangramLight, TangramLightColor, TangramLightMapping, ResolvedTangramLight, NormalizedTangramLight} from './light-definitions';
13
+ import {projectGeographicLight, projectGeographicDirection} from './geographic-lights';
14
+
15
+ import ambient_source from './ambient_light.glsl';
16
+ import directional_source from './directional_light.glsl';
17
+ import point_source from './point_light.glsl';
18
+ import spot_source from './spot_light.glsl';
19
+ import {buildNativeFalloff} from './native-falloff';
20
+
21
+ /** Legacy distance scalar, interpreted in meters unless explicitly suffixed with px. */
22
+ type LightDistance = number | string;
23
+ /** Position authored with legacy meter/pixel components. */
24
+ type LightPosition = readonly [LightDistance, LightDistance, LightDistance];
25
+ /** Radius pair after the constructor has expanded a scalar outer radius. */
26
+ type LightRadius = readonly [LightDistance | null, LightDistance];
27
+ /** Native optional fields, absent rather than fabricated for non-positional lights. */
28
+ type NativeLightFields = {
29
+ /** Optional coordinate interpretation on positional descriptors. */
30
+ positionSpace?: 'common' | 'geographic';
31
+ /** Optional orientation frame on geographic spots. */
32
+ directionSpace?: 'common' | 'enu';
33
+ };
34
+ /** Minimum per-eye view data consumed by lighting, independent of scene/deck classes. */
35
+ export interface LightView {
36
+ /** Style zoom used by legacy pixel-distance conversion. */
37
+ zoom: number | null;
38
+ /** Active projection, if the host supplies one. */
39
+ projection?: {type: string};
40
+ /** Longitude used to select the nearest planar world copy. */
41
+ center?: {lng: number} | null;
42
+ /** Active camera transform and eye origin. */
43
+ camera?: {
44
+ /** Hosted cameras bind the full eye origin rather than only altitude. */
45
+ type?: string;
46
+ /** Column-major common-to-eye transform. */
47
+ view_matrix: ArrayLike<number>;
48
+ /** Eye origin in Tangram lighting coordinates. */
49
+ position_meters: ArrayLike<number>;
50
+ /** Optional projection-aware transform for incoming directions. */
51
+ transformVector?: (vector: number[]) => number[];
52
+ };
53
+ }
54
+ /** Authored legacy light or the normalized configuration of a native luma light. */
55
+ export interface LightConfig {
56
+ /** Shader-safe instance name provided by scene normalization. */
57
+ name: string;
58
+ /** Legacy type discriminator; native inputs obtain it during conversion. */
59
+ type?: string;
60
+ /** Native luma descriptor before conversion. */
61
+ luma?: TangramLight;
62
+ /** Detached descriptor after conversion. */
63
+ lumaLight?: NormalizedTangramLight & NativeLightFields;
64
+ /** Independent ambient contribution. */
65
+ ambient?: TangramLightColor;
66
+ /** Independent diffuse contribution. */
67
+ diffuse?: TangramLightColor;
68
+ /** Independent specular contribution. */
69
+ specular?: TangramLightColor;
70
+ /** Legacy world/ground/camera interpretation or native projected coordinates. */
71
+ origin?: 'world' | 'ground' | 'camera' | 'luma';
72
+ /** Legacy unit-bearing or native numeric position. */
73
+ position?: LightPosition;
74
+ /** Incoming direction; legacy numeric strings are accepted. */
75
+ direction?: readonly (number | string)[];
76
+ /** Legacy falloff exponent, not native polynomial coefficients. */
77
+ attenuation?: number | string;
78
+ /** Optional inner/outer radius controls. */
79
+ radius?: LightDistance | LightRadius;
80
+ /** Legacy cutoff in degrees. */
81
+ angle?: number | string;
82
+ /** Legacy cosine falloff exponent. */
83
+ exponent?: number | string;
84
+ }
85
+ /** Structural shader program boundary used by both GPU backends. */
86
+ export interface LightUniformProgram {
87
+ /** Set a named scalar or vector uniform, preserving legacy GL setter names. */
88
+ uniform(method: string, name: string, value: import('@luma.gl/core').UniformValue): void;
89
+ }
90
+ /** Optional subclass data inspected by the shared descriptor mapper. */
91
+ type LightShadingFields = {
92
+ direction?: readonly number[];
93
+ position_eye?: number[];
94
+ attenuation?: number;
95
+ radius?: LightRadius | null;
96
+ angle?: number;
97
+ exponent?: number | string;
98
+ };
99
+ /** Constructor/GLSL composer registry, retaining custom registered light types. */
100
+ type LightConstructor = {new(view: LightView, config: LightConfig): Light; inject(): void};
101
+ // parseFloat already coerces numeric inputs at runtime; describe that existing JS boundary.
102
+ type LightFloatParser = (value: number | string | undefined) => number;
103
+
104
+ /** Shared contribution parsing and shader composition for registered light kinds. */
105
+ export default class Light {
106
+
107
+ /** Short-name constructor registry populated below. */
108
+ declare static types: Record<string, LightConstructor>;
109
+ /** Lighting shader block name. */
110
+ declare static block: string;
111
+ /** Global lighting switch used by live editing. */
112
+ declare static enabled: boolean;
113
+ /** Instance uniform name. */
114
+ declare name: string;
115
+ /** Active per-eye camera and style zoom. */
116
+ declare view: LightView;
117
+ /** A camera is needed for eye-space positions, but not light construction. */
118
+ protected get activeCamera(): NonNullable<LightView['camera']> {
119
+ if (!this.view.camera) throw new Error('Light view camera is not initialized');
120
+ return this.view.camera;
121
+ }
122
+ /** Native source descriptor, absent for legacy lights. */
123
+ declare lumaLight: (NormalizedTangramLight & NativeLightFields) | undefined;
124
+ /** Parsed ambient contribution vector. */
125
+ declare ambient: number[];
126
+ /** Parsed diffuse contribution vector. */
127
+ declare diffuse: number[];
128
+ /** Parsed specular contribution vector. */
129
+ declare specular: number[];
130
+ /** Registered shader discriminator, including custom light kinds. */
131
+ declare type: string;
132
+ /** Corresponding GLSL struct name. */
133
+ declare struct_name: string;
134
+
135
+ /** Resolve contribution colors without allocating scene or GPU resources. */
136
+ constructor (view: LightView, config: LightConfig) {
137
+ this.name = config.name;
138
+ this.view = view;
139
+ this.lumaLight = config.lumaLight;
140
+
141
+ if (config.ambient == null || typeof config.ambient === 'number') {
142
+ this.ambient = GLSL.expandVec3(config.ambient || 0)!;
143
+ }
144
+ else {
145
+ this.ambient = StyleParser.parseColor(config.ambient).slice(0, 3);
146
+ }
147
+
148
+ if (config.diffuse == null || typeof config.diffuse === 'number') {
149
+ this.diffuse = GLSL.expandVec3(config.diffuse != null ? config.diffuse : 1)!;
150
+ }
151
+ else {
152
+ this.diffuse = StyleParser.parseColor(config.diffuse).slice(0, 3);
153
+ }
154
+
155
+ if (config.specular == null || typeof config.specular === 'number') {
156
+ this.specular = GLSL.expandVec3(config.specular || 0)!;
157
+ }
158
+ else {
159
+ this.specular = StyleParser.parseColor(config.specular).slice(0, 3);
160
+ }
161
+ }
162
+
163
+ // Create a light by type name, factory-style
164
+ // 'config' must include 'name' and 'type', along with any other type-specific properties
165
+ /** Known built-in definitions create a light; unknown registered names remain optional. */
166
+ static create(view: LightView, config: LightConfig & ({type: ResolvedTangramLight['type']} | {luma: TangramLight})): Light & LightShadingFields;
167
+ static create(view: LightView, config: LightConfig): Light | undefined;
168
+ static create (view: LightView, config: LightConfig) {
169
+ if ('luma' in config) {
170
+ // Preserve the converter's runtime rejection of malformed scene values;
171
+ // only this untrusted configuration boundary can supply an absent descriptor.
172
+ config = {...config, ...convertLumaLight(config.luma!)};
173
+ }
174
+ if (Light.types[config.type!]) {
175
+ return new Light.types[config.type!](view, config);
176
+ }
177
+ }
178
+
179
+ // Set light for a style: fragment lighting, vertex lighting, or none
180
+ /** Select vertex/fragment lighting, retaining the global disable override. */
181
+ static setMode (mode: boolean | string | null | undefined, style: {defines: Record<string, unknown>}) {
182
+ if (mode === true) {
183
+ mode = 'fragment';
184
+ }
185
+ mode = Light.enabled && ((mode != null) ? mode : 'fragment'); // default to fragment lighting
186
+ style.defines['TANGRAM_LIGHTING_FRAGMENT'] = (mode === 'fragment');
187
+ style.defines['TANGRAM_LIGHTING_VERTEX'] = (mode === 'vertex');
188
+ }
189
+
190
+ // Inject all provided light definitions, and calculate cumulative light function
191
+ /** Recompose the shader block in authored instance order. */
192
+ static inject (lights?: Record<string, Light & LightShadingFields> | null) {
193
+ // Clear previous injections
194
+ ShaderProgram.removeBlock(Light.block);
195
+
196
+ // If lighting is globally disabled, nothing is injected (mostly for debugging or live editing)
197
+ if (!Light.enabled) {
198
+ return;
199
+ }
200
+
201
+ // Construct code to calculate each light instance
202
+ let calculateLights = '';
203
+ if (lights && Object.keys(lights).length > 0) {
204
+ // Collect uniques types of lights
205
+ let types: Record<string, boolean> = {};
206
+ for (let light_name in lights) {
207
+ types[lights[light_name].type] = true;
208
+ }
209
+
210
+ // Native falloff is shared across point/spot structs without replacing Tangram materials.
211
+ if (types.point || types.spotlight) ShaderProgram.addBlock(Light.block, buildNativeFalloff('glsl'));
212
+
213
+ // Inject each type of light
214
+ for (let type in types) {
215
+ Light.types[type].inject();
216
+ }
217
+
218
+ // Inject per-instance blocks and construct the list of functions to calculate each light
219
+ for (let light_name in lights) {
220
+ // Define instance
221
+ lights[light_name].inject();
222
+
223
+ // Add the calculation function to the list
224
+ calculateLights += `calculateLight(${light_name}, _eyeToPoint, _normal);\n`;
225
+ }
226
+ // Keep aggregate defines for custom blocks; built-in falloff is selected per light.
227
+ const points = Object.values(lights).filter(light => light.type === 'point' || light.type === 'spotlight');
228
+ ShaderProgram.defines['TANGRAM_POINTLIGHT_ATTENUATION_EXPONENT'] = points.some(light => light.attenuation !== 0);
229
+ ShaderProgram.defines['TANGRAM_POINTLIGHT_ATTENUATION_INNER_RADIUS'] = points.some(light => light.radius?.[0] != null);
230
+ ShaderProgram.defines['TANGRAM_POINTLIGHT_ATTENUATION_OUTER_RADIUS'] = points.some(light => light.radius != null);
231
+ }
232
+
233
+ // Glue together the final lighting function that sums all the lights
234
+ let calculateFunction = `
235
+ vec4 calculateLighting(in vec3 _eyeToPoint, in vec3 _normal, in vec4 _color) {
236
+
237
+ // Do initial material calculations over normal, emission, ambient, diffuse and specular values
238
+ calculateMaterial(_eyeToPoint,_normal);
239
+
240
+ // Un roll the loop of individual ligths to calculate
241
+ ${calculateLights}
242
+
243
+ // Final light intensity calculation
244
+ vec4 color = vec4(vec3(0.), _color.a); // start with vertex color alpha
245
+
246
+ #ifdef TANGRAM_MATERIAL_EMISSION
247
+ color.rgb = material.emission.rgb;
248
+ color.a *= material.emission.a;
249
+ #endif
250
+
251
+ #ifdef TANGRAM_MATERIAL_AMBIENT
252
+ color.rgb += light_accumulator_ambient.rgb * _color.rgb * material.ambient.rgb;
253
+ color.a *= material.ambient.a;
254
+ #else
255
+ #ifdef TANGRAM_MATERIAL_DIFFUSE
256
+ color.rgb += light_accumulator_ambient.rgb * _color.rgb * material.diffuse.rgb;
257
+ #endif
258
+ #endif
259
+
260
+ #ifdef TANGRAM_MATERIAL_DIFFUSE
261
+ color.rgb += light_accumulator_diffuse.rgb * _color.rgb * material.diffuse.rgb;
262
+ color.a *= material.diffuse.a;
263
+ #endif
264
+
265
+ #ifdef TANGRAM_MATERIAL_SPECULAR
266
+ color.rgb += light_accumulator_specular.rgb * material.specular.rgb;
267
+ color.a *= material.specular.a;
268
+ #endif
269
+
270
+ // Clamp final color
271
+ color = clamp(color, 0.0, 1.0);
272
+
273
+ return color;
274
+ }`;
275
+
276
+ ShaderProgram.addBlock(Light.block, calculateFunction);
277
+ }
278
+
279
+ // Common instance definition
280
+ /** Inject this instance's uniforms and setup assignment. */
281
+ inject () {
282
+ let instance = `
283
+ uniform ${this.struct_name} u_${this.name};
284
+ ${this.struct_name} ${this.name};
285
+ `;
286
+ let assign = `
287
+ ${this.name} = u_${this.name};\n
288
+ `;
289
+
290
+ ShaderProgram.addBlock(Light.block, instance);
291
+ ShaderProgram.addBlock('setup', assign);
292
+ }
293
+
294
+ // Update method called once per frame
295
+ /** Refresh eye-relative state; ambient/directional lights have no position to refresh. */
296
+ update () {
297
+ }
298
+
299
+ /** Return a detached luma.gl descriptor and the exact resolved Tangram shading extensions. */
300
+ toLumaLight(this: Light & LightShadingFields): TangramLightMapping {
301
+ this.update();
302
+ const direction = this.getLightingDirection();
303
+ const mapping = mapTangramLight({
304
+ // The mapper rejects custom kinds at its existing runtime conversion boundary.
305
+ type: this.type as ResolvedTangramLight['type'],
306
+ ambient: this.ambient, diffuse: this.diffuse, specular: this.specular,
307
+ ...(this.position_eye ? {position: this.position_eye.slice(0, 3) as [number, number, number]} : {}),
308
+ ...(direction ? {direction: direction as [number, number, number]} : {}),
309
+ ...(this.attenuation != null ? {attenuation: this.attenuation} : {}),
310
+ ...(this.radius ? {radius: this.radius.map(value => value == null ? null :
311
+ StyleParser.convertUnits(value, {zoom: Number(this.view.zoom), meters_per_pixel: Geo.metersPerPixel(Number(this.view.zoom))})) as [number | null, number]} : {}),
312
+ ...(this.angle != null ? {angle: this.angle, exponent: this.exponent as number} : {})
313
+ });
314
+ if (this.lumaLight) {
315
+ mapping.light = {...this.lumaLight,
316
+ color: [...this.lumaLight.color] satisfies [number, number, number],
317
+ ambient: [...mapping.tangram.ambient] satisfies [number, number, number],
318
+ diffuse: [...mapping.tangram.diffuse] satisfies [number, number, number],
319
+ specular: [...mapping.tangram.specular] satisfies [number, number, number],
320
+ ...('position' in this.lumaLight ? {position: [...(mapping.light as {position: readonly [number, number, number]}).position] as [number, number, number],
321
+ attenuation: [...this.lumaLight.attenuation] satisfies [number, number, number],
322
+ ...(mapping.tangram.radius ? {radius: [...mapping.tangram.radius] satisfies [number | null, number]} : {})} : {}),
323
+ ...('direction' in this.lumaLight ? {direction: [...direction!] as [number, number, number]} : {})};
324
+ }
325
+ return mapping;
326
+ }
327
+
328
+ /** Resolve native geographic spot orientation before applying the active eye's camera. */
329
+ getLightingDirection(this: Light & LightShadingFields): readonly number[] | undefined {
330
+ let direction = this.direction;
331
+ if (this.lumaLight?.positionSpace === 'geographic' && this.lumaLight.directionSpace !== 'common' && direction) {
332
+ direction = projectGeographicDirection((this.lumaLight as Extract<NormalizedTangramLight, {type: 'point' | 'spot'}>).position, direction as [number, number, number], this.view.projection?.type === 'globe');
333
+ }
334
+ return direction && (this.type === 'directional' || this.lumaLight) && typeof this.view.camera?.transformVector === 'function'
335
+ ? this.view.camera.transformVector(direction as number[]) : direction;
336
+ }
337
+
338
+ // Called once per frame per program (e.g. for main render pass, then for each additional
339
+ // pass for feature selection, etc.)
340
+ /** Bind resolved contribution vectors to the active program. */
341
+ setupProgram (_program: LightUniformProgram) {
342
+ // Three common light properties
343
+ _program.uniform('3fv', `u_${this.name}.ambient`, this.ambient);
344
+ _program.uniform('3fv', `u_${this.name}.diffuse`, this.diffuse);
345
+ _program.uniform('3fv', `u_${this.name}.specular`, this.specular);
346
+ }
347
+
348
+ }
349
+
350
+ Light.types = {}; // references to subclasses by short name
351
+ Light.block = 'lighting'; // shader block name
352
+ Light.enabled = true; // lighting can be globally enabled/disabled
353
+
354
+
355
+ // Light subclasses
356
+ class AmbientLight extends Light {
357
+
358
+ constructor(view: LightView, config: LightConfig) {
359
+ super(view, config);
360
+ this.type = 'ambient';
361
+ this.struct_name = 'AmbientLight';
362
+ }
363
+
364
+ // Inject struct and calculate function
365
+ static inject() {
366
+ ShaderProgram.addBlock(Light.block, ambient_source);
367
+ }
368
+
369
+ setupProgram (_program: LightUniformProgram) {
370
+ _program.uniform('3fv', `u_${this.name}.ambient`, this.ambient);
371
+ }
372
+
373
+ }
374
+ Light.types['ambient'] = AmbientLight;
375
+
376
+ class DirectionalLight extends Light {
377
+ /** Normalized incoming direction, retaining legacy string coercion during construction. */
378
+ declare _direction: readonly (number | string)[];
379
+
380
+ constructor(view: LightView, config: LightConfig) {
381
+ super(view, config);
382
+ this.type = 'directional';
383
+ this.struct_name = 'DirectionalLight';
384
+
385
+ if (config.direction) {
386
+ this._direction = config.direction;
387
+ }
388
+ else {
389
+ // Default directional light maintains full intensity on ground, with basic extrusion shading
390
+ let theta = 135; // angle of light in xy plane (rotated around z axis)
391
+ let scale = Math.sin(Math.PI*60/180); // scaling factor to keep total directional intensity to 0.5
392
+ this._direction = [
393
+ Math.cos(Math.PI*theta/180) * scale,
394
+ Math.sin(Math.PI*theta/180) * scale,
395
+ -0.5
396
+ ];
397
+
398
+ if (config.ambient == null) {
399
+ this.ambient = GLSL.expandVec3(0.5)!;
400
+ }
401
+ }
402
+ this.direction = this._direction.map(parseFloat as LightFloatParser);
403
+ }
404
+
405
+ get direction (): readonly number[] {
406
+ return this._direction as readonly number[];
407
+ }
408
+
409
+ set direction (v: readonly number[]) {
410
+ this._direction = new Vector3(v).normalize().toArray() as number[];
411
+ }
412
+
413
+ // Inject struct and calculate function
414
+ static inject() {
415
+ ShaderProgram.addBlock(Light.block, directional_source);
416
+ }
417
+
418
+ setupProgram (_program: LightUniformProgram) {
419
+ super.setupProgram(_program);
420
+ const direction = this.getLightingDirection();
421
+ _program.uniform('3fv', `u_${this.name}.direction`, direction as number[]);
422
+ }
423
+
424
+ }
425
+ Light.types['directional'] = DirectionalLight;
426
+
427
+
428
+ class PointLight extends Light {
429
+ /** Positional native descriptors are validated before reaching this constructor. */
430
+ declare lumaLight: Extract<NormalizedTangramLight, {type: 'point' | 'spot'}> | undefined;
431
+ /** Authored unit-bearing position. */
432
+ declare position: LightPosition;
433
+ /** Eye-relative homogeneous position, updated before binding. */
434
+ declare position_eye: number[];
435
+ /** Coordinate interpretation retained from the scene. */
436
+ declare origin: NonNullable<LightConfig['origin']>;
437
+ /** Legacy falloff exponent. */
438
+ declare attenuation: number;
439
+ /** Optional inner/outer unit-bearing radii. */
440
+ declare radius: LightRadius | null;
441
+
442
+ constructor (view: LightView, config: LightConfig) {
443
+ super(view, config);
444
+ this.type = 'point';
445
+ this.struct_name = 'PointLight';
446
+
447
+ this.position = config.position || [0, 0, '100px'];
448
+ this.position_eye = []; // position in eyespace
449
+ this.origin = config.origin || 'ground';
450
+ this.attenuation = !isNaN((parseFloat as LightFloatParser)(config.attenuation)) ? (parseFloat as LightFloatParser)(config.attenuation) : 0;
451
+
452
+ if (config.radius) {
453
+ if (Array.isArray(config.radius) && config.radius.length === 2) {
454
+ this.radius = config.radius as LightRadius;
455
+ }
456
+ else {
457
+ this.radius = [null, config.radius as LightDistance];
458
+ }
459
+ }
460
+ else {
461
+ this.radius = null;
462
+ }
463
+ }
464
+
465
+ // Inject struct and calculate function
466
+ static inject () {
467
+ ShaderProgram.addBlock(Light.block, point_source);
468
+ }
469
+
470
+ // Inject isntance-specific settings
471
+ inject() {
472
+ super.inject();
473
+
474
+ ShaderProgram.defines['TANGRAM_POINTLIGHT_ATTENUATION_EXPONENT'] = (this.attenuation !== 0);
475
+ ShaderProgram.defines['TANGRAM_POINTLIGHT_ATTENUATION_INNER_RADIUS'] = (this.radius != null && this.radius[0] != null);
476
+ ShaderProgram.defines['TANGRAM_POINTLIGHT_ATTENUATION_OUTER_RADIUS'] = (this.radius != null);
477
+ }
478
+
479
+ update () {
480
+ this.updateEyePosition();
481
+ }
482
+
483
+ updateEyePosition () {
484
+ if (this.origin === 'luma') {
485
+ // Native luma positions share the projected common space of geometry.
486
+ const camera = this.activeCamera;
487
+ const position = this.lumaLight?.positionSpace === 'geographic'
488
+ ? projectGeographicLight(this.position as [number, number, number], this.view.projection?.type === 'globe', this.view.center?.lng)
489
+ : this.position as [number, number, number];
490
+ const matrix = camera.view_matrix;
491
+ const globe = this.view.projection?.type === 'globe';
492
+ const projected = globe ? position : [
493
+ matrix[0] * position[0] + matrix[4] * position[1] + matrix[8] * position[2] + matrix[12],
494
+ matrix[1] * position[0] + matrix[5] * position[1] + matrix[9] * position[2] + matrix[13],
495
+ matrix[2] * position[0] + matrix[6] * position[1] + matrix[10] * position[2] + matrix[14]
496
+ ];
497
+ // Standalone cameras bind only their height as u_eye; hosted cameras bind all components.
498
+ const eye = camera.type === 'external' || globe ? camera.position_meters : [0, 0, camera.position_meters[2]];
499
+ this.position_eye = projected.map((value, index) => value - eye[index]);
500
+ }
501
+ else if (this.origin === 'world') {
502
+ // For world origin, format is: [longitude, latitude, meters (default) or pixels w/px units]
503
+
504
+ // Move light's world position into camera space
505
+ const m = Geo.latLngToMeters([...this.position] as number[]);
506
+ this.position_eye[0] = m[0] - this.activeCamera.position_meters[0];
507
+ this.position_eye[1] = m[1] - this.activeCamera.position_meters[1];
508
+
509
+ this.position_eye[2] = StyleParser.convertUnits(this.position[2],
510
+ { zoom: Number(this.view.zoom), meters_per_pixel: Geo.metersPerPixel(Number(this.view.zoom)) }) as number;
511
+ this.position_eye[2] = this.position_eye[2] - this.activeCamera.position_meters[2];
512
+ }
513
+ else if (this.origin === 'ground' || this.origin === 'camera') {
514
+ // For camera or ground origin, format is: [x, y, z] in meters (default) or pixels w/px units
515
+
516
+ // Light is in camera space by default
517
+ this.position_eye = StyleParser.convertUnits(this.position,
518
+ { zoom: Number(this.view.zoom), meters_per_pixel: Geo.metersPerPixel(Number(this.view.zoom)) }) as number[];
519
+
520
+ if (this.origin === 'ground') {
521
+ // Leave light's xy in camera space, but z needs to be moved relative to ground plane
522
+ this.position_eye[2] = this.position_eye[2] - this.activeCamera.position_meters[2];
523
+ }
524
+ }
525
+ this.position_eye[3] = 1;
526
+ }
527
+
528
+ setupProgram (_program: LightUniformProgram) {
529
+ super.setupProgram(_program);
530
+
531
+ _program.uniform('4fv', `u_${this.name}.position`, this.position_eye);
532
+ _program.uniform('1f', `u_${this.name}.useLumaAttenuation`, this.lumaLight ? 1 : 0);
533
+ _program.uniform('3fv', `u_${this.name}.attenuationCoefficients`, this.lumaLight?.attenuation || [1, 0, 0]);
534
+
535
+ _program.uniform('1f', `u_${this.name}.attenuationExponent`, this.attenuation);
536
+ _program.uniform('1f', `u_${this.name}.innerRadius`, this.radius?.[0] == null ? -1 :
537
+ StyleParser.convertUnits(this.radius[0], {zoom: Number(this.view.zoom), meters_per_pixel: Geo.metersPerPixel(Number(this.view.zoom))}) as number);
538
+ _program.uniform('1f', `u_${this.name}.outerRadius`, this.radius == null ? -1 :
539
+ StyleParser.convertUnits(this.radius[1], {zoom: Number(this.view.zoom), meters_per_pixel: Geo.metersPerPixel(Number(this.view.zoom))}) as number);
540
+ }
541
+ }
542
+ Light.types['point'] = PointLight;
543
+
544
+
545
+ class SpotLight extends PointLight {
546
+ /** Only native spot definitions can reach this registered constructor. */
547
+ declare lumaLight: Extract<NormalizedTangramLight, {type: 'spot'}> | undefined;
548
+ /** Parsed and normalized incoming direction. */
549
+ declare _direction: number[];
550
+ /** Legacy cosine falloff exponent. */
551
+ declare exponent: number | string;
552
+ /** Legacy cutoff in degrees. */
553
+ declare angle: number;
554
+
555
+ constructor (view: LightView, config: LightConfig) {
556
+ super(view, config);
557
+ this.type = 'spotlight';
558
+ this.struct_name = 'SpotLight';
559
+
560
+ this.direction = this._direction = (config.direction || [0, 0, -1]).map(parseFloat as LightFloatParser); // [x, y, z]
561
+ this.exponent = this.lumaLight ? config.exponent ?? 0 : config.exponent ? (parseFloat as LightFloatParser)(config.exponent) : 0.2;
562
+ this.angle = config.angle ? (parseFloat as LightFloatParser)(config.angle) : 20;
563
+ }
564
+
565
+ get direction () {
566
+ return this._direction;
567
+ }
568
+
569
+ set direction (v: readonly number[]) {
570
+ this._direction = new Vector3(v).normalize().toArray() as number[];
571
+ }
572
+
573
+ // Inject struct and calculate function
574
+ static inject () {
575
+ ShaderProgram.addBlock(Light.block, spot_source);
576
+ }
577
+
578
+ setupProgram (_program: LightUniformProgram) {
579
+ super.setupProgram(_program);
580
+
581
+ const direction = this.getLightingDirection();
582
+ _program.uniform('3fv', `u_${this.name}.direction`, direction as number[]);
583
+ _program.uniform('1f', `u_${this.name}.spotCosCutoff`, Math.cos(this.angle * 3.14159 / 180));
584
+ _program.uniform('1f', `u_${this.name}.spotExponent`, this.exponent as number);
585
+ _program.uniform('2fv', `u_${this.name}.lumaConeCos`, this.lumaLight ?
586
+ [Math.cos(this.lumaLight.innerConeAngle), Math.cos(this.lumaLight.outerConeAngle)] : [1, 0]);
587
+ }
588
+
589
+ }
590
+ Light.types['spotlight'] = SpotLight;
@@ -0,0 +1,80 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {TangramLightMapping} from './light-definitions';
6
+
7
+ /** Bounded portable scene-light capacity; oversized scenes fail instead of truncating. */
8
+ export const MAX_PORTABLE_LIGHTS = 16;
9
+
10
+ /** Fixed layout shared by all configured WGSL surface styles and every render eye. */
11
+ export const PORTABLE_LIGHT_UNIFORMS: Record<string, string> = {u_light_count: 'int'};
12
+ for (let index = 0; index < MAX_PORTABLE_LIGHTS; index++) {
13
+ for (const field of ['ambient', 'diffuse', 'specular', 'position', 'direction', 'falloff', 'cone', 'coefficients']) {
14
+ PORTABLE_LIGHT_UNIFORMS[`u_light_${index}_${field}`] = 'vec4';
15
+ }
16
+ }
17
+
18
+ /** Fixed untextured material layout, stored separately for each surface style. */
19
+ export const PORTABLE_MATERIAL_UNIFORMS = {
20
+ u_material_emission: 'vec4', u_material_ambient: 'vec4', u_material_diffuse: 'vec4',
21
+ u_material_specular: 'vec4', u_material_flags: 'ivec4', u_material_shininess: 'float'
22
+ };
23
+
24
+ /** A resolved constant Material contribution; textures remain a separate WGSL migration. */
25
+ interface MaterialContribution {
26
+ /** Resolved RGBA multiplier. */
27
+ amount?: ArrayLike<number>;
28
+ /** Texture presence is rejected rather than silently approximated. */
29
+ texture?: unknown;
30
+ /** Resolved specular exponent. */
31
+ shininess?: number;
32
+ }
33
+
34
+ /** Minimal material surface consumed by the portable lighting adapter. */
35
+ export interface PortableMaterial {
36
+ /** Constant emitted color. */
37
+ emission?: MaterialContribution;
38
+ /** Independent ambient response. */
39
+ ambient?: MaterialContribution;
40
+ /** Diffuse response, also the ambient fallback when ambient is absent. */
41
+ diffuse?: MaterialContribution;
42
+ /** Untinted specular response. */
43
+ specular?: MaterialContribution;
44
+ /** Tangent-space normal maps are not yet implemented. */
45
+ normal?: unknown;
46
+ }
47
+
48
+ /** Pack detached, active-eye light snapshots without mixing legacy and native falloff. */
49
+ export function getPortableLightUniforms(lights: readonly TangramLightMapping[]): Record<string, number | number[]> {
50
+ if (lights.length > MAX_PORTABLE_LIGHTS) throw new Error(`Portable lighting supports at most ${MAX_PORTABLE_LIGHTS} visible lights`);
51
+ const uniforms: Record<string, number | number[]> = {u_light_count: lights.length};
52
+ lights.forEach(({light, tangram}, index) => {
53
+ const prefix = `u_light_${index}_`;
54
+ const positional = light.type === 'point' || light.type === 'spot';
55
+ uniforms[`${prefix}ambient`] = [...tangram.ambient, 0];
56
+ uniforms[`${prefix}diffuse`] = [...tangram.diffuse, 0];
57
+ uniforms[`${prefix}specular`] = [...tangram.specular, 0];
58
+ uniforms[`${prefix}position`] = [...(tangram.position ?? [0, 0, 0]), light.type === 'ambient' ? 0 : light.type === 'directional' ? 1 : light.type === 'point' ? 2 : 3];
59
+ uniforms[`${prefix}direction`] = [...(tangram.direction ?? [0, 0, -1]), 0];
60
+ uniforms[`${prefix}falloff`] = [tangram.attenuation ?? 0, tangram.radius?.[0] ?? -1, tangram.radius?.[1] ?? -1, tangram.exponent ?? 0];
61
+ // Standard descriptors from legacy mappings have no coefficients/cones;
62
+ // only native descriptors carry them, keeping the two equations distinct.
63
+ const native = positional && light.attenuation != null;
64
+ uniforms[`${prefix}coefficients`] = positional ? [...(light.attenuation ?? [1, 0, 0]), native ? 1 : 0] : [1, 0, 0, 0];
65
+ uniforms[`${prefix}cone`] = light.type === 'spot' ? [Math.cos(light.innerConeAngle ?? 0), Math.cos(light.outerConeAngle ?? Math.PI / 4), Math.cos((tangram.angle ?? 20) * 3.14159 / 180), 0] : [1, 0, 0, 0];
66
+ });
67
+ return uniforms;
68
+ }
69
+
70
+ /** Reject unsupported texture/normal mappings and pack Tangram's constant material semantics. */
71
+ export function getPortableMaterialUniforms(material: PortableMaterial): Record<string, number | number[]> {
72
+ const fields = ['emission', 'ambient', 'diffuse', 'specular'] as const;
73
+ if (material.normal || fields.some(field => material[field]?.texture != null)) {
74
+ throw new Error('Configured WGSL lighting currently requires constant materials; material textures and normal maps are not supported');
75
+ }
76
+ const uniforms: Record<string, number | number[]> = {u_material_flags: fields.map(field => material[field] ? 1 : 0),
77
+ u_material_shininess: material.specular?.shininess ?? 0.2};
78
+ for (const field of fields) uniforms[`u_material_${field}`] = Array.from(material[field]?.amount ?? [0, 0, 0, 1]);
79
+ return uniforms;
80
+ }