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