@luma.gl/engine 9.0.0-alpha.5 → 9.0.0-alpha.50

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 (171) hide show
  1. package/LICENSE +3 -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 +6 -27
  5. package/dist/animation/key-frames.js.map +1 -1
  6. package/dist/animation/timeline.d.ts +9 -9
  7. package/dist/animation/timeline.d.ts.map +1 -1
  8. package/dist/animation/timeline.js +18 -49
  9. package/dist/animation/timeline.js.map +1 -1
  10. package/dist/{lib → animation-loop}/animation-loop.d.ts +30 -31
  11. package/dist/animation-loop/animation-loop.d.ts.map +1 -0
  12. package/dist/{lib → animation-loop}/animation-loop.js +115 -199
  13. package/dist/animation-loop/animation-loop.js.map +1 -0
  14. package/dist/{lib → animation-loop}/animation-props.d.ts +6 -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 +7064 -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 +10 -19
  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 +14 -23
  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 -4
  51. package/dist/geometries/truncated-cone-geometry.d.ts.map +1 -1
  52. package/dist/geometries/truncated-cone-geometry.js +9 -25
  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 +50 -59
  61. package/dist/geometry/geometry.d.ts.map +1 -1
  62. package/dist/geometry/geometry.js +32 -97
  63. package/dist/geometry/geometry.js.map +1 -1
  64. package/dist/geometry/gpu-geometry.d.ts +37 -0
  65. package/dist/geometry/gpu-geometry.d.ts.map +1 -0
  66. package/dist/geometry/gpu-geometry.js +107 -0
  67. package/dist/geometry/gpu-geometry.js.map +1 -0
  68. package/dist/geometry/gpu-table.d.ts +1 -0
  69. package/dist/geometry/gpu-table.d.ts.map +1 -0
  70. package/dist/geometry/gpu-table.js +2 -0
  71. package/dist/geometry/gpu-table.js.map +1 -0
  72. package/dist/index.cjs +2959 -0
  73. package/dist/index.d.ts +26 -9
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +23 -13
  76. package/dist/index.js.map +1 -1
  77. package/dist/lib/clip-space.d.ts +8 -0
  78. package/dist/lib/clip-space.d.ts.map +1 -1
  79. package/dist/lib/clip-space.js +47 -0
  80. package/dist/lib/clip-space.js.map +1 -1
  81. package/dist/lib/pipeline-factory.d.ts +12 -40
  82. package/dist/lib/pipeline-factory.d.ts.map +1 -1
  83. package/dist/lib/pipeline-factory.js +50 -148
  84. package/dist/lib/pipeline-factory.js.map +1 -1
  85. package/dist/model/model.d.ts +192 -0
  86. package/dist/model/model.d.ts.map +1 -0
  87. package/dist/model/model.js +312 -0
  88. package/dist/model/model.js.map +1 -0
  89. package/dist/scenegraph/group-node.d.ts +21 -0
  90. package/dist/scenegraph/group-node.d.ts.map +1 -0
  91. package/dist/scenegraph/group-node.js +94 -0
  92. package/dist/scenegraph/group-node.js.map +1 -0
  93. package/dist/scenegraph/model-node.d.ts +18 -0
  94. package/dist/scenegraph/model-node.d.ts.map +1 -0
  95. package/dist/scenegraph/model-node.js +28 -0
  96. package/dist/scenegraph/model-node.js.map +1 -0
  97. package/dist/scenegraph/scenegraph-node.d.ts +56 -0
  98. package/dist/scenegraph/scenegraph-node.d.ts.map +1 -0
  99. package/dist/scenegraph/scenegraph-node.js +141 -0
  100. package/dist/scenegraph/scenegraph-node.js.map +1 -0
  101. package/dist/shader-inputs.d.ts +62 -0
  102. package/dist/shader-inputs.d.ts.map +1 -0
  103. package/dist/shader-inputs.js +49 -0
  104. package/dist/shader-inputs.js.map +1 -0
  105. package/dist/transform/buffer-transform.d.ts +35 -0
  106. package/dist/transform/buffer-transform.d.ts.map +1 -0
  107. package/dist/transform/buffer-transform.js +62 -0
  108. package/dist/transform/buffer-transform.js.map +1 -0
  109. package/dist/transform/texture-transform.d.ts +57 -0
  110. package/dist/transform/texture-transform.d.ts.map +1 -0
  111. package/dist/transform/texture-transform.js +122 -0
  112. package/dist/transform/texture-transform.js.map +1 -0
  113. package/dist.min.js +286 -0
  114. package/package.json +21 -12
  115. package/src/animation/timeline.ts +16 -15
  116. package/src/{lib → animation-loop}/animation-loop.ts +120 -107
  117. package/src/{lib → animation-loop}/animation-props.ts +5 -5
  118. package/src/animation-loop/make-animation-loop.ts +44 -0
  119. package/src/animation-loop/render-loop.ts +23 -0
  120. package/src/geometries/cone-geometry.ts +1 -1
  121. package/src/geometries/cube-geometry.ts +6 -3
  122. package/src/geometries/cylinder-geometry.ts +2 -2
  123. package/src/geometries/ico-sphere-geometry.ts +7 -6
  124. package/src/geometries/plane-geometry.ts +5 -4
  125. package/src/geometries/sphere-geometry.ts +4 -3
  126. package/src/geometries/truncated-cone-geometry.ts +4 -13
  127. package/src/geometry/geometry-table.ts +1 -1
  128. package/src/geometry/geometry-utils.ts +3 -3
  129. package/src/geometry/geometry.ts +79 -119
  130. package/src/geometry/gpu-geometry.ts +124 -0
  131. package/src/geometry/gpu-table.ts +41 -0
  132. package/src/index.ts +38 -12
  133. package/src/lib/clip-space.ts +22 -21
  134. package/src/lib/pipeline-factory.ts +60 -168
  135. package/src/model/model.ts +549 -0
  136. package/src/scenegraph/group-node.ts +103 -0
  137. package/src/scenegraph/model-node.ts +50 -0
  138. package/src/scenegraph/scenegraph-node.ts +204 -0
  139. package/src/shader-inputs.ts +132 -0
  140. package/src/transform/buffer-transform.ts +94 -0
  141. package/src/transform/texture-transform.ts +169 -0
  142. package/dist/bundle.d.ts +0 -2
  143. package/dist/bundle.d.ts.map +0 -1
  144. package/dist/bundle.js +0 -5
  145. package/dist/bundle.js.map +0 -1
  146. package/dist/geometry/primitive-utils.d.ts +0 -1
  147. package/dist/geometry/primitive-utils.d.ts.map +0 -1
  148. package/dist/geometry/primitive-utils.js +0 -2
  149. package/dist/geometry/primitive-utils.js.map +0 -1
  150. package/dist/lib/animation-loop.d.ts.map +0 -1
  151. package/dist/lib/animation-loop.js.map +0 -1
  152. package/dist/lib/animation-props.d.ts.map +0 -1
  153. package/dist/lib/animation-props.js.map +0 -1
  154. package/dist/lib/model-utils.d.ts +0 -5
  155. package/dist/lib/model-utils.d.ts.map +0 -1
  156. package/dist/lib/model-utils.js +0 -45
  157. package/dist/lib/model-utils.js.map +0 -1
  158. package/dist/lib/model.d.ts +0 -41
  159. package/dist/lib/model.d.ts.map +0 -1
  160. package/dist/lib/model.js +0 -176
  161. package/dist/lib/model.js.map +0 -1
  162. package/dist/lib/render-loop.d.ts +0 -14
  163. package/dist/lib/render-loop.d.ts.map +0 -1
  164. package/dist/lib/render-loop.js +0 -49
  165. package/dist/lib/render-loop.js.map +0 -1
  166. package/src/bundle.ts +0 -4
  167. package/src/geometry/primitive-utils.ts +0 -30
  168. package/src/lib/model-utils.ts +0 -124
  169. package/src/lib/model.ts +0 -179
  170. package/src/lib/render-loop.ts +0 -58
  171. /package/dist/{lib → animation-loop}/animation-props.js +0 -0
@@ -0,0 +1,549 @@
1
+ // luma.gl, MIT license
2
+ // Copyright (c) vis.gl contributors
3
+
4
+ import type {TypedArray, RenderPipelineProps, RenderPipelineParameters} from '@luma.gl/core';
5
+ import type {BufferLayout, VertexArray, TransformFeedback} from '@luma.gl/core';
6
+ import type {AttributeInfo, Binding, UniformValue, PrimitiveTopology} from '@luma.gl/core';
7
+ import {Device, Buffer, RenderPipeline, RenderPass, UniformStore} from '@luma.gl/core';
8
+ import {log, uid, deepEqual, splitUniformsAndBindings} from '@luma.gl/core';
9
+ import {getAttributeInfosFromLayouts} from '@luma.gl/core';
10
+ import type {ShaderModule, PlatformInfo} from '@luma.gl/shadertools';
11
+ import {ShaderAssembler} from '@luma.gl/shadertools';
12
+ import {ShaderInputs} from '../shader-inputs';
13
+ import type {Geometry} from '../geometry/geometry';
14
+ import {GPUGeometry, makeGPUGeometry} from '../geometry/gpu-geometry';
15
+ import {PipelineFactory} from '../lib/pipeline-factory';
16
+
17
+ export type ModelProps = Omit<RenderPipelineProps, 'vs' | 'fs'> & {
18
+ // Model also accepts a string shaders
19
+ vs: {glsl?: string; wgsl?: string} | string | null;
20
+ fs: {glsl?: string; wgsl?: string} | string | null;
21
+ /** shadertool shader modules (added to shader code) */
22
+ modules?: ShaderModule[];
23
+ /** Shadertool module defines (configures shader code)*/
24
+ defines?: Record<string, string | number | boolean>;
25
+ // TODO - injections, hooks etc?
26
+
27
+ /** Shader inputs, used to generated uniform buffers and bindings */
28
+ shaderInputs?: ShaderInputs;
29
+ /** pipeline factory to use to create render pipelines. Defaults to default factory for the device */
30
+ pipelineFactory?: PipelineFactory;
31
+ /** Shader assembler. Defaults to the ShaderAssembler.getShaderAssembler() */
32
+ shaderAssembler?: ShaderAssembler;
33
+
34
+ /** Parameters that are built into the pipeline */
35
+ parameters?: RenderPipelineParameters;
36
+
37
+ /** Geometry */
38
+ geometry?: GPUGeometry | Geometry | null;
39
+
40
+ /** Vertex count */
41
+ vertexCount?: number;
42
+ /** instance count */
43
+ instanceCount?: number;
44
+
45
+ indexBuffer?: Buffer | null;
46
+ /** @note this is really a map of buffers, not a map of attributes */
47
+ attributes?: Record<string, Buffer>;
48
+ /** */
49
+ constantAttributes?: Record<string, TypedArray>;
50
+
51
+ /** @internal For use with {@link TransformFeedback}, WebGL 2 only. */
52
+ varyings?: string[];
53
+
54
+ transformFeedback?: TransformFeedback;
55
+
56
+ /** Mapped uniforms for shadertool modules */
57
+ moduleSettings?: Record<string, Record<string, any>>;
58
+ };
59
+
60
+ /**
61
+ * v9 Model API
62
+ * A model
63
+ * - automatically reuses pipelines (programs) when possible
64
+ * - automatically rebuilds pipelines if necessary to accommodate changed settings
65
+ * shadertools integration
66
+ * - accepts modules and performs shader transpilation
67
+ */
68
+ export class Model {
69
+ static defaultProps: Required<ModelProps> = {
70
+ ...RenderPipeline.defaultProps,
71
+ vs: null,
72
+ fs: null,
73
+ id: 'unnamed',
74
+ handle: undefined,
75
+ userData: {},
76
+ defines: {},
77
+ modules: [],
78
+ moduleSettings: undefined!,
79
+ geometry: null,
80
+ indexBuffer: null,
81
+ attributes: {},
82
+ constantAttributes: {},
83
+ varyings: [],
84
+
85
+ shaderInputs: undefined!,
86
+ pipelineFactory: undefined!,
87
+ transformFeedback: undefined,
88
+ shaderAssembler: ShaderAssembler.getDefaultShaderAssembler()
89
+ };
90
+
91
+ readonly device: Device;
92
+ readonly id: string;
93
+ readonly vs: string;
94
+ readonly fs: string;
95
+ readonly pipelineFactory: PipelineFactory;
96
+ userData: {[key: string]: any} = {};
97
+
98
+ // Fixed properties (change can trigger pipeline rebuild)
99
+
100
+ /** The render pipeline GPU parameters, depth testing etc */
101
+ parameters: RenderPipelineParameters;
102
+
103
+ /** The primitive topology */
104
+ topology: PrimitiveTopology;
105
+ /** Buffer layout */
106
+ bufferLayout: BufferLayout[];
107
+
108
+ // Dynamic properties
109
+
110
+ /** Vertex count */
111
+ vertexCount: number;
112
+ /** instance count */
113
+ instanceCount: number = 0;
114
+
115
+ /** Index buffer */
116
+ indexBuffer: Buffer | null = null;
117
+ /** Buffer-valued attributes */
118
+ bufferAttributes: Record<string, Buffer> = {};
119
+ /** Constant-valued attributes */
120
+ constantAttributes: Record<string, TypedArray> = {};
121
+ /** Bindings (textures, samplers, uniform buffers) */
122
+ bindings: Record<string, Binding> = {};
123
+ /** Sets uniforms @deprecated Use uniform buffers and setBindings() for portability*/
124
+ uniforms: Record<string, UniformValue> = {};
125
+
126
+ /**
127
+ * VertexArray
128
+ * @note not implemented: if bufferLayout is updated, vertex array has to be rebuilt!
129
+ * @todo - allow application to define multiple vertex arrays?
130
+ * */
131
+ vertexArray: VertexArray;
132
+
133
+ /** TransformFeedback, WebGL 2 only. */
134
+ transformFeedback: TransformFeedback | null = null;
135
+
136
+ /** The underlying GPU "program". @note May be recreated if parameters change */
137
+ pipeline: RenderPipeline;
138
+
139
+ /** ShaderInputs instance */
140
+ shaderInputs: ShaderInputs;
141
+
142
+ _uniformStore: UniformStore;
143
+
144
+ _pipelineNeedsUpdate: string | false = 'newly created';
145
+ _attributeInfos: Record<string, AttributeInfo> = {};
146
+ _gpuGeometry: GPUGeometry | null = null;
147
+ private _getModuleUniforms: (props?: Record<string, Record<string, any>>) => Record<string, any>;
148
+ private props: Required<ModelProps>;
149
+
150
+ constructor(device: Device, props: ModelProps) {
151
+ this.props = {...Model.defaultProps, ...props};
152
+ props = this.props;
153
+ this.id = props.id || uid('model');
154
+ this.device = device;
155
+
156
+ Object.assign(this.userData, props.userData);
157
+
158
+ // Setup shader module inputs
159
+ const moduleMap = Object.fromEntries(this.props.modules?.map(module => [module.name, module]) || []);
160
+ this.setShaderInputs(props.shaderInputs || new ShaderInputs(moduleMap));
161
+
162
+ // Setup shader assembler
163
+ const platformInfo = getPlatformInfo(device);
164
+ const modules = (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
165
+ const {vs, fs, getUniforms} = this.props.shaderAssembler.assembleShaders({platformInfo, ...this.props, modules});
166
+
167
+ this.vs = vs;
168
+ this.fs = fs;
169
+ this._getModuleUniforms = getUniforms;
170
+
171
+ this.vertexCount = this.props.vertexCount;
172
+ this.instanceCount = this.props.instanceCount;
173
+
174
+ this.topology = this.props.topology;
175
+ this.bufferLayout = this.props.bufferLayout;
176
+ this.parameters = this.props.parameters;
177
+
178
+ // Geometry, if provided, sets topology and vertex cound
179
+ if (props.geometry) {
180
+ this._gpuGeometry = this.setGeometry(props.geometry);
181
+ }
182
+
183
+ this.pipelineFactory =
184
+ props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
185
+
186
+ // Create the pipeline
187
+ // @note order is important
188
+ this.pipeline = this._updatePipeline();
189
+
190
+ this.vertexArray = device.createVertexArray({
191
+ renderPipeline: this.pipeline
192
+ });
193
+
194
+ // Now we can apply geometry attributes
195
+ if (this._gpuGeometry) {
196
+ this._setGeometryAttributes(this._gpuGeometry);
197
+ }
198
+
199
+ // Apply any dynamic settings that will not trigger pipeline change
200
+ if (props.vertexCount) {
201
+ this.setVertexCount(props.vertexCount);
202
+ }
203
+ if (props.instanceCount) {
204
+ this.setInstanceCount(props.instanceCount);
205
+ }
206
+ // @ts-expect-error
207
+ if (props.indices) {
208
+ throw new Error('Model.props.indices removed. Use props.indexBuffer');
209
+ }
210
+ if (props.indexBuffer) {
211
+ this.setIndexBuffer(props.indexBuffer);
212
+ }
213
+ if (props.attributes) {
214
+ this.setAttributes(props.attributes);
215
+ }
216
+ if (props.constantAttributes) {
217
+ this.setConstantAttributes(props.constantAttributes);
218
+ }
219
+ if (props.bindings) {
220
+ this.setBindings(props.bindings);
221
+ }
222
+ if (props.uniforms) {
223
+ this.setUniforms(props.uniforms);
224
+ }
225
+ if (props.moduleSettings) {
226
+ // eslint-disable-next-line no-console
227
+ console.warn('Model.props.moduleSettings is deprecated. Use Model.shaderInputs.setProps()');
228
+ this.updateModuleSettings(props.moduleSettings);
229
+ }
230
+ if (props.transformFeedback) {
231
+ this.transformFeedback = props.transformFeedback;
232
+ }
233
+
234
+ // WebGL1?
235
+ // this.setUniforms(this._getModuleUniforms()); // Get all default module uniforms
236
+
237
+ // Catch any access to non-standard props
238
+ Object.seal(this);
239
+ }
240
+
241
+ destroy(): void {
242
+ this.pipelineFactory.release(this.pipeline);
243
+ this._uniformStore.destroy();
244
+ }
245
+
246
+ // Draw call
247
+
248
+ predraw() {
249
+ // Update uniform buffers if needed
250
+ this.updateShaderInputs();
251
+ }
252
+
253
+ draw(renderPass: RenderPass): void {
254
+ this.predraw();
255
+
256
+ // Check if the pipeline is invalidated
257
+ // TODO - this is likely the worst place to do this from performance perspective. Perhaps add a predraw()?
258
+ this.pipeline = this._updatePipeline();
259
+
260
+ // Set pipeline state, we may be sharing a pipeline so we need to set all state on every draw
261
+ // Any caching needs to be done inside the pipeline functions
262
+ this.pipeline.setBindings(this.bindings);
263
+ this.pipeline.setUniforms(this.uniforms);
264
+
265
+ this.pipeline.draw({
266
+ renderPass,
267
+ vertexArray: this.vertexArray,
268
+ vertexCount: this.vertexCount,
269
+ instanceCount: this.instanceCount,
270
+ transformFeedback: this.transformFeedback
271
+ });
272
+ }
273
+
274
+ // Update fixed fields (can trigger pipeline rebuild)
275
+
276
+ /**
277
+ * Updates the optional geometry
278
+ * Geometry, set topology and bufferLayout
279
+ * @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
280
+ */
281
+ setGeometry(geometry: GPUGeometry | Geometry): GPUGeometry {
282
+ const gpuGeometry = geometry && makeGPUGeometry(this.device, geometry);
283
+ this.setTopology(gpuGeometry.topology || 'triangle-list');
284
+ this.bufferLayout = mergeBufferLayouts(this.bufferLayout, gpuGeometry.bufferLayout);
285
+ if (this.vertexArray) {
286
+ this._setGeometryAttributes(gpuGeometry);
287
+ }
288
+ return gpuGeometry;
289
+ }
290
+
291
+ /**
292
+ * Updates the optional geometry attributes
293
+ * Geometry, sets several attributes, indexBuffer, and also vertex count
294
+ * @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
295
+ */
296
+ _setGeometryAttributes(gpuGeometry: GPUGeometry): void {
297
+ // TODO - delete previous geometry?
298
+ this.vertexCount = gpuGeometry.vertexCount;
299
+ this.setAttributes(gpuGeometry.attributes);
300
+ this.setIndexBuffer(gpuGeometry.indices);
301
+ }
302
+
303
+ /**
304
+ * Updates the primitive topology ('triangle-list', 'triangle-strip' etc).
305
+ * @note Triggers a pipeline rebuild / pipeline cache fetch on WebGPU
306
+ */
307
+ setTopology(topology: PrimitiveTopology): void {
308
+ if (topology !== this.topology) {
309
+ this.topology = topology;
310
+ this._setPipelineNeedsUpdate('topology');
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Updates the buffer layout.
316
+ * @note Triggers a pipeline rebuild / pipeline cache fetch on WebGPU
317
+ */
318
+ setBufferLayout(bufferLayout: BufferLayout[]): void {
319
+ this.bufferLayout = this._gpuGeometry
320
+ ? mergeBufferLayouts(bufferLayout, this._gpuGeometry.bufferLayout)
321
+ : bufferLayout;
322
+ this._setPipelineNeedsUpdate('bufferLayout');
323
+
324
+ // Recreate the pipeline
325
+ this.pipeline = this._updatePipeline();
326
+
327
+ // vertex array needs to be updated if we update buffer layout,
328
+ // but not if we update parameters
329
+ this.vertexArray = this.device.createVertexArray({
330
+ renderPipeline: this.pipeline
331
+ });
332
+
333
+ // Reapply geometry attributes to the new vertex array
334
+ if (this._gpuGeometry) {
335
+ this._setGeometryAttributes(this._gpuGeometry);
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Set GPU parameters.
341
+ * @note Can trigger a pipeline rebuild / pipeline cache fetch.
342
+ * @param parameters
343
+ */
344
+ setParameters(parameters: RenderPipelineParameters) {
345
+ if (!deepEqual(parameters, this.parameters, 2)) {
346
+ this.parameters = parameters;
347
+ this._setPipelineNeedsUpdate('parameters');
348
+ }
349
+ }
350
+
351
+ // Update dynamic fields
352
+
353
+ /**
354
+ * Updates the vertex count (used in draw calls)
355
+ * @note Any attributes with stepMode=vertex need to be at least this big
356
+ */
357
+ setVertexCount(vertexCount: number): void {
358
+ this.vertexCount = vertexCount;
359
+ }
360
+
361
+ /**
362
+ * Updates the instance count (used in draw calls)
363
+ * @note Any attributes with stepMode=instance need to be at least this big
364
+ */
365
+ setInstanceCount(instanceCount: number): void {
366
+ this.instanceCount = instanceCount;
367
+ }
368
+
369
+ setShaderInputs(shaderInputs: ShaderInputs): void {
370
+ this.shaderInputs = shaderInputs;
371
+ this._uniformStore = new UniformStore(this.shaderInputs.modules);
372
+ // Create uniform buffer bindings for all modules
373
+ for (const moduleName of Object.keys(this.shaderInputs.modules)) {
374
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(
375
+ this.device,
376
+ moduleName
377
+ );
378
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
379
+ }
380
+ }
381
+
382
+ updateShaderInputs(): void {
383
+ this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
384
+ }
385
+
386
+ /**
387
+ * @deprecated Updates shader module settings (which results in uniforms being set)
388
+ */
389
+ updateModuleSettings(props: Record<string, any>): void {
390
+ // eslint-disable-next-line no-console
391
+ console.warn('Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()');
392
+ const {bindings, uniforms} = splitUniformsAndBindings(this._getModuleUniforms(props));
393
+ Object.assign(this.bindings, bindings);
394
+ Object.assign(this.uniforms, uniforms);
395
+ }
396
+
397
+ /**
398
+ * Sets bindings (textures, samplers, uniform buffers)
399
+ */
400
+ setBindings(bindings: Record<string, Binding>): void {
401
+ Object.assign(this.bindings, bindings);
402
+ }
403
+
404
+ /**
405
+ * Sets individual uniforms
406
+ * @deprecated WebGL only, use uniform buffers for portability
407
+ * @param uniforms
408
+ * @returns self for chaining
409
+ */
410
+ setUniforms(uniforms: Record<string, UniformValue>): void {
411
+ this.pipeline.setUniforms(uniforms);
412
+ Object.assign(this.uniforms, uniforms);
413
+ }
414
+
415
+ /**
416
+ * Sets the index buffer
417
+ * @todo - how to unset it if we change geometry?
418
+ */
419
+ setIndexBuffer(indexBuffer: Buffer | null): void {
420
+ this.vertexArray.setIndexBuffer(indexBuffer);
421
+ }
422
+
423
+ /**
424
+ * Updates optional transform feedback. WebGL 2 only.
425
+ */
426
+ setTransformFeedback(transformFeedback: TransformFeedback | null): void {
427
+ this.transformFeedback = transformFeedback;
428
+ }
429
+
430
+ /**
431
+ * Sets attributes (buffers)
432
+ * @note Overrides any attributes previously set with the same name
433
+ */
434
+ setAttributes(buffers: Record<string, Buffer>): void {
435
+ if (buffers.indices) {
436
+ log.warn(
437
+ `Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`
438
+ );
439
+ }
440
+ for (const [bufferName, buffer] of Object.entries(buffers)) {
441
+ const bufferLayout = this.bufferLayout.find(layout => layout.name === bufferName);
442
+ if (!bufferLayout) {
443
+ log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
444
+ continue; // eslint-disable-line no-continue
445
+ }
446
+
447
+ // For an interleaved attribute we may need to set multiple attributes
448
+ const attributeNames = bufferLayout.attributes
449
+ ? bufferLayout.attributes?.map(layout => layout.attribute)
450
+ : [bufferLayout.name];
451
+ let set = false;
452
+ for (const attributeName of attributeNames) {
453
+ const attributeInfo = this._attributeInfos[attributeName];
454
+ if (attributeInfo) {
455
+ this.vertexArray.setBuffer(attributeInfo.location, buffer);
456
+ set = true;
457
+ }
458
+ }
459
+ if (!set) {
460
+ log.warn(
461
+ `Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`
462
+ )();
463
+ }
464
+ }
465
+ }
466
+
467
+ /**
468
+ * Sets constant attributes
469
+ * @note Overrides any attributes previously set with the same name
470
+ * Constant attributes are only supported in WebGL, not in WebGPU
471
+ * Any attribute that is disabled in the current vertex array object
472
+ * is read from the context's global constant value for that attribute location.
473
+ * @param constantAttributes
474
+ */
475
+ setConstantAttributes(attributes: Record<string, TypedArray>): void {
476
+ for (const [attributeName, value] of Object.entries(attributes)) {
477
+ const attributeInfo = this._attributeInfos[attributeName];
478
+ if (attributeInfo) {
479
+ this.vertexArray.setConstant(attributeInfo.location, value);
480
+ } else {
481
+ log.warn(
482
+ `Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`
483
+ )();
484
+ }
485
+ }
486
+ }
487
+
488
+ _setPipelineNeedsUpdate(reason: string): void {
489
+ this._pipelineNeedsUpdate = this._pipelineNeedsUpdate || reason;
490
+ }
491
+
492
+ _updatePipeline(): RenderPipeline {
493
+ if (this._pipelineNeedsUpdate) {
494
+ if (this.pipeline) {
495
+ log.log(
496
+ 1,
497
+ `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
498
+ )();
499
+ }
500
+ this._pipelineNeedsUpdate = false;
501
+ this.pipeline = this.device.createRenderPipeline({
502
+ ...this.props,
503
+ bufferLayout: this.bufferLayout,
504
+ topology: this.topology,
505
+ parameters: this.parameters,
506
+ vs: this.device.createShader({id: '{$this.id}-vertex', stage: 'vertex', source: this.vs}),
507
+ fs: this.fs
508
+ ? this.device.createShader({
509
+ id: '{$this.id}-fragment',
510
+ stage: 'fragment',
511
+ source: this.fs
512
+ })
513
+ : null
514
+ });
515
+ this._attributeInfos = getAttributeInfosFromLayouts(
516
+ this.pipeline.shaderLayout,
517
+ this.bufferLayout
518
+ );
519
+ }
520
+ return this.pipeline;
521
+ }
522
+ }
523
+
524
+ // HELPERS
525
+
526
+ /** TODO - move to core, document add tests */
527
+ function mergeBufferLayouts(layouts1: BufferLayout[], layouts2: BufferLayout[]): BufferLayout[] {
528
+ const layouts = [...layouts1];
529
+ for (const attribute of layouts2) {
530
+ const index = layouts.findIndex(attribute2 => attribute2.name === attribute.name);
531
+ if (index < 0) {
532
+ layouts.push(attribute);
533
+ } else {
534
+ layouts[index] = attribute;
535
+ }
536
+ }
537
+ return layouts;
538
+ }
539
+
540
+ /** Create a shadertools platform info from the Device */
541
+ export function getPlatformInfo(device: Device): PlatformInfo {
542
+ return {
543
+ type: device.info.type,
544
+ shaderLanguage: device.info.shadingLanguage,
545
+ shaderLanguageVersion: device.info.shadingLanguageVersion as 100 | 300,
546
+ gpu: device.info.gpu,
547
+ features: device.features
548
+ };
549
+ }
@@ -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
+ }