@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,814 @@
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
+ // Texture management
7
+
8
+ import log from '../utils/log';
9
+ import Utils from '../utils/utils';
10
+ import subscribeMixin from '../utils/subscribe';
11
+ import WorkerBroker from '../utils/worker_broker';
12
+ import Context from './context';
13
+ import type {Texture as GPUTexture} from '@luma.gl/core';
14
+ import type {TangramTextureOptions} from '../gpu/tangram_gpu_backend';
15
+ import type {SubscriptionMethods} from '../utils/subscribe';
16
+
17
+ /** Browser image sources accepted by the classic texture wrapper. */
18
+ type TextureElement = HTMLCanvasElement | HTMLVideoElement | HTMLImageElement;
19
+ /** Shared raw pixel uploads and DOM sources; names/selectors are resolved by load. */
20
+ type TextureSource = TextureElement | Uint8Array | null;
21
+ /** The wrapper owns disposal only; portable draw binding remains the backend's responsibility. */
22
+ export type TextureResource = Pick<GPUTexture, 'destroy'>;
23
+ /** Named texture options, including worker-safe sprite definitions. */
24
+ export interface TextureOptions {
25
+ filtering?: 'nearest' | 'linear' | 'mipmap';
26
+ sprites?: Record<string, [number, number, number, number]>;
27
+ url?: string;
28
+ element?: string | TextureElement;
29
+ data?: number[] | Uint8Array | null;
30
+ width?: number;
31
+ height?: number;
32
+ density?: number;
33
+ repeat?: boolean;
34
+ TEXTURE_WRAP_S?: number;
35
+ TEXTURE_WRAP_T?: number;
36
+ UNPACK_FLIP_Y_WEBGL?: boolean;
37
+ UNPACK_PREMULTIPLY_ALPHA_WEBGL?: boolean;
38
+ textureFactory?: (options: TangramTextureOptions) => TextureResource;
39
+ skip_create?: boolean;
40
+ }
41
+ /** Structured-clone metadata: workers never acquire GPU resources or live methods. */
42
+ export interface TextureInfo {
43
+ name: string;
44
+ width: number;
45
+ height: number;
46
+ density: number;
47
+ css_size?: number[];
48
+ aspect?: number;
49
+ sprites?: TextureOptions['sprites'];
50
+ texcoords: Record<string, number[]>;
51
+ sizes: Record<string, number[]>;
52
+ css_sizes: Record<string, number[]>;
53
+ aspects: Record<string, number>;
54
+ filtering?: TextureOptions['filtering'];
55
+ power_of_2?: boolean;
56
+ valid: boolean;
57
+ }
58
+ /** Legacy bulk requests may recursively expand empty names to the entire registry. */
59
+ export type TextureInfoResult = TextureInfo | null | TextureInfoResult[];
60
+
61
+ function isTextureElement(source: unknown): source is TextureElement {
62
+ return (typeof HTMLCanvasElement !== 'undefined' && source instanceof HTMLCanvasElement) ||
63
+ (typeof HTMLVideoElement !== 'undefined' && source instanceof HTMLVideoElement) ||
64
+ (typeof HTMLImageElement !== 'undefined' && source instanceof HTMLImageElement && source.complete);
65
+ }
66
+
67
+ // GL texture wrapper object for keeping track of a global set of textures, keyed by a unique user-defined name
68
+ export default class Texture {
69
+ /** Release one mesh-owned reference to a named texture. */
70
+ declare static release: (name: string) => void;
71
+ /** Create or replace a named style texture through the existing registry. */
72
+ declare static create: (context: object | null, name: string, options?: TextureOptions) => Texture;
73
+ /** Placeholder sampler used by untextured mesh variants. */
74
+ declare static default: string;
75
+ /** Dimensions available to line styles in either execution thread. */
76
+ declare static textures: Record<string, Texture | TextureInfo>;
77
+ /** Synchronize named texture dimensions from the main thread to a worker. */
78
+ declare static syncTexturesToWorker: (names?: string[] | null) => Promise<Record<string, Texture | TextureInfo>>;
79
+ /** Live context resources; detached worker metadata has no GPU ownership. */
80
+ declare gl: object | null;
81
+ declare texture_factory: TextureOptions['textureFactory'];
82
+ declare texture_resource: TextureResource | null;
83
+ declare texture: TextureResource | WebGLTexture | null;
84
+ declare valid: boolean;
85
+ declare name: string;
86
+ declare retain_count: number;
87
+ declare config_type: 'url' | 'element' | 'data' | null;
88
+ declare loading: Promise<Texture> | null;
89
+ declare loaded: boolean;
90
+ declare filtering: TextureOptions['filtering'];
91
+ declare density: number;
92
+ declare sprites: TextureOptions['sprites'];
93
+ declare texcoords: TextureInfo['texcoords'];
94
+ declare sizes: TextureInfo['sizes'];
95
+ declare css_sizes: TextureInfo['css_sizes'];
96
+ declare aspects: TextureInfo['aspects'];
97
+ declare width: number;
98
+ declare height: number;
99
+ declare power_of_2: boolean | undefined;
100
+ declare url: string;
101
+ /** Existing dynamic static methods, declared without runtime field initialization. */
102
+ declare static retain: (name: string) => void;
103
+ declare static destroy: (context: object | null) => void;
104
+ declare static getSpriteInfo: (name: string, sprite: string) => {size: number[]; css_size: number[]; aspect: number; texcoords: number[]} | undefined;
105
+ declare static getTexcoordsForSprite: (origin: readonly number[], size: readonly number[], dimensions: readonly number[]) => number[];
106
+ declare static createFromObject: (context: object | null, textures?: Record<string, TextureOptions>) => Promise<(Texture | null)[]>;
107
+ declare static createDefault: (context: object | null) => Texture;
108
+ declare static sliceOptions: (options: TextureOptions) => TextureOptions;
109
+ declare static changed: (name: string, options: TextureOptions) => boolean;
110
+ declare static getInfo: typeof getTextureInfo;
111
+ declare static getMaxTextureSize: (context: object | null) => number;
112
+ declare static setResourceFactory: (context: object, factory: TextureOptions['textureFactory']) => void;
113
+ declare static getResourceFactory: (context: object) => TextureOptions['textureFactory'];
114
+ declare static clearResourceFactory: (context: object) => void;
115
+ declare static texture_configs: Record<string, string>;
116
+ declare static resource_factories: WeakMap<object, NonNullable<TextureOptions['textureFactory']>>;
117
+ declare static boundTexture: Texture['texture'];
118
+ declare static activeUnit: number | null;
119
+ declare static resetBindings: () => void;
120
+ declare static trigger: SubscriptionMethods['trigger'];
121
+ declare static subscribe: SubscriptionMethods['subscribe'];
122
+ declare static unsubscribe: SubscriptionMethods['unsubscribe'];
123
+ declare static unsubscribeAll: SubscriptionMethods['unsubscribeAll'];
124
+ declare static hasSubscribersFor: SubscriptionMethods['hasSubscribersFor'];
125
+
126
+ constructor(gl: object | null, name: string, options: TextureOptions = {}) {
127
+ options = Texture.sliceOptions(options); // exclude any non-texture-specific props
128
+ this.gl = gl;
129
+ this.texture_factory = options.textureFactory || (gl && Texture.getResourceFactory(gl)) || undefined;
130
+ this.texture_resource = null;
131
+ this.texture = this.texture_factory ? null : getClassicTextureContext(gl).createTexture();
132
+ this.valid = Boolean(this.texture_factory || this.texture);
133
+ this.bind();
134
+
135
+ this.name = name;
136
+ this.retain_count = 0;
137
+ this.config_type = null;
138
+ this.loading = null; // a Promise object to track the loading state of this texture
139
+ this.loaded = false; // successfully loaded as expected
140
+ this.filtering = options.filtering;
141
+ this.density = options.density || 1; // native pixel density of texture
142
+ this.sprites = options.sprites;
143
+ this.texcoords = {}; // sprite UVs ([0, 1] range)
144
+ this.sizes = {}; // sprite sizes (pixel size)
145
+ this.css_sizes = {}; // sprite sizes, adjusted for native texture pixel density
146
+ this.aspects = {}; // sprite aspect ratios
147
+
148
+ // Default to a 1-pixel transparent black texture so we can safely render while we wait for an image to load
149
+ // See: http://stackoverflow.com/questions/19722247/webgl-wait-for-texture-to-load
150
+ this.setData(1, 1, new Uint8Array([0, 0, 0, 0]), { filtering: 'nearest' });
151
+ // The placeholder's sampler is temporary. Resource uploads happen before
152
+ // setFiltering(), so do not let it become the loaded image's default.
153
+ this.filtering = options.filtering;
154
+ this.loaded = false; // don't consider loaded when only placeholder data is present
155
+
156
+ // Destroy previous texture if present
157
+ const previousTexture = Texture.textures[this.name];
158
+ if (previousTexture instanceof Texture) {
159
+ // Preserve previous retain count
160
+ this.retain_count = previousTexture.retain_count;
161
+ previousTexture.retain_count = 0; // allow to be freed
162
+ previousTexture.destroy();
163
+ }
164
+
165
+ // Cache texture instance and definition
166
+ Texture.textures[this.name] = this;
167
+ Texture.texture_configs[this.name] = JSON.stringify(Object.assign({ name }, options));
168
+
169
+ this.load(options);
170
+ log('trace', `creating Texture ${this.name}`);
171
+ }
172
+
173
+ // Destroy a single texture instance
174
+ destroy(options: {force?: boolean} = {}) {
175
+ if (this.texture_factory) {
176
+ return this.destroyTexture(options);
177
+ }
178
+ return Context.withContext(this.gl, () => this.destroyTexture(options));
179
+ }
180
+
181
+ destroyTexture({ force }: {force?: boolean} = {}) {
182
+ if (this.retain_count > 0 && !force) {
183
+ log('error', `Texture '${this.name}': destroying texture with retain count of '${this.retain_count}'`);
184
+ return;
185
+ }
186
+
187
+ if (!this.valid) {
188
+ return;
189
+ }
190
+ if (this.texture_resource) {
191
+ this.texture_resource.destroy();
192
+ this.texture_resource = null;
193
+ }
194
+ else {
195
+ getClassicTextureContext(this.gl).deleteTexture(this.texture);
196
+ }
197
+ this.texture = null;
198
+ if (Texture.textures[this.name] === this) {
199
+ delete Texture.textures[this.name];
200
+ delete Texture.texture_configs[this.name];
201
+ }
202
+ this.valid = false;
203
+ log('trace', `destroying Texture ${this.name}`);
204
+ }
205
+
206
+ retain () {
207
+ this.retain_count++;
208
+ }
209
+
210
+ release () {
211
+ if (this.retain_count <= 0) {
212
+ log('error', `Texture '${this.name}': releasing texture with retain count of '${this.retain_count}'`);
213
+ }
214
+
215
+ this.retain_count--;
216
+ if (this.retain_count <= 0) {
217
+ this.destroy();
218
+ }
219
+ }
220
+
221
+ bind(unit = 0) {
222
+ if (!this.valid) {
223
+ return;
224
+ }
225
+
226
+ if (this.texture_factory) {
227
+ return;
228
+ }
229
+
230
+ const gl = getClassicTextureContext(this.gl);
231
+ if (Texture.activeUnit !== unit) {
232
+ gl.activeTexture(gl.TEXTURE0 + unit);
233
+ Texture.activeUnit = unit;
234
+ Texture.boundTexture = null; // texture must be re-bound when unit changes
235
+ }
236
+
237
+ if (Texture.boundTexture !== this.texture) {
238
+ gl.bindTexture(gl.TEXTURE_2D, this.texture);
239
+ Texture.boundTexture = this.texture;
240
+ }
241
+ }
242
+
243
+ load(options?: TextureOptions): Promise<Texture> {
244
+ if (!options) {
245
+ return this.loading || Promise.resolve(this);
246
+ }
247
+
248
+ this.loading = null;
249
+ if (typeof options.url === 'string') {
250
+ this.config_type = 'url';
251
+ this.setUrl(options.url, options);
252
+ } else if (options.element) {
253
+ this.config_type = 'element';
254
+ this.setElement(options.element, options);
255
+ } else if (options.data && options.width && options.height) {
256
+ this.config_type = 'data';
257
+ this.setData(options.width, options.height, options.data, options);
258
+ }
259
+
260
+ this.loading =
261
+ (this.getLoadingPromise()?.then(() => { this.calculateSprites(); return this; })) ||
262
+ Promise.resolve(this);
263
+ return this.loading;
264
+ }
265
+
266
+ // Sets texture from an url
267
+ setUrl(url: string, options: TextureOptions = {}) {
268
+ if (!this.valid) {
269
+ return;
270
+ }
271
+
272
+ this.url = url; // save URL reference (will be overwritten when element is loaded below)
273
+
274
+ this.loading = new Promise(resolve => {
275
+ let image = new Image();
276
+ image.onload = () => {
277
+ try {
278
+ // For data URL images, first draw the image to a separate canvas element. Workaround for
279
+ // obscure bug seen with small (<28px) SVG images encoded as data URLs in Chrome and Safari.
280
+ if (this.url.slice(0, 5) === 'data:') {
281
+ const canvas = document.createElement('canvas');
282
+ const ctx = canvas.getContext('2d');
283
+ canvas.width = image.width;
284
+ canvas.height = image.height;
285
+ if (!ctx) throw new Error('Texture canvas requires a 2D context');
286
+ ctx.drawImage(image, 0, 0);
287
+ this.setElement(canvas, options);
288
+ }
289
+ else {
290
+ this.setElement(image, options);
291
+ }
292
+ }
293
+ catch (e) {
294
+ this.loaded = false;
295
+ log('warn', `Texture '${this.name}': failed to load url: '${this.url}'`, e, options);
296
+ Texture.trigger('warning', { message: `Failed to load texture from ${this.url}`, error: e, texture: options });
297
+ }
298
+
299
+ this.loaded = true;
300
+ resolve(this);
301
+ };
302
+ image.onerror = e => {
303
+ // Warn and resolve on error
304
+ this.loaded = false;
305
+ log('warn', `Texture '${this.name}': failed to load url: '${this.url}'`, e, options);
306
+ Texture.trigger('warning', { message: `Failed to load texture from ${this.url}`, error: e, texture: options });
307
+ resolve(this);
308
+ };
309
+
310
+ // Safari has a bug loading data-URL images with CORS enabled, so it must be disabled in that case
311
+ // https://bugs.webkit.org/show_bug.cgi?id=123978
312
+ if (!(Utils.isSafari() && this.url.slice(0, 5) === 'data:')) {
313
+ image.crossOrigin = 'anonymous';
314
+ }
315
+
316
+ image.src = this.url;
317
+ });
318
+ return this.loading;
319
+ }
320
+
321
+ // Sets texture to a raw image buffer
322
+ setData(width: number, height: number, data: TextureOptions['data'], options: TextureOptions = {}) {
323
+ this.width = width;
324
+ this.height = height;
325
+
326
+ // Convert regular array to typed array
327
+ if (Array.isArray(data)) {
328
+ data = new Uint8Array(data);
329
+ }
330
+
331
+ this.update(data ?? null, options);
332
+ this.setFiltering(options);
333
+
334
+ this.loaded = true;
335
+ this.loading = Promise.resolve(this);
336
+ return this.loading;
337
+ }
338
+
339
+ // Sets the texture to track a element (canvas/image)
340
+ setElement(element: string | TextureElement, options: TextureOptions = {}) {
341
+ const originalElement = element;
342
+
343
+ // a string element is interpeted as a CSS selector
344
+ const resolvedElement = typeof element === 'string' ? document.querySelector(element) : element;
345
+
346
+ if (resolvedElement instanceof HTMLCanvasElement ||
347
+ resolvedElement instanceof HTMLImageElement ||
348
+ resolvedElement instanceof HTMLVideoElement) {
349
+ this.update(resolvedElement, options);
350
+ this.setFiltering(options);
351
+ }
352
+ else {
353
+ this.loaded = false;
354
+ let msg = `the 'element' parameter (\`element: ${JSON.stringify(originalElement)}\`) must be a CSS `;
355
+ msg += 'selector string, or a <canvas>, <image> or <video> object';
356
+ log('warn', `Texture '${this.name}': ${msg}`, options);
357
+ Texture.trigger('warning', { message: `Failed to load texture because ${msg}`, texture: options });
358
+ }
359
+
360
+ this.loaded = true;
361
+ this.loading = Promise.resolve(this);
362
+ return this.loading;
363
+ }
364
+
365
+ // Uploads current image or buffer to the GPU (can be used to update animated textures on the fly)
366
+ update(source: TextureSource, options: TextureOptions = {}) {
367
+ if (this.texture_factory) {
368
+ return this.updateTexture(source, options);
369
+ }
370
+ return Context.withContext(this.gl, () => {
371
+ if (Context.hasContextScope(this.gl)) {
372
+ Texture.resetBindings();
373
+ }
374
+ return this.updateTexture(source, options);
375
+ });
376
+ }
377
+
378
+ updateTexture(source: TextureSource, options: TextureOptions = {}) {
379
+ if (!this.valid) {
380
+ return;
381
+ }
382
+
383
+ if (this.texture_factory) {
384
+ return this.updateTextureResource(source, options);
385
+ }
386
+
387
+ this.bind();
388
+
389
+ // Image or Canvas element
390
+ const gl = getClassicTextureContext(this.gl);
391
+ if (source instanceof HTMLCanvasElement || source instanceof HTMLVideoElement ||
392
+ (source instanceof HTMLImageElement && source.complete)) {
393
+
394
+ this.width = source.width;
395
+ this.height = source.height;
396
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, (options.UNPACK_FLIP_Y_WEBGL === false ? false : true));
397
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, options.UNPACK_PREMULTIPLY_ALPHA_WEBGL || false);
398
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
399
+ }
400
+ // Raw image buffer
401
+ else {
402
+ // these pixel store params are deprecated for non-DOM element uploads
403
+ // (e.g. when creating texture from raw data)
404
+ // setting them to null avoids a Firefox warning
405
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
406
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
407
+ if (source instanceof HTMLImageElement) throw new Error('Texture image must finish loading before upload');
408
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, source);
409
+ }
410
+
411
+ Texture.trigger('update', this);
412
+ }
413
+
414
+ // Replace the injected GPU texture resource because luma.gl texture dimensions are immutable.
415
+ updateTextureResource(source: TextureSource, options: TextureOptions = {}) {
416
+ if (isTextureElement(source)) {
417
+ this.width = source.width;
418
+ this.height = source.height;
419
+ }
420
+
421
+ const power_of_2 = Utils.isPowerOf2(this.width) && Utils.isPowerOf2(this.height);
422
+ const filtering = options.filtering === 'mipmap' && !power_of_2 ?
423
+ 'linear' : (options.filtering || this.filtering || 'linear');
424
+
425
+ const factory = this.texture_factory;
426
+ if (!factory) throw new Error('Texture resource update requires a texture factory');
427
+ const resource = factory({
428
+ id: this.name,
429
+ width: this.width,
430
+ height: this.height,
431
+ data: source ?? undefined,
432
+ filtering,
433
+ repeat: options.repeat === true && power_of_2,
434
+ flipY: options.UNPACK_FLIP_Y_WEBGL !== false,
435
+ premultipliedAlpha: options.UNPACK_PREMULTIPLY_ALPHA_WEBGL === true
436
+ });
437
+ if (!resource || typeof resource.destroy !== 'function') {
438
+ throw new Error(`Texture '${this.name}': texture factory must return a GPU resource`);
439
+ }
440
+
441
+ const previous_resource = this.texture_resource;
442
+ this.texture_resource = resource;
443
+ this.texture = resource;
444
+ if (previous_resource) {
445
+ previous_resource.destroy();
446
+ }
447
+ Texture.trigger('update', this);
448
+ }
449
+
450
+ // Return the injected GPU resource for renderers that own texture bindings.
451
+ getResource() {
452
+ return this.texture_resource;
453
+ }
454
+
455
+ // Determines appropriate filtering mode
456
+ setFiltering(options: TextureOptions = {}) {
457
+ if (this.texture_factory) {
458
+ return this.updateFiltering(options);
459
+ }
460
+ return Context.withContext(this.gl, () => {
461
+ if (Context.hasContextScope(this.gl)) {
462
+ Texture.resetBindings();
463
+ }
464
+ return this.updateFiltering(options);
465
+ });
466
+ }
467
+
468
+ updateFiltering(options: TextureOptions = {}) {
469
+ if (!this.valid) {
470
+ return;
471
+ }
472
+
473
+ options.filtering = options.filtering || 'linear';
474
+
475
+ if (this.texture_resource) {
476
+ this.power_of_2 = Utils.isPowerOf2(this.width) && Utils.isPowerOf2(this.height);
477
+ this.filtering = options.filtering === 'mipmap' && !this.power_of_2 ?
478
+ 'linear' : options.filtering;
479
+ Texture.trigger('update', this);
480
+ return;
481
+ }
482
+
483
+ const gl = getClassicTextureContext(this.gl);
484
+ this.bind();
485
+
486
+ // For power-of-2 textures, the following presets are available:
487
+ // mipmap: linear blend from nearest mip
488
+ // linear: linear blend from original image (no mips)
489
+ // nearest: nearest pixel from original image (no mips, 'blocky' look)
490
+ if (Utils.isPowerOf2(this.width) && Utils.isPowerOf2(this.height)) {
491
+ this.power_of_2 = true;
492
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, options.TEXTURE_WRAP_S || (options.repeat && gl.REPEAT) || gl.CLAMP_TO_EDGE);
493
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, options.TEXTURE_WRAP_T || (options.repeat && gl.REPEAT) || gl.CLAMP_TO_EDGE);
494
+
495
+ if (options.filtering === 'mipmap') {
496
+ this.filtering = 'mipmap';
497
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); // TODO: use trilinear filtering by defualt instead?
498
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
499
+ gl.generateMipmap(gl.TEXTURE_2D);
500
+ }
501
+ else if (options.filtering === 'linear') {
502
+ this.filtering = 'linear';
503
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
504
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
505
+ }
506
+ else if (options.filtering === 'nearest') {
507
+ this.filtering = 'nearest';
508
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
509
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
510
+ }
511
+ }
512
+ else {
513
+ // WebGL has strict requirements on non-power-of-2 textures:
514
+ // No mipmaps and must clamp to edge
515
+ this.power_of_2 = false;
516
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
517
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
518
+
519
+ if (options.filtering === 'nearest') {
520
+ this.filtering = 'nearest';
521
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
522
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
523
+ }
524
+ else { // default to linear for non-power-of-2 textures
525
+ this.filtering = 'linear';
526
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
527
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
528
+ }
529
+ }
530
+
531
+ Texture.trigger('update', this);
532
+ }
533
+
534
+ // Pre-calc sprite regions for a texture sprite in UV [0, 1] space
535
+ calculateSprites() {
536
+ if (this.sprites) {
537
+ for (let s in this.sprites) {
538
+ let sprite = this.sprites[s];
539
+
540
+ // Map [0, 0] to [1, 1] coords to the appropriate sprite sub-area of the texture
541
+ this.texcoords[s] = Texture.getTexcoordsForSprite(
542
+ [sprite[0], sprite[1]],
543
+ [sprite[2], sprite[3]],
544
+ [this.width, this.height]
545
+ );
546
+
547
+ // Pixel size of sprite
548
+ // Divide by native texture density to get correct CSS pixels
549
+ this.sizes[s] = [sprite[2], sprite[3]];
550
+ this.css_sizes[s] = [sprite[2] / this.density, sprite[3] / this.density];
551
+ this.aspects[s] = sprite[2] / sprite[3];
552
+ }
553
+ }
554
+ }
555
+
556
+ // Get the tetxure size in bytes
557
+ byteSize() {
558
+ // mipmaps use 33% additional memory
559
+ return Math.round(this.width * this.height * 4 * (this.filtering == 'mipmap' ? 1.33 : 1));
560
+ }
561
+
562
+ /** Observe asynchronous updates assigned by load helpers without narrowing them to the prior null value. */
563
+ private getLoadingPromise(): Promise<Texture> | null { return this.loading; }
564
+
565
+ }
566
+
567
+
568
+ // Static/class methods and state
569
+
570
+ Texture.create = function (gl, name, options) {
571
+ return new Texture(gl, name, options);
572
+ };
573
+
574
+ Texture.retain = function (name) {
575
+ const texture = Texture.textures[name];
576
+ if (texture && 'retain' in texture) {
577
+ texture.retain();
578
+ }
579
+ };
580
+
581
+ Texture.release = function (name) {
582
+ const texture = Texture.textures[name];
583
+ if (texture && 'release' in texture) {
584
+ texture.release();
585
+ }
586
+ };
587
+
588
+ // Destroy all texture instances for a given GL context
589
+ Texture.destroy = function (gl) {
590
+ var textures = Object.keys(Texture.textures);
591
+ textures.forEach(t => {
592
+ var texture = Texture.textures[t];
593
+ if ('gl' in texture && texture.gl === gl) {
594
+ texture.destroy({ force: true });
595
+ }
596
+ });
597
+ };
598
+
599
+ // Get sprite pixel size and UVs
600
+ Texture.getSpriteInfo = function (texname, sprite) {
601
+ let texture = Texture.textures[texname];
602
+ return texture && {
603
+ size: texture.sizes[sprite],
604
+ css_size: texture.css_sizes[sprite],
605
+ aspect: texture.aspects[sprite],
606
+ texcoords: texture.texcoords[sprite]
607
+ };
608
+ };
609
+
610
+ // Re-scale UVs from [0, 1] range to a smaller area within the image
611
+ Texture.getTexcoordsForSprite = function (area_origin, area_size, tex_size) {
612
+ var area_origin_y = tex_size[1] - area_origin[1] - area_size[1];
613
+
614
+ return [
615
+ area_origin[0] / tex_size[0],
616
+ area_origin_y / tex_size[1],
617
+ (area_size[0] + area_origin[0]) / tex_size[0],
618
+ (area_size[1] + area_origin_y) / tex_size[1]
619
+ ];
620
+ };
621
+
622
+ // Create a set of textures keyed in an object
623
+ // Optionally load each if it has a URL specified
624
+ Texture.createFromObject = function (gl, textures) {
625
+ let loading = [];
626
+ if (textures) {
627
+ for (let texname in textures) {
628
+ let config = textures[texname];
629
+
630
+ if (config.skip_create) {
631
+ // explicitly skip (re-)creating this texture
632
+ // used for dynamic canvas textures that we *know* haven't changed
633
+ // (internal raster tiles, vs. user-supplied canvas where pixels may have changed)
634
+ continue;
635
+ }
636
+
637
+ // If texture already exists and definition hasn't changed, no need to re-create
638
+ // Note: to avoid flicker when other textures/scene items change
639
+ if (!Texture.changed(texname, config)) {
640
+ continue;
641
+ }
642
+
643
+ let texture = Texture.create(gl, texname, config);
644
+ loading.push(texture.loading);
645
+ }
646
+ }
647
+ return Promise.all(loading);
648
+ };
649
+
650
+ // Create a 'default' texture (1x1 pixel) that can be used as a placeholder
651
+ // (for example to prevent GL from complaining about unbound textures)
652
+ Texture.default = '__default';
653
+ Texture.createDefault = function (gl) {
654
+ return Texture.create(gl, Texture.default);
655
+ };
656
+
657
+ // Only include texture-specific properties (avoid faulty equality comparisons between
658
+ // textures when caller may include other ancillary props)
659
+ Texture.sliceOptions = function(options) {
660
+ return {
661
+ filtering: options.filtering,
662
+ sprites: options.sprites,
663
+ url: options.url,
664
+ element: options.element,
665
+ data: options.data,
666
+ width: options.width,
667
+ height: options.height,
668
+ density: options.density,
669
+ repeat: options.repeat,
670
+ TEXTURE_WRAP_S: options.TEXTURE_WRAP_S,
671
+ TEXTURE_WRAP_T: options.TEXTURE_WRAP_T,
672
+ UNPACK_FLIP_Y_WEBGL: options.UNPACK_FLIP_Y_WEBGL,
673
+ UNPACK_PREMULTIPLY_ALPHA_WEBGL: options.UNPACK_PREMULTIPLY_ALPHA_WEBGL,
674
+ textureFactory: options.textureFactory
675
+ };
676
+ };
677
+
678
+ // Indicate if a texture definition would be a change from the current cache
679
+ Texture.changed = function (name, config) {
680
+ let texture = Texture.textures[name];
681
+ if (texture) { // cached texture
682
+ // canvas/image-based textures are considered dynamic and always refresh
683
+ if ('config_type' in texture && texture.config_type === 'element' || config.element != null) {
684
+ return true;
685
+ }
686
+
687
+ // compare definitions
688
+ config = Texture.sliceOptions(config); // exclude any non-texture-specific props
689
+ if (Texture.texture_configs[name] === JSON.stringify(Object.assign({ name }, config))) {
690
+ return false;
691
+ }
692
+ }
693
+ return true;
694
+ };
695
+
696
+ // Get metadata for a texture by name
697
+ // Returns via promise, in case texture is still loading
698
+ // Can be called on main thread from worker, to sync texture info to worker
699
+ /** Return one detached metadata record or the requested list, preserving nullable missing names. */
700
+ function getTextureInfo(name?: string | string[] | null): Promise<TextureInfoResult> {
701
+ // Get info for all textures by default
702
+ if (!name) {
703
+ name = Object.keys(Texture.textures);
704
+ }
705
+
706
+ // Get multiple textures
707
+ if (Array.isArray(name)) {
708
+ return Promise.all(name.map(n => Texture.getInfo(n)));
709
+ }
710
+
711
+ // Get single texture
712
+ var tex = Texture.textures[name];
713
+ if (tex) {
714
+ // Wait for this texture to finish loading, or return immediately
715
+ var loading = ('loading' in tex && tex.loading) || Promise.resolve(tex);
716
+ return loading.then(() => {
717
+ // Return a subset of texture info
718
+ // (compatible w/structured cloning, suitable for passing to a worker)
719
+ return {
720
+ name: tex.name,
721
+ width: tex.width,
722
+ height: tex.height,
723
+ density: tex.density,
724
+ css_size: [ tex.width / tex.density, tex.height / tex.density ],
725
+ aspect: tex.width / tex.height,
726
+ sprites: tex.sprites,
727
+ texcoords: tex.texcoords,
728
+ sizes: tex.sizes,
729
+ css_sizes: tex.css_sizes,
730
+ aspects: tex.aspects,
731
+ filtering: tex.filtering,
732
+ power_of_2: tex.power_of_2,
733
+ valid: tex.valid
734
+ };
735
+ });
736
+ }
737
+ else {
738
+ // No texture found
739
+ return Promise.resolve(null);
740
+ }
741
+ }
742
+ Texture.getInfo = getTextureInfo;
743
+
744
+ // Sync texture info to worker
745
+ // Called from worker, gets info on one or more textures info from main thread via remote call, then stores it
746
+ // locally in worker. 'textures' can be an array of texture names to sync, or if null, all textures are synced.
747
+ Texture.syncTexturesToWorker = function (names) {
748
+ return WorkerBroker.postMessage('Texture.getInfo', names).
749
+ then(textures => {
750
+ if (Array.isArray(textures)) {
751
+ textures
752
+ .filter(isTextureInfo) // remove nulls and validate detached metadata
753
+ .forEach(t => Texture.textures[t.name] = t);
754
+ }
755
+ return Texture.textures;
756
+ });
757
+ };
758
+
759
+ // Report max texture size for a GL context
760
+ Texture.getMaxTextureSize = function (gl) {
761
+ const context = getClassicTextureContext(gl);
762
+ return context.getParameter(context.MAX_TEXTURE_SIZE);
763
+ };
764
+
765
+ // Register an injected GPU texture factory for all textures created against a context.
766
+ Texture.setResourceFactory = function (gl, texture_factory) {
767
+ if (texture_factory) {
768
+ Texture.resource_factories.set(gl, texture_factory);
769
+ }
770
+ };
771
+
772
+ Texture.getResourceFactory = function (gl) {
773
+ return Texture.resource_factories.get(gl);
774
+ };
775
+
776
+ Texture.clearResourceFactory = function (gl) {
777
+ Texture.resource_factories.delete(gl);
778
+ };
779
+
780
+ // Global set of textures, by name
781
+ Texture.textures = {};
782
+ Texture.texture_configs = {};
783
+ Texture.resource_factories = new WeakMap();
784
+ Texture.boundTexture = null;
785
+ Texture.activeUnit = null;
786
+ Texture.resetBindings = function () {
787
+ Texture.boundTexture = null;
788
+ Texture.activeUnit = null;
789
+ };
790
+
791
+ WorkerBroker.addTarget('Texture', Texture);
792
+ subscribeMixin(Texture);
793
+
794
+ /** The broker returns unknown; recognize detached metadata, never invent live GPU methods. */
795
+ function isTextureInfo(value: unknown): value is TextureInfo {
796
+ return typeof value === 'object' && value !== null &&
797
+ 'name' in value && typeof value.name === 'string' &&
798
+ 'width' in value && typeof value.width === 'number' &&
799
+ 'height' in value && typeof value.height === 'number' &&
800
+ 'density' in value && typeof value.density === 'number' &&
801
+ 'valid' in value && typeof value.valid === 'boolean' &&
802
+ ['texcoords', 'sizes', 'css_sizes', 'aspects'].every(key => typeof Reflect.get(value, key) === 'object' && Reflect.get(value, key) !== null);
803
+ }
804
+
805
+ /** Portable contexts are opaque/null; only the classic path may require texture GL operations. */
806
+ function getClassicTextureContext(context: object | null): WebGLRenderingContext {
807
+ if (isClassicTextureContext(context)) return context;
808
+ throw new Error('Classic textures require a WebGL context');
809
+ }
810
+ /** Narrow the classic texture capability boundary while allowing hermetic structural GL fixtures. */
811
+ function isClassicTextureContext(context: object | null): context is WebGLRenderingContext {
812
+ return context !== null && 'createTexture' in context && typeof context.createTexture === 'function' &&
813
+ 'texImage2D' in context && typeof context.texImage2D === 'function';
814
+ }