@luma.gl/engine 9.0.0-alpha.3 → 9.0.0-alpha.31

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 (148) hide show
  1. package/LICENSE +2 -1
  2. package/dist/animation/key-frames.d.ts +1 -1
  3. package/dist/animation/key-frames.d.ts.map +1 -1
  4. package/dist/animation/key-frames.js +0 -20
  5. package/dist/animation/key-frames.js.map +1 -1
  6. package/dist/animation/timeline.d.ts +5 -5
  7. package/dist/animation/timeline.d.ts.map +1 -1
  8. package/dist/animation/timeline.js +0 -30
  9. package/dist/animation/timeline.js.map +1 -1
  10. package/dist/{lib → animation-loop}/animation-loop.d.ts +28 -29
  11. package/dist/animation-loop/animation-loop.d.ts.map +1 -0
  12. package/dist/{lib → animation-loop}/animation-loop.js +96 -175
  13. package/dist/animation-loop/animation-loop.js.map +1 -0
  14. package/dist/{lib → animation-loop}/animation-props.d.ts +7 -7
  15. package/dist/animation-loop/animation-props.d.ts.map +1 -0
  16. package/dist/animation-loop/animation-props.js.map +1 -0
  17. package/dist/animation-loop/make-animation-loop.d.ts +6 -0
  18. package/dist/animation-loop/make-animation-loop.d.ts.map +1 -0
  19. package/dist/animation-loop/make-animation-loop.js +28 -0
  20. package/dist/animation-loop/make-animation-loop.js.map +1 -0
  21. package/dist/animation-loop/render-loop.d.ts +23 -0
  22. package/dist/animation-loop/render-loop.d.ts.map +1 -0
  23. package/dist/animation-loop/render-loop.js +7 -0
  24. package/dist/animation-loop/render-loop.js.map +1 -0
  25. package/dist/dist.dev.js +14977 -0
  26. package/dist/geometries/cone-geometry.d.ts +1 -1
  27. package/dist/geometries/cone-geometry.d.ts.map +1 -1
  28. package/dist/geometries/cone-geometry.js +6 -5
  29. package/dist/geometries/cone-geometry.js.map +1 -1
  30. package/dist/geometries/cube-geometry.d.ts +2 -2
  31. package/dist/geometries/cube-geometry.d.ts.map +1 -1
  32. package/dist/geometries/cube-geometry.js +15 -8
  33. package/dist/geometries/cube-geometry.js.map +1 -1
  34. package/dist/geometries/cylinder-geometry.d.ts +1 -1
  35. package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
  36. package/dist/geometries/cylinder-geometry.js +6 -5
  37. package/dist/geometries/cylinder-geometry.js.map +1 -1
  38. package/dist/geometries/ico-sphere-geometry.d.ts +2 -2
  39. package/dist/geometries/ico-sphere-geometry.d.ts.map +1 -1
  40. package/dist/geometries/ico-sphere-geometry.js +9 -18
  41. package/dist/geometries/ico-sphere-geometry.js.map +1 -1
  42. package/dist/geometries/plane-geometry.d.ts +2 -2
  43. package/dist/geometries/plane-geometry.d.ts.map +1 -1
  44. package/dist/geometries/plane-geometry.js +10 -19
  45. package/dist/geometries/plane-geometry.js.map +1 -1
  46. package/dist/geometries/sphere-geometry.d.ts +2 -2
  47. package/dist/geometries/sphere-geometry.d.ts.map +1 -1
  48. package/dist/geometries/sphere-geometry.js +9 -13
  49. package/dist/geometries/sphere-geometry.js.map +1 -1
  50. package/dist/geometries/truncated-cone-geometry.d.ts +2 -2
  51. package/dist/geometries/truncated-cone-geometry.d.ts.map +1 -1
  52. package/dist/geometries/truncated-cone-geometry.js +9 -14
  53. package/dist/geometries/truncated-cone-geometry.js.map +1 -1
  54. package/dist/geometry/geometry-table.d.ts +2 -2
  55. package/dist/geometry/geometry-table.d.ts.map +1 -1
  56. package/dist/geometry/geometry-table.js.map +1 -1
  57. package/dist/geometry/geometry-utils.d.ts.map +1 -1
  58. package/dist/geometry/geometry-utils.js +0 -9
  59. package/dist/geometry/geometry-utils.js.map +1 -1
  60. package/dist/geometry/geometry.d.ts +35 -58
  61. package/dist/geometry/geometry.d.ts.map +1 -1
  62. package/dist/geometry/geometry.js +26 -89
  63. package/dist/geometry/geometry.js.map +1 -1
  64. package/dist/geometry/primitive-utils.js.map +1 -1
  65. package/dist/index.cjs +2576 -0
  66. package/dist/index.d.ts +18 -9
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +20 -13
  69. package/dist/index.js.map +1 -1
  70. package/dist/lib/clip-space.d.ts +8 -0
  71. package/dist/lib/clip-space.d.ts.map +1 -1
  72. package/dist/lib/clip-space.js +36 -1
  73. package/dist/lib/clip-space.js.map +1 -1
  74. package/dist/lib/pipeline-factory.d.ts +19 -14
  75. package/dist/lib/pipeline-factory.d.ts.map +1 -1
  76. package/dist/lib/pipeline-factory.js +52 -63
  77. package/dist/lib/pipeline-factory.js.map +1 -1
  78. package/dist/{lib → model}/model-utils.d.ts +2 -2
  79. package/dist/model/model-utils.d.ts.map +1 -0
  80. package/dist/{lib → model}/model-utils.js +3 -8
  81. package/dist/model/model-utils.js.map +1 -0
  82. package/dist/model/model.d.ts +67 -0
  83. package/dist/model/model.d.ts.map +1 -0
  84. package/dist/{lib → model}/model.js +59 -76
  85. package/dist/model/model.js.map +1 -0
  86. package/dist/scenegraph/group-node.d.ts +21 -0
  87. package/dist/scenegraph/group-node.d.ts.map +1 -0
  88. package/dist/scenegraph/group-node.js +95 -0
  89. package/dist/scenegraph/group-node.js.map +1 -0
  90. package/dist/scenegraph/model-node.d.ts +18 -0
  91. package/dist/scenegraph/model-node.d.ts.map +1 -0
  92. package/dist/scenegraph/model-node.js +29 -0
  93. package/dist/scenegraph/model-node.js.map +1 -0
  94. package/dist/scenegraph/scenegraph-node.d.ts +56 -0
  95. package/dist/scenegraph/scenegraph-node.d.ts.map +1 -0
  96. package/dist/scenegraph/scenegraph-node.js +142 -0
  97. package/dist/scenegraph/scenegraph-node.js.map +1 -0
  98. package/dist/transform/transform.d.ts +98 -0
  99. package/dist/transform/transform.d.ts.map +1 -0
  100. package/dist/transform/transform.js +67 -0
  101. package/dist/transform/transform.js.map +1 -0
  102. package/dist.min.js +314 -0
  103. package/package.json +22 -12
  104. package/src/animation/timeline.ts +2 -2
  105. package/src/{lib → animation-loop}/animation-loop.ts +115 -97
  106. package/src/{lib → animation-loop}/animation-props.ts +6 -5
  107. package/src/animation-loop/make-animation-loop.ts +44 -0
  108. package/src/animation-loop/render-loop.ts +23 -0
  109. package/src/geometries/cone-geometry.ts +1 -1
  110. package/src/geometries/cube-geometry.ts +6 -3
  111. package/src/geometries/cylinder-geometry.ts +2 -2
  112. package/src/geometries/ico-sphere-geometry.ts +7 -6
  113. package/src/geometries/plane-geometry.ts +5 -4
  114. package/src/geometries/sphere-geometry.ts +4 -3
  115. package/src/geometries/truncated-cone-geometry.ts +4 -3
  116. package/src/geometry/geometry-table.ts +1 -1
  117. package/src/geometry/geometry-utils.ts +3 -3
  118. package/src/geometry/geometry.ts +72 -115
  119. package/src/index.ts +28 -12
  120. package/src/lib/clip-space.ts +17 -15
  121. package/src/lib/pipeline-factory.ts +60 -50
  122. package/src/{lib → model}/model-utils.ts +5 -4
  123. package/src/model/model.ts +230 -0
  124. package/src/scenegraph/group-node.ts +103 -0
  125. package/src/scenegraph/model-node.ts +50 -0
  126. package/src/scenegraph/scenegraph-node.ts +204 -0
  127. package/src/transform/transform.ts +246 -0
  128. package/dist/bundle.d.ts +0 -2
  129. package/dist/bundle.d.ts.map +0 -1
  130. package/dist/bundle.js +0 -5
  131. package/dist/bundle.js.map +0 -1
  132. package/dist/lib/animation-loop.d.ts.map +0 -1
  133. package/dist/lib/animation-loop.js.map +0 -1
  134. package/dist/lib/animation-props.d.ts.map +0 -1
  135. package/dist/lib/animation-props.js.map +0 -1
  136. package/dist/lib/model-utils.d.ts.map +0 -1
  137. package/dist/lib/model-utils.js.map +0 -1
  138. package/dist/lib/model.d.ts +0 -41
  139. package/dist/lib/model.d.ts.map +0 -1
  140. package/dist/lib/model.js.map +0 -1
  141. package/dist/lib/render-loop.d.ts +0 -14
  142. package/dist/lib/render-loop.d.ts.map +0 -1
  143. package/dist/lib/render-loop.js +0 -49
  144. package/dist/lib/render-loop.js.map +0 -1
  145. package/src/bundle.ts +0 -4
  146. package/src/lib/model.ts +0 -179
  147. package/src/lib/render-loop.ts +0 -58
  148. /package/dist/{lib → animation-loop}/animation-props.js +0 -0
@@ -1,34 +1,36 @@
1
- import type {RenderPipelineProps, RenderPipelineParameters} from '@luma.gl/api';
2
- import {Device, RenderPipeline, ComputePipeline} from '@luma.gl/api/';
1
+ import type {RenderPipelineParameters, PrimitiveTopology, ShaderLayout} from '@luma.gl/core';
2
+ import {Device, RenderPipeline} from '@luma.gl/core';
3
3
  import type { ShaderModule } from '@luma.gl/shadertools';
4
4
  import {assembleShaders} from '@luma.gl/shadertools';
5
5
 
6
6
  export type GetRenderPipelineOptions = {
7
- vs: string,
8
- fs: string,
9
- topology;
7
+ vs: string;
8
+ fs: string | null;
9
+ topology: PrimitiveTopology;
10
+ shaderLayout?: ShaderLayout | null;
10
11
  parameters?: RenderPipelineParameters;
11
12
 
12
13
  modules?: ShaderModule[];
13
- defines?: Record<string, string>,
14
- inject?: Record<string, string>,
14
+ defines?: Record<string, string | number | boolean>;
15
+ inject?: Record<string, string>;
15
16
  transpileToGLSL100?: boolean;
16
17
 
17
- varyings?: string[],
18
+ varyings?: string[];
18
19
  bufferMode?: number,
19
20
  };
20
21
 
21
22
  export type GetComputePipelineOptions = {
22
- cs: string,
23
+ cs: string;
24
+
23
25
  parameters?: RenderPipelineParameters;
24
26
 
25
27
  modules?: ShaderModule[];
26
- defines?: Record<string, string>,
27
- inject?: Record<string, string>,
28
+ defines?: Record<string, string>;
29
+ inject?: Record<string, string>;
28
30
  transpileToGLSL100?: boolean;
29
31
 
30
- varyings?: string[],
31
- bufferMode?: number,
32
+ varyings?: string[];
33
+ bufferMode?: number;
32
34
  };
33
35
 
34
36
  const DEFAULT_RENDER_PIPELINE_OPTIONS: Required<GetRenderPipelineOptions> = {
@@ -38,6 +40,7 @@ const DEFAULT_RENDER_PIPELINE_OPTIONS: Required<GetRenderPipelineOptions> = {
38
40
  defines: {},
39
41
  inject: {},
40
42
  transpileToGLSL100: false,
43
+ shaderLayout: null,
41
44
 
42
45
  varyings: [],
43
46
  bufferMode: 0x8c8d, // // varyings/bufferMode for xform feedback, 0x8c8d: SEPARATE_ATTRIBS
@@ -45,8 +48,10 @@ const DEFAULT_RENDER_PIPELINE_OPTIONS: Required<GetRenderPipelineOptions> = {
45
48
  parameters: {}
46
49
  };
47
50
 
51
+ type GetUniformsFunc = (props?: Record<string, any>) => Record<string, any>;
52
+
48
53
  /** Efficiently create shared pipelines with varying parameters */
49
- export default class PipelineFactory {
54
+ export class PipelineFactory {
50
55
  readonly device: Device;
51
56
 
52
57
  stateHash: number = 0; // Used to change hashing if hooks are modified
@@ -56,7 +61,7 @@ export default class PipelineFactory {
56
61
 
57
62
  private readonly _pipelineCache: Record<string, RenderPipeline> = {};
58
63
 
59
- private readonly _getUniforms: Record<string, any> = {};
64
+ private readonly _getUniforms: Record<string, GetUniformsFunc> = {};
60
65
  private readonly _hookFunctions: any[] = [];
61
66
  private _defaultModules: any[] = [];
62
67
  // private readonly _registeredModules = {}; // TODO: Remove? This isn't used anywhere in luma.gl
@@ -72,20 +77,20 @@ export default class PipelineFactory {
72
77
  this.device = device;
73
78
  }
74
79
 
75
- // addDefaultModule(module: ShaderModule): void {
76
- // if (!this._defaultModules.find((m) => m.name === (typeof module === 'string' ? module : module.name))) {
77
- // this._defaultModules.push(module);
78
- // }
79
- // this.stateHash++;
80
- // }
80
+ addDefaultModule(module: ShaderModule): void {
81
+ if (!this._defaultModules.find((m) => m.name === (typeof module === 'string' ? module : module.name))) {
82
+ this._defaultModules.push(module);
83
+ }
84
+ this.stateHash++;
85
+ }
81
86
 
82
- // removeDefaultModule(module: ShaderModule): void {
83
- // const moduleName = typeof module === 'string' ? module : module.name;
84
- // this._defaultModules = this._defaultModules.filter((m) => m.name !== moduleName);
85
- // this.stateHash++;
86
- // }
87
+ removeDefaultModule(module: ShaderModule): void {
88
+ const moduleName = typeof module === 'string' ? module : module.name;
89
+ this._defaultModules = this._defaultModules.filter((m) => m.name !== moduleName);
90
+ this.stateHash++;
91
+ }
87
92
 
88
- addShaderHook(hook, opts?): void {
93
+ addShaderHook(hook: string, opts?: any): void {
89
94
  if (opts) {
90
95
  hook = Object.assign(opts, {hook});
91
96
  }
@@ -94,8 +99,8 @@ export default class PipelineFactory {
94
99
  }
95
100
 
96
101
  createRenderPipeline(options: GetRenderPipelineOptions): {
97
- renderPipeline: RenderPipeline;
98
- getUniforms: (props: Record<string, Record<string, any>>) => Record<string, any>;
102
+ pipeline: RenderPipeline;
103
+ getUniforms: GetUniformsFunc;
99
104
  } {
100
105
  const props: Required<GetRenderPipelineOptions> = {...DEFAULT_RENDER_PIPELINE_OPTIONS, ...options};
101
106
 
@@ -104,17 +109,17 @@ export default class PipelineFactory {
104
109
  const hash = this._hashRenderPipeline({...props, modules});
105
110
 
106
111
  if (!this._pipelineCache[hash]) {
107
- const {renderPipeline, getUniforms} = this._createRenderPipeline({...props, modules});
108
- renderPipeline.hash = hash;
109
- this._pipelineCache[hash] = renderPipeline;
110
- this._getUniforms[hash] = getUniforms || ((x: Record<string, Record<string, any>>) => {});
112
+ const {pipeline, getUniforms} = this._createRenderPipeline({...props, modules});
113
+ pipeline.hash = hash;
114
+ this._pipelineCache[hash] = pipeline;
115
+ this._getUniforms[hash] = getUniforms || ((x?: unknown) => ({}));
111
116
  this._useCounts[hash] = 0;
112
117
  }
113
118
 
114
119
  this._useCounts[hash]++;
115
120
 
116
121
  return {
117
- renderPipeline: this._pipelineCache[hash],
122
+ pipeline: this._pipelineCache[hash],
118
123
  getUniforms: this._getUniforms[hash]
119
124
  };
120
125
  }
@@ -137,50 +142,55 @@ export default class PipelineFactory {
137
142
  // PRIVATE
138
143
 
139
144
  _createRenderPipeline(props: GetRenderPipelineOptions): {
140
- renderPipeline: RenderPipeline,
141
- getUniforms: (props: Record<string, Record<string, any>>) => Record<string, any>
145
+ pipeline: RenderPipeline,
146
+ getUniforms: GetUniformsFunc
142
147
  } {
143
148
  const platformInfo = {
144
149
  gpu: this.device.info.gpu,
145
150
  features: this.device.features
146
151
  };
147
152
 
148
- const assembled = assembleShaders(platformInfo, {...props, hookFunctions: this._hookFunctions});
153
+ if (!props.fs) {
154
+ throw new Error('fs');
155
+ }
156
+
157
+ const assembled = assembleShaders(platformInfo, {...props, fs: props.fs, hookFunctions: this._hookFunctions});
149
158
 
150
- const renderPipeline = this.device.createRenderPipeline({
159
+ const pipeline = this.device.createRenderPipeline({
151
160
  ...props,
152
161
  vs: this.device.createShader({stage: 'vertex', source: assembled.vs}),
153
- fs: assembled.fs && this.device.createShader({stage: 'fragment', source: assembled.fs}),
162
+ fs: assembled.fs ? this.device.createShader({stage: 'fragment', source: assembled.fs}) : null,
154
163
  });
155
164
 
156
- return {renderPipeline, getUniforms: assembled.getUniforms};
165
+ return {pipeline, getUniforms: assembled.getUniforms};
157
166
  }
158
167
 
159
168
  /** Calculate a hash based on all the inputs for a render pipeline */
160
169
  _hashRenderPipeline(props: GetRenderPipelineOptions): string {
170
+ const {modules = [], varyings = [], defines = {}, inject = {}, parameters = {}} = props;
161
171
  const vsHash = this._getHash(props.vs);
162
- const fsHash = this._getHash(props.fs);
172
+ const fsHash = props.fs ? this._getHash(props.fs) : 0;
163
173
 
164
- const moduleHashes = props.modules.map((m) => this._getHash(typeof m === 'string' ? m : m.name)).sort();
165
- const varyingHashes = props.varyings.map((v) => this._getHash(v));
174
+ const moduleHashes = modules.map((m) => this._getHash(typeof m === 'string' ? m : m.name)).sort();
175
+ const varyingHashes = varyings.map((v) => this._getHash(v));
166
176
 
167
- const defineKeys = Object.keys(props.defines).sort();
168
- const injectKeys = Object.keys(props.inject).sort();
169
- const defineHashes = [];
170
- const injectHashes = [];
177
+ const defineKeys = Object.keys(defines).sort();
178
+ const injectKeys = Object.keys(inject).sort();
179
+ const defineHashes: number[] = [];
180
+ const injectHashes: number[] = [];
171
181
 
172
182
  for (const key of defineKeys) {
173
183
  defineHashes.push(this._getHash(key));
174
- defineHashes.push(this._getHash(props.defines[key]));
184
+ defineHashes.push(this._getHash(String(defines[key])));
175
185
  }
176
186
 
177
187
  for (const key of injectKeys) {
178
188
  injectHashes.push(this._getHash(key));
179
- injectHashes.push(this._getHash(props.inject[key]));
189
+ injectHashes.push(this._getHash(inject[key]));
180
190
  }
181
191
 
182
192
  // TODO - hash parameters!
183
- const parameterHash = JSON.stringify(props.parameters);
193
+ const parameterHash = JSON.stringify(parameters);
184
194
 
185
195
  return `${vsHash}/${fsHash}D${defineHashes.join('/')}M${moduleHashes.join(
186
196
  '/'
@@ -1,5 +1,5 @@
1
- import {Device, Buffer, assert} from '@luma.gl/api';
2
- import type Geometry from '../geometry/geometry';
1
+ import {Device, Buffer, assert} from '@luma.gl/core';
2
+ import type {Geometry} from '../geometry/geometry';
3
3
 
4
4
  // Support for mapping new geometries with glTF attribute names to "classic" luma.gl shader names
5
5
  const GLTF_TO_LUMA_ATTRIBUTE_MAP = {
@@ -72,10 +72,10 @@ export function getAttributeBuffersFromGeometry(device: Device, geometry: Geomet
72
72
 
73
73
  for (const [name, attribute] of Object.entries(geometry.attributes)) {
74
74
  const remappedName = mapAttributeName(name);
75
- if (attribute.constant) {
75
+ if (attribute?.constant) {
76
76
  throw new Error('constant attributes not supported');
77
77
  } else {
78
- const typedArray = attribute.value;
78
+ const typedArray = attribute?.value;
79
79
  buffers[remappedName] = device.createBuffer({data: typedArray, id: `${remappedName}-buffer`});
80
80
  }
81
81
  }
@@ -84,6 +84,7 @@ export function getAttributeBuffersFromGeometry(device: Device, geometry: Geomet
84
84
  }
85
85
 
86
86
  function mapAttributeName(name: string): string {
87
+ // @ts-ignore-error
87
88
  return GLTF_TO_LUMA_ATTRIBUTE_MAP[name] || name;
88
89
  }
89
90
 
@@ -0,0 +1,230 @@
1
+ // luma.gl, MIT license
2
+
3
+ import {
4
+ Device,
5
+ Buffer,
6
+ RenderPipelineProps,
7
+ RenderPass,
8
+ Binding,
9
+ PrimitiveTopology,
10
+ log
11
+ } from '@luma.gl/core';
12
+ import {RenderPipeline} from '@luma.gl/core';
13
+ import type {ShaderModule} from '@luma.gl/shadertools';
14
+ import type {Geometry} from '../geometry/geometry';
15
+ import {getAttributeBuffersFromGeometry, getIndexBufferFromGeometry} from './model-utils';
16
+ import {PipelineFactory} from '../lib/pipeline-factory';
17
+ import {TypedArray} from '@math.gl/core';
18
+
19
+ /** @todo import type */
20
+ type UniformValue = unknown;
21
+
22
+ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs'> & {
23
+ // Model also accepts a string
24
+ vs?: {glsl?: string; wgsl?: string} | string | null;
25
+ fs?: {glsl?: string; wgsl?: string} | string | null;
26
+ defines?: Record<string, string | number | boolean>;
27
+ modules?: ShaderModule[];
28
+ moduleSettings?: Record<string, Record<string, any>>;
29
+ geometry?: Geometry | null;
30
+ /** deprecated pipeline factory to use to create renderpipelines */
31
+ pipelineFactory?: PipelineFactory;
32
+ vertexCount?: number;
33
+ instanceCount?: number;
34
+ };
35
+
36
+ const DEFAULT_MODEL_PROPS: Required<ModelProps> = {
37
+ ...RenderPipeline.defaultProps,
38
+ vs: null,
39
+ fs: null,
40
+ id: 'unnamed',
41
+ handle: undefined,
42
+ userData: {},
43
+ defines: {},
44
+ modules: [],
45
+ moduleSettings: {},
46
+ geometry: null,
47
+ pipelineFactory: undefined,
48
+ vertexCount: 0,
49
+ instanceCount: 0
50
+ };
51
+
52
+ /** v9 API */
53
+ export class Model {
54
+ readonly device: Device;
55
+ readonly id: string;
56
+ readonly vs: string;
57
+ readonly fs: string | null = null;
58
+ readonly topology: PrimitiveTopology;
59
+ readonly pipelineFactory: PipelineFactory;
60
+ /** The underlying GPU "program". @note May be recreated if parameters change */
61
+ pipeline: RenderPipeline;
62
+ userData: {[key: string]: any} = {};
63
+
64
+ // readonly props: Required<ModelProps>;
65
+
66
+ /** Vertex count */
67
+ vertexCount: number;
68
+ /** instance count */
69
+ instanceCount: number = 0;
70
+ /** Buffer-valued attributes */
71
+ bufferAttributes: Record<string, Buffer> = {};
72
+ /** Constant-valued attributes */
73
+ constantAttributes: Record<string, TypedArray> = {};
74
+ /** Bindings (textures, samplers, uniform buffers) */
75
+ bindings: Record<string, Binding> = {};
76
+ /** Uniforms */
77
+ uniforms: Record<string, UniformValue> = {};
78
+
79
+ private _getModuleUniforms: (props?: Record<string, Record<string, any>>) => Record<string, any>;
80
+
81
+ constructor(device: Device, props: ModelProps) {
82
+ props = {...DEFAULT_MODEL_PROPS, ...props};
83
+ this.id = props.id;
84
+ this.device = device;
85
+
86
+ Object.assign(this.userData, props.userData);
87
+
88
+ // Create the pipeline
89
+ if (!props.vs) {
90
+ throw new Error('no vertex shader');
91
+ }
92
+ this.vs = getShaderSource(this.device, props.vs);
93
+ if (props.fs) {
94
+ this.fs = getShaderSource(this.device, props.fs);
95
+ }
96
+
97
+ this.vertexCount = props.vertexCount;
98
+ this.instanceCount = props.instanceCount;
99
+ this.topology = props.topology;
100
+
101
+ if (props.geometry) {
102
+ this.vertexCount = props.geometry.vertexCount;
103
+ this.topology = props.geometry.topology || 'triangle-list';
104
+ }
105
+
106
+ this.pipelineFactory =
107
+ props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
108
+ const {pipeline, getUniforms} = this.pipelineFactory.createRenderPipeline({
109
+ ...props,
110
+ vs: this.vs,
111
+ fs: this.fs,
112
+ topology: this.topology,
113
+ defines: props.defines,
114
+ parameters: props.parameters,
115
+ shaderLayout: props.shaderLayout
116
+ });
117
+
118
+ this.pipeline = pipeline;
119
+ this._getModuleUniforms = getUniforms;
120
+
121
+ if (props.geometry) {
122
+ this._setGeometry(props.geometry);
123
+ }
124
+
125
+ this.setUniforms(this._getModuleUniforms()); // Get all default module uniforms
126
+
127
+ // Props can update any of the above, so call setProps last.
128
+ this.setProps(props);
129
+ }
130
+
131
+ destroy(): void {
132
+ this.pipelineFactory.release(this.pipeline);
133
+ }
134
+
135
+ draw(renderPass: RenderPass): void {
136
+ this.pipeline.draw({
137
+ renderPass,
138
+ vertexCount: this.vertexCount,
139
+ instanceCount: this.instanceCount
140
+ });
141
+ }
142
+
143
+ setProps(props: ModelProps): void {
144
+ if (props.indices) {
145
+ this.setIndexBuffer(props.indices);
146
+ }
147
+ if (props.attributes) {
148
+ this.setAttributes(props.attributes);
149
+ }
150
+ if (props.bindings) {
151
+ this.setBindings(props.bindings);
152
+ }
153
+ if (props.uniforms) {
154
+ this.setUniforms(props.uniforms);
155
+ }
156
+ if (props.moduleSettings) {
157
+ this.updateModuleSettings(props.moduleSettings);
158
+ }
159
+ }
160
+
161
+ updateModuleSettings(props: Record<string, any>): void {
162
+ const uniforms = this._getModuleUniforms(props);
163
+ this.setUniforms(uniforms);
164
+ }
165
+
166
+ setIndexBuffer(indices: Buffer | null): void {
167
+ this.pipeline.setIndexBuffer(indices);
168
+ // this._indices = indices;
169
+ }
170
+
171
+ setAttributes(bufferAttributes: Record<string, Buffer>): void {
172
+ if (bufferAttributes.indices) {
173
+ log.warn(`Model:${this.id} setAttributes() - indices should be set using setIndexBuffer()`);
174
+ }
175
+
176
+ this.pipeline.setAttributes(bufferAttributes);
177
+ Object.assign(this.bufferAttributes, bufferAttributes);
178
+ }
179
+
180
+ setConstantAttributes(constantAttributes: Record<string, TypedArray>): void {
181
+ // TODO - this doesn't work under WebGPU, we'll need to create buffers or inject uniforms
182
+ this.pipeline.setConstantAttributes(constantAttributes);
183
+ Object.assign(this.constantAttributes, constantAttributes);
184
+ }
185
+
186
+ /** Set the bindings */
187
+ setBindings(bindings: Record<string, Binding>): void {
188
+ this.pipeline.setBindings(bindings);
189
+ Object.assign(this.bindings, bindings);
190
+ }
191
+
192
+ setUniforms(uniforms: Record<string, any>): void {
193
+ this.pipeline.setUniforms(uniforms);
194
+ Object.assign(this.uniforms, uniforms);
195
+ }
196
+
197
+ _setGeometry(geometry: Geometry): void {
198
+ // this._deleteGeometryBuffers();
199
+
200
+ const geometryBuffers = getAttributeBuffersFromGeometry(this.device, geometry);
201
+ this.setAttributes(geometryBuffers);
202
+
203
+ const indexBuffer = getIndexBufferFromGeometry(this.device, geometry);
204
+ if (indexBuffer) {
205
+ this.setIndexBuffer(indexBuffer);
206
+ }
207
+ }
208
+ }
209
+
210
+ /** Create a shader from the different overloads */
211
+ function getShaderSource(device: Device, shader: string | {glsl?: string; wgsl?: string}): string {
212
+ // TODO - detect WGSL/GLSL and throw an error if not supported
213
+ if (typeof shader === 'string') {
214
+ return shader;
215
+ }
216
+
217
+ switch (device.info.type) {
218
+ case 'webgpu':
219
+ if (shader?.wgsl) {
220
+ return shader.wgsl;
221
+ }
222
+ throw new Error('WebGPU does not support GLSL shaders');
223
+
224
+ default:
225
+ if (shader?.glsl) {
226
+ return shader.glsl;
227
+ }
228
+ throw new Error('WebGL does not support WGSL shaders');
229
+ }
230
+ }
@@ -0,0 +1,103 @@
1
+ import {Matrix4, Vector3} from '@math.gl/core';
2
+ import {log} from '@luma.gl/core';
3
+ import {ScenegraphNode, ScenegraphNodeProps} from './scenegraph-node';
4
+
5
+ export type GroupNodeProps = ScenegraphNodeProps & {
6
+ children?: ScenegraphNode[];
7
+ }
8
+
9
+ export class GroupNode extends ScenegraphNode {
10
+ children: ScenegraphNode[];
11
+
12
+ constructor(children: ScenegraphNode[]);
13
+ constructor(props?: GroupNodeProps);
14
+
15
+ constructor(props: ScenegraphNode[] | GroupNodeProps = {}) {
16
+ props = Array.isArray(props) ? {children: props} : props;
17
+ const {children = []} = props;
18
+ log.assert(
19
+ children.every((child) => child instanceof ScenegraphNode),
20
+ 'every child must an instance of ScenegraphNode'
21
+ );
22
+ super(props);
23
+ this.children = children;
24
+ }
25
+
26
+ override getBounds(): [number[], number[]] | null {
27
+ const result: [number[], number[]] = [[Infinity, Infinity, Infinity], [-Infinity, -Infinity, -Infinity]];
28
+
29
+ this.traverse((node, {worldMatrix}) => {
30
+ const bounds = node.getBounds();
31
+ if (!bounds) {
32
+ return;
33
+ }
34
+ const [min, max] = bounds;
35
+ const center = new Vector3(min).add(max).divide([2, 2, 2]);
36
+ worldMatrix.transformAsPoint(center, center);
37
+ const halfSize = new Vector3(max).subtract(min).divide([2, 2, 2]);
38
+ worldMatrix.transformAsVector(halfSize, halfSize);
39
+
40
+ for (let v = 0; v < 8; v++) {
41
+ // Test all 8 corners of the box
42
+ const position = new Vector3(
43
+ v & 0b001 ? -1 : 1,
44
+ v & 0b010 ? -1 : 1,
45
+ v & 0b100 ? -1 : 1
46
+ ).multiply(halfSize).add(center);
47
+
48
+ for (let i = 0; i < 3; i++) {
49
+ result[0][i] = Math.min(result[0][i], position[i]);
50
+ result[1][i] = Math.max(result[1][i], position[i]);
51
+ }
52
+ }
53
+ });
54
+ if (!Number.isFinite(result[0][0])) {
55
+ return null;
56
+ }
57
+ return result;
58
+ }
59
+
60
+ override destroy(): void {
61
+ this.children.forEach((child) => child.destroy());
62
+ this.removeAll();
63
+ super.destroy();
64
+ }
65
+
66
+ // Unpacks arrays and nested arrays of children
67
+ add(...children: (ScenegraphNode | ScenegraphNode[])[]): this {
68
+ for (const child of children) {
69
+ if (Array.isArray(child)) {
70
+ this.add(...child);
71
+ } else {
72
+ this.children.push(child);
73
+ }
74
+ }
75
+ return this;
76
+ }
77
+
78
+ remove(child: ScenegraphNode): this {
79
+ const children = this.children;
80
+ const indexOf = children.indexOf(child);
81
+ if (indexOf > -1) {
82
+ children.splice(indexOf, 1);
83
+ }
84
+ return this;
85
+ }
86
+
87
+ removeAll(): this {
88
+ this.children = [];
89
+ return this;
90
+ }
91
+
92
+ traverse(visitor: (node: ScenegraphNode, context: {worldMatrix: Matrix4}) => void, {worldMatrix = new Matrix4()} = {}) {
93
+ const modelMatrix = new Matrix4(worldMatrix).multiplyRight(this.matrix);
94
+
95
+ for (const child of this.children) {
96
+ if (child instanceof GroupNode) {
97
+ child.traverse(visitor, {worldMatrix: modelMatrix});
98
+ } else {
99
+ visitor(child, {worldMatrix: modelMatrix});
100
+ }
101
+ }
102
+ }
103
+ }
@@ -0,0 +1,50 @@
1
+ import {RenderPass} from '@luma.gl/core';
2
+ import {ScenegraphNode, ScenegraphNodeProps} from './scenegraph-node';
3
+ import {Model} from '../model/model';
4
+
5
+ export type ModelNodeProps = ScenegraphNodeProps & {
6
+ model: Model;
7
+ managedResources?: any[];
8
+ bounds?: [number[], number[]];
9
+ }
10
+
11
+ export class ModelNode extends ScenegraphNode {
12
+ readonly model: Model;
13
+ bounds: [number[], number[]] | null = null;
14
+ managedResources: any[];
15
+
16
+ // TODO - is this used? override callbacks to make sure we call them with this
17
+ // onBeforeRender = null;
18
+ // onAfterRender = null;
19
+ // AfterRender = null;
20
+
21
+ constructor(props: ModelNodeProps) {
22
+ super(props);
23
+
24
+ // Create new Model or used supplied Model
25
+ this.model = props.model;
26
+ this.managedResources = props.managedResources || [];
27
+ this.bounds = props.bounds || null;
28
+ this.setProps(props);
29
+ }
30
+
31
+ override getBounds(): [number[], number[]] | null {
32
+ return this.bounds;
33
+ }
34
+
35
+ override destroy(): void {
36
+ if (this.model) {
37
+ this.model.destroy();
38
+ // @ts-expect-error
39
+ this.model = null;
40
+ }
41
+ this.managedResources.forEach((resource) => resource.destroy());
42
+ this.managedResources = [];
43
+ }
44
+
45
+ // Expose model methods
46
+ draw(renderPass?: RenderPass) {
47
+ // Return value indicates if something was actually drawn
48
+ return this.model.draw(renderPass);
49
+ }
50
+ }