@luma.gl/engine 9.3.0-alpha.2 → 9.3.0-alpha.6

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 (87) 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 +70 -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/dist.dev.js +442 -209
  11. package/dist/dist.min.js +37 -79
  12. package/dist/dynamic-texture/dynamic-texture.d.ts +3 -3
  13. package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -1
  14. package/dist/dynamic-texture/dynamic-texture.js +187 -36
  15. package/dist/dynamic-texture/dynamic-texture.js.map +1 -1
  16. package/dist/dynamic-texture/texture-data.d.ts +4 -0
  17. package/dist/dynamic-texture/texture-data.d.ts.map +1 -1
  18. package/dist/dynamic-texture/texture-data.js +9 -1
  19. package/dist/dynamic-texture/texture-data.js.map +1 -1
  20. package/dist/factories/pipeline-factory.d.ts +7 -5
  21. package/dist/factories/pipeline-factory.d.ts.map +1 -1
  22. package/dist/factories/pipeline-factory.js +71 -36
  23. package/dist/factories/pipeline-factory.js.map +1 -1
  24. package/dist/factories/shader-factory.d.ts +0 -3
  25. package/dist/factories/shader-factory.d.ts.map +1 -1
  26. package/dist/factories/shader-factory.js +13 -19
  27. package/dist/factories/shader-factory.js.map +1 -1
  28. package/dist/geometries/cone-geometry.d.ts +3 -1
  29. package/dist/geometries/cone-geometry.d.ts.map +1 -1
  30. package/dist/geometries/cone-geometry.js.map +1 -1
  31. package/dist/geometries/cylinder-geometry.d.ts +2 -1
  32. package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
  33. package/dist/geometries/cylinder-geometry.js.map +1 -1
  34. package/dist/index.cjs +433 -208
  35. package/dist/index.cjs.map +2 -2
  36. package/dist/model/model.d.ts +3 -1
  37. package/dist/model/model.d.ts.map +1 -1
  38. package/dist/model/model.js +11 -9
  39. package/dist/model/model.js.map +1 -1
  40. package/dist/models/billboard-texture-model.d.ts.map +1 -1
  41. package/dist/models/billboard-texture-model.js +10 -8
  42. package/dist/models/billboard-texture-model.js.map +1 -1
  43. package/dist/models/clip-space.js +7 -7
  44. package/dist/modules/picking/index-picking.d.ts +1 -1
  45. package/dist/modules/picking/index-picking.d.ts.map +1 -1
  46. package/dist/modules/picking/index-picking.js +0 -6
  47. package/dist/modules/picking/index-picking.js.map +1 -1
  48. package/dist/passes/get-fragment-shader.js +11 -30
  49. package/dist/passes/get-fragment-shader.js.map +1 -1
  50. package/dist/passes/shader-pass-renderer.d.ts +0 -2
  51. package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
  52. package/dist/passes/shader-pass-renderer.js +4 -31
  53. package/dist/passes/shader-pass-renderer.js.map +1 -1
  54. package/dist/scenegraph/group-node.d.ts +5 -0
  55. package/dist/scenegraph/group-node.d.ts.map +1 -1
  56. package/dist/scenegraph/group-node.js +12 -0
  57. package/dist/scenegraph/group-node.js.map +1 -1
  58. package/dist/scenegraph/model-node.d.ts +2 -2
  59. package/dist/scenegraph/model-node.d.ts.map +1 -1
  60. package/dist/scenegraph/model-node.js.map +1 -1
  61. package/dist/scenegraph/scenegraph-node.d.ts +1 -1
  62. package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
  63. package/dist/scenegraph/scenegraph-node.js +23 -15
  64. package/dist/scenegraph/scenegraph-node.js.map +1 -1
  65. package/dist/utils/buffer-layout-order.d.ts.map +1 -1
  66. package/dist/utils/buffer-layout-order.js +12 -2
  67. package/dist/utils/buffer-layout-order.js.map +1 -1
  68. package/package.json +4 -4
  69. package/src/animation-loop/animation-loop.ts +75 -46
  70. package/src/animation-loop/make-animation-loop.ts +13 -5
  71. package/src/animation-loop/request-animation-frame.ts +32 -6
  72. package/src/dynamic-texture/dynamic-texture.ts +248 -39
  73. package/src/dynamic-texture/texture-data.ts +15 -1
  74. package/src/factories/pipeline-factory.ts +87 -46
  75. package/src/factories/shader-factory.ts +16 -20
  76. package/src/geometries/cone-geometry.ts +6 -1
  77. package/src/geometries/cylinder-geometry.ts +5 -1
  78. package/src/model/model.ts +14 -10
  79. package/src/models/billboard-texture-model.ts +10 -8
  80. package/src/models/clip-space.ts +7 -7
  81. package/src/modules/picking/index-picking.ts +0 -6
  82. package/src/passes/get-fragment-shader.ts +11 -30
  83. package/src/passes/shader-pass-renderer.ts +4 -33
  84. package/src/scenegraph/group-node.ts +16 -0
  85. package/src/scenegraph/model-node.ts +2 -2
  86. package/src/scenegraph/scenegraph-node.ts +27 -16
  87. package/src/utils/buffer-layout-order.ts +18 -2
@@ -5,6 +5,8 @@
5
5
  import {Device, Shader, ShaderProps, log} from '@luma.gl/core';
6
6
  import type {EngineModuleState} from '../types';
7
7
 
8
+ type CacheItem = {resource: Shader; useCount: number};
9
+
8
10
  /** Manages a cached pool of Shaders for reuse. */
9
11
  export class ShaderFactory {
10
12
  static readonly defaultProps: Required<ShaderProps> = {...Shader.defaultProps};
@@ -17,11 +19,8 @@ export class ShaderFactory {
17
19
  }
18
20
 
19
21
  public readonly device: Device;
20
- readonly cachingEnabled: boolean;
21
- readonly destroyPolicy: 'unused' | 'never';
22
- readonly debug: boolean;
23
22
 
24
- private readonly _cache: Record<string, {shader: Shader; useCount: number}> = {};
23
+ private readonly _cache: Record<string, CacheItem> = {};
25
24
 
26
25
  get [Symbol.toStringTag](): string {
27
26
  return 'ShaderFactory';
@@ -34,14 +33,11 @@ export class ShaderFactory {
34
33
  /** @internal */
35
34
  constructor(device: Device) {
36
35
  this.device = device;
37
- this.cachingEnabled = device.props._cacheShaders;
38
- this.destroyPolicy = device.props._cacheDestroyPolicy;
39
- this.debug = true; // device.props.debugFactories;
40
36
  }
41
37
 
42
38
  /** Requests a {@link Shader} from the cache, creating a new Shader only if necessary. */
43
39
  createShader(props: ShaderProps): Shader {
44
- if (!this.cachingEnabled) {
40
+ if (!this.device.props._cacheShaders) {
45
41
  return this.device.createShader(props);
46
42
  }
47
43
 
@@ -49,30 +45,30 @@ export class ShaderFactory {
49
45
 
50
46
  let cacheEntry = this._cache[key];
51
47
  if (!cacheEntry) {
52
- const shader = this.device.createShader({
48
+ const resource = this.device.createShader({
53
49
  ...props,
54
50
  id: props.id ? `${props.id}-cached` : undefined
55
51
  });
56
- this._cache[key] = cacheEntry = {shader, useCount: 1};
57
- if (this.debug) {
58
- log.log(3, `${this}: Created new shader ${shader.id}`)();
52
+ this._cache[key] = cacheEntry = {resource, useCount: 1};
53
+ if (this.device.props.debugFactories) {
54
+ log.log(3, `${this}: Created new shader ${resource.id}`)();
59
55
  }
60
56
  } else {
61
57
  cacheEntry.useCount++;
62
- if (this.debug) {
58
+ if (this.device.props.debugFactories) {
63
59
  log.log(
64
60
  3,
65
- `${this}: Reusing shader ${cacheEntry.shader.id} count=${cacheEntry.useCount}`
61
+ `${this}: Reusing shader ${cacheEntry.resource.id} count=${cacheEntry.useCount}`
66
62
  )();
67
63
  }
68
64
  }
69
65
 
70
- return cacheEntry.shader;
66
+ return cacheEntry.resource;
71
67
  }
72
68
 
73
69
  /** Releases a previously-requested {@link Shader}, destroying it if no users remain. */
74
70
  release(shader: Shader): void {
75
- if (!this.cachingEnabled) {
71
+ if (!this.device.props._cacheShaders) {
76
72
  shader.destroy();
77
73
  return;
78
74
  }
@@ -82,16 +78,16 @@ export class ShaderFactory {
82
78
  if (cacheEntry) {
83
79
  cacheEntry.useCount--;
84
80
  if (cacheEntry.useCount === 0) {
85
- if (this.destroyPolicy === 'unused') {
81
+ if (this.device.props._destroyShaders) {
86
82
  delete this._cache[key];
87
- cacheEntry.shader.destroy();
88
- if (this.debug) {
83
+ cacheEntry.resource.destroy();
84
+ if (this.device.props.debugFactories) {
89
85
  log.log(3, `${this}: Releasing shader ${shader.id}, destroyed`)();
90
86
  }
91
87
  }
92
88
  } else if (cacheEntry.useCount < 0) {
93
89
  throw new Error(`ShaderFactory: Shader ${shader.id} released too many times`);
94
- } else if (this.debug) {
90
+ } else if (this.device.props.debugFactories) {
95
91
  log.log(3, `${this}: Releasing shader ${shader.id} count=${cacheEntry.useCount}`)();
96
92
  }
97
93
  }
@@ -2,13 +2,18 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ import type {TruncatedConeGeometryProps} from './truncated-cone-geometry';
5
6
  import {TruncatedConeGeometry} from './truncated-cone-geometry';
6
7
  import {uid} from '../utils/uid';
7
8
 
8
- export type ConeGeometryProps = {
9
+ export type ConeGeometryProps = Omit<
10
+ TruncatedConeGeometryProps,
11
+ 'topRadius' | 'bottomRadius' | 'topCap' | 'bottomCap'
12
+ > & {
9
13
  id?: string;
10
14
  radius?: number;
11
15
  cap?: boolean;
16
+ attributes?: any;
12
17
  };
13
18
 
14
19
  export class ConeGeometry extends TruncatedConeGeometry {
@@ -2,10 +2,14 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ import type {TruncatedConeGeometryProps} from './truncated-cone-geometry';
5
6
  import {TruncatedConeGeometry} from './truncated-cone-geometry';
6
7
  import {uid} from '../utils/uid';
7
8
 
8
- export type CylinderGeometryProps = {
9
+ export type CylinderGeometryProps = Omit<
10
+ TruncatedConeGeometryProps,
11
+ 'topRadius' | 'bottomRadius'
12
+ > & {
9
13
  id?: string;
10
14
  radius?: number;
11
15
  attributes?: any;
@@ -64,6 +64,8 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs' | 'bindings'> & {
64
64
  shaderInputs?: ShaderInputs;
65
65
  /** Bindings */
66
66
  bindings?: Record<string, Binding | DynamicTexture>;
67
+ /** WebGL-only uniforms */
68
+ uniforms?: Record<string, unknown>;
67
69
  /** Parameters that are built into the pipeline */
68
70
  parameters?: RenderPipelineParameters;
69
71
 
@@ -114,7 +116,7 @@ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs' | 'bindings'> & {
114
116
  * - Reuses and lazily recompiles {@link RenderPipeline | pipelines} as needed.
115
117
  * - Integrates with `@luma.gl/shadertools` to assemble GLSL or WGSL from shader modules.
116
118
  * - Manages geometry attributes and buffer bindings.
117
- * - Accepts textures, samplers and uniform buffers as bindings, including `AsyncTexture`.
119
+ * - Accepts textures, samplers and uniform buffers as bindings, including `DynamicTexture`.
118
120
  * - Provides detailed debug logging and optional shader source inspection.
119
121
  */
120
122
  export class Model {
@@ -132,6 +134,8 @@ export class Model {
132
134
  indexBuffer: null,
133
135
  attributes: {},
134
136
  constantAttributes: {},
137
+ bindings: {},
138
+ uniforms: {},
135
139
  varyings: [],
136
140
 
137
141
  isInstanced: undefined!,
@@ -357,7 +361,7 @@ export class Model {
357
361
  this.pipelineFactory.release(this.pipeline);
358
362
  // Release the shaders
359
363
  this.shaderFactory.release(this.pipeline.vs);
360
- if (this.pipeline.fs) {
364
+ if (this.pipeline.fs && this.pipeline.fs !== this.pipeline.vs) {
361
365
  this.shaderFactory.release(this.pipeline.fs);
362
366
  }
363
367
  this._uniformStore.destroy();
@@ -422,14 +426,7 @@ export class Model {
422
426
  // Application can call Model.predraw() to avoid this.
423
427
  this.pipeline = this._updatePipeline();
424
428
 
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
429
  const syncBindings = this._getBindings();
430
- this.pipeline.setBindings(syncBindings, {
431
- disableWarnings: this.props.disableWarnings
432
- });
433
430
 
434
431
  const {indexBuffer} = this.vertexArray;
435
432
  const indexCount = indexBuffer
@@ -444,6 +441,11 @@ export class Model {
444
441
  instanceCount: this.instanceCount,
445
442
  indexCount,
446
443
  transformFeedback: this.transformFeedback || undefined,
444
+ // Pipelines may be shared across models when caching is enabled, so bindings
445
+ // and WebGL uniforms must be supplied on every draw instead of being stored
446
+ // on the pipeline instance.
447
+ bindings: syncBindings,
448
+ uniforms: this.props.uniforms,
447
449
  // WebGL shares underlying cached pipelines even for models that have different parameters and topology,
448
450
  // so we must provide our unique parameters to each draw
449
451
  // (In WebGPU most parameters are encoded in the pipeline and cannot be changed per draw call)
@@ -827,7 +829,9 @@ export class Model {
827
829
  );
828
830
 
829
831
  if (prevShaderVs) this.shaderFactory.release(prevShaderVs);
830
- if (prevShaderFs) this.shaderFactory.release(prevShaderFs);
832
+ if (prevShaderFs && prevShaderFs !== prevShaderVs) {
833
+ this.shaderFactory.release(prevShaderFs);
834
+ }
831
835
  }
832
836
  return this.pipeline;
833
837
  }
@@ -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
  `;
@@ -16,12 +16,6 @@ const INDEX_PICKING_MODE_INSTANCE = 0;
16
16
  const INDEX_PICKING_MODE_CUSTOM = 1;
17
17
  const INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
18
18
 
19
- struct indexPickingFragmentInputs = {
20
- objectIndex: int32;
21
- };
22
-
23
- let indexPickingFragmentInputs: indexPickingFragmentInputs;
24
-
25
19
  /**
26
20
  * Vertex shaders should call this function to set the object index.
27
21
  * If using instance or vertex mode, argument will be ignored, 0 can be supplied.
@@ -36,26 +36,16 @@ export function getFragmentShaderForRenderPass(options: {
36
36
  /** Get a filtering WGSL fragment shader */
37
37
  function getFilterShaderWGSL(func: string) {
38
38
  return /* wgsl */ `\
39
- // Binding 0:1 is reserved for shader passes
40
- // @group(0) @binding(0) var<uniform> brightnessContrast : brightnessContrastUniforms;
41
- @group(0) @binding(1) var texture: texture_2d<f32>;
42
- @group(0) @binding(2) var textureSampler: sampler;
43
-
44
- // This needs to be aligned with
45
- // struct FragmentInputs {
46
- // @location(0) fragUV: vec2f,
47
- // @location(1) fragPosition: vec4f,
48
- // @location(2) fragCoordinate: vec4f
49
- // };
39
+ @group(0) @binding(0) var sourceTexture: texture_2d<f32>;
40
+ @group(0) @binding(2) var sourceTextureSampler: sampler;
50
41
 
51
42
  @fragment
52
43
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
53
- let fragUV = inputs.uv;
54
- let fragCoordinate = inputs.coordinate;
55
- let texSize = vec2f(textureDimensions(texture, 0));
44
+ let texCoord = inputs.coordinate;
45
+ let texSize = vec2f(textureDimensions(sourceTexture));
56
46
 
57
- var fragColor = textureSample(texture, textureSampler, fragUV);
58
- fragColor = ${func}(fragColor, texSize, fragCoordinate);
47
+ var fragColor = textureSample(sourceTexture, sourceTextureSampler, texCoord);
48
+ fragColor = ${func}(fragColor, texSize, texCoord);
59
49
  return fragColor;
60
50
  }
61
51
  `;
@@ -64,23 +54,14 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
64
54
  /** Get a sampling WGSL fragment shader */
65
55
  function getSamplerShaderWGSL(func: string) {
66
56
  return /* wgsl */ `\
67
- // Binding 0:1 is reserved for shader passes
68
- @group(0) @binding(0) var<uniform> brightnessContrast : brightnessContrastUniforms;
69
- @group(0) @binding(1) var texture: texture_2d<f32>;
70
- @group(0) @binding(2) var sampler: sampler;
71
-
72
- struct FragmentInputs = {
73
- @location(0) fragUV: vec2f,
74
- @location(1) fragPosition: vec4f,
75
- @location(2) fragCoordinate: vec4f
76
- };
57
+ @group(0) @binding(0) var sourceTexture: texture_2d<f32>;
58
+ @group(0) @binding(2) var sourceTextureSampler: sampler;
77
59
 
78
60
  @fragment
79
61
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
80
- let texSize = vec2f(textureDimensions(texture, 0));
81
- var fragColor = textureSample(texture, sampler, fragUV);
82
- fragColor = ${func}(fragColor, texSize, texCoord);
83
- return fragColor;
62
+ let texCoord = inputs.coordinate;
63
+ let texSize = vec2f(textureDimensions(sourceTexture));
64
+ return ${func}(sourceTexture, sourceTextureSampler, texSize, texCoord);
84
65
  }
85
66
  `;
86
67
  }
@@ -29,8 +29,6 @@ export class ShaderPassRenderer {
29
29
  shaderInputs: ShaderInputs;
30
30
  passRenderers: PassRenderer[];
31
31
  swapFramebuffers: SwapFramebuffers;
32
- /** For rendering to the screen */
33
- clipSpace: ClipSpace;
34
32
  textureModel: BackgroundTextureModel;
35
33
 
36
34
  constructor(device: Device, props: ShaderPassRendererProps) {
@@ -55,31 +53,6 @@ export class ShaderPassRenderer {
55
53
  backgroundTexture: this.swapFramebuffers.current.colorAttachments[0].texture
56
54
  });
57
55
 
58
- this.clipSpace = new ClipSpace(device, {
59
- source: /* wgsl */ `\
60
- @group(0) @binding(0) var sourceTexture: texture_2d<f32>;
61
- @group(0) @binding(1) var sourceTextureSampler: sampler;
62
-
63
- @fragment
64
- fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
65
- let texCoord: vec2<f32> = inputs.coordinate;
66
- return textureSample(sourceTexture, sourceTextureSampler, texCoord);
67
- }
68
- `,
69
-
70
- fs: /* glsl */ `\
71
- #version 300 es
72
-
73
- uniform sampler2D sourceTexture;
74
- in vec2 uv;
75
- out vec4 fragColor;
76
-
77
- void main() {
78
- fragColor = texture(sourceTexture, uv);
79
- }
80
- `
81
- });
82
-
83
56
  this.passRenderers = props.shaderPasses.map(shaderPass => new PassRenderer(device, shaderPass));
84
57
  }
85
58
 
@@ -89,7 +62,6 @@ void main() {
89
62
  subPassRenderer.destroy();
90
63
  }
91
64
  this.swapFramebuffers.destroy();
92
- this.clipSpace.destroy();
93
65
  this.textureModel.destroy();
94
66
  }
95
67
 
@@ -112,16 +84,15 @@ void main() {
112
84
 
113
85
  const framebuffer = this.device
114
86
  .getDefaultCanvasContext()
115
- // @ts-expect-error TODO - remove after republish
116
- .getCurrentFramebuffer({depthStencilAttachment: false});
87
+ .getCurrentFramebuffer({depthStencilFormat: false});
117
88
  const renderPass = this.device.beginRenderPass({
118
89
  id: 'shader-pass-renderer-to-screen',
119
90
  framebuffer,
120
91
  // clearColor: [1, 1, 0, 1],
121
- clearDepth: 1
92
+ clearDepth: false
122
93
  });
123
- this.clipSpace.setBindings({sourceTexture: outputTexture});
124
- this.clipSpace.draw(renderPass);
94
+ this.textureModel.setProps({backgroundTexture: outputTexture});
95
+ this.textureModel.draw(renderPass);
125
96
  renderPass.end();
126
97
  return true;
127
98
  }
@@ -108,4 +108,20 @@ export class GroupNode extends ScenegraphNode {
108
108
  }
109
109
  }
110
110
  }
111
+
112
+ preorderTraversal(
113
+ visitor: (node: ScenegraphNode, context: {worldMatrix: Matrix4}) => void,
114
+ {worldMatrix = new Matrix4()} = {}
115
+ ) {
116
+ const modelMatrix = new Matrix4(worldMatrix).multiplyRight(this.matrix);
117
+ visitor(this, {worldMatrix: modelMatrix});
118
+
119
+ for (const child of this.children) {
120
+ if (child instanceof GroupNode) {
121
+ child.preorderTraversal(visitor, {worldMatrix: modelMatrix});
122
+ } else {
123
+ visitor(child, {worldMatrix: modelMatrix});
124
+ }
125
+ }
126
+ }
111
127
  }
@@ -9,12 +9,12 @@ import {Model} from '../model/model';
9
9
  export type ModelNodeProps = ScenegraphNodeProps & {
10
10
  model: Model;
11
11
  managedResources?: any[];
12
- bounds?: [number[], number[]];
12
+ bounds?: [[number, number, number], [number, number, number]];
13
13
  };
14
14
 
15
15
  export class ModelNode extends ScenegraphNode {
16
16
  readonly model: Model;
17
- bounds: [number[], number[]] | null = null;
17
+ bounds: [[number, number, number], [number, number, number]] | null = null;
18
18
  managedResources: any[];
19
19
 
20
20
  // TODO - is this used? override callbacks to make sure we call them with this
@@ -5,6 +5,12 @@
5
5
  import {Vector3, Matrix4, NumericArray} from '@math.gl/core';
6
6
  import {uid} from '../utils/uid';
7
7
 
8
+ function assert(condition: boolean, message?: string): asserts condition {
9
+ if (!condition) {
10
+ throw new Error(message);
11
+ }
12
+ }
13
+
8
14
  /** Properties for creating a new Scenegraph */
9
15
  export type ScenegraphNodeProps = {
10
16
  id?: string;
@@ -57,19 +63,19 @@ export class ScenegraphNode {
57
63
  }
58
64
 
59
65
  setPosition(position: any): this {
60
- // assert(position.length === 3, 'setPosition requires vector argument');
66
+ assert(position.length === 3, 'setPosition requires vector argument');
61
67
  this.position = position;
62
68
  return this;
63
69
  }
64
70
 
65
71
  setRotation(rotation: any): this {
66
- // assert(rotation.length === 3, 'setRotation requires vector argument');
72
+ assert(rotation.length === 3 || rotation.length === 4, 'setRotation requires vector argument');
67
73
  this.rotation = rotation;
68
74
  return this;
69
75
  }
70
76
 
71
77
  setScale(scale: any): this {
72
- // assert(scale.length === 3, 'setScale requires vector argument');
78
+ assert(scale.length === 3, 'setScale requires vector argument');
73
79
  this.scale = scale;
74
80
  return this;
75
81
  }
@@ -105,19 +111,20 @@ export class ScenegraphNode {
105
111
  }
106
112
 
107
113
  updateMatrix(): this {
108
- const pos = this.position;
109
- const rot = this.rotation;
110
- const scale = this.scale;
111
-
112
114
  this.matrix.identity();
113
- this.matrix.translate(pos);
114
- this.matrix.rotateXYZ(rot);
115
- this.matrix.scale(scale);
115
+ this.matrix.translate(this.position);
116
+ if (this.rotation.length === 4) {
117
+ const rotationMatrix = new Matrix4().fromQuaternion(this.rotation);
118
+ this.matrix.multiplyRight(rotationMatrix);
119
+ } else {
120
+ this.matrix.rotateXYZ(this.rotation);
121
+ }
122
+ this.matrix.scale(this.scale);
123
+
116
124
  return this;
117
125
  }
118
126
 
119
- update(options: {position?: any; rotation?: any; scale?: any} = {}): this {
120
- const {position, rotation, scale} = options;
127
+ update({position, rotation, scale}: {position?: any; rotation?: any; scale?: any} = {}): this {
121
128
  if (position) {
122
129
  this.setPosition(position);
123
130
  }
@@ -127,7 +134,9 @@ export class ScenegraphNode {
127
134
  if (scale) {
128
135
  this.setScale(scale);
129
136
  }
137
+
130
138
  this.updateMatrix();
139
+
131
140
  return this;
132
141
  }
133
142
 
@@ -188,18 +197,20 @@ export class ScenegraphNode {
188
197
  // this.display = props.display;
189
198
  // }
190
199
 
191
- if ('position' in props) {
200
+ if (props?.position) {
192
201
  this.setPosition(props.position);
193
202
  }
194
- if ('rotation' in props) {
203
+ if (props?.rotation) {
195
204
  this.setRotation(props.rotation);
196
205
  }
197
- if ('scale' in props) {
206
+ if (props?.scale) {
198
207
  this.setScale(props.scale);
199
208
  }
200
209
 
210
+ this.updateMatrix();
211
+
201
212
  // Matrix overwrites other props
202
- if ('matrix' in props) {
213
+ if (props?.matrix) {
203
214
  this.setMatrix(props.matrix);
204
215
  }
205
216
 
@@ -4,6 +4,22 @@
4
4
 
5
5
  import {type BufferLayout, type ShaderLayout} from '@luma.gl/core';
6
6
 
7
+ function getMinLocation(
8
+ attributeNames: string[],
9
+ shaderLayoutMap: Record<string, number | undefined>
10
+ ): number {
11
+ let minLocation = Infinity;
12
+
13
+ for (const name of attributeNames) {
14
+ const location = shaderLayoutMap[name];
15
+ if (location !== undefined) {
16
+ minLocation = Math.min(minLocation, location);
17
+ }
18
+ }
19
+
20
+ return minLocation;
21
+ }
22
+
7
23
  export function sortedBufferLayoutByShaderSourceLocations(
8
24
  shaderLayout: ShaderLayout,
9
25
  bufferLayout: BufferLayout[]
@@ -16,8 +32,8 @@ export function sortedBufferLayoutByShaderSourceLocations(
16
32
  sortedLayout.sort((a, b) => {
17
33
  const attributeNamesA = a.attributes ? a.attributes.map(attr => attr.attribute) : [a.name];
18
34
  const attributeNamesB = b.attributes ? b.attributes.map(attr => attr.attribute) : [b.name];
19
- const minLocationA = Math.min(...attributeNamesA.map(name => shaderLayoutMap[name]));
20
- const minLocationB = Math.min(...attributeNamesB.map(name => shaderLayoutMap[name]));
35
+ const minLocationA = getMinLocation(attributeNamesA, shaderLayoutMap);
36
+ const minLocationB = getMinLocation(attributeNamesB, shaderLayoutMap);
21
37
 
22
38
  return minLocationA - minLocationB;
23
39
  });