@luma.gl/engine 9.3.0-alpha.4 → 9.3.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/dist/animation-loop/animation-loop.d.ts +8 -4
  2. package/dist/animation-loop/animation-loop.d.ts.map +1 -1
  3. package/dist/animation-loop/animation-loop.js +73 -43
  4. package/dist/animation-loop/animation-loop.js.map +1 -1
  5. package/dist/animation-loop/make-animation-loop.js +7 -1
  6. package/dist/animation-loop/make-animation-loop.js.map +1 -1
  7. package/dist/animation-loop/request-animation-frame.d.ts.map +1 -1
  8. package/dist/animation-loop/request-animation-frame.js +23 -6
  9. package/dist/animation-loop/request-animation-frame.js.map +1 -1
  10. package/dist/compute/computation.d.ts +3 -7
  11. package/dist/compute/computation.d.ts.map +1 -1
  12. package/dist/compute/computation.js +14 -12
  13. package/dist/compute/computation.js.map +1 -1
  14. package/dist/dist.dev.js +2310 -1638
  15. package/dist/dist.min.js +307 -360
  16. package/dist/dynamic-texture/dynamic-texture.d.ts +12 -5
  17. package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -1
  18. package/dist/dynamic-texture/dynamic-texture.js +222 -55
  19. package/dist/dynamic-texture/dynamic-texture.js.map +1 -1
  20. package/dist/dynamic-texture/texture-data.d.ts +8 -1
  21. package/dist/dynamic-texture/texture-data.d.ts.map +1 -1
  22. package/dist/dynamic-texture/texture-data.js +27 -2
  23. package/dist/dynamic-texture/texture-data.js.map +1 -1
  24. package/dist/geometries/cone-geometry.d.ts +3 -1
  25. package/dist/geometries/cone-geometry.d.ts.map +1 -1
  26. package/dist/geometries/cone-geometry.js.map +1 -1
  27. package/dist/geometries/cylinder-geometry.d.ts +2 -1
  28. package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
  29. package/dist/geometries/cylinder-geometry.js.map +1 -1
  30. package/dist/geometry/gpu-geometry.d.ts.map +1 -1
  31. package/dist/geometry/gpu-geometry.js +8 -3
  32. package/dist/geometry/gpu-geometry.js.map +1 -1
  33. package/dist/index.cjs +2897 -2272
  34. package/dist/index.cjs.map +4 -4
  35. package/dist/index.d.ts +12 -3
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +8 -3
  38. package/dist/index.js.map +1 -1
  39. package/dist/material/material-factory.d.ts +73 -0
  40. package/dist/material/material-factory.d.ts.map +1 -0
  41. package/dist/material/material-factory.js +111 -0
  42. package/dist/material/material-factory.js.map +1 -0
  43. package/dist/material/material.d.ts +84 -0
  44. package/dist/material/material.d.ts.map +1 -0
  45. package/dist/material/material.js +176 -0
  46. package/dist/material/material.js.map +1 -0
  47. package/dist/model/model.d.ts +17 -7
  48. package/dist/model/model.d.ts.map +1 -1
  49. package/dist/model/model.js +80 -34
  50. package/dist/model/model.js.map +1 -1
  51. package/dist/model/split-uniforms-and-bindings.d.ts +4 -3
  52. package/dist/model/split-uniforms-and-bindings.d.ts.map +1 -1
  53. package/dist/model/split-uniforms-and-bindings.js +2 -2
  54. package/dist/model/split-uniforms-and-bindings.js.map +1 -1
  55. package/dist/models/billboard-texture-model.d.ts.map +1 -1
  56. package/dist/models/billboard-texture-model.js +10 -8
  57. package/dist/models/billboard-texture-model.js.map +1 -1
  58. package/dist/models/clip-space.js +7 -7
  59. package/dist/models/directional-light-model.d.ts +7 -0
  60. package/dist/models/directional-light-model.d.ts.map +1 -0
  61. package/dist/models/directional-light-model.js +23 -0
  62. package/dist/models/directional-light-model.js.map +1 -0
  63. package/dist/models/light-model-utils.d.ts +69 -0
  64. package/dist/models/light-model-utils.d.ts.map +1 -0
  65. package/dist/models/light-model-utils.js +395 -0
  66. package/dist/models/light-model-utils.js.map +1 -0
  67. package/dist/models/point-light-model.d.ts +7 -0
  68. package/dist/models/point-light-model.d.ts.map +1 -0
  69. package/dist/models/point-light-model.js +22 -0
  70. package/dist/models/point-light-model.js.map +1 -0
  71. package/dist/models/spot-light-model.d.ts +7 -0
  72. package/dist/models/spot-light-model.d.ts.map +1 -0
  73. package/dist/models/spot-light-model.js +23 -0
  74. package/dist/models/spot-light-model.js.map +1 -0
  75. package/dist/modules/picking/color-picking.d.ts +5 -9
  76. package/dist/modules/picking/color-picking.d.ts.map +1 -1
  77. package/dist/modules/picking/color-picking.js +122 -115
  78. package/dist/modules/picking/color-picking.js.map +1 -1
  79. package/dist/modules/picking/index-picking.d.ts +2 -2
  80. package/dist/modules/picking/index-picking.d.ts.map +1 -1
  81. package/dist/modules/picking/index-picking.js +36 -16
  82. package/dist/modules/picking/index-picking.js.map +1 -1
  83. package/dist/modules/picking/legacy-color-picking.d.ts +26 -0
  84. package/dist/modules/picking/legacy-color-picking.d.ts.map +1 -0
  85. package/dist/modules/picking/legacy-color-picking.js +7 -0
  86. package/dist/modules/picking/legacy-color-picking.js.map +1 -0
  87. package/dist/modules/picking/picking-manager.d.ts +29 -3
  88. package/dist/modules/picking/picking-manager.d.ts.map +1 -1
  89. package/dist/modules/picking/picking-manager.js +188 -41
  90. package/dist/modules/picking/picking-manager.js.map +1 -1
  91. package/dist/modules/picking/picking-uniforms.d.ts +12 -11
  92. package/dist/modules/picking/picking-uniforms.d.ts.map +1 -1
  93. package/dist/modules/picking/picking-uniforms.js +26 -13
  94. package/dist/modules/picking/picking-uniforms.js.map +1 -1
  95. package/dist/modules/picking/picking.d.ts +25 -0
  96. package/dist/modules/picking/picking.d.ts.map +1 -0
  97. package/dist/modules/picking/picking.js +18 -0
  98. package/dist/modules/picking/picking.js.map +1 -0
  99. package/dist/passes/get-fragment-shader.js +11 -30
  100. package/dist/passes/get-fragment-shader.js.map +1 -1
  101. package/dist/passes/shader-pass-renderer.d.ts +0 -2
  102. package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
  103. package/dist/passes/shader-pass-renderer.js +4 -31
  104. package/dist/passes/shader-pass-renderer.js.map +1 -1
  105. package/dist/scenegraph/group-node.d.ts +5 -0
  106. package/dist/scenegraph/group-node.d.ts.map +1 -1
  107. package/dist/scenegraph/group-node.js +12 -0
  108. package/dist/scenegraph/group-node.js.map +1 -1
  109. package/dist/scenegraph/model-node.d.ts +2 -2
  110. package/dist/scenegraph/model-node.d.ts.map +1 -1
  111. package/dist/scenegraph/model-node.js.map +1 -1
  112. package/dist/scenegraph/scenegraph-node.d.ts +1 -1
  113. package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
  114. package/dist/scenegraph/scenegraph-node.js +23 -15
  115. package/dist/scenegraph/scenegraph-node.js.map +1 -1
  116. package/dist/shader-inputs.d.ts +9 -7
  117. package/dist/shader-inputs.d.ts.map +1 -1
  118. package/dist/shader-inputs.js +84 -4
  119. package/dist/shader-inputs.js.map +1 -1
  120. package/dist/utils/shader-module-utils.d.ts +7 -0
  121. package/dist/utils/shader-module-utils.d.ts.map +1 -0
  122. package/dist/utils/shader-module-utils.js +46 -0
  123. package/dist/utils/shader-module-utils.js.map +1 -0
  124. package/package.json +4 -4
  125. package/src/animation-loop/animation-loop.ts +78 -46
  126. package/src/animation-loop/make-animation-loop.ts +13 -5
  127. package/src/animation-loop/request-animation-frame.ts +32 -6
  128. package/src/compute/computation.ts +31 -17
  129. package/src/dynamic-texture/dynamic-texture.ts +301 -68
  130. package/src/dynamic-texture/texture-data.ts +39 -4
  131. package/src/geometries/cone-geometry.ts +6 -1
  132. package/src/geometries/cylinder-geometry.ts +5 -1
  133. package/src/geometry/gpu-geometry.ts +8 -3
  134. package/src/index.ts +29 -4
  135. package/src/material/material-factory.ts +157 -0
  136. package/src/material/material.ts +254 -0
  137. package/src/model/model.ts +122 -50
  138. package/src/model/split-uniforms-and-bindings.ts +8 -6
  139. package/src/models/billboard-texture-model.ts +10 -8
  140. package/src/models/clip-space.ts +7 -7
  141. package/src/models/directional-light-model.ts +32 -0
  142. package/src/models/light-model-utils.ts +587 -0
  143. package/src/models/point-light-model.ts +31 -0
  144. package/src/models/spot-light-model.ts +32 -0
  145. package/src/modules/picking/color-picking.ts +123 -122
  146. package/src/modules/picking/index-picking.ts +36 -16
  147. package/src/modules/picking/legacy-color-picking.ts +8 -0
  148. package/src/modules/picking/picking-manager.ts +252 -50
  149. package/src/modules/picking/picking-uniforms.ts +38 -23
  150. package/src/modules/picking/picking.ts +22 -0
  151. package/src/passes/get-fragment-shader.ts +11 -30
  152. package/src/passes/shader-pass-renderer.ts +4 -33
  153. package/src/scenegraph/group-node.ts +16 -0
  154. package/src/scenegraph/model-node.ts +2 -2
  155. package/src/scenegraph/scenegraph-node.ts +27 -16
  156. package/src/shader-inputs.ts +165 -15
  157. package/src/utils/shader-module-utils.ts +65 -0
  158. package/dist/dynamic-texture/mipmaps.d.ts +0 -6
  159. package/dist/dynamic-texture/mipmaps.d.ts.map +0 -1
  160. package/dist/dynamic-texture/mipmaps.js +0 -441
  161. package/dist/dynamic-texture/mipmaps.js.map +0 -1
  162. package/dist/factories/pipeline-factory.d.ts +0 -37
  163. package/dist/factories/pipeline-factory.d.ts.map +0 -1
  164. package/dist/factories/pipeline-factory.js +0 -181
  165. package/dist/factories/pipeline-factory.js.map +0 -1
  166. package/dist/factories/shader-factory.d.ts +0 -22
  167. package/dist/factories/shader-factory.d.ts.map +0 -1
  168. package/dist/factories/shader-factory.js +0 -89
  169. package/dist/factories/shader-factory.js.map +0 -1
  170. package/dist/types.d.ts +0 -7
  171. package/dist/types.d.ts.map +0 -1
  172. package/dist/types.js +0 -5
  173. package/dist/types.js.map +0 -1
  174. package/src/dynamic-texture/mipmaps.ts +0 -517
  175. package/src/factories/pipeline-factory.ts +0 -225
  176. package/src/factories/shader-factory.ts +0 -105
  177. package/src/types.ts +0 -11
@@ -3,19 +3,18 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  // A lot of imports, but then Model is where it all comes together...
6
- import type {TypedArray} from '@math.gl/types';
7
- import type {
8
- RenderPipelineProps,
9
- RenderPipelineParameters,
10
- BufferLayout,
11
- Shader,
12
- VertexArray,
13
- TransformFeedback,
14
- AttributeInfo,
15
- Binding,
16
- PrimitiveTopology
17
- } from '@luma.gl/core';
6
+ import {type TypedArray} from '@math.gl/types';
18
7
  import {
8
+ type RenderPipelineProps,
9
+ type RenderPipelineParameters,
10
+ type BufferLayout,
11
+ type Shader,
12
+ type VertexArray,
13
+ type TransformFeedback,
14
+ type AttributeInfo,
15
+ type Binding,
16
+ type BindingsByGroup,
17
+ type PrimitiveTopology,
19
18
  Device,
20
19
  DeviceFeature,
21
20
  Buffer,
@@ -24,27 +23,33 @@ import {
24
23
  Sampler,
25
24
  RenderPipeline,
26
25
  RenderPass,
26
+ PipelineFactory,
27
+ ShaderFactory,
27
28
  UniformStore,
28
29
  log,
29
- getTypedArrayConstructor,
30
- getAttributeInfosFromLayouts
30
+ dataTypeDecoder,
31
+ getAttributeInfosFromLayouts,
32
+ normalizeBindingsByGroup
31
33
  } from '@luma.gl/core';
32
34
 
33
- import type {ShaderModule, PlatformInfo} from '@luma.gl/shadertools';
35
+ import type {ShaderBindingDebugRow, ShaderModule, PlatformInfo} from '@luma.gl/shadertools';
34
36
  import {ShaderAssembler} from '@luma.gl/shadertools';
35
37
 
36
38
  import type {Geometry} from '../geometry/geometry';
37
39
  import {GPUGeometry, makeGPUGeometry} from '../geometry/gpu-geometry';
38
- import {PipelineFactory} from '../factories/pipeline-factory';
39
- import {ShaderFactory} from '../factories/shader-factory';
40
40
  import {getDebugTableForShaderLayout} from '../debug/debug-shader-layout';
41
41
  import {debugFramebuffer} from '../debug/debug-framebuffer';
42
42
  import {deepEqual} from '../utils/deep-equal';
43
43
  import {BufferLayoutHelper} from '../utils/buffer-layout-helper';
44
44
  import {sortedBufferLayoutByShaderSourceLocations} from '../utils/buffer-layout-order';
45
+ import {
46
+ mergeShaderModuleBindingsIntoLayout,
47
+ shaderModuleHasUniforms
48
+ } from '../utils/shader-module-utils';
45
49
  import {uid} from '../utils/uid';
46
50
  import {ShaderInputs} from '../shader-inputs';
47
51
  import {DynamicTexture} from '../dynamic-texture/dynamic-texture';
52
+ import {Material} from '../material/material';
48
53
 
49
54
  const LOG_DRAW_PRIORITY = 2;
50
55
  const LOG_DRAW_TIMEOUT = 10000;
@@ -62,8 +67,12 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs' | 'bindings'> & {
62
67
 
63
68
  /** Shader inputs, used to generated uniform buffers and bindings */
64
69
  shaderInputs?: ShaderInputs;
70
+ /** Material-owned group-3 bindings */
71
+ material?: Material;
65
72
  /** Bindings */
66
73
  bindings?: Record<string, Binding | DynamicTexture>;
74
+ /** WebGL-only uniforms */
75
+ uniforms?: Record<string, unknown>;
67
76
  /** Parameters that are built into the pipeline */
68
77
  parameters?: RenderPipelineParameters;
69
78
 
@@ -114,7 +123,7 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs' | 'bindings'> & {
114
123
  * - Reuses and lazily recompiles {@link RenderPipeline | pipelines} as needed.
115
124
  * - Integrates with `@luma.gl/shadertools` to assemble GLSL or WGSL from shader modules.
116
125
  * - Manages geometry attributes and buffer bindings.
117
- * - Accepts textures, samplers and uniform buffers as bindings, including `AsyncTexture`.
126
+ * - Accepts textures, samplers and uniform buffers as bindings, including `DynamicTexture`.
118
127
  * - Provides detailed debug logging and optional shader source inspection.
119
128
  */
120
129
  export class Model {
@@ -132,6 +141,8 @@ export class Model {
132
141
  indexBuffer: null,
133
142
  attributes: {},
134
143
  constantAttributes: {},
144
+ bindings: {},
145
+ uniforms: {},
135
146
  varyings: [],
136
147
 
137
148
  isInstanced: undefined!,
@@ -139,6 +150,7 @@ export class Model {
139
150
  vertexCount: 0,
140
151
 
141
152
  shaderInputs: undefined!,
153
+ material: undefined!,
142
154
  pipelineFactory: undefined!,
143
155
  shaderFactory: undefined!,
144
156
  transformFeedback: undefined!,
@@ -212,6 +224,7 @@ export class Model {
212
224
  /** ShaderInputs instance */
213
225
  // @ts-expect-error Assigned in function called by constructor
214
226
  shaderInputs: ShaderInputs;
227
+ material: Material | null = null;
215
228
  // @ts-expect-error Assigned in function called by constructor
216
229
  _uniformStore: UniformStore;
217
230
 
@@ -225,6 +238,7 @@ export class Model {
225
238
 
226
239
  /** "Time" of last draw. Monotonically increasing timestamp */
227
240
  _lastDrawTimestamp: number = -1;
241
+ private _bindingTable: ShaderBindingDebugRow[] = [];
228
242
 
229
243
  get [Symbol.toStringTag](): string {
230
244
  return 'Model';
@@ -242,6 +256,8 @@ export class Model {
242
256
 
243
257
  Object.assign(this.userData, props.userData);
244
258
 
259
+ this.material = props.material || null;
260
+
245
261
  // Setup shader module inputs
246
262
  const moduleMap = Object.fromEntries(
247
263
  this.props.modules?.map(module => [module.name, module]) || []
@@ -261,6 +277,9 @@ export class Model {
261
277
  // @ts-ignore shaderInputs is assigned in setShaderInputs above.
262
278
  (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
263
279
 
280
+ this.props.shaderLayout =
281
+ mergeShaderModuleBindingsIntoLayout(this.props.shaderLayout, modules) || null;
282
+
264
283
  const isWebGPU = this.device.type === 'webgpu';
265
284
 
266
285
  // WebGPU
@@ -268,7 +287,7 @@ export class Model {
268
287
  // TODO - this is wrong, compile a single shader
269
288
  if (isWebGPU && this.props.source) {
270
289
  // WGSL
271
- const {source, getUniforms} = this.props.shaderAssembler.assembleWGSLShader({
290
+ const {source, getUniforms, bindingTable} = this.props.shaderAssembler.assembleWGSLShader({
272
291
  platformInfo,
273
292
  ...this.props,
274
293
  modules
@@ -276,9 +295,16 @@ export class Model {
276
295
  this.source = source;
277
296
  // @ts-expect-error
278
297
  this._getModuleUniforms = getUniforms;
298
+ this._bindingTable = bindingTable;
279
299
  // Extract shader layout after modules have been added to WGSL source, to include any bindings added by modules
280
- // @ts-expect-error Method on WebGPUDevice
281
- this.props.shaderLayout ||= device.getShaderLayout(this.source);
300
+ const inferredShaderLayout = (
301
+ device as Device & {getShaderLayout?: (source: string) => any}
302
+ ).getShaderLayout?.(this.source);
303
+ this.props.shaderLayout =
304
+ mergeShaderModuleBindingsIntoLayout(
305
+ this.props.shaderLayout || inferredShaderLayout || null,
306
+ modules
307
+ ) || null;
282
308
  } else {
283
309
  // GLSL
284
310
  const {vs, fs, getUniforms} = this.props.shaderAssembler.assembleGLSLShaderPair({
@@ -291,6 +317,7 @@ export class Model {
291
317
  this.fs = fs;
292
318
  // @ts-expect-error
293
319
  this._getModuleUniforms = getUniforms;
320
+ this._bindingTable = [];
294
321
  }
295
322
 
296
323
  this.vertexCount = this.props.vertexCount;
@@ -357,7 +384,7 @@ export class Model {
357
384
  this.pipelineFactory.release(this.pipeline);
358
385
  // Release the shaders
359
386
  this.shaderFactory.release(this.pipeline.vs);
360
- if (this.pipeline.fs) {
387
+ if (this.pipeline.fs && this.pipeline.fs !== this.pipeline.vs) {
361
388
  this.shaderFactory.release(this.pipeline.fs);
362
389
  }
363
390
  this._uniformStore.destroy();
@@ -385,6 +412,11 @@ export class Model {
385
412
  this._needsRedraw ||= reason;
386
413
  }
387
414
 
415
+ /** Returns WGSL binding debug rows for the assembled shader. Returns an empty array for GLSL models. */
416
+ getBindingDebugTable(): readonly ShaderBindingDebugRow[] {
417
+ return this._bindingTable;
418
+ }
419
+
388
420
  /** Update uniforms and pipeline state prior to drawing. */
389
421
  predraw(): void {
390
422
  // Update uniform buffers if needed
@@ -422,14 +454,8 @@ export class Model {
422
454
  // Application can call Model.predraw() to avoid this.
423
455
  this.pipeline = this._updatePipeline();
424
456
 
425
- // Set pipeline state, we may be sharing a pipeline so we need to set all state on every draw
426
- // Any caching needs to be done inside the pipeline functions
427
- // TODO this is a busy initialized check for all bindings every frame
428
-
429
457
  const syncBindings = this._getBindings();
430
- this.pipeline.setBindings(syncBindings, {
431
- disableWarnings: this.props.disableWarnings
432
- });
458
+ const syncBindGroups = this._getBindGroups();
433
459
 
434
460
  const {indexBuffer} = this.vertexArray;
435
461
  const indexCount = indexBuffer
@@ -444,6 +470,13 @@ export class Model {
444
470
  instanceCount: this.instanceCount,
445
471
  indexCount,
446
472
  transformFeedback: this.transformFeedback || undefined,
473
+ // Pipelines may be shared across models when caching is enabled, so bindings
474
+ // and WebGL uniforms must be supplied on every draw instead of being stored
475
+ // on the pipeline instance.
476
+ bindings: syncBindings,
477
+ bindGroups: syncBindGroups,
478
+ _bindGroupCacheKeys: this._getBindGroupCacheKeys(),
479
+ uniforms: this.props.uniforms,
447
480
  // WebGL shares underlying cached pipelines even for models that have different parameters and topology,
448
481
  // so we must provide our unique parameters to each draw
449
482
  // (In WebGPU most parameters are encoded in the pipeline and cannot be changed per draw call)
@@ -571,7 +604,7 @@ export class Model {
571
604
  this._uniformStore = new UniformStore(this.shaderInputs.modules);
572
605
  // Create uniform buffer bindings for all modules that actually have uniforms
573
606
  for (const [moduleName, module] of Object.entries(this.shaderInputs.modules)) {
574
- if (shaderModuleHasUniforms(module)) {
607
+ if (shaderModuleHasUniforms(module) && !this.material?.ownsModule(moduleName)) {
575
608
  const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
576
609
  this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
577
610
  }
@@ -579,10 +612,15 @@ export class Model {
579
612
  this.setNeedsRedraw('shaderInputs');
580
613
  }
581
614
 
615
+ setMaterial(material: Material | null): void {
616
+ this.material = material;
617
+ this.setNeedsRedraw('material');
618
+ }
619
+
582
620
  /** Update uniform buffers from the model's shader inputs */
583
621
  updateShaderInputs(): void {
584
622
  this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
585
- this.setBindings(this.shaderInputs.getBindingValues());
623
+ this.setBindings(this._getNonMaterialBindings(this.shaderInputs.getBindingValues()));
586
624
  // TODO - this is already tracked through buffer/texture update times?
587
625
  this.setNeedsRedraw('shaderInputs');
588
626
  }
@@ -701,6 +739,11 @@ export class Model {
701
739
  return binding.id;
702
740
  }
703
741
  }
742
+ for (const binding of Object.values(this.material?.bindings || {})) {
743
+ if (binding instanceof DynamicTexture && !binding.isReady) {
744
+ return binding.id;
745
+ }
746
+ }
704
747
  return false;
705
748
  }
706
749
 
@@ -722,6 +765,32 @@ export class Model {
722
765
  return validBindings;
723
766
  }
724
767
 
768
+ _getBindGroups(): BindingsByGroup {
769
+ const shaderLayout = this.pipeline?.shaderLayout || this.props.shaderLayout || {bindings: []};
770
+ const bindGroups = shaderLayout.bindings.length
771
+ ? normalizeBindingsByGroup(shaderLayout, this._getBindings())
772
+ : {0: this._getBindings()};
773
+
774
+ if (!this.material) {
775
+ return bindGroups;
776
+ }
777
+
778
+ for (const [groupKey, groupBindings] of Object.entries(this.material.getBindingsByGroup())) {
779
+ const group = Number(groupKey);
780
+ bindGroups[group] = {
781
+ ...(bindGroups[group] || {}),
782
+ ...groupBindings
783
+ };
784
+ }
785
+
786
+ return bindGroups;
787
+ }
788
+
789
+ _getBindGroupCacheKeys(): Partial<Record<number, object>> {
790
+ const bindGroupCacheKey = this.material?.getBindGroupCacheKey(3);
791
+ return bindGroupCacheKey ? {3: bindGroupCacheKey} : {};
792
+ }
793
+
725
794
  /** Get the timestamp of the latest updated bound GPU memory resource (buffer/texture). */
726
795
  _getBindingsUpdateTimestamp(): number {
727
796
  let timestamp = 0;
@@ -739,7 +808,7 @@ export class Model {
739
808
  timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
740
809
  }
741
810
  }
742
- return timestamp;
811
+ return Math.max(timestamp, this.material?.getBindingsUpdateTimestamp() || 0);
743
812
  }
744
813
 
745
814
  /**
@@ -811,12 +880,11 @@ export class Model {
811
880
 
812
881
  this.pipeline = this.pipelineFactory.createRenderPipeline({
813
882
  ...this.props,
883
+ bindings: undefined,
814
884
  bufferLayout: this.bufferLayout,
815
885
  topology: this.topology,
816
886
  parameters: this.parameters,
817
- // TODO - why set bindings here when we reset them every frame?
818
- // Should we expose a BindGroup abstraction?
819
- bindings: this._getBindings(),
887
+ bindGroups: this._getBindGroups(),
820
888
  vs,
821
889
  fs
822
890
  });
@@ -827,7 +895,9 @@ export class Model {
827
895
  );
828
896
 
829
897
  if (prevShaderVs) this.shaderFactory.release(prevShaderVs);
830
- if (prevShaderFs) this.shaderFactory.release(prevShaderFs);
898
+ if (prevShaderFs && prevShaderFs !== prevShaderVs) {
899
+ this.shaderFactory.release(prevShaderFs);
900
+ }
831
901
  }
832
902
  return this.pipeline;
833
903
  }
@@ -915,15 +985,27 @@ export class Model {
915
985
 
916
986
  // TODO - fix typing of luma data types
917
987
  _getBufferOrConstantValues(attribute: Buffer | TypedArray, dataType: any): string {
918
- const TypedArrayConstructor = getTypedArrayConstructor(dataType);
988
+ const TypedArrayConstructor = dataTypeDecoder.getTypedArrayConstructor(dataType);
919
989
  const typedArray =
920
990
  attribute instanceof Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
921
991
  return typedArray.toString();
922
992
  }
923
- }
924
993
 
925
- function shaderModuleHasUniforms(module: ShaderModule): boolean {
926
- return Boolean(module.uniformTypes && !isObjectEmpty(module.uniformTypes));
994
+ private _getNonMaterialBindings(
995
+ bindings: Record<string, Binding | DynamicTexture>
996
+ ): Record<string, Binding | DynamicTexture> {
997
+ if (!this.material) {
998
+ return bindings;
999
+ }
1000
+
1001
+ const filteredBindings: Record<string, Binding | DynamicTexture> = {};
1002
+ for (const [name, binding] of Object.entries(bindings)) {
1003
+ if (!this.material.ownsBinding(name)) {
1004
+ filteredBindings[name] = binding;
1005
+ }
1006
+ }
1007
+ return filteredBindings;
1008
+ }
927
1009
  }
928
1010
 
929
1011
  // HELPERS
@@ -939,13 +1021,3 @@ export function getPlatformInfo(device: Device): PlatformInfo {
939
1021
  features: device.features as unknown as Set<DeviceFeature>
940
1022
  };
941
1023
  }
942
-
943
- /** Returns true if given object is empty, false otherwise. */
944
- function isObjectEmpty(obj: object): boolean {
945
- // @ts-ignore key is unused
946
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
947
- for (const key in obj) {
948
- return false;
949
- }
950
- return true;
951
- }
@@ -2,7 +2,8 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import type {UniformValue, Binding} from '@luma.gl/core';
5
+ import type {Binding, CompositeShaderType, UniformValue} from '@luma.gl/core';
6
+ import type {ShaderModuleUniformValue} from '@luma.gl/shadertools';
6
7
  import {isNumericArray} from '@math.gl/types';
7
8
 
8
9
  export function isUniformValue(value: unknown): value is UniformValue {
@@ -11,19 +12,20 @@ export function isUniformValue(value: unknown): value is UniformValue {
11
12
 
12
13
  type UniformsAndBindings = {
13
14
  bindings: Record<string, Binding>;
14
- uniforms: Record<string, UniformValue>;
15
+ uniforms: Record<string, ShaderModuleUniformValue>;
15
16
  };
16
17
 
17
18
  export function splitUniformsAndBindings(
18
- uniforms: Record<string, Binding | UniformValue>
19
+ uniforms: Record<string, Binding | ShaderModuleUniformValue>,
20
+ uniformTypes: Readonly<Record<string, CompositeShaderType>> = {}
19
21
  ): UniformsAndBindings {
20
22
  const result: UniformsAndBindings = {bindings: {}, uniforms: {}};
21
23
  Object.keys(uniforms).forEach(name => {
22
24
  const uniform = uniforms[name];
23
- if (isUniformValue(uniform)) {
24
- result.uniforms[name] = uniform;
25
+ if (Object.prototype.hasOwnProperty.call(uniformTypes, name) || isUniformValue(uniform)) {
26
+ result.uniforms[name] = uniform as ShaderModuleUniformValue;
25
27
  } else {
26
- result.bindings[name] = uniform;
28
+ result.bindings[name] = uniform as Binding;
27
29
  }
28
30
  });
29
31
 
@@ -22,15 +22,15 @@ struct backgroundUniforms {
22
22
  };
23
23
  @group(0) @binding(2) var<uniform> background: backgroundUniforms;
24
24
 
25
- fn billboardTexture_getTextureUV(coordinates: vec2<f32>) -> vec2<f32> {
25
+ fn billboardTexture_getTextureUV(uv: vec2<f32>) -> vec2<f32> {
26
26
  let scale: vec2<f32> = background.scale;
27
- var position: vec2<f32> = (coordinates - vec2<f32>(0.5, 0.5)) / scale + vec2<f32>(0.5, 0.5);
27
+ var position: vec2<f32> = (uv - vec2<f32>(0.5, 0.5)) / scale + vec2<f32>(0.5, 0.5);
28
28
  return position;
29
29
  }
30
30
 
31
31
  @fragment
32
32
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
33
- let position: vec2<f32> = billboardTexture_getTextureUV(inputs.coordinate);
33
+ let position: vec2<f32> = billboardTexture_getTextureUV(inputs.uv);
34
34
  return textureSample(backgroundTexture, backgroundTextureSampler, position);
35
35
  }
36
36
  `;
@@ -79,21 +79,23 @@ export class BackgroundTextureModel extends ClipSpace {
79
79
 
80
80
  constructor(device: Device, props: BackgroundTextureModelProps) {
81
81
  super(device, {
82
+ ...props,
82
83
  id: props.id || 'background-texture-model',
83
84
  source: BACKGROUND_FS_WGSL,
84
85
  fs: BACKGROUND_FS,
85
- modules: [backgroundModule],
86
+ modules: [...(props.modules || []), backgroundModule],
86
87
  parameters: {
87
88
  depthWriteEnabled: false,
89
+ ...(props.parameters || {}),
88
90
  ...(props.blend
89
91
  ? {
90
92
  blend: true,
91
93
  blendColorOperation: 'add',
92
94
  blendAlphaOperation: 'add',
93
- blendColorSrcFactor: 'one',
94
- blendColorDstFactor: 'one-minus-src',
95
- blendAlphaSrcFactor: 'one',
96
- blendAlphaDstFactor: 'one-minus-src-alpha'
95
+ blendColorSrcFactor: 'one-minus-dst-alpha',
96
+ blendColorDstFactor: 'one',
97
+ blendAlphaSrcFactor: 'one-minus-dst-alpha',
98
+ blendAlphaDstFactor: 'one'
97
99
  }
98
100
  : {})
99
101
  }
@@ -10,9 +10,9 @@ import {uid} from '../utils/uid';
10
10
 
11
11
  const CLIPSPACE_VERTEX_SHADER_WGSL = /* wgsl */ `\
12
12
  struct VertexInputs {
13
- @location(0) clipSpacePosition: vec2<f32>,
14
- @location(1) texCoord: vec2<f32>,
15
- @location(2) coordinate: vec2<f32>
13
+ @location(0) clipSpacePositions: vec2<f32>,
14
+ @location(1) texCoords: vec2<f32>,
15
+ @location(2) coordinates: vec2<f32>
16
16
  }
17
17
 
18
18
  struct FragmentInputs {
@@ -25,10 +25,10 @@ struct FragmentInputs {
25
25
  @vertex
26
26
  fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
27
27
  var outputs: FragmentInputs;
28
- outputs.Position = vec4(inputs.clipSpacePosition, 0., 1.);
29
- outputs.position = inputs.clipSpacePosition;
30
- outputs.coordinate = inputs.coordinate;
31
- outputs.uv = inputs.texCoord;
28
+ outputs.Position = vec4(inputs.clipSpacePositions, 0., 1.);
29
+ outputs.position = inputs.clipSpacePositions;
30
+ outputs.coordinate = inputs.coordinates;
31
+ outputs.uv = inputs.texCoords;
32
32
  return outputs;
33
33
  }
34
34
  `;
@@ -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
+ buildDirectionalLightInstanceData,
10
+ type DirectionalLightModelProps
11
+ } from './light-model-utils';
12
+
13
+ const DIRECTIONAL_LIGHT_GEOMETRY = new ConeGeometry({
14
+ cap: true,
15
+ nradial: 12,
16
+ nvertical: 1,
17
+ radius: 1
18
+ });
19
+
20
+ export type {DirectionalLightModelProps} from './light-model-utils';
21
+
22
+ export class DirectionalLightModel extends BaseLightModel<DirectionalLightModelProps> {
23
+ constructor(device: Device, props: DirectionalLightModelProps) {
24
+ super(device, props, {
25
+ anchorMode: 'apex',
26
+ buildInstanceData: buildDirectionalLightInstanceData,
27
+ geometry: DIRECTIONAL_LIGHT_GEOMETRY,
28
+ idPrefix: 'directional-light-model',
29
+ sizePropNames: ['directionalLightLength']
30
+ });
31
+ }
32
+ }