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