@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.
- package/LICENSE +2 -1
- package/dist/animation/key-frames.d.ts +1 -1
- package/dist/animation/key-frames.d.ts.map +1 -1
- package/dist/animation/key-frames.js +0 -20
- package/dist/animation/key-frames.js.map +1 -1
- package/dist/animation/timeline.d.ts +5 -5
- package/dist/animation/timeline.d.ts.map +1 -1
- package/dist/animation/timeline.js +0 -30
- package/dist/animation/timeline.js.map +1 -1
- package/dist/{lib → animation-loop}/animation-loop.d.ts +28 -29
- package/dist/animation-loop/animation-loop.d.ts.map +1 -0
- package/dist/{lib → animation-loop}/animation-loop.js +96 -175
- package/dist/animation-loop/animation-loop.js.map +1 -0
- package/dist/{lib → animation-loop}/animation-props.d.ts +7 -7
- package/dist/animation-loop/animation-props.d.ts.map +1 -0
- package/dist/animation-loop/animation-props.js.map +1 -0
- package/dist/animation-loop/make-animation-loop.d.ts +6 -0
- package/dist/animation-loop/make-animation-loop.d.ts.map +1 -0
- package/dist/animation-loop/make-animation-loop.js +28 -0
- package/dist/animation-loop/make-animation-loop.js.map +1 -0
- package/dist/animation-loop/render-loop.d.ts +23 -0
- package/dist/animation-loop/render-loop.d.ts.map +1 -0
- package/dist/animation-loop/render-loop.js +7 -0
- package/dist/animation-loop/render-loop.js.map +1 -0
- package/dist/dist.dev.js +14977 -0
- package/dist/geometries/cone-geometry.d.ts +1 -1
- package/dist/geometries/cone-geometry.d.ts.map +1 -1
- package/dist/geometries/cone-geometry.js +6 -5
- package/dist/geometries/cone-geometry.js.map +1 -1
- package/dist/geometries/cube-geometry.d.ts +2 -2
- package/dist/geometries/cube-geometry.d.ts.map +1 -1
- package/dist/geometries/cube-geometry.js +15 -8
- package/dist/geometries/cube-geometry.js.map +1 -1
- package/dist/geometries/cylinder-geometry.d.ts +1 -1
- package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
- package/dist/geometries/cylinder-geometry.js +6 -5
- package/dist/geometries/cylinder-geometry.js.map +1 -1
- package/dist/geometries/ico-sphere-geometry.d.ts +2 -2
- package/dist/geometries/ico-sphere-geometry.d.ts.map +1 -1
- package/dist/geometries/ico-sphere-geometry.js +9 -18
- package/dist/geometries/ico-sphere-geometry.js.map +1 -1
- package/dist/geometries/plane-geometry.d.ts +2 -2
- package/dist/geometries/plane-geometry.d.ts.map +1 -1
- package/dist/geometries/plane-geometry.js +10 -19
- package/dist/geometries/plane-geometry.js.map +1 -1
- package/dist/geometries/sphere-geometry.d.ts +2 -2
- package/dist/geometries/sphere-geometry.d.ts.map +1 -1
- package/dist/geometries/sphere-geometry.js +9 -13
- package/dist/geometries/sphere-geometry.js.map +1 -1
- package/dist/geometries/truncated-cone-geometry.d.ts +2 -2
- package/dist/geometries/truncated-cone-geometry.d.ts.map +1 -1
- package/dist/geometries/truncated-cone-geometry.js +9 -14
- package/dist/geometries/truncated-cone-geometry.js.map +1 -1
- package/dist/geometry/geometry-table.d.ts +2 -2
- package/dist/geometry/geometry-table.d.ts.map +1 -1
- package/dist/geometry/geometry-table.js.map +1 -1
- package/dist/geometry/geometry-utils.d.ts.map +1 -1
- package/dist/geometry/geometry-utils.js +0 -9
- package/dist/geometry/geometry-utils.js.map +1 -1
- package/dist/geometry/geometry.d.ts +35 -58
- package/dist/geometry/geometry.d.ts.map +1 -1
- package/dist/geometry/geometry.js +26 -89
- package/dist/geometry/geometry.js.map +1 -1
- package/dist/geometry/primitive-utils.js.map +1 -1
- package/dist/index.cjs +2576 -0
- package/dist/index.d.ts +18 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -13
- package/dist/index.js.map +1 -1
- package/dist/lib/clip-space.d.ts +8 -0
- package/dist/lib/clip-space.d.ts.map +1 -1
- package/dist/lib/clip-space.js +36 -1
- package/dist/lib/clip-space.js.map +1 -1
- package/dist/lib/pipeline-factory.d.ts +19 -14
- package/dist/lib/pipeline-factory.d.ts.map +1 -1
- package/dist/lib/pipeline-factory.js +52 -63
- package/dist/lib/pipeline-factory.js.map +1 -1
- package/dist/{lib → model}/model-utils.d.ts +2 -2
- package/dist/model/model-utils.d.ts.map +1 -0
- package/dist/{lib → model}/model-utils.js +3 -8
- package/dist/model/model-utils.js.map +1 -0
- package/dist/model/model.d.ts +67 -0
- package/dist/model/model.d.ts.map +1 -0
- package/dist/{lib → model}/model.js +59 -76
- package/dist/model/model.js.map +1 -0
- package/dist/scenegraph/group-node.d.ts +21 -0
- package/dist/scenegraph/group-node.d.ts.map +1 -0
- package/dist/scenegraph/group-node.js +95 -0
- package/dist/scenegraph/group-node.js.map +1 -0
- package/dist/scenegraph/model-node.d.ts +18 -0
- package/dist/scenegraph/model-node.d.ts.map +1 -0
- package/dist/scenegraph/model-node.js +29 -0
- package/dist/scenegraph/model-node.js.map +1 -0
- package/dist/scenegraph/scenegraph-node.d.ts +56 -0
- package/dist/scenegraph/scenegraph-node.d.ts.map +1 -0
- package/dist/scenegraph/scenegraph-node.js +142 -0
- package/dist/scenegraph/scenegraph-node.js.map +1 -0
- package/dist/transform/transform.d.ts +98 -0
- package/dist/transform/transform.d.ts.map +1 -0
- package/dist/transform/transform.js +67 -0
- package/dist/transform/transform.js.map +1 -0
- package/dist.min.js +314 -0
- package/package.json +22 -12
- package/src/animation/timeline.ts +2 -2
- package/src/{lib → animation-loop}/animation-loop.ts +115 -97
- package/src/{lib → animation-loop}/animation-props.ts +6 -5
- package/src/animation-loop/make-animation-loop.ts +44 -0
- package/src/animation-loop/render-loop.ts +23 -0
- package/src/geometries/cone-geometry.ts +1 -1
- package/src/geometries/cube-geometry.ts +6 -3
- package/src/geometries/cylinder-geometry.ts +2 -2
- package/src/geometries/ico-sphere-geometry.ts +7 -6
- package/src/geometries/plane-geometry.ts +5 -4
- package/src/geometries/sphere-geometry.ts +4 -3
- package/src/geometries/truncated-cone-geometry.ts +4 -3
- package/src/geometry/geometry-table.ts +1 -1
- package/src/geometry/geometry-utils.ts +3 -3
- package/src/geometry/geometry.ts +72 -115
- package/src/index.ts +28 -12
- package/src/lib/clip-space.ts +17 -15
- package/src/lib/pipeline-factory.ts +60 -50
- package/src/{lib → model}/model-utils.ts +5 -4
- package/src/model/model.ts +230 -0
- package/src/scenegraph/group-node.ts +103 -0
- package/src/scenegraph/model-node.ts +50 -0
- package/src/scenegraph/scenegraph-node.ts +204 -0
- package/src/transform/transform.ts +246 -0
- package/dist/bundle.d.ts +0 -2
- package/dist/bundle.d.ts.map +0 -1
- package/dist/bundle.js +0 -5
- package/dist/bundle.js.map +0 -1
- package/dist/lib/animation-loop.d.ts.map +0 -1
- package/dist/lib/animation-loop.js.map +0 -1
- package/dist/lib/animation-props.d.ts.map +0 -1
- package/dist/lib/animation-props.js.map +0 -1
- package/dist/lib/model-utils.d.ts.map +0 -1
- package/dist/lib/model-utils.js.map +0 -1
- package/dist/lib/model.d.ts +0 -41
- package/dist/lib/model.d.ts.map +0 -1
- package/dist/lib/model.js.map +0 -1
- package/dist/lib/render-loop.d.ts +0 -14
- package/dist/lib/render-loop.d.ts.map +0 -1
- package/dist/lib/render-loop.js +0 -49
- package/dist/lib/render-loop.js.map +0 -1
- package/src/bundle.ts +0 -4
- package/src/lib/model.ts +0 -179
- package/src/lib/render-loop.ts +0 -58
- /package/dist/{lib → animation-loop}/animation-props.js +0 -0
|
@@ -1,34 +1,36 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {Device, RenderPipeline
|
|
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
|
|
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,
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
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
|
-
|
|
98
|
-
getUniforms:
|
|
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 {
|
|
108
|
-
|
|
109
|
-
this._pipelineCache[hash] =
|
|
110
|
-
this._getUniforms[hash] = getUniforms || ((x
|
|
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
|
-
|
|
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
|
-
|
|
141
|
-
getUniforms:
|
|
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
|
-
|
|
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
|
|
159
|
+
const pipeline = this.device.createRenderPipeline({
|
|
151
160
|
...props,
|
|
152
161
|
vs: this.device.createShader({stage: 'vertex', source: assembled.vs}),
|
|
153
|
-
fs: assembled.fs
|
|
162
|
+
fs: assembled.fs ? this.device.createShader({stage: 'fragment', source: assembled.fs}) : null,
|
|
154
163
|
});
|
|
155
164
|
|
|
156
|
-
return {
|
|
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 =
|
|
165
|
-
const varyingHashes =
|
|
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(
|
|
168
|
-
const injectKeys = Object.keys(
|
|
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(
|
|
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(
|
|
189
|
+
injectHashes.push(this._getHash(inject[key]));
|
|
180
190
|
}
|
|
181
191
|
|
|
182
192
|
// TODO - hash parameters!
|
|
183
|
-
const parameterHash = JSON.stringify(
|
|
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/
|
|
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
|
|
75
|
+
if (attribute?.constant) {
|
|
76
76
|
throw new Error('constant attributes not supported');
|
|
77
77
|
} else {
|
|
78
|
-
const typedArray = attribute
|
|
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
|
+
}
|