@luma.gl/engine 9.1.0-alpha.9 → 9.1.0-beta.3

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 (196) hide show
  1. package/dist/animation/key-frames.js +1 -0
  2. package/dist/animation/key-frames.js.map +1 -0
  3. package/dist/animation/timeline.js +1 -0
  4. package/dist/animation/timeline.js.map +1 -0
  5. package/dist/animation-loop/animation-loop-template.js +1 -0
  6. package/dist/animation-loop/animation-loop-template.js.map +1 -0
  7. package/dist/animation-loop/animation-loop.d.ts +2 -0
  8. package/dist/animation-loop/animation-loop.d.ts.map +1 -1
  9. package/dist/animation-loop/animation-loop.js +24 -6
  10. package/dist/animation-loop/animation-loop.js.map +1 -0
  11. package/dist/animation-loop/animation-props.js +1 -0
  12. package/dist/animation-loop/animation-props.js.map +1 -0
  13. package/dist/animation-loop/make-animation-loop.d.ts +5 -1
  14. package/dist/animation-loop/make-animation-loop.d.ts.map +1 -1
  15. package/dist/animation-loop/make-animation-loop.js +3 -1
  16. package/dist/animation-loop/make-animation-loop.js.map +1 -0
  17. package/dist/animation-loop/request-animation-frame.d.ts +4 -2
  18. package/dist/animation-loop/request-animation-frame.d.ts.map +1 -1
  19. package/dist/animation-loop/request-animation-frame.js +5 -3
  20. package/dist/animation-loop/request-animation-frame.js.map +1 -0
  21. package/dist/application-utils/load-file.d.ts +1 -1
  22. package/dist/application-utils/load-file.d.ts.map +1 -1
  23. package/dist/application-utils/load-file.js +2 -2
  24. package/dist/application-utils/load-file.js.map +1 -0
  25. package/dist/application-utils/random.js +1 -0
  26. package/dist/application-utils/random.js.map +1 -0
  27. package/dist/async-texture/async-texture.d.ts +14 -2
  28. package/dist/async-texture/async-texture.d.ts.map +1 -1
  29. package/dist/async-texture/async-texture.js +31 -0
  30. package/dist/async-texture/async-texture.js.map +1 -0
  31. package/dist/compute/buffer-transform.d.ts +41 -0
  32. package/dist/compute/buffer-transform.d.ts.map +1 -0
  33. package/dist/{transform → compute}/buffer-transform.js +19 -12
  34. package/dist/compute/buffer-transform.js.map +1 -0
  35. package/dist/{computation.d.ts → compute/computation.d.ts} +3 -3
  36. package/dist/compute/computation.d.ts.map +1 -0
  37. package/dist/{computation.js → compute/computation.js} +7 -8
  38. package/dist/compute/computation.js.map +1 -0
  39. package/dist/compute/swap.d.ts +48 -0
  40. package/dist/compute/swap.d.ts.map +1 -0
  41. package/dist/compute/swap.js +91 -0
  42. package/dist/compute/swap.js.map +1 -0
  43. package/dist/{transform → compute}/texture-transform.d.ts +0 -6
  44. package/dist/compute/texture-transform.d.ts.map +1 -0
  45. package/dist/{transform → compute}/texture-transform.js +10 -15
  46. package/dist/compute/texture-transform.js.map +1 -0
  47. package/dist/debug/copy-texture-to-image.js +1 -0
  48. package/dist/debug/copy-texture-to-image.js.map +1 -0
  49. package/dist/debug/debug-framebuffer.js +2 -1
  50. package/dist/debug/debug-framebuffer.js.map +1 -0
  51. package/dist/debug/debug-shader-layout.js +2 -1
  52. package/dist/debug/debug-shader-layout.js.map +1 -0
  53. package/dist/debug/pixel-data-utils.js +1 -0
  54. package/dist/debug/pixel-data-utils.js.map +1 -0
  55. package/dist/dist.dev.js +2697 -5857
  56. package/dist/dist.min.js +420 -103
  57. package/dist/{lib → factories}/pipeline-factory.d.ts +11 -1
  58. package/dist/factories/pipeline-factory.d.ts.map +1 -0
  59. package/dist/factories/pipeline-factory.js +181 -0
  60. package/dist/factories/pipeline-factory.js.map +1 -0
  61. package/dist/{lib → factories}/shader-factory.d.ts +5 -1
  62. package/dist/factories/shader-factory.d.ts.map +1 -0
  63. package/dist/{lib → factories}/shader-factory.js +39 -4
  64. package/dist/factories/shader-factory.js.map +1 -0
  65. package/dist/geometries/cone-geometry.js +1 -0
  66. package/dist/geometries/cone-geometry.js.map +1 -0
  67. package/dist/geometries/cube-geometry.js +1 -0
  68. package/dist/geometries/cube-geometry.js.map +1 -0
  69. package/dist/geometries/cylinder-geometry.js +1 -0
  70. package/dist/geometries/cylinder-geometry.js.map +1 -0
  71. package/dist/geometries/ico-sphere-geometry.js +1 -0
  72. package/dist/geometries/ico-sphere-geometry.js.map +1 -0
  73. package/dist/geometries/plane-geometry.js +1 -0
  74. package/dist/geometries/plane-geometry.js.map +1 -0
  75. package/dist/geometries/sphere-geometry.js +1 -0
  76. package/dist/geometries/sphere-geometry.js.map +1 -0
  77. package/dist/geometries/truncated-cone-geometry.js +1 -0
  78. package/dist/geometries/truncated-cone-geometry.js.map +1 -0
  79. package/dist/geometry/geometry-table.js +1 -0
  80. package/dist/geometry/geometry-table.js.map +1 -0
  81. package/dist/geometry/geometry-utils.js +1 -0
  82. package/dist/geometry/geometry-utils.js.map +1 -0
  83. package/dist/geometry/geometry.js +1 -0
  84. package/dist/geometry/geometry.js.map +1 -0
  85. package/dist/geometry/gpu-geometry.js +1 -0
  86. package/dist/geometry/gpu-geometry.js.map +1 -0
  87. package/dist/geometry/gpu-table.js +1 -0
  88. package/dist/geometry/gpu-table.js.map +1 -0
  89. package/dist/index.cjs +1416 -209
  90. package/dist/index.cjs.map +4 -4
  91. package/dist/index.d.ts +23 -12
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +19 -9
  94. package/dist/index.js.map +1 -0
  95. package/dist/model/model.d.ts +11 -10
  96. package/dist/model/model.d.ts.map +1 -1
  97. package/dist/model/model.js +93 -70
  98. package/dist/model/model.js.map +1 -0
  99. package/dist/model/split-uniforms-and-bindings.d.ts +1 -1
  100. package/dist/model/split-uniforms-and-bindings.d.ts.map +1 -1
  101. package/dist/model/split-uniforms-and-bindings.js +2 -1
  102. package/dist/model/split-uniforms-and-bindings.js.map +1 -0
  103. package/dist/models/billboard-texture-model.d.ts +23 -0
  104. package/dist/models/billboard-texture-model.d.ts.map +1 -0
  105. package/dist/models/billboard-texture-model.js +78 -0
  106. package/dist/models/billboard-texture-model.js.map +1 -0
  107. package/dist/models/billboard-texture-module.d.ts +10 -0
  108. package/dist/models/billboard-texture-module.d.ts.map +1 -0
  109. package/dist/models/billboard-texture-module.js +37 -0
  110. package/dist/models/billboard-texture-module.js.map +1 -0
  111. package/dist/{lib → models}/clip-space.d.ts +3 -1
  112. package/dist/models/clip-space.d.ts.map +1 -0
  113. package/dist/models/clip-space.js +77 -0
  114. package/dist/models/clip-space.js.map +1 -0
  115. package/dist/modules/picking/color-picking.d.ts +28 -0
  116. package/dist/modules/picking/color-picking.d.ts.map +1 -0
  117. package/dist/modules/picking/color-picking.js +177 -0
  118. package/dist/modules/picking/color-picking.js.map +1 -0
  119. package/dist/modules/picking/index-picking.d.ts +32 -0
  120. package/dist/modules/picking/index-picking.d.ts.map +1 -0
  121. package/dist/modules/picking/index-picking.js +148 -0
  122. package/dist/modules/picking/index-picking.js.map +1 -0
  123. package/dist/modules/picking/legacy-picking-manager.d.ts +27 -0
  124. package/dist/modules/picking/legacy-picking-manager.d.ts.map +1 -0
  125. package/dist/modules/picking/legacy-picking-manager.js +76 -0
  126. package/dist/modules/picking/legacy-picking-manager.js.map +1 -0
  127. package/dist/modules/picking/picking-manager.d.ts +45 -0
  128. package/dist/modules/picking/picking-manager.d.ts.map +1 -0
  129. package/dist/modules/picking/picking-manager.js +101 -0
  130. package/dist/modules/picking/picking-manager.js.map +1 -0
  131. package/dist/modules/picking/picking-uniforms.d.ts +79 -0
  132. package/dist/modules/picking/picking-uniforms.d.ts.map +1 -0
  133. package/dist/modules/picking/picking-uniforms.js +109 -0
  134. package/dist/modules/picking/picking-uniforms.js.map +1 -0
  135. package/dist/passes/get-fragment-shader.d.ts +12 -0
  136. package/dist/passes/get-fragment-shader.d.ts.map +1 -0
  137. package/dist/passes/get-fragment-shader.js +117 -0
  138. package/dist/passes/get-fragment-shader.js.map +1 -0
  139. package/dist/passes/shader-pass-renderer.d.ts +63 -0
  140. package/dist/passes/shader-pass-renderer.d.ts.map +1 -0
  141. package/dist/passes/shader-pass-renderer.js +197 -0
  142. package/dist/passes/shader-pass-renderer.js.map +1 -0
  143. package/dist/scenegraph/group-node.js +1 -0
  144. package/dist/scenegraph/group-node.js.map +1 -0
  145. package/dist/scenegraph/model-node.js +1 -0
  146. package/dist/scenegraph/model-node.js.map +1 -0
  147. package/dist/scenegraph/scenegraph-node.js +1 -0
  148. package/dist/scenegraph/scenegraph-node.js.map +1 -0
  149. package/dist/shader-inputs.d.ts +8 -21
  150. package/dist/shader-inputs.d.ts.map +1 -1
  151. package/dist/shader-inputs.js +15 -11
  152. package/dist/shader-inputs.js.map +1 -0
  153. package/dist/utils/deep-equal.js +1 -0
  154. package/dist/utils/deep-equal.js.map +1 -0
  155. package/dist/utils/uid.js +1 -0
  156. package/dist/utils/uid.js.map +1 -0
  157. package/package.json +6 -6
  158. package/src/animation-loop/animation-loop.ts +27 -6
  159. package/src/animation-loop/make-animation-loop.ts +8 -3
  160. package/src/animation-loop/request-animation-frame.ts +4 -3
  161. package/src/application-utils/load-file.ts +2 -4
  162. package/src/async-texture/async-texture.ts +39 -7
  163. package/src/{transform → compute}/buffer-transform.ts +30 -14
  164. package/src/{computation.ts → compute/computation.ts} +14 -8
  165. package/src/compute/swap.ts +116 -0
  166. package/src/{transform → compute}/texture-transform.ts +9 -18
  167. package/src/debug/debug-framebuffer.ts +1 -1
  168. package/src/debug/debug-shader-layout.ts +1 -1
  169. package/src/factories/pipeline-factory.ts +222 -0
  170. package/src/{lib → factories}/shader-factory.ts +41 -5
  171. package/src/index.ts +35 -16
  172. package/src/model/model.ts +133 -78
  173. package/src/model/split-uniforms-and-bindings.ts +4 -4
  174. package/src/models/billboard-texture-model.ts +98 -0
  175. package/src/models/billboard-texture-module.ts +49 -0
  176. package/src/models/clip-space.ts +88 -0
  177. package/src/modules/picking/README.md +88 -0
  178. package/src/modules/picking/color-picking.ts +190 -0
  179. package/src/modules/picking/index-picking.ts +156 -0
  180. package/src/modules/picking/legacy-picking-manager.ts +99 -0
  181. package/src/modules/picking/picking-manager.ts +137 -0
  182. package/src/modules/picking/picking-uniforms.ts +179 -0
  183. package/src/passes/get-fragment-shader.ts +129 -0
  184. package/src/passes/shader-pass-renderer.ts +252 -0
  185. package/src/shader-inputs.ts +27 -48
  186. package/dist/computation.d.ts.map +0 -1
  187. package/dist/lib/clip-space.d.ts.map +0 -1
  188. package/dist/lib/clip-space.js +0 -46
  189. package/dist/lib/pipeline-factory.d.ts.map +0 -1
  190. package/dist/lib/pipeline-factory.js +0 -98
  191. package/dist/lib/shader-factory.d.ts.map +0 -1
  192. package/dist/transform/buffer-transform.d.ts +0 -35
  193. package/dist/transform/buffer-transform.d.ts.map +0 -1
  194. package/dist/transform/texture-transform.d.ts.map +0 -1
  195. package/src/lib/clip-space.ts +0 -53
  196. package/src/lib/pipeline-factory.ts +0 -126
@@ -4,31 +4,51 @@
4
4
 
5
5
  // A lot of imports, but then Model is where it all comes together...
6
6
  import type {TypedArray} from '@math.gl/types';
7
- import type {RenderPipelineProps, RenderPipelineParameters} from '@luma.gl/core';
8
- import type {BufferLayout, Shader, VertexArray, TransformFeedback} from '@luma.gl/core';
9
- import type {AttributeInfo, Binding, UniformValue, PrimitiveTopology} from '@luma.gl/core';
10
- import {Device, DeviceFeature, Buffer, Texture, TextureView, Sampler} from '@luma.gl/core';
11
- import {RenderPipeline, RenderPass, UniformStore} from '@luma.gl/core';
12
- import {log} from '@luma.gl/core';
13
- import {getTypedArrayFromDataType, getAttributeInfosFromLayouts} from '@luma.gl/core';
7
+ import type {
8
+ RenderPipelineProps,
9
+ RenderPipelineParameters,
10
+ BufferLayout,
11
+ Shader,
12
+ VertexArray,
13
+ TransformFeedback,
14
+ AttributeInfo,
15
+ Binding,
16
+ UniformValue,
17
+ PrimitiveTopology
18
+ } from '@luma.gl/core';
19
+ import {
20
+ Device,
21
+ DeviceFeature,
22
+ Buffer,
23
+ Texture,
24
+ TextureView,
25
+ Sampler,
26
+ RenderPipeline,
27
+ RenderPass,
28
+ UniformStore,
29
+ log,
30
+ getTypedArrayFromDataType,
31
+ getAttributeInfosFromLayouts,
32
+ _BufferLayoutHelper
33
+ } from '@luma.gl/core';
14
34
 
15
35
  import type {ShaderModule, PlatformInfo} from '@luma.gl/shadertools';
16
36
  import {ShaderAssembler, getShaderLayoutFromWGSL} from '@luma.gl/shadertools';
17
37
 
18
38
  import type {Geometry} from '../geometry/geometry';
19
39
  import {GPUGeometry, makeGPUGeometry} from '../geometry/gpu-geometry';
20
- import {PipelineFactory} from '../lib/pipeline-factory';
21
- import {ShaderFactory} from '../lib/shader-factory';
40
+ import {PipelineFactory} from '../factories/pipeline-factory';
41
+ import {ShaderFactory} from '../factories/shader-factory';
22
42
  import {getDebugTableForShaderLayout} from '../debug/debug-shader-layout';
23
43
  import {debugFramebuffer} from '../debug/debug-framebuffer';
24
44
  import {deepEqual} from '../utils/deep-equal';
25
45
  import {uid} from '../utils/uid';
26
- import {splitUniformsAndBindings} from './split-uniforms-and-bindings';
27
-
28
46
  import {ShaderInputs} from '../shader-inputs';
29
47
  // import type {AsyncTextureProps} from '../async-texture/async-texture';
30
48
  import {AsyncTexture} from '../async-texture/async-texture';
31
49
 
50
+ import {splitUniformsAndBindings} from './split-uniforms-and-bindings';
51
+
32
52
  const LOG_DRAW_PRIORITY = 2;
33
53
  const LOG_DRAW_TIMEOUT = 10000;
34
54
 
@@ -201,6 +221,14 @@ export class Model {
201
221
  /** "Time" of last draw. Monotonically increasing timestamp */
202
222
  _lastDrawTimestamp: number = -1;
203
223
 
224
+ get [Symbol.toStringTag](): string {
225
+ return 'Model';
226
+ }
227
+
228
+ toString(): string {
229
+ return `Model(${this.id})`;
230
+ }
231
+
204
232
  constructor(device: Device, props: ModelProps) {
205
233
  this.props = {...Model.defaultProps, ...props};
206
234
  props = this.props;
@@ -213,6 +241,7 @@ export class Model {
213
241
  const moduleMap = Object.fromEntries(
214
242
  this.props.modules?.map(module => [module.name, module]) || []
215
243
  );
244
+ // @ts-expect-error Fix typings
216
245
  this.setShaderInputs(props.shaderInputs || new ShaderInputs(moduleMap));
217
246
 
218
247
  // Setup shader assembler
@@ -274,7 +303,8 @@ export class Model {
274
303
  this.pipeline = this._updatePipeline();
275
304
 
276
305
  this.vertexArray = device.createVertexArray({
277
- renderPipeline: this.pipeline
306
+ shaderLayout: this.pipeline.shaderLayout,
307
+ bufferLayout: this.pipeline.bufferLayout
278
308
  });
279
309
 
280
310
  // Now we can apply geometry attributes
@@ -306,11 +336,11 @@ export class Model {
306
336
  this.setBindings(props.bindings);
307
337
  }
308
338
  if (props.uniforms) {
309
- this.setUniforms(props.uniforms);
339
+ this.setUniformsWebGL(props.uniforms);
310
340
  }
311
341
  if (props.moduleSettings) {
312
342
  // log.warn('Model.props.moduleSettings is deprecated. Use Model.shaderInputs.setProps()')();
313
- this.updateModuleSettings(props.moduleSettings);
343
+ this.updateModuleSettingsWebGL(props.moduleSettings);
314
344
  }
315
345
  if (props.transformFeedback) {
316
346
  this.transformFeedback = props.transformFeedback;
@@ -321,16 +351,19 @@ export class Model {
321
351
  }
322
352
 
323
353
  destroy(): void {
324
- if (this._destroyed) return;
325
- this.pipelineFactory.release(this.pipeline);
326
- this.shaderFactory.release(this.pipeline.vs);
327
- if (this.pipeline.fs) {
328
- this.shaderFactory.release(this.pipeline.fs);
329
- }
330
- this._uniformStore.destroy();
331
- // TODO - mark resource as managed and destroyIfManaged() ?
332
- this._gpuGeometry?.destroy();
333
- this._destroyed = true;
354
+ if (!this._destroyed) {
355
+ // Release pipeline before we destroy the shaders used by the pipeline
356
+ this.pipelineFactory.release(this.pipeline);
357
+ // Release the shaders
358
+ this.shaderFactory.release(this.pipeline.vs);
359
+ if (this.pipeline.fs) {
360
+ this.shaderFactory.release(this.pipeline.fs);
361
+ }
362
+ this._uniformStore.destroy();
363
+ // TODO - mark resource as managed and destroyIfManaged() ?
364
+ this._gpuGeometry?.destroy();
365
+ this._destroyed = true;
366
+ }
334
367
  }
335
368
 
336
369
  // Draw call
@@ -351,7 +384,7 @@ export class Model {
351
384
  this._needsRedraw ||= reason;
352
385
  }
353
386
 
354
- predraw() {
387
+ predraw(): void {
355
388
  // Update uniform buffers if needed
356
389
  this.updateShaderInputs();
357
390
  // Check if the pipeline is invalidated
@@ -359,10 +392,22 @@ export class Model {
359
392
  }
360
393
 
361
394
  draw(renderPass: RenderPass): boolean {
362
- this.predraw();
395
+ const loadingBinding = this._areBindingsLoading();
396
+ if (loadingBinding) {
397
+ log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
398
+ return false;
399
+ }
400
+
401
+ try {
402
+ renderPass.pushDebugGroup(`${this}.predraw(${renderPass})`);
403
+ this.predraw();
404
+ } finally {
405
+ renderPass.popDebugGroup();
406
+ }
363
407
 
364
408
  let drawSuccess: boolean;
365
409
  try {
410
+ renderPass.pushDebugGroup(`${this}.draw(${renderPass})`);
366
411
  this._logDrawCallStart();
367
412
 
368
413
  // Update the pipeline if invalidated
@@ -373,6 +418,7 @@ export class Model {
373
418
  // Set pipeline state, we may be sharing a pipeline so we need to set all state on every draw
374
419
  // Any caching needs to be done inside the pipeline functions
375
420
  // TODO this is a busy initialized check for all bindings every frame
421
+
376
422
  const syncBindings = this._getBindings();
377
423
  this.pipeline.setBindings(syncBindings, {
378
424
  disableWarnings: this.props.disableWarnings
@@ -401,6 +447,7 @@ export class Model {
401
447
  topology: this.topology
402
448
  });
403
449
  } finally {
450
+ renderPass.popDebugGroup();
404
451
  this._logDrawCallEnd();
405
452
  }
406
453
  this._logFramebuffer(renderPass);
@@ -427,7 +474,11 @@ export class Model {
427
474
  const gpuGeometry = geometry && makeGPUGeometry(this.device, geometry);
428
475
  if (gpuGeometry) {
429
476
  this.setTopology(gpuGeometry.topology || 'triangle-list');
430
- this.bufferLayout = mergeBufferLayouts(gpuGeometry.bufferLayout, this.bufferLayout);
477
+ const bufferLayoutHelper = new _BufferLayoutHelper(this.bufferLayout);
478
+ this.bufferLayout = bufferLayoutHelper.mergeBufferLayouts(
479
+ gpuGeometry.bufferLayout,
480
+ this.bufferLayout
481
+ );
431
482
  if (this.vertexArray) {
432
483
  this._setGeometryAttributes(gpuGeometry);
433
484
  }
@@ -451,8 +502,9 @@ export class Model {
451
502
  * @note Triggers a pipeline rebuild / pipeline cache fetch
452
503
  */
453
504
  setBufferLayout(bufferLayout: BufferLayout[]): void {
505
+ const bufferLayoutHelper = new _BufferLayoutHelper(this.bufferLayout);
454
506
  this.bufferLayout = this._gpuGeometry
455
- ? mergeBufferLayouts(bufferLayout, this._gpuGeometry.bufferLayout)
507
+ ? bufferLayoutHelper.mergeBufferLayouts(bufferLayout, this._gpuGeometry.bufferLayout)
456
508
  : bufferLayout;
457
509
  this._setPipelineNeedsUpdate('bufferLayout');
458
510
 
@@ -462,7 +514,8 @@ export class Model {
462
514
  // vertex array needs to be updated if we update buffer layout,
463
515
  // but not if we update parameters
464
516
  this.vertexArray = this.device.createVertexArray({
465
- renderPipeline: this.pipeline
517
+ shaderLayout: this.pipeline.shaderLayout,
518
+ bufferLayout: this.pipeline.bufferLayout
466
519
  });
467
520
 
468
521
  // Reapply geometry attributes to the new vertex array
@@ -512,10 +565,12 @@ export class Model {
512
565
  setShaderInputs(shaderInputs: ShaderInputs): void {
513
566
  this.shaderInputs = shaderInputs;
514
567
  this._uniformStore = new UniformStore(this.shaderInputs.modules);
515
- // Create uniform buffer bindings for all modules
516
- for (const moduleName of Object.keys(this.shaderInputs.modules)) {
517
- const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
518
- this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
568
+ // Create uniform buffer bindings for all modules that actually have uniforms
569
+ for (const [moduleName, module] of Object.entries(this.shaderInputs.modules)) {
570
+ if (shaderModuleHasUniforms(module)) {
571
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
572
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
573
+ }
519
574
  }
520
575
  this.setNeedsRedraw('shaderInputs');
521
576
  }
@@ -523,7 +578,7 @@ export class Model {
523
578
  /** Update uniform buffers from the model's shader inputs */
524
579
  updateShaderInputs(): void {
525
580
  this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
526
- this.setBindings(this.shaderInputs.getBindings());
581
+ this.setBindings(this.shaderInputs.getBindingValues());
527
582
  // TODO - this is already tracked through buffer/texture update times?
528
583
  this.setNeedsRedraw('shaderInputs');
529
584
  }
@@ -558,22 +613,27 @@ export class Model {
558
613
  * @note Overrides any attributes previously set with the same name
559
614
  */
560
615
  setAttributes(buffers: Record<string, Buffer>, options?: {disableWarnings?: boolean}): void {
616
+ const disableWarnings = options?.disableWarnings ?? this.props.disableWarnings;
561
617
  if (buffers.indices) {
562
618
  log.warn(
563
619
  `Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`
564
620
  )();
565
621
  }
622
+
623
+ const bufferLayoutHelper = new _BufferLayoutHelper(this.bufferLayout);
624
+
625
+ // Check if all buffers have a layout
566
626
  for (const [bufferName, buffer] of Object.entries(buffers)) {
567
- const bufferLayout = this.bufferLayout.find(layout =>
568
- getAttributeNames(layout).includes(bufferName)
569
- );
627
+ const bufferLayout = bufferLayoutHelper.getBufferLayout(bufferName);
570
628
  if (!bufferLayout) {
571
- log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
629
+ if (!disableWarnings) {
630
+ log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
631
+ }
572
632
  continue; // eslint-disable-line no-continue
573
633
  }
574
634
 
575
635
  // For an interleaved attribute we may need to set multiple attributes
576
- const attributeNames = getAttributeNames(bufferLayout);
636
+ const attributeNames = bufferLayoutHelper.getAttributeNamesForBuffer(bufferLayout);
577
637
  let set = false;
578
638
  for (const attributeName of attributeNames) {
579
639
  const attributeInfo = this._attributeInfos[attributeName];
@@ -582,7 +642,7 @@ export class Model {
582
642
  set = true;
583
643
  }
584
644
  }
585
- if (!set && !(options?.disableWarnings ?? this.props.disableWarnings)) {
645
+ if (!set && !disableWarnings) {
586
646
  log.warn(
587
647
  `Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`
588
648
  )();
@@ -623,7 +683,7 @@ export class Model {
623
683
  * @deprecated WebGL only, use uniform buffers for portability
624
684
  * @param uniforms
625
685
  */
626
- setUniforms(uniforms: Record<string, UniformValue>): void {
686
+ setUniformsWebGL(uniforms: Record<string, UniformValue>): void {
627
687
  if (!isObjectEmpty(uniforms)) {
628
688
  this.pipeline.setUniformsWebGL(uniforms);
629
689
  Object.assign(this.uniforms, uniforms);
@@ -634,7 +694,7 @@ export class Model {
634
694
  /**
635
695
  * @deprecated Updates shader module settings (which results in uniforms being set)
636
696
  */
637
- updateModuleSettings(props: Record<string, any>): void {
697
+ updateModuleSettingsWebGL(props: Record<string, any>): void {
638
698
  // log.warn('Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()')();
639
699
  const {bindings, uniforms} = splitUniformsAndBindings(this._getModuleUniforms(props));
640
700
  Object.assign(this.bindings, bindings);
@@ -644,19 +704,32 @@ export class Model {
644
704
 
645
705
  // Internal methods
646
706
 
647
- /** Get texture / texture view from any async textures */
707
+ /** Check that bindings are loaded. Returns id of first binding that is still loading. */
708
+ _areBindingsLoading(): string | false {
709
+ for (const binding of Object.values(this.bindings)) {
710
+ if (binding instanceof AsyncTexture && !binding.isReady) {
711
+ return binding.id;
712
+ }
713
+ }
714
+ return false;
715
+ }
716
+
717
+ /** Extracts texture view from loaded async textures. Returns null if any textures have not yet been loaded. */
648
718
  _getBindings(): Record<string, Binding> {
649
- // Extract actual textures from async textures. If not loaded, null
650
- return Object.entries(this.bindings).reduce<Record<string, Binding>>((acc, [name, binding]) => {
719
+ const validBindings: Record<string, Binding> = {};
720
+
721
+ for (const [name, binding] of Object.entries(this.bindings)) {
651
722
  if (binding instanceof AsyncTexture) {
723
+ // Check that async textures are loaded
652
724
  if (binding.isReady) {
653
- acc[name] = binding.texture;
725
+ validBindings[name] = binding.texture;
654
726
  }
655
727
  } else {
656
- acc[name] = binding;
728
+ validBindings[name] = binding;
657
729
  }
658
- return acc;
659
- }, {});
730
+ }
731
+
732
+ return validBindings;
660
733
  }
661
734
 
662
735
  /** Get the timestamp of the latest updated bound GPU memory resource (buffer/texture). */
@@ -731,7 +804,7 @@ export class Model {
731
804
  id: `${this.id}-vertex`,
732
805
  stage: 'vertex',
733
806
  source: this.source || this.vs,
734
- debug: this.props.debugShaders
807
+ debugShaders: this.props.debugShaders
735
808
  });
736
809
 
737
810
  let fs: Shader | null = null;
@@ -742,7 +815,7 @@ export class Model {
742
815
  id: `${this.id}-fragment`,
743
816
  stage: 'fragment',
744
817
  source: this.source || this.fs,
745
- debug: this.props.debugShaders
818
+ debugShaders: this.props.debugShaders
746
819
  });
747
820
  }
748
821
 
@@ -812,10 +885,11 @@ export class Model {
812
885
 
813
886
  protected _drawCount = 0;
814
887
  _logFramebuffer(renderPass: RenderPass): void {
815
- const debugFramebuffers = log.get('framebuffer');
888
+ const debugFramebuffers = this.device.props.debugFramebuffers;
816
889
  this._drawCount++;
817
890
  // Update first 3 frames and then every 60 frames
818
- if (!debugFramebuffers || (this._drawCount++ > 3 && this._drawCount % 60)) {
891
+ if (!debugFramebuffers) {
892
+ // } || (this._drawCount++ > 3 && this._drawCount % 60)) {
819
893
  return;
820
894
  }
821
895
  // TODO - display framebuffer output in debug window
@@ -862,22 +936,12 @@ export class Model {
862
936
  }
863
937
  }
864
938
 
865
- // HELPERS
866
-
867
- /** TODO - move to core, document add tests */
868
- function mergeBufferLayouts(layouts1: BufferLayout[], layouts2: BufferLayout[]): BufferLayout[] {
869
- const layouts = [...layouts1];
870
- for (const attribute of layouts2) {
871
- const index = layouts.findIndex(attribute2 => attribute2.name === attribute.name);
872
- if (index < 0) {
873
- layouts.push(attribute);
874
- } else {
875
- layouts[index] = attribute;
876
- }
877
- }
878
- return layouts;
939
+ function shaderModuleHasUniforms(module: ShaderModule): boolean {
940
+ return Boolean(module.uniformTypes && !isObjectEmpty(module.uniformTypes));
879
941
  }
880
942
 
943
+ // HELPERS
944
+
881
945
  /** Create a shadertools platform info from the Device */
882
946
  export function getPlatformInfo(device: Device): PlatformInfo {
883
947
  return {
@@ -890,21 +954,12 @@ export function getPlatformInfo(device: Device): PlatformInfo {
890
954
  };
891
955
  }
892
956
 
893
- /** Get attribute names from a BufferLayout */
894
- function getAttributeNames(bufferLayout: BufferLayout): string[] {
895
- return bufferLayout.attributes
896
- ? bufferLayout.attributes?.map(layout => layout.attribute)
897
- : [bufferLayout.name];
898
- }
899
-
900
957
  /** Returns true if given object is empty, false otherwise. */
901
958
  function isObjectEmpty(obj: object): boolean {
902
- let isEmpty = true;
903
959
  // @ts-ignore key is unused
904
960
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
905
961
  for (const key in obj) {
906
- isEmpty = false;
907
- break;
962
+ return false;
908
963
  }
909
- return isEmpty;
964
+ return true;
910
965
  }
@@ -5,8 +5,8 @@
5
5
  import type {UniformValue, Binding} from '@luma.gl/core';
6
6
  import {isNumericArray} from '@math.gl/types';
7
7
 
8
- export function isUniformValue(value: unknown): boolean {
9
- return isNumericArray(value) !== null || typeof value === 'number' || typeof value === 'boolean';
8
+ export function isUniformValue(value: unknown): value is UniformValue {
9
+ return isNumericArray(value) || typeof value === 'number' || typeof value === 'boolean';
10
10
  }
11
11
 
12
12
  type UniformsAndBindings = {
@@ -21,9 +21,9 @@ export function splitUniformsAndBindings(
21
21
  Object.keys(uniforms).forEach(name => {
22
22
  const uniform = uniforms[name];
23
23
  if (isUniformValue(uniform)) {
24
- result.uniforms[name] = uniform as UniformValue;
24
+ result.uniforms[name] = uniform;
25
25
  } else {
26
- result.bindings[name] = uniform as Binding;
26
+ result.bindings[name] = uniform;
27
27
  }
28
28
  });
29
29
 
@@ -0,0 +1,98 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {Device, Texture} from '@luma.gl/core';
6
+ import {AsyncTexture} from '../async-texture/async-texture';
7
+ import {ClipSpace} from './clip-space';
8
+
9
+ const BACKGROUND_FS_WGSL = /* wgsl */ `\
10
+ @group(0) @binding(0) var backgroundTexture: texture_2d<f32>;
11
+ @group(0) @binding(1) var backgroundTextureSampler: sampler;
12
+
13
+ fn billboardTexture_getTextureUV(coordinates: vec2<f32>) -> vec2<f32> {
14
+ let iTexSize: vec2<u32> = textureDimensions(backgroundTexture, 0) * 2;
15
+ let texSize: vec2<f32> = vec2<f32>(f32(iTexSize.x), f32(iTexSize.y));
16
+ var position: vec2<f32> = coordinates.xy / texSize;
17
+ return position;
18
+ }
19
+
20
+ @fragment
21
+ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
22
+ let position: vec2<f32> = billboardTexture_getTextureUV(inputs.coordinate);
23
+ return textureSample(backgroundTexture, backgroundTextureSampler, position);
24
+ }
25
+ `;
26
+
27
+ const BACKGROUND_FS = /* glsl */ `\
28
+ #version 300 es
29
+ precision highp float;
30
+
31
+ uniform sampler2D backgroundTexture;
32
+ out vec4 fragColor;
33
+
34
+ vec2 billboardTexture_getTextureUV() {
35
+ ivec2 iTexSize = textureDimensions(backgroundTexture, 0) * 2;
36
+ vec2 texSize = vec2(float(iTexSize.x), float(iTexSize.y));
37
+ vec2 position = gl_FragCoord.xy / texSize;
38
+ return position;
39
+ }
40
+
41
+ void main(void) {
42
+ vec2 position = billboardTexture_getTextureUV();
43
+ fragColor = texture(backgroundTexture, position);
44
+ }
45
+ `;
46
+
47
+ /**
48
+ * Props for a Model that renders a bitmap into the "background", i.e covering the screen
49
+ */
50
+ export type BackgroundTextureModelProps = {
51
+ /** id of this model */
52
+ id?: string;
53
+ /** The texture to render */
54
+ backgroundTexture: Texture | AsyncTexture;
55
+ /** If true, the texture is rendered into transparent areas of the screen only, i.e blended in where background alpha is small */
56
+ blend?: boolean;
57
+ };
58
+
59
+ /**
60
+ * Model that renders a bitmap into the "background", i.e covering the screen
61
+ */
62
+ export class BackgroundTextureModel extends ClipSpace {
63
+ constructor(device: Device, props: BackgroundTextureModelProps) {
64
+ super(device, {
65
+ id: props.id || 'background-texture-model',
66
+ source: BACKGROUND_FS_WGSL,
67
+ fs: BACKGROUND_FS,
68
+ parameters: {
69
+ depthWriteEnabled: false,
70
+ depthCompare: 'always',
71
+ ...(props.blend
72
+ ? {
73
+ blend: true,
74
+ blendColorOperation: 'add',
75
+ blendAlphaOperation: 'add',
76
+ blendColorSrcFactor: 'one',
77
+ blendColorDstFactor: 'one-minus-src-color',
78
+ blendAlphaSrcFactor: 'one',
79
+ blendAlphaDstFactor: 'one-minus-src-alpha'
80
+ }
81
+ : {})
82
+ }
83
+ });
84
+
85
+ this.setTexture(props.backgroundTexture);
86
+ }
87
+
88
+ setTexture(backgroundTexture: Texture | AsyncTexture): void {
89
+ this.setBindings({
90
+ backgroundTexture
91
+ });
92
+ }
93
+
94
+ override predraw(): void {
95
+ this.shaderInputs.setProps({});
96
+ super.predraw();
97
+ }
98
+ }
@@ -0,0 +1,49 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {ShaderModule} from '@luma.gl/shadertools';
6
+
7
+ const BACKGROUND_FS = /* glsl */ `\
8
+ #version 300 es
9
+
10
+ uniform billboardTextureUniforms {
11
+ vec2 topLeft;
12
+ vec2 bottomRight;
13
+ } billboardTexture;
14
+
15
+ precision highp float;
16
+ uniform sampler2D backgroundTexture;
17
+ out vec4 fragColor;
18
+
19
+ vec2 billboardTexture_getTextureUV() {
20
+ ivec2 iTexSize = textureSize(backgroundTexture, 0) * 2;
21
+ vec2 texSize = vec2(float(iTexSize.x), float(iTexSize.y));
22
+ vec2 position = gl_FragCoord.xy / texSize;
23
+ return position;
24
+ }
25
+
26
+ void main(void) {
27
+ vec2 position = billboardTexture_getTextureUV();
28
+ fragColor = texture(backgroundTexture, position);
29
+ }
30
+ `;
31
+
32
+ type BillboardTextureProps = {
33
+ aspect: number;
34
+ };
35
+
36
+ type BillboardTextureUniforms = {
37
+ topLeft: [number, number];
38
+ bottomRight: [number, number];
39
+ };
40
+
41
+ export const billboardTexture = {
42
+ name: 'billboardTexture',
43
+ fs: BACKGROUND_FS,
44
+ dependencies: [],
45
+ uniformTypes: {
46
+ topLeft: 'vec2<f32>',
47
+ bottomRight: 'vec2<f32>'
48
+ }
49
+ } as const satisfies ShaderModule<BillboardTextureProps, BillboardTextureUniforms>;
@@ -0,0 +1,88 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ // ClipSpace
6
+ import {Device} from '@luma.gl/core';
7
+ import {Model, ModelProps} from '../model/model';
8
+ import {Geometry} from '../geometry/geometry';
9
+ import {uid} from '../utils/uid';
10
+
11
+ const CLIPSPACE_VERTEX_SHADER_WGSL = /* wgsl */ `\
12
+ struct VertexInputs {
13
+ @location(0) clipSpacePosition: vec2<f32>,
14
+ @location(1) texCoord: vec2<f32>,
15
+ @location(2) coordinate: vec2<f32>
16
+ }
17
+
18
+ struct FragmentInputs {
19
+ @builtin(position) Position : vec4<f32>,
20
+ @location(0) position : vec2<f32>,
21
+ @location(1) coordinate : vec2<f32>,
22
+ @location(2) uv : vec2<f32>
23
+ };
24
+
25
+ @vertex
26
+ fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
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;
32
+ return outputs;
33
+ }
34
+ `;
35
+
36
+ const CLIPSPACE_VERTEX_SHADER = /* glsl */ `\
37
+ #version 300 es
38
+ in vec2 clipSpacePosition;
39
+ in vec2 texCoord;
40
+ in vec2 coordinate;
41
+
42
+ out vec2 position;
43
+ out vec2 coordinate;
44
+ out vec2 uv;
45
+
46
+ void main(void) {
47
+ gl_Position = vec4(clipSpacePosition, 0., 1.);
48
+ position = clipSpacePosition;
49
+ coordinate = coordinate;
50
+ uv = texCoord;
51
+ }
52
+ `;
53
+
54
+ /* eslint-disable indent, no-multi-spaces */
55
+ const POSITIONS = [-1, -1, 1, -1, -1, 1, 1, 1];
56
+
57
+ /** Props for ClipSpace */
58
+ export type ClipSpaceProps = Omit<ModelProps, 'vs' | 'vertexCount' | 'geometry'>;
59
+
60
+ /**
61
+ * A flat geometry that covers the "visible area" that the GPU renders.
62
+ */
63
+ export class ClipSpace extends Model {
64
+ constructor(device: Device, props: ClipSpaceProps) {
65
+ const TEX_COORDS = POSITIONS.map(coord => (coord === -1 ? 0 : coord));
66
+
67
+ // For WGSL we need to append the supplied fragment shader to the default vertex shader source
68
+ if (props.source) {
69
+ props = {...props, source: `${CLIPSPACE_VERTEX_SHADER_WGSL}\n${props.source}`};
70
+ }
71
+
72
+ super(device, {
73
+ id: props.id || uid('clip-space'),
74
+ ...props,
75
+ vs: CLIPSPACE_VERTEX_SHADER,
76
+ vertexCount: 4,
77
+ geometry: new Geometry({
78
+ topology: 'triangle-strip',
79
+ vertexCount: 4,
80
+ attributes: {
81
+ clipSpacePosition: {size: 2, value: new Float32Array(POSITIONS)},
82
+ texCoord: {size: 2, value: new Float32Array(TEX_COORDS)},
83
+ coordinate: {size: 2, value: new Float32Array(TEX_COORDS)}
84
+ }
85
+ })
86
+ });
87
+ }
88
+ }