@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.
- package/README.md +59 -2
- package/dist/core.js +36923 -0
- package/dist/core.js.map +6 -0
- package/dist/index.js +32 -0
- package/dist/loaders-gl-worker.js +82 -0
- package/dist/loaders-gl-worker.js.map +6 -0
- package/dist/map-logic.js +2 -0
- package/dist/map-logic.js.map +6 -0
- package/dist/projected-basemaps-worker.js +3 -0
- package/dist/projected-basemaps-worker.js.map +6 -0
- package/dist/shader-modules.js +141 -0
- package/dist/shader-modules.js.map +6 -0
- package/dist/style-schema.js +126 -0
- package/dist/style-schema.js.map +6 -0
- package/dist/tangram-style.schema.json +882 -0
- package/dist/tangram.debug.mjs +37139 -0
- package/dist/tangram.debug.mjs.map +6 -0
- package/dist/tangram.min.mjs +4950 -0
- package/dist/types/builders/earcut.d.ts +15 -0
- package/dist/types/core.d.ts +29 -0
- package/dist/types/gl/constants.d.ts +2 -0
- package/dist/types/gl/globe_mesh.d.ts +48 -0
- package/dist/types/gl/glsl.d.ts +17 -0
- package/dist/types/gl/vertex-constants.d.ts +4 -0
- package/dist/types/gl/vertex-types.d.ts +34 -0
- package/dist/types/gl/vertex_data.d.ts +32 -0
- package/dist/types/gl/vertex_elements.d.ts +8 -0
- package/dist/types/gl/vertex_layout.d.ts +32 -0
- package/dist/types/glsl.d.ts +14 -0
- package/dist/types/gpu/luma_device_renderer.d.ts +66 -0
- package/dist/types/gpu/resource_lifecycle.d.ts +4 -0
- package/dist/types/gpu/tangram_gpu_backend.d.ts +151 -0
- package/dist/types/index.d.ts +155 -0
- package/dist/types/lights/light-definitions.d.ts +121 -0
- package/dist/types/map-logic/draped-terrain-bounds.d.ts +41 -0
- package/dist/types/map-logic/globe-terrain-preparation.d.ts +85 -0
- package/dist/types/map-logic/globe-terrain.d.ts +54 -0
- package/dist/types/map-logic/index.d.ts +33 -0
- package/dist/types/map-logic/label-identity.d.ts +22 -0
- package/dist/types/map-logic/label-placement.d.ts +32 -0
- package/dist/types/map-logic/screen-bounds.d.ts +15 -0
- package/dist/types/map-logic/terrain-data.d.ts +55 -0
- package/dist/types/map-logic/terrain-error.d.ts +51 -0
- package/dist/types/map-logic/terrain-ground-topology.d.ts +26 -0
- package/dist/types/map-logic/terrain-height-field.d.ts +59 -0
- package/dist/types/map-logic/terrain-navigation.d.ts +27 -0
- package/dist/types/map-logic/terrain-neighborhood.d.ts +5 -0
- package/dist/types/map-logic/terrain-query.d.ts +33 -0
- package/dist/types/map-logic/terrain-resource-budget.d.ts +51 -0
- package/dist/types/map-logic/terrain-transition-topology.d.ts +23 -0
- package/dist/types/map-logic/tile-build-queue.d.ts +51 -0
- package/dist/types/map-logic/tile-cache-policy.d.ts +46 -0
- package/dist/types/map-logic/tile-residency.d.ts +17 -0
- package/dist/types/map-logic/triangle-query-index.d.ts +87 -0
- package/dist/types/procedures/js-yaml.d.ts +15 -0
- package/dist/types/procedures/mesh-projector.d.ts +107 -0
- package/dist/types/procedures/mvt-dependencies.d.ts +20 -0
- package/dist/types/procedures/projected-coordinate-transform.d.ts +27 -0
- package/dist/types/procedures/projection-batch-executor.d.ts +53 -0
- package/dist/types/procedures/scene-yaml-legacy.d.ts +6 -0
- package/dist/types/procedures/terrain-draping.d.ts +165 -0
- package/dist/types/procedures/terrain-globe.d.ts +6 -0
- package/dist/types/procedures/terrain-styled-topology.d.ts +34 -0
- package/dist/types/procedures/web-mercator-legacy.d.ts +4 -0
- package/dist/types/procedures/web-mercator-math.d.ts +4 -0
- package/dist/types/scene/globals.d.ts +11 -0
- package/dist/types/scene/ground_footprint.d.ts +16 -0
- package/dist/types/scene/host_frame.d.ts +54 -0
- package/dist/types/scene/projection_math.d.ts +52 -0
- package/dist/types/scene/scene-resource-types.d.ts +177 -0
- package/dist/types/scene/scene_bundle.d.ts +69 -0
- package/dist/types/scene/visibility_adapter.d.ts +106 -0
- package/dist/types/selection/draped-terrain-surface.d.ts +78 -0
- package/dist/types/selection/terrain_surface.d.ts +149 -0
- package/dist/types/shader-modules/globe-horizon.d.ts +15 -0
- package/dist/types/shader-modules/height-decode.d.ts +13 -0
- package/dist/types/shader-modules/hillshade.d.ts +13 -0
- package/dist/types/shader-modules/index.d.ts +6 -0
- package/dist/types/shader-modules/planar.d.ts +12 -0
- package/dist/types/shader-modules/sphere-map.d.ts +13 -0
- package/dist/types/shader-modules/triplanar.d.ts +15 -0
- package/dist/types/sources/decoded_tile_store.d.ts +95 -0
- package/dist/types/sources/tile_source_metadata.d.ts +32 -0
- package/dist/types/styles/text/fontfaceobserver.d.ts +10 -0
- package/dist/types/tile/tile_id.d.ts +37 -0
- package/dist/types/tile/tile_traversal_adapter.d.ts +63 -0
- package/dist/types/types.d.ts +293 -0
- package/dist/types/utils/geo.d.ts +57 -0
- package/dist/types/utils/hash.d.ts +1 -0
- package/dist/types/utils/log.d.ts +27 -0
- package/dist/types/utils/props.d.ts +6 -0
- package/dist/types/utils/subscribe.d.ts +10 -0
- package/dist/types/utils/thread.d.ts +6 -0
- package/dist/types/utils/urls.d.ts +26 -0
- package/dist/types/utils/utils.d.ts +26 -0
- package/dist/types/utils/version.d.ts +2 -0
- package/dist/types/utils/worker-types.d.ts +68 -0
- package/dist/types/utils/worker_broker.d.ts +3 -0
- package/package.json +103 -3
- package/src/builders/common.ts +41 -0
- package/src/builders/earcut.d.ts +15 -0
- package/src/builders/geometry-types.ts +60 -0
- package/src/builders/points.ts +115 -0
- package/src/builders/polygons.ts +225 -0
- package/src/builders/polylines.ts +682 -0
- package/src/builders/wireframe.ts +31 -0
- package/src/core.d.ts +29 -0
- package/src/core.ts +33 -0
- package/src/experimental/loaders-gl-worker.ts +19 -0
- package/src/experimental/projected-basemaps-worker.ts +13 -0
- package/src/experimental/projected-mesh-cache.ts +137 -0
- package/src/experimental/projected-mesh-domain.ts +110 -0
- package/src/experimental/projected-mesh-refinement.ts +193 -0
- package/src/experimental/projected-mesh.ts +399 -0
- package/src/experimental/projected-normal.ts +26 -0
- package/src/experimental/projected-symbol-mesh.ts +74 -0
- package/src/gl/constants.ts +18 -0
- package/src/gl/context.ts +103 -0
- package/src/gl/extensions.ts +30 -0
- package/src/gl/globe_mesh.ts +285 -0
- package/src/gl/glsl.ts +257 -0
- package/src/gl/mesh-types.ts +56 -0
- package/src/gl/render_state.ts +111 -0
- package/src/gl/shader_program.ts +1310 -0
- package/src/gl/texture.ts +814 -0
- package/src/gl/uniform_buffer.ts +340 -0
- package/src/gl/vao.ts +154 -0
- package/src/gl/vbo_mesh.ts +349 -0
- package/src/gl/vertex-constants.ts +19 -0
- package/src/gl/vertex-types.ts +35 -0
- package/src/gl/vertex_data.ts +126 -0
- package/src/gl/vertex_elements.ts +54 -0
- package/src/gl/vertex_layout.ts +241 -0
- package/src/glsl.d.ts +14 -0
- package/src/gpu/luma_device_renderer.ts +579 -0
- package/src/gpu/pixel_readback.ts +51 -0
- package/src/gpu/resource_lifecycle.ts +32 -0
- package/src/gpu/tangram_gpu_backend.ts +195 -0
- package/src/index.d.ts +155 -0
- package/src/index.ts +105 -0
- package/src/labels/collision-types.ts +94 -0
- package/src/labels/collision.ts +273 -0
- package/src/labels/collision_grid.ts +61 -0
- package/src/labels/intersect.ts +30 -0
- package/src/labels/label-types.ts +91 -0
- package/src/labels/label.ts +207 -0
- package/src/labels/label_line.ts +801 -0
- package/src/labels/label_point.ts +131 -0
- package/src/labels/main-pass-types.ts +73 -0
- package/src/labels/main_pass.ts +221 -0
- package/src/labels/point_anchor.ts +100 -0
- package/src/labels/point_placement.ts +150 -0
- package/src/labels/projected-pass.ts +259 -0
- package/src/labels/repeat_group.ts +72 -0
- package/src/lights/ambient_light.glsl +18 -0
- package/src/lights/directional_light.glsl +39 -0
- package/src/lights/geographic-lights.ts +21 -0
- package/src/lights/light-definitions.ts +285 -0
- package/src/lights/light.ts +590 -0
- package/src/lights/lighting-uniforms.ts +80 -0
- package/src/lights/lighting-wgsl.ts +116 -0
- package/src/lights/material.glsl +237 -0
- package/src/lights/material.ts +145 -0
- package/src/lights/native-falloff.ts +37 -0
- package/src/lights/point_light.glsl +96 -0
- package/src/lights/spot_light.glsl +111 -0
- package/src/map-logic/draped-terrain-bounds.ts +71 -0
- package/src/map-logic/globe-terrain-preparation.ts +217 -0
- package/src/map-logic/globe-terrain.ts +296 -0
- package/src/map-logic/index.ts +37 -0
- package/src/map-logic/label-identity.ts +36 -0
- package/src/map-logic/label-placement.ts +117 -0
- package/src/map-logic/screen-bounds.ts +34 -0
- package/src/map-logic/terrain-data.ts +107 -0
- package/src/map-logic/terrain-error.ts +134 -0
- package/src/map-logic/terrain-ground-topology.ts +164 -0
- package/src/map-logic/terrain-height-field.ts +254 -0
- package/src/map-logic/terrain-navigation.ts +50 -0
- package/src/map-logic/terrain-neighborhood.ts +43 -0
- package/src/map-logic/terrain-query.ts +51 -0
- package/src/map-logic/terrain-resource-budget.ts +124 -0
- package/src/map-logic/terrain-transition-topology.ts +164 -0
- package/src/map-logic/tile-build-queue.ts +110 -0
- package/src/map-logic/tile-cache-policy.ts +89 -0
- package/src/map-logic/tile-residency.ts +50 -0
- package/src/map-logic/triangle-query-index.ts +265 -0
- package/src/procedures/js-yaml.d.ts +15 -0
- package/src/procedures/mesh-projector.ts +191 -0
- package/src/procedures/mlt-loaders.ts +55 -0
- package/src/procedures/mvt-dependencies.d.ts +20 -0
- package/src/procedures/mvt-legacy.ts +150 -0
- package/src/procedures/mvt-loaders.ts +53 -0
- package/src/procedures/mvt-parser.ts +48 -0
- package/src/procedures/mvt-properties.ts +36 -0
- package/src/procedures/mvt-tile-provider.ts +72 -0
- package/src/procedures/pmtiles-loader.ts +75 -0
- package/src/procedures/projected-coordinate-transform.ts +79 -0
- package/src/procedures/projection-batch-executor.ts +140 -0
- package/src/procedures/scene-yaml-legacy.ts +16 -0
- package/src/procedures/scene-yaml-loaders.ts +10 -0
- package/src/procedures/terrain-draping.ts +713 -0
- package/src/procedures/terrain-globe.ts +32 -0
- package/src/procedures/terrain-styled-topology.ts +184 -0
- package/src/procedures/web-mercator-legacy.ts +24 -0
- package/src/procedures/web-mercator-math.ts +50 -0
- package/src/scene/camera.ts +298 -0
- package/src/scene/camera_base.ts +116 -0
- package/src/scene/classic_scene.ts +20 -0
- package/src/scene/external_camera.ts +101 -0
- package/src/scene/globals.ts +120 -0
- package/src/scene/ground_footprint.ts +139 -0
- package/src/scene/host_frame.ts +326 -0
- package/src/scene/projected_tile_lod.ts +125 -0
- package/src/scene/projection_math.ts +142 -0
- package/src/scene/projection_shaders.ts +183 -0
- package/src/scene/renderer.ts +516 -0
- package/src/scene/scene-resource-types.ts +174 -0
- package/src/scene/scene.ts +2378 -0
- package/src/scene/scene_bundle.ts +304 -0
- package/src/scene/scene_debug.ts +129 -0
- package/src/scene/scene_loader.ts +402 -0
- package/src/scene/scene_worker.ts +528 -0
- package/src/scene/view.ts +683 -0
- package/src/scene/visibility_adapter.ts +319 -0
- package/src/scene/worker_url.ts +15 -0
- package/src/selection/draped-terrain-surface.ts +377 -0
- package/src/selection/selection-types.ts +79 -0
- package/src/selection/selection.ts +542 -0
- package/src/selection/selection_fragment.glsl +18 -0
- package/src/selection/selection_globals.glsl +9 -0
- package/src/selection/selection_pixels.ts +59 -0
- package/src/selection/selection_vertex.glsl +15 -0
- package/src/selection/terrain_surface.ts +298 -0
- package/src/shader-modules/globe-horizon.ts +52 -0
- package/src/shader-modules/height-decode.ts +41 -0
- package/src/shader-modules/hillshade.ts +70 -0
- package/src/shader-modules/index.ts +10 -0
- package/src/shader-modules/planar.ts +24 -0
- package/src/shader-modules/sphere-map.ts +38 -0
- package/src/shader-modules/triplanar.ts +45 -0
- package/src/sources/aligned_tile_source.ts +122 -0
- package/src/sources/data_source.ts +655 -0
- package/src/sources/decoded_tile_store.ts +245 -0
- package/src/sources/geojson.ts +300 -0
- package/src/sources/mvt.ts +247 -0
- package/src/sources/raster.ts +341 -0
- package/src/sources/shared_tile_source_adapter.ts +117 -0
- package/src/sources/sources.ts +10 -0
- package/src/sources/tile_source_adapter.ts +84 -0
- package/src/sources/tile_source_metadata.ts +65 -0
- package/src/sources/tile_source_state.ts +112 -0
- package/src/sources/topojson.ts +90 -0
- package/src/styles/feature_annotations.ts +16 -0
- package/src/styles/filter.ts +190 -0
- package/src/styles/geometry-style-types.ts +209 -0
- package/src/styles/globe_visibility_wgsl.ts +22 -0
- package/src/styles/layer-types.ts +45 -0
- package/src/styles/layer.ts +586 -0
- package/src/styles/lines/dasharray.ts +49 -0
- package/src/styles/lines/lines.ts +707 -0
- package/src/styles/lines/lines_wgsl.ts +224 -0
- package/src/styles/points/point-runtime-types.ts +125 -0
- package/src/styles/points/points.ts +1079 -0
- package/src/styles/points/points_fragment.glsl +126 -0
- package/src/styles/points/points_vertex.glsl +230 -0
- package/src/styles/points/points_wgsl.ts +189 -0
- package/src/styles/polygons/polygons.ts +268 -0
- package/src/styles/polygons/polygons_fragment.glsl +173 -0
- package/src/styles/polygons/polygons_vertex.glsl +213 -0
- package/src/styles/polygons/polygons_wgsl.ts +127 -0
- package/src/styles/property-types.ts +125 -0
- package/src/styles/raster/raster.ts +33 -0
- package/src/styles/raster/raster_globals.glsl +46 -0
- package/src/styles/style-mixing-types.ts +101 -0
- package/src/styles/style-runtime-types.ts +220 -0
- package/src/styles/style-schema.ts +133 -0
- package/src/styles/style.ts +1033 -0
- package/src/styles/style_globals.glsl +69 -0
- package/src/styles/style_manager.ts +464 -0
- package/src/styles/style_parser.ts +620 -0
- package/src/styles/text/font_manager.ts +180 -0
- package/src/styles/text/fontfaceobserver.d.ts +10 -0
- package/src/styles/text/text-types.ts +220 -0
- package/src/styles/text/text.ts +378 -0
- package/src/styles/text/text_canvas.ts +773 -0
- package/src/styles/text/text_labels.ts +436 -0
- package/src/styles/text/text_segments.ts +142 -0
- package/src/styles/text/text_settings.ts +228 -0
- package/src/styles/text/text_wgsl.ts +159 -0
- package/src/styles/text/text_wrap.ts +194 -0
- package/src/tile/globe_tile_preload.ts +40 -0
- package/src/tile/tangram_tileset_2d.ts +108 -0
- package/src/tile/tile-build-types.ts +155 -0
- package/src/tile/tile.ts +807 -0
- package/src/tile/tile_id.ts +172 -0
- package/src/tile/tile_manager.ts +889 -0
- package/src/tile/tile_pyramid.ts +119 -0
- package/src/tile/tile_resource_cache.ts +91 -0
- package/src/tile/tile_traversal_adapter.ts +132 -0
- package/src/types.ts +332 -0
- package/src/utils/debounce.ts +17 -0
- package/src/utils/debug_settings.ts +43 -0
- package/src/utils/errors.ts +12 -0
- package/src/utils/functions.ts +95 -0
- package/src/utils/geo.ts +364 -0
- package/src/utils/hash.ts +14 -0
- package/src/utils/log.ts +108 -0
- package/src/utils/media_capture.ts +193 -0
- package/src/utils/merge.ts +44 -0
- package/src/utils/obb.ts +134 -0
- package/src/utils/props.ts +43 -0
- package/src/utils/slice.ts +14 -0
- package/src/utils/subscribe.ts +58 -0
- package/src/utils/task.ts +154 -0
- package/src/utils/thread.ts +26 -0
- package/src/utils/urls.ts +223 -0
- package/src/utils/utils.ts +310 -0
- package/src/utils/vector.ts +197 -0
- package/src/utils/version.ts +8 -0
- package/src/utils/worker-types.ts +79 -0
- 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
|
+
}
|