@luma.gl/engine 9.3.0-alpha.4 → 9.3.0-alpha.8
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 +73 -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/compute/computation.d.ts +3 -7
- package/dist/compute/computation.d.ts.map +1 -1
- package/dist/compute/computation.js +14 -12
- package/dist/compute/computation.js.map +1 -1
- package/dist/dist.dev.js +2310 -1638
- package/dist/dist.min.js +307 -360
- package/dist/dynamic-texture/dynamic-texture.d.ts +12 -5
- package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -1
- package/dist/dynamic-texture/dynamic-texture.js +222 -55
- package/dist/dynamic-texture/dynamic-texture.js.map +1 -1
- package/dist/dynamic-texture/texture-data.d.ts +8 -1
- package/dist/dynamic-texture/texture-data.d.ts.map +1 -1
- package/dist/dynamic-texture/texture-data.js +27 -2
- package/dist/dynamic-texture/texture-data.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/geometry/gpu-geometry.d.ts.map +1 -1
- package/dist/geometry/gpu-geometry.js +8 -3
- package/dist/geometry/gpu-geometry.js.map +1 -1
- package/dist/index.cjs +2897 -2272
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/material/material-factory.d.ts +73 -0
- package/dist/material/material-factory.d.ts.map +1 -0
- package/dist/material/material-factory.js +111 -0
- package/dist/material/material-factory.js.map +1 -0
- package/dist/material/material.d.ts +84 -0
- package/dist/material/material.d.ts.map +1 -0
- package/dist/material/material.js +176 -0
- package/dist/material/material.js.map +1 -0
- package/dist/model/model.d.ts +17 -7
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +80 -34
- package/dist/model/model.js.map +1 -1
- package/dist/model/split-uniforms-and-bindings.d.ts +4 -3
- package/dist/model/split-uniforms-and-bindings.d.ts.map +1 -1
- package/dist/model/split-uniforms-and-bindings.js +2 -2
- package/dist/model/split-uniforms-and-bindings.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/models/directional-light-model.d.ts +7 -0
- package/dist/models/directional-light-model.d.ts.map +1 -0
- package/dist/models/directional-light-model.js +23 -0
- package/dist/models/directional-light-model.js.map +1 -0
- package/dist/models/light-model-utils.d.ts +69 -0
- package/dist/models/light-model-utils.d.ts.map +1 -0
- package/dist/models/light-model-utils.js +395 -0
- package/dist/models/light-model-utils.js.map +1 -0
- package/dist/models/point-light-model.d.ts +7 -0
- package/dist/models/point-light-model.d.ts.map +1 -0
- package/dist/models/point-light-model.js +22 -0
- package/dist/models/point-light-model.js.map +1 -0
- package/dist/models/spot-light-model.d.ts +7 -0
- package/dist/models/spot-light-model.d.ts.map +1 -0
- package/dist/models/spot-light-model.js +23 -0
- package/dist/models/spot-light-model.js.map +1 -0
- package/dist/modules/picking/color-picking.d.ts +5 -9
- package/dist/modules/picking/color-picking.d.ts.map +1 -1
- package/dist/modules/picking/color-picking.js +122 -115
- package/dist/modules/picking/color-picking.js.map +1 -1
- package/dist/modules/picking/index-picking.d.ts +2 -2
- package/dist/modules/picking/index-picking.d.ts.map +1 -1
- package/dist/modules/picking/index-picking.js +36 -16
- package/dist/modules/picking/index-picking.js.map +1 -1
- package/dist/modules/picking/legacy-color-picking.d.ts +26 -0
- package/dist/modules/picking/legacy-color-picking.d.ts.map +1 -0
- package/dist/modules/picking/legacy-color-picking.js +7 -0
- package/dist/modules/picking/legacy-color-picking.js.map +1 -0
- package/dist/modules/picking/picking-manager.d.ts +29 -3
- package/dist/modules/picking/picking-manager.d.ts.map +1 -1
- package/dist/modules/picking/picking-manager.js +188 -41
- package/dist/modules/picking/picking-manager.js.map +1 -1
- package/dist/modules/picking/picking-uniforms.d.ts +12 -11
- package/dist/modules/picking/picking-uniforms.d.ts.map +1 -1
- package/dist/modules/picking/picking-uniforms.js +26 -13
- package/dist/modules/picking/picking-uniforms.js.map +1 -1
- package/dist/modules/picking/picking.d.ts +25 -0
- package/dist/modules/picking/picking.d.ts.map +1 -0
- package/dist/modules/picking/picking.js +18 -0
- package/dist/modules/picking/picking.js.map +1 -0
- 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/shader-inputs.d.ts +9 -7
- package/dist/shader-inputs.d.ts.map +1 -1
- package/dist/shader-inputs.js +84 -4
- package/dist/shader-inputs.js.map +1 -1
- package/dist/utils/shader-module-utils.d.ts +7 -0
- package/dist/utils/shader-module-utils.d.ts.map +1 -0
- package/dist/utils/shader-module-utils.js +46 -0
- package/dist/utils/shader-module-utils.js.map +1 -0
- package/package.json +4 -4
- package/src/animation-loop/animation-loop.ts +78 -46
- package/src/animation-loop/make-animation-loop.ts +13 -5
- package/src/animation-loop/request-animation-frame.ts +32 -6
- package/src/compute/computation.ts +31 -17
- package/src/dynamic-texture/dynamic-texture.ts +301 -68
- package/src/dynamic-texture/texture-data.ts +39 -4
- package/src/geometries/cone-geometry.ts +6 -1
- package/src/geometries/cylinder-geometry.ts +5 -1
- package/src/geometry/gpu-geometry.ts +8 -3
- package/src/index.ts +29 -4
- package/src/material/material-factory.ts +157 -0
- package/src/material/material.ts +254 -0
- package/src/model/model.ts +122 -50
- package/src/model/split-uniforms-and-bindings.ts +8 -6
- package/src/models/billboard-texture-model.ts +10 -8
- package/src/models/clip-space.ts +7 -7
- package/src/models/directional-light-model.ts +32 -0
- package/src/models/light-model-utils.ts +587 -0
- package/src/models/point-light-model.ts +31 -0
- package/src/models/spot-light-model.ts +32 -0
- package/src/modules/picking/color-picking.ts +123 -122
- package/src/modules/picking/index-picking.ts +36 -16
- package/src/modules/picking/legacy-color-picking.ts +8 -0
- package/src/modules/picking/picking-manager.ts +252 -50
- package/src/modules/picking/picking-uniforms.ts +38 -23
- package/src/modules/picking/picking.ts +22 -0
- 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/shader-inputs.ts +165 -15
- package/src/utils/shader-module-utils.ts +65 -0
- package/dist/dynamic-texture/mipmaps.d.ts +0 -6
- package/dist/dynamic-texture/mipmaps.d.ts.map +0 -1
- package/dist/dynamic-texture/mipmaps.js +0 -441
- package/dist/dynamic-texture/mipmaps.js.map +0 -1
- package/dist/factories/pipeline-factory.d.ts +0 -37
- package/dist/factories/pipeline-factory.d.ts.map +0 -1
- package/dist/factories/pipeline-factory.js +0 -181
- package/dist/factories/pipeline-factory.js.map +0 -1
- package/dist/factories/shader-factory.d.ts +0 -22
- package/dist/factories/shader-factory.d.ts.map +0 -1
- package/dist/factories/shader-factory.js +0 -89
- package/dist/factories/shader-factory.js.map +0 -1
- package/dist/types.d.ts +0 -7
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -5
- package/dist/types.js.map +0 -1
- package/src/dynamic-texture/mipmaps.ts +0 -517
- package/src/factories/pipeline-factory.ts +0 -225
- package/src/factories/shader-factory.ts +0 -105
- package/src/types.ts +0 -11
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
// luma.gl, MIT license
|
|
2
2
|
// Copyright (c) vis.gl contributors
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
TextureProps,
|
|
6
|
+
SamplerProps,
|
|
7
|
+
TextureView,
|
|
8
|
+
Device,
|
|
9
|
+
TextureFormat,
|
|
10
|
+
TextureReadOptions
|
|
11
|
+
} from '@luma.gl/core';
|
|
5
12
|
|
|
6
|
-
import {Texture, Sampler, log} from '@luma.gl/core';
|
|
13
|
+
import {Buffer, Texture, Sampler, log} from '@luma.gl/core';
|
|
7
14
|
|
|
8
15
|
// import {loadImageBitmap} from '../application-utils/load-file';
|
|
9
16
|
import {uid} from '../utils/uid';
|
|
@@ -26,10 +33,10 @@ import {
|
|
|
26
33
|
type TextureArrayData,
|
|
27
34
|
type TextureCubeArrayData,
|
|
28
35
|
type TextureCubeData,
|
|
29
|
-
type TextureImageData,
|
|
30
36
|
|
|
31
37
|
// Helpers
|
|
32
38
|
getTextureSizeFromData,
|
|
39
|
+
resolveTextureImageFormat,
|
|
33
40
|
getTexture1DSubresources,
|
|
34
41
|
getTexture2DSubresources,
|
|
35
42
|
getTexture3DSubresources,
|
|
@@ -37,7 +44,6 @@ import {
|
|
|
37
44
|
getTextureArraySubresources,
|
|
38
45
|
getTextureCubeArraySubresources
|
|
39
46
|
} from './texture-data';
|
|
40
|
-
import {generateMipmap} from './mipmaps';
|
|
41
47
|
|
|
42
48
|
/**
|
|
43
49
|
* Properties for a dynamic texture
|
|
@@ -131,7 +137,7 @@ export class DynamicTexture {
|
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
/** @note Fire and forget; caller can await `ready` */
|
|
134
|
-
async initAsync(originalPropsWithAsyncData:
|
|
140
|
+
async initAsync(originalPropsWithAsyncData: DynamicTextureProps): Promise<void> {
|
|
135
141
|
try {
|
|
136
142
|
// TODO - Accept URL string for 2D: turn into ExternalImage promise
|
|
137
143
|
// const dataProps =
|
|
@@ -141,6 +147,18 @@ export class DynamicTexture {
|
|
|
141
147
|
|
|
142
148
|
const propsWithSyncData = await this._loadAllData(originalPropsWithAsyncData);
|
|
143
149
|
this._checkNotDestroyed();
|
|
150
|
+
const subresources = propsWithSyncData.data
|
|
151
|
+
? getTextureSubresources({
|
|
152
|
+
...propsWithSyncData,
|
|
153
|
+
width: originalPropsWithAsyncData.width,
|
|
154
|
+
height: originalPropsWithAsyncData.height,
|
|
155
|
+
format: originalPropsWithAsyncData.format
|
|
156
|
+
})
|
|
157
|
+
: [];
|
|
158
|
+
const userProvidedFormat =
|
|
159
|
+
'format' in originalPropsWithAsyncData && originalPropsWithAsyncData.format !== undefined;
|
|
160
|
+
const userProvidedUsage =
|
|
161
|
+
'usage' in originalPropsWithAsyncData && originalPropsWithAsyncData.usage !== undefined;
|
|
144
162
|
|
|
145
163
|
// Deduce size when not explicitly provided
|
|
146
164
|
// TODO - what about depth?
|
|
@@ -162,15 +180,32 @@ export class DynamicTexture {
|
|
|
162
180
|
throw new Error(`${this} size could not be determined or was zero`);
|
|
163
181
|
}
|
|
164
182
|
|
|
183
|
+
// Normalize caller-provided subresources into one validated mip chain description.
|
|
184
|
+
const textureData = analyzeTextureSubresources(this.device, subresources, size, {
|
|
185
|
+
format: userProvidedFormat ? originalPropsWithAsyncData.format : undefined
|
|
186
|
+
});
|
|
187
|
+
const resolvedFormat = textureData.format ?? this.props.format;
|
|
188
|
+
|
|
165
189
|
// Create a minimal TextureProps and validate via `satisfies`
|
|
166
190
|
const baseTextureProps = {
|
|
167
191
|
...this.props,
|
|
168
192
|
...size,
|
|
193
|
+
format: resolvedFormat,
|
|
169
194
|
mipLevels: 1, // temporary; updated below
|
|
170
195
|
data: undefined
|
|
171
196
|
} satisfies TextureProps;
|
|
172
197
|
|
|
173
|
-
if (this.device.
|
|
198
|
+
if (this.device.isTextureFormatCompressed(resolvedFormat) && !userProvidedUsage) {
|
|
199
|
+
baseTextureProps.usage = Texture.SAMPLE | Texture.COPY_DST;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Explicit mip arrays take ownership of the mip chain; otherwise we may auto-generate it.
|
|
203
|
+
const shouldGenerateMipmaps =
|
|
204
|
+
this.props.mipmaps &&
|
|
205
|
+
!textureData.hasExplicitMipChain &&
|
|
206
|
+
!this.device.isTextureFormatCompressed(resolvedFormat);
|
|
207
|
+
|
|
208
|
+
if (this.device.type === 'webgpu' && shouldGenerateMipmaps) {
|
|
174
209
|
const requiredUsage =
|
|
175
210
|
this.props.dimension === '3d'
|
|
176
211
|
? Texture.SAMPLE | Texture.STORAGE | Texture.COPY_DST | Texture.COPY_SRC
|
|
@@ -180,8 +215,9 @@ export class DynamicTexture {
|
|
|
180
215
|
|
|
181
216
|
// Compute mip levels (auto clamps to max)
|
|
182
217
|
const maxMips = this.device.getMipLevelCount(baseTextureProps.width, baseTextureProps.height);
|
|
183
|
-
const desired =
|
|
184
|
-
|
|
218
|
+
const desired = textureData.hasExplicitMipChain
|
|
219
|
+
? textureData.mipLevels
|
|
220
|
+
: this.props.mipLevels === 'auto'
|
|
185
221
|
? maxMips
|
|
186
222
|
: Math.max(1, Math.min(maxMips, this.props.mipLevels ?? 1));
|
|
187
223
|
|
|
@@ -192,33 +228,15 @@ export class DynamicTexture {
|
|
|
192
228
|
this._view = this.texture.view;
|
|
193
229
|
|
|
194
230
|
// Upload data if provided
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
this.setTexture2DData(propsWithSyncData.data);
|
|
202
|
-
break;
|
|
203
|
-
case '3d':
|
|
204
|
-
this.setTexture3DData(propsWithSyncData.data);
|
|
205
|
-
break;
|
|
206
|
-
case '2d-array':
|
|
207
|
-
this.setTextureArrayData(propsWithSyncData.data);
|
|
208
|
-
break;
|
|
209
|
-
case 'cube':
|
|
210
|
-
this.setTextureCubeData(propsWithSyncData.data);
|
|
211
|
-
break;
|
|
212
|
-
case 'cube-array':
|
|
213
|
-
this.setTextureCubeArrayData(propsWithSyncData.data);
|
|
214
|
-
break;
|
|
215
|
-
default: {
|
|
216
|
-
throw new Error(`Unhandled dimension ${propsWithSyncData.dimension}`);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
231
|
+
if (textureData.subresources.length) {
|
|
232
|
+
this._setTextureSubresources(textureData.subresources);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (this.props.mipmaps && !textureData.hasExplicitMipChain && !shouldGenerateMipmaps) {
|
|
236
|
+
log.warn(`${this} skipping auto-generated mipmaps for compressed texture format`)();
|
|
219
237
|
}
|
|
220
238
|
|
|
221
|
-
if (
|
|
239
|
+
if (shouldGenerateMipmaps) {
|
|
222
240
|
this.generateMipmaps();
|
|
223
241
|
}
|
|
224
242
|
|
|
@@ -229,7 +247,6 @@ export class DynamicTexture {
|
|
|
229
247
|
} catch (e) {
|
|
230
248
|
const err = e instanceof Error ? e : new Error(String(e));
|
|
231
249
|
this.rejectReady(err);
|
|
232
|
-
throw err;
|
|
233
250
|
}
|
|
234
251
|
}
|
|
235
252
|
|
|
@@ -247,7 +264,7 @@ export class DynamicTexture {
|
|
|
247
264
|
if (this.device.type === 'webgl') {
|
|
248
265
|
this.texture.generateMipmapsWebGL();
|
|
249
266
|
} else if (this.device.type === 'webgpu') {
|
|
250
|
-
|
|
267
|
+
this.device.generateMipmapsWebGPU(this.texture);
|
|
251
268
|
} else {
|
|
252
269
|
log.warn(`${this} mipmaps not supported on ${this.device.type}`);
|
|
253
270
|
}
|
|
@@ -261,6 +278,59 @@ export class DynamicTexture {
|
|
|
261
278
|
this._sampler = s;
|
|
262
279
|
}
|
|
263
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Copies texture contents into a GPU buffer and waits until the copy is complete.
|
|
283
|
+
* The caller owns the returned buffer and must destroy it when finished.
|
|
284
|
+
*/
|
|
285
|
+
async readBuffer(options: TextureReadOptions = {}): Promise<Buffer> {
|
|
286
|
+
if (!this.isReady) {
|
|
287
|
+
await this.ready;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const width = options.width ?? this.texture.width;
|
|
291
|
+
const height = options.height ?? this.texture.height;
|
|
292
|
+
const depthOrArrayLayers = options.depthOrArrayLayers ?? this.texture.depth;
|
|
293
|
+
const layout = this.texture.computeMemoryLayout({width, height, depthOrArrayLayers});
|
|
294
|
+
|
|
295
|
+
const buffer = this.device.createBuffer({
|
|
296
|
+
byteLength: layout.byteLength,
|
|
297
|
+
usage: Buffer.COPY_DST | Buffer.MAP_READ
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
this.texture.readBuffer(
|
|
301
|
+
{
|
|
302
|
+
...options,
|
|
303
|
+
width,
|
|
304
|
+
height,
|
|
305
|
+
depthOrArrayLayers
|
|
306
|
+
},
|
|
307
|
+
buffer
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
const fence = this.device.createFence();
|
|
311
|
+
await fence.signaled;
|
|
312
|
+
fence.destroy();
|
|
313
|
+
|
|
314
|
+
return buffer;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** Reads texture contents back to CPU memory. */
|
|
318
|
+
async readAsync(options: TextureReadOptions = {}): Promise<ArrayBuffer> {
|
|
319
|
+
if (!this.isReady) {
|
|
320
|
+
await this.ready;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const width = options.width ?? this.texture.width;
|
|
324
|
+
const height = options.height ?? this.texture.height;
|
|
325
|
+
const depthOrArrayLayers = options.depthOrArrayLayers ?? this.texture.depth;
|
|
326
|
+
const layout = this.texture.computeMemoryLayout({width, height, depthOrArrayLayers});
|
|
327
|
+
|
|
328
|
+
const buffer = await this.readBuffer(options);
|
|
329
|
+
const data = await buffer.readAsync(0, layout.byteLength);
|
|
330
|
+
buffer.destroy();
|
|
331
|
+
return data.buffer;
|
|
332
|
+
}
|
|
333
|
+
|
|
264
334
|
/**
|
|
265
335
|
* Resize by cloning the underlying immutable texture.
|
|
266
336
|
* Does not copy contents; caller may need to re-upload and/or regenerate mips.
|
|
@@ -342,7 +412,7 @@ export class DynamicTexture {
|
|
|
342
412
|
}
|
|
343
413
|
|
|
344
414
|
/** Cube array: multiple cubes (faces×layers), each face may carry multiple mips */
|
|
345
|
-
|
|
415
|
+
setTextureCubeArrayData(data: TextureCubeArrayData): void {
|
|
346
416
|
if (this.texture.props.dimension !== 'cube-array') {
|
|
347
417
|
throw new Error(`${this} is not cube-array`);
|
|
348
418
|
}
|
|
@@ -367,10 +437,13 @@ export class DynamicTexture {
|
|
|
367
437
|
this.texture.copyExternalImage({image, z, mipLevel, flipY});
|
|
368
438
|
break;
|
|
369
439
|
case 'texture-data':
|
|
370
|
-
const {data} = subresource;
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
440
|
+
const {data, textureFormat} = subresource;
|
|
441
|
+
if (textureFormat && textureFormat !== this.texture.format) {
|
|
442
|
+
throw new Error(
|
|
443
|
+
`${this} mip level ${mipLevel} uses format "${textureFormat}" but texture format is "${this.texture.format}"`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
this.texture.writeData(data.data, {
|
|
374
447
|
x: 0,
|
|
375
448
|
y: 0,
|
|
376
449
|
z,
|
|
@@ -415,36 +488,196 @@ export class DynamicTexture {
|
|
|
415
488
|
};
|
|
416
489
|
}
|
|
417
490
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
491
|
+
type TextureSubresourceAnalysis = {
|
|
492
|
+
readonly subresources: TextureSubresource[];
|
|
493
|
+
readonly mipLevels: number;
|
|
494
|
+
readonly format?: TextureFormat;
|
|
495
|
+
readonly hasExplicitMipChain: boolean;
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// Flatten dimension-specific texture data into one list of uploadable subresources.
|
|
499
|
+
function getTextureSubresources(
|
|
500
|
+
props: TextureDataProps & Partial<Pick<TextureProps, 'width' | 'height' | 'format'>>
|
|
501
|
+
): TextureSubresource[] {
|
|
502
|
+
if (!props.data) {
|
|
503
|
+
return [];
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const baseLevelSize =
|
|
507
|
+
props.width && props.height ? {width: props.width, height: props.height} : undefined;
|
|
508
|
+
const textureFormat = 'format' in props ? props.format : undefined;
|
|
509
|
+
|
|
510
|
+
switch (props.dimension) {
|
|
511
|
+
case '1d':
|
|
512
|
+
return getTexture1DSubresources(props.data);
|
|
513
|
+
case '2d':
|
|
514
|
+
return getTexture2DSubresources(0, props.data, baseLevelSize, textureFormat);
|
|
515
|
+
case '3d':
|
|
516
|
+
return getTexture3DSubresources(props.data);
|
|
517
|
+
case '2d-array':
|
|
518
|
+
return getTextureArraySubresources(props.data);
|
|
519
|
+
case 'cube':
|
|
520
|
+
return getTextureCubeSubresources(props.data);
|
|
521
|
+
case 'cube-array':
|
|
522
|
+
return getTextureCubeArraySubresources(props.data);
|
|
523
|
+
default:
|
|
524
|
+
throw new Error(`Unhandled dimension ${(props as TextureDataProps).dimension}`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Resolve a consistent texture format and the longest mip chain valid across all slices.
|
|
529
|
+
function analyzeTextureSubresources(
|
|
530
|
+
device: Device,
|
|
531
|
+
subresources: TextureSubresource[],
|
|
532
|
+
size: {width: number; height: number},
|
|
533
|
+
options: {format?: TextureFormat}
|
|
534
|
+
): TextureSubresourceAnalysis {
|
|
535
|
+
if (subresources.length === 0) {
|
|
536
|
+
return {
|
|
537
|
+
subresources,
|
|
538
|
+
mipLevels: 1,
|
|
539
|
+
format: options.format,
|
|
540
|
+
hasExplicitMipChain: false
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const groupedSubresources = new Map<number, TextureSubresource[]>();
|
|
545
|
+
for (const subresource of subresources) {
|
|
546
|
+
const group = groupedSubresources.get(subresource.z) ?? [];
|
|
547
|
+
group.push(subresource);
|
|
548
|
+
groupedSubresources.set(subresource.z, group);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const hasExplicitMipChain = subresources.some(subresource => subresource.mipLevel > 0);
|
|
552
|
+
let resolvedFormat = options.format;
|
|
553
|
+
let resolvedMipLevels = Number.POSITIVE_INFINITY;
|
|
554
|
+
const validSubresources: TextureSubresource[] = [];
|
|
555
|
+
|
|
556
|
+
for (const [z, sliceSubresources] of groupedSubresources) {
|
|
557
|
+
// Validate each slice independently, then keep only the mip levels that are valid everywhere.
|
|
558
|
+
const sortedSubresources = [...sliceSubresources].sort(
|
|
559
|
+
(left, right) => left.mipLevel - right.mipLevel
|
|
444
560
|
);
|
|
561
|
+
const baseLevel = sortedSubresources[0];
|
|
562
|
+
if (!baseLevel || baseLevel.mipLevel !== 0) {
|
|
563
|
+
throw new Error(`DynamicTexture: slice ${z} is missing mip level 0`);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const baseSize = getTextureSubresourceSize(device, baseLevel);
|
|
567
|
+
if (baseSize.width !== size.width || baseSize.height !== size.height) {
|
|
568
|
+
throw new Error(
|
|
569
|
+
`DynamicTexture: slice ${z} base level dimensions ${baseSize.width}x${baseSize.height} do not match expected ${size.width}x${size.height}`
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const baseFormat = getTextureSubresourceFormat(baseLevel);
|
|
574
|
+
if (baseFormat) {
|
|
575
|
+
if (resolvedFormat && resolvedFormat !== baseFormat) {
|
|
576
|
+
throw new Error(
|
|
577
|
+
`DynamicTexture: slice ${z} base level format "${baseFormat}" does not match texture format "${resolvedFormat}"`
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
resolvedFormat = baseFormat;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const mipLevelLimit =
|
|
584
|
+
resolvedFormat && device.isTextureFormatCompressed(resolvedFormat)
|
|
585
|
+
? // Block-compressed formats cannot have mips smaller than a single compression block.
|
|
586
|
+
getMaxCompressedMipLevels(device, baseSize.width, baseSize.height, resolvedFormat)
|
|
587
|
+
: device.getMipLevelCount(baseSize.width, baseSize.height);
|
|
588
|
+
|
|
589
|
+
let validMipLevelsForSlice = 0;
|
|
590
|
+
for (
|
|
591
|
+
let expectedMipLevel = 0;
|
|
592
|
+
expectedMipLevel < sortedSubresources.length;
|
|
593
|
+
expectedMipLevel++
|
|
594
|
+
) {
|
|
595
|
+
const subresource = sortedSubresources[expectedMipLevel];
|
|
596
|
+
// Stop at the first gap so callers can provide extra trailing data without breaking creation.
|
|
597
|
+
if (!subresource || subresource.mipLevel !== expectedMipLevel) {
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
if (expectedMipLevel >= mipLevelLimit) {
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const subresourceSize = getTextureSubresourceSize(device, subresource);
|
|
605
|
+
const expectedWidth = Math.max(1, baseSize.width >> expectedMipLevel);
|
|
606
|
+
const expectedHeight = Math.max(1, baseSize.height >> expectedMipLevel);
|
|
607
|
+
if (subresourceSize.width !== expectedWidth || subresourceSize.height !== expectedHeight) {
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const subresourceFormat = getTextureSubresourceFormat(subresource);
|
|
612
|
+
if (subresourceFormat) {
|
|
613
|
+
if (!resolvedFormat) {
|
|
614
|
+
resolvedFormat = subresourceFormat;
|
|
615
|
+
}
|
|
616
|
+
// Later mip levels must stay on the same format as the validated base level.
|
|
617
|
+
if (subresourceFormat !== resolvedFormat) {
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
validMipLevelsForSlice++;
|
|
623
|
+
validSubresources.push(subresource);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
resolvedMipLevels = Math.min(resolvedMipLevels, validMipLevelsForSlice);
|
|
445
627
|
}
|
|
446
628
|
|
|
447
|
-
|
|
629
|
+
const mipLevels = Number.isFinite(resolvedMipLevels) ? Math.max(1, resolvedMipLevels) : 1;
|
|
630
|
+
|
|
631
|
+
return {
|
|
632
|
+
// Keep every slice trimmed to the same mip count so the texture shape stays internally consistent.
|
|
633
|
+
subresources: validSubresources.filter(subresource => subresource.mipLevel < mipLevels),
|
|
634
|
+
mipLevels,
|
|
635
|
+
format: resolvedFormat,
|
|
636
|
+
hasExplicitMipChain
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Read the per-level format using the transitional textureFormat -> format fallback rules.
|
|
641
|
+
function getTextureSubresourceFormat(subresource: TextureSubresource): TextureFormat | undefined {
|
|
642
|
+
if (subresource.type !== 'texture-data') {
|
|
643
|
+
return undefined;
|
|
644
|
+
}
|
|
645
|
+
return subresource.textureFormat ?? resolveTextureImageFormat(subresource.data);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Resolve dimensions from either raw bytes or external-image subresources.
|
|
649
|
+
function getTextureSubresourceSize(
|
|
650
|
+
device: Device,
|
|
651
|
+
subresource: TextureSubresource
|
|
652
|
+
): {width: number; height: number} {
|
|
653
|
+
switch (subresource.type) {
|
|
654
|
+
case 'external-image':
|
|
655
|
+
return device.getExternalImageSize(subresource.image);
|
|
656
|
+
case 'texture-data':
|
|
657
|
+
return {width: subresource.data.width, height: subresource.data.height};
|
|
658
|
+
default:
|
|
659
|
+
throw new Error('Unsupported texture subresource');
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Count the mip levels that stay at or above one compression block in each dimension.
|
|
664
|
+
function getMaxCompressedMipLevels(
|
|
665
|
+
device: Device,
|
|
666
|
+
baseWidth: number,
|
|
667
|
+
baseHeight: number,
|
|
668
|
+
format: TextureFormat
|
|
669
|
+
): number {
|
|
670
|
+
const {blockWidth = 1, blockHeight = 1} = device.getTextureFormatInfo(format);
|
|
671
|
+
let mipLevels = 1;
|
|
672
|
+
for (let mipLevel = 1; ; mipLevel++) {
|
|
673
|
+
const width = Math.max(1, baseWidth >> mipLevel);
|
|
674
|
+
const height = Math.max(1, baseHeight >> mipLevel);
|
|
675
|
+
if (width < blockWidth || height < blockHeight) {
|
|
676
|
+
break;
|
|
677
|
+
}
|
|
678
|
+
mipLevels++;
|
|
679
|
+
}
|
|
680
|
+
return mipLevels;
|
|
448
681
|
}
|
|
449
682
|
|
|
450
683
|
// HELPERS
|
|
@@ -457,7 +690,7 @@ async function awaitAllPromises(x: any): Promise<any> {
|
|
|
457
690
|
}
|
|
458
691
|
if (x && typeof x === 'object' && x.constructor === Object) {
|
|
459
692
|
const object: Record<string, any> = x;
|
|
460
|
-
const values = await Promise.all(Object.values(object));
|
|
693
|
+
const values = await Promise.all(Object.values(object).map(awaitAllPromises));
|
|
461
694
|
const keys = Object.keys(object);
|
|
462
695
|
const resolvedObject: Record<string, any> = {};
|
|
463
696
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -9,6 +9,8 @@ export type TextureImageSource = ExternalImage;
|
|
|
9
9
|
* additional optional fields can describe compressed texture data.
|
|
10
10
|
*/
|
|
11
11
|
export type TextureImageData = {
|
|
12
|
+
/** Preferred WebGPU style format string. */
|
|
13
|
+
textureFormat?: TextureFormat;
|
|
12
14
|
/** WebGPU style format string. Defaults to 'rgba8unorm' */
|
|
13
15
|
format?: TextureFormat;
|
|
14
16
|
/** Typed Array with the bytes of the image. @note beware row byte alignment requirements */
|
|
@@ -104,6 +106,7 @@ export type TextureSubresource = {
|
|
|
104
106
|
| {
|
|
105
107
|
type: 'texture-data';
|
|
106
108
|
data: TextureImageData;
|
|
109
|
+
textureFormat?: TextureFormat;
|
|
107
110
|
}
|
|
108
111
|
);
|
|
109
112
|
|
|
@@ -173,8 +176,8 @@ function getTextureMipLevelSize(data: TextureMipLevelData): {width: number; heig
|
|
|
173
176
|
throw new Error('Unsupported mip-level data');
|
|
174
177
|
}
|
|
175
178
|
|
|
176
|
-
/** Type guard: is a mip-level `TextureImageData` (vs ExternalImage) */
|
|
177
|
-
function isTextureImageData(data:
|
|
179
|
+
/** Type guard: is a mip-level `TextureImageData` (vs ExternalImage or bare typed array) */
|
|
180
|
+
function isTextureImageData(data: unknown): data is TextureImageData {
|
|
178
181
|
return (
|
|
179
182
|
typeof data === 'object' &&
|
|
180
183
|
data !== null &&
|
|
@@ -184,6 +187,20 @@ function isTextureImageData(data: TextureMipLevelData): data is TextureImageData
|
|
|
184
187
|
);
|
|
185
188
|
}
|
|
186
189
|
|
|
190
|
+
function isTypedArrayMipLevelData(data: unknown): data is TypedArray {
|
|
191
|
+
return ArrayBuffer.isView(data);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function resolveTextureImageFormat(data: TextureImageData): TextureFormat | undefined {
|
|
195
|
+
const {textureFormat, format} = data;
|
|
196
|
+
if (textureFormat && format && textureFormat !== format) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
`Conflicting texture formats "${textureFormat}" and "${format}" provided for the same mip level`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
return textureFormat ?? format;
|
|
202
|
+
}
|
|
203
|
+
|
|
187
204
|
/** Resolve size for a single mip-level datum */
|
|
188
205
|
// function getTextureMipLevelSizeFromData(data: TextureMipLevelData): {
|
|
189
206
|
// width: number;
|
|
@@ -222,14 +239,18 @@ export function getTexture1DSubresources(data: Texture1DData): TextureSubresourc
|
|
|
222
239
|
}
|
|
223
240
|
|
|
224
241
|
/** Normalize 2D layer payload into an array of mip-level items */
|
|
225
|
-
function _normalizeTexture2DData(
|
|
242
|
+
function _normalizeTexture2DData(
|
|
243
|
+
data: Texture2DData
|
|
244
|
+
): (TextureImageData | ExternalImage | TypedArray)[] {
|
|
226
245
|
return Array.isArray(data) ? data : [data];
|
|
227
246
|
}
|
|
228
247
|
|
|
229
248
|
/** Experimental: Set multiple mip levels (2D), optionally at `z` (depth/array index) */
|
|
230
249
|
export function getTexture2DSubresources(
|
|
231
250
|
slice: number,
|
|
232
|
-
lodData: Texture2DData
|
|
251
|
+
lodData: Texture2DData,
|
|
252
|
+
baseLevelSize?: {width: number; height: number},
|
|
253
|
+
textureFormat?: TextureFormat
|
|
233
254
|
): TextureSubresource[] {
|
|
234
255
|
const lodArray = _normalizeTexture2DData(lodData);
|
|
235
256
|
const z = slice;
|
|
@@ -249,6 +270,20 @@ export function getTexture2DSubresources(
|
|
|
249
270
|
subresources.push({
|
|
250
271
|
type: 'texture-data',
|
|
251
272
|
data: imageData,
|
|
273
|
+
textureFormat: resolveTextureImageFormat(imageData),
|
|
274
|
+
z,
|
|
275
|
+
mipLevel
|
|
276
|
+
});
|
|
277
|
+
} else if (isTypedArrayMipLevelData(imageData) && baseLevelSize) {
|
|
278
|
+
subresources.push({
|
|
279
|
+
type: 'texture-data',
|
|
280
|
+
data: {
|
|
281
|
+
data: imageData,
|
|
282
|
+
width: Math.max(1, baseLevelSize.width >> mipLevel),
|
|
283
|
+
height: Math.max(1, baseLevelSize.height >> mipLevel),
|
|
284
|
+
...(textureFormat ? {format: textureFormat} : {})
|
|
285
|
+
},
|
|
286
|
+
textureFormat,
|
|
252
287
|
z,
|
|
253
288
|
mipLevel
|
|
254
289
|
});
|
|
@@ -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;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import type {PrimitiveTopology, BufferLayout} from '@luma.gl/core';
|
|
6
|
-
import {Device, Buffer,
|
|
6
|
+
import {Device, Buffer, vertexFormatDecoder} from '@luma.gl/core';
|
|
7
7
|
import type {Geometry} from '../geometry/geometry';
|
|
8
8
|
import {uid} from '../utils/uid';
|
|
9
9
|
|
|
@@ -127,8 +127,13 @@ export function getAttributeBuffersFromGeometry(
|
|
|
127
127
|
id: `${attributeName}-buffer`
|
|
128
128
|
});
|
|
129
129
|
const {value, size, normalized} = attribute;
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
if (size === undefined) {
|
|
131
|
+
throw new Error(`Attribute ${attributeName} is missing a size`);
|
|
132
|
+
}
|
|
133
|
+
bufferLayout.push({
|
|
134
|
+
name,
|
|
135
|
+
format: vertexFormatDecoder.getVertexFormatFromAttribute(value, size, normalized)
|
|
136
|
+
});
|
|
132
137
|
}
|
|
133
138
|
}
|
|
134
139
|
|