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