@luma.gl/engine 9.2.6 → 9.3.0-alpha.10
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/dist/animation-loop/animation-loop.d.ts +11 -5
- package/dist/animation-loop/animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/animation-loop.js +83 -47
- package/dist/animation-loop/animation-loop.js.map +1 -1
- package/dist/animation-loop/make-animation-loop.js +7 -1
- package/dist/animation-loop/make-animation-loop.js.map +1 -1
- package/dist/animation-loop/request-animation-frame.d.ts.map +1 -1
- package/dist/animation-loop/request-animation-frame.js +23 -6
- package/dist/animation-loop/request-animation-frame.js.map +1 -1
- package/dist/compute/computation.d.ts +3 -7
- package/dist/compute/computation.d.ts.map +1 -1
- package/dist/compute/computation.js +16 -13
- package/dist/compute/computation.js.map +1 -1
- package/dist/compute/swap.d.ts +2 -0
- package/dist/compute/swap.d.ts.map +1 -1
- package/dist/compute/swap.js +10 -5
- package/dist/compute/swap.js.map +1 -1
- package/dist/dist.dev.js +2639 -1290
- package/dist/dist.min.js +325 -210
- package/dist/dynamic-texture/dynamic-texture.d.ts +102 -0
- package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -0
- package/dist/dynamic-texture/dynamic-texture.js +556 -0
- package/dist/dynamic-texture/dynamic-texture.js.map +1 -0
- package/dist/dynamic-texture/texture-data.d.ts +144 -0
- package/dist/dynamic-texture/texture-data.d.ts.map +1 -0
- package/dist/dynamic-texture/texture-data.js +208 -0
- package/dist/dynamic-texture/texture-data.js.map +1 -0
- package/dist/geometries/cone-geometry.d.ts +3 -1
- package/dist/geometries/cone-geometry.d.ts.map +1 -1
- package/dist/geometries/cone-geometry.js.map +1 -1
- package/dist/geometries/cylinder-geometry.d.ts +2 -1
- package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
- package/dist/geometries/cylinder-geometry.js.map +1 -1
- package/dist/geometry/gpu-geometry.d.ts.map +1 -1
- package/dist/geometry/gpu-geometry.js +8 -3
- package/dist/geometry/gpu-geometry.js.map +1 -1
- package/dist/index.cjs +2497 -1212
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +20 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/material/material-factory.d.ts +73 -0
- package/dist/material/material-factory.d.ts.map +1 -0
- package/dist/material/material-factory.js +111 -0
- package/dist/material/material-factory.js.map +1 -0
- package/dist/material/material.d.ts +84 -0
- package/dist/material/material.d.ts.map +1 -0
- package/dist/material/material.js +176 -0
- package/dist/material/material.js.map +1 -0
- package/dist/model/model.d.ts +47 -16
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +113 -47
- package/dist/model/model.js.map +1 -1
- package/dist/model/split-uniforms-and-bindings.d.ts +4 -3
- package/dist/model/split-uniforms-and-bindings.d.ts.map +1 -1
- package/dist/model/split-uniforms-and-bindings.js +2 -2
- package/dist/model/split-uniforms-and-bindings.js.map +1 -1
- package/dist/models/billboard-texture-model.d.ts +8 -5
- package/dist/models/billboard-texture-model.d.ts.map +1 -1
- package/dist/models/billboard-texture-model.js +77 -23
- package/dist/models/billboard-texture-model.js.map +1 -1
- package/dist/models/billboard-texture-module.d.ts +1 -1
- package/dist/models/billboard-texture-module.js +1 -1
- package/dist/models/clip-space.js +7 -7
- package/dist/models/directional-light-model.d.ts +7 -0
- package/dist/models/directional-light-model.d.ts.map +1 -0
- package/dist/models/directional-light-model.js +23 -0
- package/dist/models/directional-light-model.js.map +1 -0
- package/dist/models/light-model-utils.d.ts +69 -0
- package/dist/models/light-model-utils.d.ts.map +1 -0
- package/dist/models/light-model-utils.js +395 -0
- package/dist/models/light-model-utils.js.map +1 -0
- package/dist/models/point-light-model.d.ts +7 -0
- package/dist/models/point-light-model.d.ts.map +1 -0
- package/dist/models/point-light-model.js +22 -0
- package/dist/models/point-light-model.js.map +1 -0
- package/dist/models/spot-light-model.d.ts +7 -0
- package/dist/models/spot-light-model.d.ts.map +1 -0
- package/dist/models/spot-light-model.js +23 -0
- package/dist/models/spot-light-model.js.map +1 -0
- package/dist/modules/picking/color-picking.d.ts +5 -9
- package/dist/modules/picking/color-picking.d.ts.map +1 -1
- package/dist/modules/picking/color-picking.js +122 -115
- package/dist/modules/picking/color-picking.js.map +1 -1
- package/dist/modules/picking/index-picking.d.ts +4 -4
- package/dist/modules/picking/index-picking.d.ts.map +1 -1
- package/dist/modules/picking/index-picking.js +36 -16
- package/dist/modules/picking/index-picking.js.map +1 -1
- package/dist/modules/picking/legacy-color-picking.d.ts +26 -0
- package/dist/modules/picking/legacy-color-picking.d.ts.map +1 -0
- package/dist/modules/picking/legacy-color-picking.js +7 -0
- package/dist/modules/picking/legacy-color-picking.js.map +1 -0
- package/dist/modules/picking/picking-manager.d.ts +29 -3
- package/dist/modules/picking/picking-manager.d.ts.map +1 -1
- package/dist/modules/picking/picking-manager.js +188 -41
- package/dist/modules/picking/picking-manager.js.map +1 -1
- package/dist/modules/picking/picking-uniforms.d.ts +13 -12
- package/dist/modules/picking/picking-uniforms.d.ts.map +1 -1
- package/dist/modules/picking/picking-uniforms.js +27 -14
- package/dist/modules/picking/picking-uniforms.js.map +1 -1
- package/dist/modules/picking/picking.d.ts +25 -0
- package/dist/modules/picking/picking.d.ts.map +1 -0
- package/dist/modules/picking/picking.js +18 -0
- package/dist/modules/picking/picking.js.map +1 -0
- package/dist/passes/get-fragment-shader.js +12 -27
- package/dist/passes/get-fragment-shader.js.map +1 -1
- package/dist/passes/shader-pass-renderer.d.ts +5 -7
- package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
- package/dist/passes/shader-pass-renderer.js +16 -42
- package/dist/passes/shader-pass-renderer.js.map +1 -1
- package/dist/scenegraph/group-node.d.ts +5 -0
- package/dist/scenegraph/group-node.d.ts.map +1 -1
- package/dist/scenegraph/group-node.js +12 -0
- package/dist/scenegraph/group-node.js.map +1 -1
- package/dist/scenegraph/model-node.d.ts +2 -2
- package/dist/scenegraph/model-node.d.ts.map +1 -1
- package/dist/scenegraph/model-node.js.map +1 -1
- package/dist/scenegraph/scenegraph-node.d.ts +1 -1
- package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
- package/dist/scenegraph/scenegraph-node.js +23 -15
- package/dist/scenegraph/scenegraph-node.js.map +1 -1
- package/dist/shader-inputs.d.ts +9 -7
- package/dist/shader-inputs.d.ts.map +1 -1
- package/dist/shader-inputs.js +84 -4
- package/dist/shader-inputs.js.map +1 -1
- package/dist/utils/buffer-layout-order.d.ts.map +1 -1
- package/dist/utils/buffer-layout-order.js +12 -2
- package/dist/utils/buffer-layout-order.js.map +1 -1
- package/dist/utils/shader-module-utils.d.ts +7 -0
- package/dist/utils/shader-module-utils.d.ts.map +1 -0
- package/dist/utils/shader-module-utils.js +46 -0
- package/dist/utils/shader-module-utils.js.map +1 -0
- package/package.json +6 -6
- package/src/animation-loop/animation-loop.ts +89 -50
- package/src/animation-loop/make-animation-loop.ts +13 -5
- package/src/animation-loop/request-animation-frame.ts +32 -6
- package/src/compute/computation.ts +32 -17
- package/src/compute/swap.ts +13 -7
- package/src/dynamic-texture/dynamic-texture.ts +732 -0
- package/src/dynamic-texture/texture-data.ts +336 -0
- package/src/geometries/cone-geometry.ts +6 -1
- package/src/geometries/cylinder-geometry.ts +5 -1
- package/src/geometry/gpu-geometry.ts +8 -3
- package/src/index.ts +38 -8
- package/src/material/material-factory.ts +157 -0
- package/src/material/material.ts +254 -0
- package/src/model/model.ts +158 -67
- package/src/model/split-uniforms-and-bindings.ts +8 -6
- package/src/models/billboard-texture-model.ts +88 -27
- package/src/models/billboard-texture-module.ts +1 -1
- package/src/models/clip-space.ts +7 -7
- package/src/models/directional-light-model.ts +32 -0
- package/src/models/light-model-utils.ts +587 -0
- package/src/models/point-light-model.ts +31 -0
- package/src/models/spot-light-model.ts +32 -0
- package/src/modules/picking/color-picking.ts +123 -122
- package/src/modules/picking/index-picking.ts +36 -16
- package/src/modules/picking/legacy-color-picking.ts +8 -0
- package/src/modules/picking/picking-manager.ts +252 -50
- package/src/modules/picking/picking-uniforms.ts +39 -24
- package/src/modules/picking/picking.ts +22 -0
- package/src/passes/get-fragment-shader.ts +12 -27
- package/src/passes/shader-pass-renderer.ts +25 -48
- package/src/scenegraph/group-node.ts +16 -0
- package/src/scenegraph/model-node.ts +2 -2
- package/src/scenegraph/scenegraph-node.ts +27 -16
- package/src/shader-inputs.ts +165 -15
- package/src/utils/buffer-layout-order.ts +18 -2
- package/src/utils/shader-module-utils.ts +65 -0
- package/dist/async-texture/async-texture.d.ts +0 -166
- package/dist/async-texture/async-texture.d.ts.map +0 -1
- package/dist/async-texture/async-texture.js +0 -386
- package/dist/async-texture/async-texture.js.map +0 -1
- package/dist/factories/pipeline-factory.d.ts +0 -37
- package/dist/factories/pipeline-factory.d.ts.map +0 -1
- package/dist/factories/pipeline-factory.js +0 -181
- package/dist/factories/pipeline-factory.js.map +0 -1
- package/dist/factories/shader-factory.d.ts +0 -22
- package/dist/factories/shader-factory.d.ts.map +0 -1
- package/dist/factories/shader-factory.js +0 -88
- package/dist/factories/shader-factory.js.map +0 -1
- package/src/async-texture/async-texture.ts +0 -551
- package/src/factories/pipeline-factory.ts +0 -224
- package/src/factories/shader-factory.ts +0 -103
- /package/src/{async-texture/texture-setters.ts.disabled → dynamic-texture/texture-data.ts.disabled} +0 -0
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
// luma.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Buffer, Device, type RenderPass, type RenderPipelineParameters} from '@luma.gl/core';
|
|
6
|
+
import {Matrix4, type NumericArray} from '@math.gl/core';
|
|
7
|
+
import type {
|
|
8
|
+
DirectionalLight,
|
|
9
|
+
Light,
|
|
10
|
+
PointLight,
|
|
11
|
+
ShaderModule,
|
|
12
|
+
SpotLight
|
|
13
|
+
} from '@luma.gl/shadertools';
|
|
14
|
+
import {Model, type ModelProps} from '../model/model';
|
|
15
|
+
import {ShaderInputs} from '../shader-inputs';
|
|
16
|
+
import type {Geometry} from '../geometry/geometry';
|
|
17
|
+
|
|
18
|
+
const DEFAULT_POINT_LIGHT_RADIUS_FACTOR = 0.02;
|
|
19
|
+
const DEFAULT_SPOT_LIGHT_LENGTH_FACTOR = 0.12;
|
|
20
|
+
const DEFAULT_DIRECTIONAL_LIGHT_LENGTH_FACTOR = 0.15;
|
|
21
|
+
const DEFAULT_DIRECTIONAL_LIGHT_RADIUS_FACTOR = 0.2;
|
|
22
|
+
const DEFAULT_DIRECTION_FALLBACK: [number, number, number] = [0, 1, 0];
|
|
23
|
+
const DEFAULT_LIGHT_COLOR: [number, number, number] = [255, 255, 255];
|
|
24
|
+
const DEFAULT_MARKER_SCALE = 1;
|
|
25
|
+
const DIRECTIONAL_ANCHOR_DISTANCE_FACTOR = 0.35;
|
|
26
|
+
const LIGHT_COLOR_FACTOR = 255;
|
|
27
|
+
const MIN_SCENE_SCALE = 1;
|
|
28
|
+
const SPOTLIGHT_OUTER_CONE_EPSILON = 0.01;
|
|
29
|
+
|
|
30
|
+
export type LightModelBounds = [[number, number, number], [number, number, number]];
|
|
31
|
+
|
|
32
|
+
export type BaseLightModelProps = Omit<
|
|
33
|
+
ModelProps,
|
|
34
|
+
'geometry' | 'modules' | 'shaderInputs' | 'source' | 'vs' | 'fs' | 'instanceCount'
|
|
35
|
+
> & {
|
|
36
|
+
lights: ReadonlyArray<Light>;
|
|
37
|
+
viewMatrix: NumericArray;
|
|
38
|
+
projectionMatrix: NumericArray;
|
|
39
|
+
bounds?: LightModelBounds;
|
|
40
|
+
markerScale?: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type PointLightModelProps = BaseLightModelProps & {
|
|
44
|
+
pointLightRadius?: number;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type SpotLightModelProps = BaseLightModelProps & {
|
|
48
|
+
spotLightLength?: number;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type DirectionalLightModelProps = BaseLightModelProps & {
|
|
52
|
+
directionalLightLength?: number;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type LightMarkerUniforms = {
|
|
56
|
+
viewProjectionMatrix: Matrix4;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type LightMarkerInstanceData = {
|
|
60
|
+
instanceCount: number;
|
|
61
|
+
instancePositions: Float32Array;
|
|
62
|
+
instanceDirections: Float32Array;
|
|
63
|
+
instanceScales: Float32Array;
|
|
64
|
+
instanceColors: Float32Array;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
type ManagedInstanceBuffers = Record<
|
|
68
|
+
'instancePosition' | 'instanceDirection' | 'instanceScale' | 'instanceColor',
|
|
69
|
+
Buffer
|
|
70
|
+
>;
|
|
71
|
+
|
|
72
|
+
type LightMarkerAnchorMode = 'centered' | 'apex';
|
|
73
|
+
|
|
74
|
+
type LightMarkerModelOptions<PropsT extends BaseLightModelProps> = {
|
|
75
|
+
anchorMode: LightMarkerAnchorMode;
|
|
76
|
+
buildInstanceData: (props: PropsT) => LightMarkerInstanceData;
|
|
77
|
+
geometry: Geometry;
|
|
78
|
+
idPrefix: string;
|
|
79
|
+
sizePropNames: Array<keyof PropsT>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const LIGHT_MARKER_PARAMETERS: RenderPipelineParameters = {
|
|
83
|
+
depthCompare: 'less-equal',
|
|
84
|
+
depthWriteEnabled: false,
|
|
85
|
+
cullMode: 'none'
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const INSTANCE_BUFFER_LAYOUT = [
|
|
89
|
+
{name: 'instancePosition', format: 'float32x3', stepMode: 'instance'},
|
|
90
|
+
{name: 'instanceDirection', format: 'float32x3', stepMode: 'instance'},
|
|
91
|
+
{name: 'instanceScale', format: 'float32x3', stepMode: 'instance'},
|
|
92
|
+
{name: 'instanceColor', format: 'float32x4', stepMode: 'instance'}
|
|
93
|
+
] as const;
|
|
94
|
+
|
|
95
|
+
const lightMarker = {
|
|
96
|
+
name: 'lightMarker',
|
|
97
|
+
props: {} as LightMarkerUniforms,
|
|
98
|
+
uniforms: {} as LightMarkerUniforms,
|
|
99
|
+
uniformTypes: {
|
|
100
|
+
viewProjectionMatrix: 'mat4x4<f32>'
|
|
101
|
+
}
|
|
102
|
+
} as const satisfies ShaderModule<LightMarkerUniforms, LightMarkerUniforms>;
|
|
103
|
+
|
|
104
|
+
const CENTERED_LOCAL_POSITION_WGSL = 'inputs.positions * inputs.instanceScale';
|
|
105
|
+
const APEX_LOCAL_POSITION_WGSL =
|
|
106
|
+
'vec3<f32>(inputs.positions.x * inputs.instanceScale.x, (inputs.positions.y - 0.5) * inputs.instanceScale.y, inputs.positions.z * inputs.instanceScale.z)';
|
|
107
|
+
const CENTERED_LOCAL_POSITION_GLSL = 'positions * instanceScale';
|
|
108
|
+
const APEX_LOCAL_POSITION_GLSL =
|
|
109
|
+
'vec3(positions.x * instanceScale.x, (positions.y - 0.5) * instanceScale.y, positions.z * instanceScale.z)';
|
|
110
|
+
|
|
111
|
+
export abstract class BaseLightModel<PropsT extends BaseLightModelProps> extends Model {
|
|
112
|
+
protected lightModelProps: PropsT;
|
|
113
|
+
protected _instanceData: LightMarkerInstanceData;
|
|
114
|
+
protected _managedBuffers: ManagedInstanceBuffers;
|
|
115
|
+
|
|
116
|
+
private readonly buildInstanceData: (props: PropsT) => LightMarkerInstanceData;
|
|
117
|
+
private readonly sizePropNames: Array<keyof PropsT>;
|
|
118
|
+
|
|
119
|
+
constructor(device: Device, props: PropsT, options: LightMarkerModelOptions<PropsT>) {
|
|
120
|
+
const instanceData = options.buildInstanceData(props);
|
|
121
|
+
const managedBuffers = createManagedInstanceBuffers(
|
|
122
|
+
device,
|
|
123
|
+
props.id || options.idPrefix,
|
|
124
|
+
instanceData
|
|
125
|
+
);
|
|
126
|
+
const shaderInputs = new ShaderInputs<{
|
|
127
|
+
lightMarker: typeof lightMarker.props;
|
|
128
|
+
}>({lightMarker});
|
|
129
|
+
shaderInputs.setProps({
|
|
130
|
+
lightMarker: {viewProjectionMatrix: createViewProjectionMatrix(props)}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const {source, vs, fs} = getLightMarkerShaders(options.anchorMode);
|
|
134
|
+
const modelProps: ModelProps = props;
|
|
135
|
+
|
|
136
|
+
super(device, {
|
|
137
|
+
...modelProps,
|
|
138
|
+
id: props.id || options.idPrefix,
|
|
139
|
+
source,
|
|
140
|
+
vs,
|
|
141
|
+
fs,
|
|
142
|
+
geometry: options.geometry,
|
|
143
|
+
shaderInputs,
|
|
144
|
+
bufferLayout: [...INSTANCE_BUFFER_LAYOUT],
|
|
145
|
+
attributes: managedBuffers,
|
|
146
|
+
instanceCount: instanceData.instanceCount,
|
|
147
|
+
parameters: mergeLightMarkerParameters(props.parameters)
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
this.lightModelProps = props;
|
|
151
|
+
this._instanceData = instanceData;
|
|
152
|
+
this._managedBuffers = managedBuffers;
|
|
153
|
+
this.buildInstanceData = options.buildInstanceData;
|
|
154
|
+
this.sizePropNames = options.sizePropNames;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
override destroy(): void {
|
|
158
|
+
super.destroy();
|
|
159
|
+
destroyManagedInstanceBuffers(this._managedBuffers);
|
|
160
|
+
this._managedBuffers = {} as ManagedInstanceBuffers;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
override draw(renderPass: RenderPass): boolean {
|
|
164
|
+
if (this.instanceCount === 0) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
return super.draw(renderPass);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
setProps(props: Partial<PropsT>): void {
|
|
171
|
+
this.lightModelProps = {...this.lightModelProps, ...props};
|
|
172
|
+
|
|
173
|
+
if (props.parameters) {
|
|
174
|
+
this.setParameters(mergeLightMarkerParameters(this.lightModelProps.parameters));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if ('viewMatrix' in props || 'projectionMatrix' in props) {
|
|
178
|
+
this.shaderInputs.setProps({
|
|
179
|
+
lightMarker: {viewProjectionMatrix: createViewProjectionMatrix(this.lightModelProps)}
|
|
180
|
+
});
|
|
181
|
+
this.setNeedsRedraw('lightMarker camera');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (shouldRebuildInstanceData(props, this.sizePropNames)) {
|
|
185
|
+
this.rebuildInstanceData();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private rebuildInstanceData(): void {
|
|
190
|
+
const nextInstanceData = this.buildInstanceData(this.lightModelProps);
|
|
191
|
+
const nextManagedBuffers = createManagedInstanceBuffers(this.device, this.id, nextInstanceData);
|
|
192
|
+
|
|
193
|
+
this.setAttributes(nextManagedBuffers);
|
|
194
|
+
this.setInstanceCount(nextInstanceData.instanceCount);
|
|
195
|
+
|
|
196
|
+
destroyManagedInstanceBuffers(this._managedBuffers);
|
|
197
|
+
this._managedBuffers = nextManagedBuffers;
|
|
198
|
+
this._instanceData = nextInstanceData;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function buildPointLightInstanceData(props: PointLightModelProps): LightMarkerInstanceData {
|
|
203
|
+
const pointLights = getPointLights(props.lights);
|
|
204
|
+
const context = getLightMarkerContext(props);
|
|
205
|
+
const pointLightRadius =
|
|
206
|
+
props.pointLightRadius ??
|
|
207
|
+
DEFAULT_POINT_LIGHT_RADIUS_FACTOR * context.sceneScale * context.markerScale;
|
|
208
|
+
|
|
209
|
+
return createLightMarkerInstanceData(
|
|
210
|
+
pointLights.length,
|
|
211
|
+
(light, _index) => ({
|
|
212
|
+
color: getDisplayColor(light),
|
|
213
|
+
direction: DEFAULT_DIRECTION_FALLBACK,
|
|
214
|
+
position: light.position,
|
|
215
|
+
scale: [pointLightRadius, pointLightRadius, pointLightRadius]
|
|
216
|
+
}),
|
|
217
|
+
pointLights
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function buildSpotLightInstanceData(props: SpotLightModelProps): LightMarkerInstanceData {
|
|
222
|
+
const spotLights = getSpotLights(props.lights);
|
|
223
|
+
const context = getLightMarkerContext(props);
|
|
224
|
+
const spotLightLength =
|
|
225
|
+
props.spotLightLength ??
|
|
226
|
+
DEFAULT_SPOT_LIGHT_LENGTH_FACTOR * context.sceneScale * context.markerScale;
|
|
227
|
+
|
|
228
|
+
return createLightMarkerInstanceData(
|
|
229
|
+
spotLights.length,
|
|
230
|
+
(light, _index) => {
|
|
231
|
+
const outerConeAngle = clamp(
|
|
232
|
+
light.outerConeAngle ?? Math.PI / 4,
|
|
233
|
+
0,
|
|
234
|
+
Math.PI / 2 - SPOTLIGHT_OUTER_CONE_EPSILON
|
|
235
|
+
);
|
|
236
|
+
const radius = Math.tan(outerConeAngle) * spotLightLength;
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
color: getDisplayColor(light),
|
|
240
|
+
direction: normalizeDirection(light.direction),
|
|
241
|
+
position: light.position,
|
|
242
|
+
scale: [radius, spotLightLength, radius]
|
|
243
|
+
};
|
|
244
|
+
},
|
|
245
|
+
spotLights
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function buildDirectionalLightInstanceData(
|
|
250
|
+
props: DirectionalLightModelProps
|
|
251
|
+
): LightMarkerInstanceData {
|
|
252
|
+
const directionalLights = getDirectionalLights(props.lights);
|
|
253
|
+
const context = getLightMarkerContext(props);
|
|
254
|
+
const directionalLightLength =
|
|
255
|
+
props.directionalLightLength ??
|
|
256
|
+
DEFAULT_DIRECTIONAL_LIGHT_LENGTH_FACTOR * context.sceneScale * context.markerScale;
|
|
257
|
+
const directionalLightRadius = directionalLightLength * DEFAULT_DIRECTIONAL_LIGHT_RADIUS_FACTOR;
|
|
258
|
+
|
|
259
|
+
return createLightMarkerInstanceData(
|
|
260
|
+
directionalLights.length,
|
|
261
|
+
(light, _index) => {
|
|
262
|
+
const direction = normalizeDirection(light.direction);
|
|
263
|
+
const position = [
|
|
264
|
+
context.sceneCenter[0] -
|
|
265
|
+
direction[0] * context.sceneScale * DIRECTIONAL_ANCHOR_DISTANCE_FACTOR,
|
|
266
|
+
context.sceneCenter[1] -
|
|
267
|
+
direction[1] * context.sceneScale * DIRECTIONAL_ANCHOR_DISTANCE_FACTOR,
|
|
268
|
+
context.sceneCenter[2] -
|
|
269
|
+
direction[2] * context.sceneScale * DIRECTIONAL_ANCHOR_DISTANCE_FACTOR
|
|
270
|
+
] as [number, number, number];
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
color: getDisplayColor(light),
|
|
274
|
+
direction,
|
|
275
|
+
position,
|
|
276
|
+
scale: [directionalLightRadius, directionalLightLength, directionalLightRadius]
|
|
277
|
+
};
|
|
278
|
+
},
|
|
279
|
+
directionalLights
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function getPointLights(lights: ReadonlyArray<Light>): PointLight[] {
|
|
284
|
+
return lights.filter((light): light is PointLight => light.type === 'point');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function getSpotLights(lights: ReadonlyArray<Light>): SpotLight[] {
|
|
288
|
+
return lights.filter((light): light is SpotLight => light.type === 'spot');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function getDirectionalLights(lights: ReadonlyArray<Light>): DirectionalLight[] {
|
|
292
|
+
return lights.filter((light): light is DirectionalLight => light.type === 'directional');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function getLightMarkerContext(props: BaseLightModelProps): {
|
|
296
|
+
bounds: LightModelBounds;
|
|
297
|
+
markerScale: number;
|
|
298
|
+
sceneCenter: [number, number, number];
|
|
299
|
+
sceneScale: number;
|
|
300
|
+
} {
|
|
301
|
+
const bounds = getSceneBounds(props.lights, props.bounds);
|
|
302
|
+
const sceneCenter = [
|
|
303
|
+
(bounds[0][0] + bounds[1][0]) / 2,
|
|
304
|
+
(bounds[0][1] + bounds[1][1]) / 2,
|
|
305
|
+
(bounds[0][2] + bounds[1][2]) / 2
|
|
306
|
+
] as [number, number, number];
|
|
307
|
+
const sceneScale = Math.max(
|
|
308
|
+
Math.hypot(
|
|
309
|
+
bounds[1][0] - bounds[0][0],
|
|
310
|
+
bounds[1][1] - bounds[0][1],
|
|
311
|
+
bounds[1][2] - bounds[0][2]
|
|
312
|
+
),
|
|
313
|
+
MIN_SCENE_SCALE
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
bounds,
|
|
318
|
+
markerScale: Math.max(props.markerScale ?? DEFAULT_MARKER_SCALE, 0),
|
|
319
|
+
sceneCenter,
|
|
320
|
+
sceneScale
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function getDisplayColor(light: {
|
|
325
|
+
color?: Readonly<[number, number, number]>;
|
|
326
|
+
intensity?: number;
|
|
327
|
+
}): [number, number, number, number] {
|
|
328
|
+
const color = light.color || DEFAULT_LIGHT_COLOR;
|
|
329
|
+
const intensity = Math.max(light.intensity ?? 1, 0);
|
|
330
|
+
const brightness = clamp(0.35 + 0.3 * Math.log10(intensity + 1), 0.35, 1);
|
|
331
|
+
|
|
332
|
+
return [
|
|
333
|
+
clamp(color[0] / LIGHT_COLOR_FACTOR, 0, 1) * brightness,
|
|
334
|
+
clamp(color[1] / LIGHT_COLOR_FACTOR, 0, 1) * brightness,
|
|
335
|
+
clamp(color[2] / LIGHT_COLOR_FACTOR, 0, 1) * brightness,
|
|
336
|
+
1
|
|
337
|
+
];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export function normalizeDirection(
|
|
341
|
+
direction?: Readonly<[number, number, number]>
|
|
342
|
+
): [number, number, number] {
|
|
343
|
+
const [x, y, z] = direction || DEFAULT_DIRECTION_FALLBACK;
|
|
344
|
+
const length = Math.hypot(x, y, z);
|
|
345
|
+
if (length === 0) {
|
|
346
|
+
return [...DEFAULT_DIRECTION_FALLBACK];
|
|
347
|
+
}
|
|
348
|
+
return [x / length, y / length, z / length];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function createLightMarkerInstanceData<TLight>(
|
|
352
|
+
instanceCount: number,
|
|
353
|
+
getInstance: (
|
|
354
|
+
light: TLight,
|
|
355
|
+
index: number
|
|
356
|
+
) => {
|
|
357
|
+
color: [number, number, number, number];
|
|
358
|
+
direction: [number, number, number];
|
|
359
|
+
position: Readonly<NumericArray>;
|
|
360
|
+
scale: [number, number, number];
|
|
361
|
+
},
|
|
362
|
+
lights: TLight[] = []
|
|
363
|
+
): LightMarkerInstanceData {
|
|
364
|
+
const instancePositions = new Float32Array(instanceCount * 3);
|
|
365
|
+
const instanceDirections = new Float32Array(instanceCount * 3);
|
|
366
|
+
const instanceScales = new Float32Array(instanceCount * 3);
|
|
367
|
+
const instanceColors = new Float32Array(instanceCount * 4);
|
|
368
|
+
|
|
369
|
+
for (const [index, light] of lights.entries()) {
|
|
370
|
+
const instance = getInstance(light, index);
|
|
371
|
+
instancePositions.set(instance.position, index * 3);
|
|
372
|
+
instanceDirections.set(instance.direction, index * 3);
|
|
373
|
+
instanceScales.set(instance.scale, index * 3);
|
|
374
|
+
instanceColors.set(instance.color, index * 4);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
instanceCount,
|
|
379
|
+
instancePositions,
|
|
380
|
+
instanceDirections,
|
|
381
|
+
instanceScales,
|
|
382
|
+
instanceColors
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function getSceneBounds(lights: ReadonlyArray<Light>, bounds?: LightModelBounds): LightModelBounds {
|
|
387
|
+
if (bounds) {
|
|
388
|
+
return cloneBounds(bounds);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const positions = [
|
|
392
|
+
...getPointLights(lights).map(light => light.position),
|
|
393
|
+
...getSpotLights(lights).map(light => light.position)
|
|
394
|
+
];
|
|
395
|
+
|
|
396
|
+
if (positions.length === 0) {
|
|
397
|
+
return [
|
|
398
|
+
[-0.5, -0.5, -0.5],
|
|
399
|
+
[0.5, 0.5, 0.5]
|
|
400
|
+
];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const minBounds: [number, number, number] = [...positions[0]] as [number, number, number];
|
|
404
|
+
const maxBounds: [number, number, number] = [...positions[0]] as [number, number, number];
|
|
405
|
+
|
|
406
|
+
for (const position of positions.slice(1)) {
|
|
407
|
+
minBounds[0] = Math.min(minBounds[0], position[0]);
|
|
408
|
+
minBounds[1] = Math.min(minBounds[1], position[1]);
|
|
409
|
+
minBounds[2] = Math.min(minBounds[2], position[2]);
|
|
410
|
+
|
|
411
|
+
maxBounds[0] = Math.max(maxBounds[0], position[0]);
|
|
412
|
+
maxBounds[1] = Math.max(maxBounds[1], position[1]);
|
|
413
|
+
maxBounds[2] = Math.max(maxBounds[2], position[2]);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return [minBounds, maxBounds];
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function cloneBounds(bounds: LightModelBounds): LightModelBounds {
|
|
420
|
+
return [[...bounds[0]] as [number, number, number], [...bounds[1]] as [number, number, number]];
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function createManagedInstanceBuffers(
|
|
424
|
+
device: Device,
|
|
425
|
+
idPrefix: string,
|
|
426
|
+
instanceData: LightMarkerInstanceData
|
|
427
|
+
): ManagedInstanceBuffers {
|
|
428
|
+
return {
|
|
429
|
+
instancePosition: device.createBuffer({
|
|
430
|
+
id: `${idPrefix}-instance-position`,
|
|
431
|
+
data: getBufferDataOrPlaceholder(instanceData.instancePositions, 3)
|
|
432
|
+
}),
|
|
433
|
+
instanceDirection: device.createBuffer({
|
|
434
|
+
id: `${idPrefix}-instance-direction`,
|
|
435
|
+
data: getBufferDataOrPlaceholder(instanceData.instanceDirections, 3)
|
|
436
|
+
}),
|
|
437
|
+
instanceScale: device.createBuffer({
|
|
438
|
+
id: `${idPrefix}-instance-scale`,
|
|
439
|
+
data: getBufferDataOrPlaceholder(instanceData.instanceScales, 3)
|
|
440
|
+
}),
|
|
441
|
+
instanceColor: device.createBuffer({
|
|
442
|
+
id: `${idPrefix}-instance-color`,
|
|
443
|
+
data: getBufferDataOrPlaceholder(instanceData.instanceColors, 4)
|
|
444
|
+
})
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function getBufferDataOrPlaceholder(data: Float32Array, size: number): Float32Array {
|
|
449
|
+
return data.length > 0 ? data : new Float32Array(size);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function destroyManagedInstanceBuffers(managedBuffers: Partial<ManagedInstanceBuffers>): void {
|
|
453
|
+
for (const buffer of Object.values(managedBuffers)) {
|
|
454
|
+
buffer?.destroy();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function createViewProjectionMatrix(
|
|
459
|
+
props: Pick<BaseLightModelProps, 'projectionMatrix' | 'viewMatrix'>
|
|
460
|
+
): Matrix4 {
|
|
461
|
+
return new Matrix4(props.projectionMatrix).multiplyRight(props.viewMatrix);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function shouldRebuildInstanceData<PropsT extends BaseLightModelProps>(
|
|
465
|
+
props: Partial<PropsT>,
|
|
466
|
+
sizePropNames: Array<keyof PropsT>
|
|
467
|
+
): boolean {
|
|
468
|
+
if ('lights' in props || 'bounds' in props || 'markerScale' in props) {
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
return sizePropNames.some(sizePropName => sizePropName in props);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function mergeLightMarkerParameters(
|
|
475
|
+
parameters?: RenderPipelineParameters
|
|
476
|
+
): RenderPipelineParameters {
|
|
477
|
+
return {
|
|
478
|
+
...LIGHT_MARKER_PARAMETERS,
|
|
479
|
+
...(parameters || {})
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function getLightMarkerShaders(anchorMode: LightMarkerAnchorMode): {
|
|
484
|
+
fs: string;
|
|
485
|
+
source: string;
|
|
486
|
+
vs: string;
|
|
487
|
+
} {
|
|
488
|
+
const localPositionWGSL =
|
|
489
|
+
anchorMode === 'apex' ? APEX_LOCAL_POSITION_WGSL : CENTERED_LOCAL_POSITION_WGSL;
|
|
490
|
+
const localPositionGLSL =
|
|
491
|
+
anchorMode === 'apex' ? APEX_LOCAL_POSITION_GLSL : CENTERED_LOCAL_POSITION_GLSL;
|
|
492
|
+
|
|
493
|
+
return {
|
|
494
|
+
source: `\
|
|
495
|
+
struct lightMarkerUniforms {
|
|
496
|
+
viewProjectionMatrix: mat4x4<f32>,
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
@binding(0) @group(0) var<uniform> lightMarker : lightMarkerUniforms;
|
|
500
|
+
|
|
501
|
+
struct VertexInputs {
|
|
502
|
+
@location(0) positions : vec3<f32>,
|
|
503
|
+
@location(1) instancePosition : vec3<f32>,
|
|
504
|
+
@location(2) instanceDirection : vec3<f32>,
|
|
505
|
+
@location(3) instanceScale : vec3<f32>,
|
|
506
|
+
@location(4) instanceColor : vec4<f32>,
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
struct FragmentInputs {
|
|
510
|
+
@builtin(position) Position : vec4<f32>,
|
|
511
|
+
@location(0) color : vec4<f32>,
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
fn lightMarker_rotate(localPosition: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
515
|
+
let forward = normalize(direction);
|
|
516
|
+
var helperAxis = vec3<f32>(0.0, 1.0, 0.0);
|
|
517
|
+
if (abs(forward.y) > 0.999) {
|
|
518
|
+
helperAxis = vec3<f32>(1.0, 0.0, 0.0);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
let tangent = normalize(cross(helperAxis, forward));
|
|
522
|
+
let bitangent = cross(forward, tangent);
|
|
523
|
+
return tangent * localPosition.x + forward * localPosition.y + bitangent * localPosition.z;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
@vertex
|
|
527
|
+
fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
|
|
528
|
+
var outputs : FragmentInputs;
|
|
529
|
+
let localPosition = ${localPositionWGSL};
|
|
530
|
+
let worldPosition = inputs.instancePosition + lightMarker_rotate(localPosition, inputs.instanceDirection);
|
|
531
|
+
outputs.Position = lightMarker.viewProjectionMatrix * vec4<f32>(worldPosition, 1.0);
|
|
532
|
+
outputs.color = inputs.instanceColor;
|
|
533
|
+
return outputs;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
@fragment
|
|
537
|
+
fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
|
|
538
|
+
return inputs.color;
|
|
539
|
+
}
|
|
540
|
+
`,
|
|
541
|
+
vs: `\
|
|
542
|
+
#version 300 es
|
|
543
|
+
|
|
544
|
+
in vec3 positions;
|
|
545
|
+
in vec3 instancePosition;
|
|
546
|
+
in vec3 instanceDirection;
|
|
547
|
+
in vec3 instanceScale;
|
|
548
|
+
in vec4 instanceColor;
|
|
549
|
+
|
|
550
|
+
layout(std140) uniform lightMarkerUniforms {
|
|
551
|
+
mat4 viewProjectionMatrix;
|
|
552
|
+
} lightMarker;
|
|
553
|
+
|
|
554
|
+
out vec4 vColor;
|
|
555
|
+
|
|
556
|
+
vec3 lightMarker_rotate(vec3 localPosition, vec3 direction) {
|
|
557
|
+
vec3 forward = normalize(direction);
|
|
558
|
+
vec3 helperAxis = abs(forward.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);
|
|
559
|
+
vec3 tangent = normalize(cross(helperAxis, forward));
|
|
560
|
+
vec3 bitangent = cross(forward, tangent);
|
|
561
|
+
return tangent * localPosition.x + forward * localPosition.y + bitangent * localPosition.z;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
void main(void) {
|
|
565
|
+
vec3 localPosition = ${localPositionGLSL};
|
|
566
|
+
vec3 worldPosition = instancePosition + lightMarker_rotate(localPosition, instanceDirection);
|
|
567
|
+
gl_Position = lightMarker.viewProjectionMatrix * vec4(worldPosition, 1.0);
|
|
568
|
+
vColor = instanceColor;
|
|
569
|
+
}
|
|
570
|
+
`,
|
|
571
|
+
fs: `\
|
|
572
|
+
#version 300 es
|
|
573
|
+
precision highp float;
|
|
574
|
+
|
|
575
|
+
in vec4 vColor;
|
|
576
|
+
out vec4 fragColor;
|
|
577
|
+
|
|
578
|
+
void main(void) {
|
|
579
|
+
fragColor = vColor;
|
|
580
|
+
}
|
|
581
|
+
`
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function clamp(value: number, minValue: number, maxValue: number): number {
|
|
586
|
+
return Math.min(maxValue, Math.max(minValue, value));
|
|
587
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// luma.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Device} from '@luma.gl/core';
|
|
6
|
+
import {SphereGeometry} from '../geometries/sphere-geometry';
|
|
7
|
+
import {
|
|
8
|
+
BaseLightModel,
|
|
9
|
+
buildPointLightInstanceData,
|
|
10
|
+
type PointLightModelProps
|
|
11
|
+
} from './light-model-utils';
|
|
12
|
+
|
|
13
|
+
const POINT_LIGHT_GEOMETRY = new SphereGeometry({
|
|
14
|
+
nlat: 8,
|
|
15
|
+
nlong: 12,
|
|
16
|
+
radius: 1
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export type {PointLightModelProps} from './light-model-utils';
|
|
20
|
+
|
|
21
|
+
export class PointLightModel extends BaseLightModel<PointLightModelProps> {
|
|
22
|
+
constructor(device: Device, props: PointLightModelProps) {
|
|
23
|
+
super(device, props, {
|
|
24
|
+
anchorMode: 'centered',
|
|
25
|
+
buildInstanceData: buildPointLightInstanceData,
|
|
26
|
+
geometry: POINT_LIGHT_GEOMETRY,
|
|
27
|
+
idPrefix: 'point-light-model',
|
|
28
|
+
sizePropNames: ['pointLightRadius']
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// luma.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Device} from '@luma.gl/core';
|
|
6
|
+
import {ConeGeometry} from '../geometries/cone-geometry';
|
|
7
|
+
import {
|
|
8
|
+
BaseLightModel,
|
|
9
|
+
buildSpotLightInstanceData,
|
|
10
|
+
type SpotLightModelProps
|
|
11
|
+
} from './light-model-utils';
|
|
12
|
+
|
|
13
|
+
const SPOT_LIGHT_GEOMETRY = new ConeGeometry({
|
|
14
|
+
cap: true,
|
|
15
|
+
nradial: 16,
|
|
16
|
+
nvertical: 1,
|
|
17
|
+
radius: 1
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type {SpotLightModelProps} from './light-model-utils';
|
|
21
|
+
|
|
22
|
+
export class SpotLightModel extends BaseLightModel<SpotLightModelProps> {
|
|
23
|
+
constructor(device: Device, props: SpotLightModelProps) {
|
|
24
|
+
super(device, props, {
|
|
25
|
+
anchorMode: 'apex',
|
|
26
|
+
buildInstanceData: buildSpotLightInstanceData,
|
|
27
|
+
geometry: SPOT_LIGHT_GEOMETRY,
|
|
28
|
+
idPrefix: 'spot-light-model',
|
|
29
|
+
sizePropNames: ['spotLightLength']
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|