@luma.gl/engine 9.2.5 → 9.3.0-alpha.10
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 +11 -5
- package/dist/animation-loop/animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/animation-loop.js +83 -47
- 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 +16 -13
- package/dist/compute/computation.js.map +1 -1
- package/dist/compute/swap.d.ts +2 -0
- package/dist/compute/swap.d.ts.map +1 -1
- package/dist/compute/swap.js +10 -5
- package/dist/compute/swap.js.map +1 -1
- package/dist/dist.dev.js +2639 -1290
- package/dist/dist.min.js +325 -210
- package/dist/dynamic-texture/dynamic-texture.d.ts +102 -0
- package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -0
- package/dist/dynamic-texture/dynamic-texture.js +556 -0
- package/dist/dynamic-texture/dynamic-texture.js.map +1 -0
- package/dist/dynamic-texture/texture-data.d.ts +144 -0
- package/dist/dynamic-texture/texture-data.d.ts.map +1 -0
- package/dist/dynamic-texture/texture-data.js +208 -0
- package/dist/dynamic-texture/texture-data.js.map +1 -0
- 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 +2497 -1212
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +20 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- 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 +47 -16
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +113 -47
- 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 +8 -5
- package/dist/models/billboard-texture-model.d.ts.map +1 -1
- package/dist/models/billboard-texture-model.js +77 -23
- package/dist/models/billboard-texture-model.js.map +1 -1
- package/dist/models/billboard-texture-module.d.ts +1 -1
- package/dist/models/billboard-texture-module.js +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 +4 -4
- 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 +13 -12
- package/dist/modules/picking/picking-uniforms.d.ts.map +1 -1
- package/dist/modules/picking/picking-uniforms.js +27 -14
- 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 +12 -27
- package/dist/passes/get-fragment-shader.js.map +1 -1
- package/dist/passes/shader-pass-renderer.d.ts +5 -7
- package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
- package/dist/passes/shader-pass-renderer.js +16 -42
- 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/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/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 +6 -6
- package/src/animation-loop/animation-loop.ts +89 -50
- 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 +32 -17
- package/src/compute/swap.ts +13 -7
- package/src/dynamic-texture/dynamic-texture.ts +732 -0
- package/src/dynamic-texture/texture-data.ts +336 -0
- 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 +38 -8
- package/src/material/material-factory.ts +157 -0
- package/src/material/material.ts +254 -0
- package/src/model/model.ts +158 -67
- package/src/model/split-uniforms-and-bindings.ts +8 -6
- package/src/models/billboard-texture-model.ts +88 -27
- package/src/models/billboard-texture-module.ts +1 -1
- 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 +39 -24
- package/src/modules/picking/picking.ts +22 -0
- package/src/passes/get-fragment-shader.ts +12 -27
- package/src/passes/shader-pass-renderer.ts +25 -48
- 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/buffer-layout-order.ts +18 -2
- package/src/utils/shader-module-utils.ts +65 -0
- package/dist/async-texture/async-texture.d.ts +0 -166
- package/dist/async-texture/async-texture.d.ts.map +0 -1
- package/dist/async-texture/async-texture.js +0 -386
- package/dist/async-texture/async-texture.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 -88
- package/dist/factories/shader-factory.js.map +0 -1
- package/src/async-texture/async-texture.ts +0 -551
- package/src/factories/pipeline-factory.ts +0 -224
- package/src/factories/shader-factory.ts +0 -103
- /package/src/{async-texture/texture-setters.ts.disabled → dynamic-texture/texture-data.ts.disabled} +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { TextureProps, SamplerProps, TextureView, Device, TextureReadOptions } from '@luma.gl/core';
|
|
2
|
+
import { Buffer, Texture, Sampler } from '@luma.gl/core';
|
|
3
|
+
import { type TextureCubeFace, type TextureDataAsyncProps, type Texture1DData, type Texture2DData, type Texture3DData, type TextureArrayData, type TextureCubeArrayData, type TextureCubeData } from "./texture-data.js";
|
|
4
|
+
/**
|
|
5
|
+
* Properties for a dynamic texture
|
|
6
|
+
*/
|
|
7
|
+
export type DynamicTextureProps = Omit<TextureProps, 'data' | 'mipLevels' | 'width' | 'height'> & TextureDataAsyncProps & {
|
|
8
|
+
/** Generate mipmaps after creating textures and setting data */
|
|
9
|
+
mipmaps?: boolean;
|
|
10
|
+
/** nipLevels can be set to 'auto' to generate max number of mipLevels */
|
|
11
|
+
mipLevels?: number | 'auto';
|
|
12
|
+
/** Width - can be auto-calculated when initializing from ExternalImage */
|
|
13
|
+
width?: number;
|
|
14
|
+
/** Height - can be auto-calculated when initializing from ExternalImage */
|
|
15
|
+
height?: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Dynamic Textures
|
|
19
|
+
*
|
|
20
|
+
* - Mipmaps - DynamicTexture can generate mipmaps for textures (WebGPU does not provide built-in mipmap generation).
|
|
21
|
+
*
|
|
22
|
+
* - Texture initialization and updates - complex textures (2d array textures, cube textures, 3d textures) need multiple images
|
|
23
|
+
* `DynamicTexture` provides an API that makes it easy to provide the required data.
|
|
24
|
+
*
|
|
25
|
+
* - Texture resizing - Textures are immutable in WebGPU, meaning that they cannot be resized after creation.
|
|
26
|
+
* DynamicTexture provides a `resize()` method that internally creates a new texture with the same parameters
|
|
27
|
+
* but a different size.
|
|
28
|
+
*
|
|
29
|
+
* - Async image data initialization - It is often very convenient to be able to initialize textures with promises
|
|
30
|
+
* returned by image or data loading functions, as it allows a callback-free linear style of programming.
|
|
31
|
+
*
|
|
32
|
+
* @note GPU Textures are quite complex objects, with many subresources and modes of usage.
|
|
33
|
+
* The `DynamicTexture` class allows luma.gl to provide some support for working with textures
|
|
34
|
+
* without accumulating excessive complexity in the core Texture class which is designed as an immutable nature of GPU resource.
|
|
35
|
+
*/
|
|
36
|
+
export declare class DynamicTexture {
|
|
37
|
+
readonly device: Device;
|
|
38
|
+
readonly id: string;
|
|
39
|
+
/** Props with defaults resolved (except `data` which is processed separately) */
|
|
40
|
+
props: Readonly<Required<DynamicTextureProps>>;
|
|
41
|
+
/** Created resources */
|
|
42
|
+
private _texture;
|
|
43
|
+
private _sampler;
|
|
44
|
+
private _view;
|
|
45
|
+
/** Ready when GPU texture has been created and data (if any) uploaded */
|
|
46
|
+
readonly ready: Promise<Texture>;
|
|
47
|
+
isReady: boolean;
|
|
48
|
+
destroyed: boolean;
|
|
49
|
+
private resolveReady;
|
|
50
|
+
private rejectReady;
|
|
51
|
+
get texture(): Texture;
|
|
52
|
+
get sampler(): Sampler;
|
|
53
|
+
get view(): TextureView;
|
|
54
|
+
get [Symbol.toStringTag](): string;
|
|
55
|
+
toString(): string;
|
|
56
|
+
constructor(device: Device, props: DynamicTextureProps);
|
|
57
|
+
/** @note Fire and forget; caller can await `ready` */
|
|
58
|
+
initAsync(originalPropsWithAsyncData: DynamicTextureProps): Promise<void>;
|
|
59
|
+
destroy(): void;
|
|
60
|
+
generateMipmaps(): void;
|
|
61
|
+
/** Set sampler or create one from props */
|
|
62
|
+
setSampler(sampler?: Sampler | SamplerProps): void;
|
|
63
|
+
/**
|
|
64
|
+
* Copies texture contents into a GPU buffer and waits until the copy is complete.
|
|
65
|
+
* The caller owns the returned buffer and must destroy it when finished.
|
|
66
|
+
*/
|
|
67
|
+
readBuffer(options?: TextureReadOptions): Promise<Buffer>;
|
|
68
|
+
/** Reads texture contents back to CPU memory. */
|
|
69
|
+
readAsync(options?: TextureReadOptions): Promise<ArrayBuffer>;
|
|
70
|
+
/**
|
|
71
|
+
* Resize by cloning the underlying immutable texture.
|
|
72
|
+
* Does not copy contents; caller may need to re-upload and/or regenerate mips.
|
|
73
|
+
*/
|
|
74
|
+
resize(size: {
|
|
75
|
+
width: number;
|
|
76
|
+
height: number;
|
|
77
|
+
}): boolean;
|
|
78
|
+
/** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
|
|
79
|
+
getCubeFaceIndex(face: TextureCubeFace): number;
|
|
80
|
+
/** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
|
|
81
|
+
getCubeArrayFaceIndex(cubeIndex: number, face: TextureCubeFace): number;
|
|
82
|
+
/** @note experimental: Set multiple mip levels (1D) */
|
|
83
|
+
setTexture1DData(data: Texture1DData): void;
|
|
84
|
+
/** @note experimental: Set multiple mip levels (2D), optionally at `z`, slice (depth/array level) index */
|
|
85
|
+
setTexture2DData(lodData: Texture2DData, z?: number): void;
|
|
86
|
+
/** 3D: multiple depth slices, each may carry multiple mip levels */
|
|
87
|
+
setTexture3DData(data: Texture3DData): void;
|
|
88
|
+
/** 2D array: multiple layers, each may carry multiple mip levels */
|
|
89
|
+
setTextureArrayData(data: TextureArrayData): void;
|
|
90
|
+
/** Cube: 6 faces, each may carry multiple mip levels */
|
|
91
|
+
setTextureCubeData(data: TextureCubeData): void;
|
|
92
|
+
/** Cube array: multiple cubes (faces×layers), each face may carry multiple mips */
|
|
93
|
+
setTextureCubeArrayData(data: TextureCubeArrayData): void;
|
|
94
|
+
/** Sets multiple mip levels on different `z` slices (depth/array index) */
|
|
95
|
+
private _setTextureSubresources;
|
|
96
|
+
/** Recursively resolve all promises in data structures */
|
|
97
|
+
private _loadAllData;
|
|
98
|
+
private _checkNotDestroyed;
|
|
99
|
+
private _checkReady;
|
|
100
|
+
static defaultProps: Required<DynamicTextureProps>;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=dynamic-texture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamic-texture.d.ts","sourceRoot":"","sources":["../../src/dynamic-texture/dynamic-texture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
// luma.gl, MIT license
|
|
2
|
+
// Copyright (c) vis.gl contributors
|
|
3
|
+
import { Buffer, Texture, Sampler, log } from '@luma.gl/core';
|
|
4
|
+
// import {loadImageBitmap} from '../application-utils/load-file';
|
|
5
|
+
import { uid } from "../utils/uid.js";
|
|
6
|
+
import { TEXTURE_CUBE_FACE_MAP,
|
|
7
|
+
// Helpers
|
|
8
|
+
getTextureSizeFromData, resolveTextureImageFormat, getTexture1DSubresources, getTexture2DSubresources, getTexture3DSubresources, getTextureCubeSubresources, getTextureArraySubresources, getTextureCubeArraySubresources } from "./texture-data.js";
|
|
9
|
+
/**
|
|
10
|
+
* Dynamic Textures
|
|
11
|
+
*
|
|
12
|
+
* - Mipmaps - DynamicTexture can generate mipmaps for textures (WebGPU does not provide built-in mipmap generation).
|
|
13
|
+
*
|
|
14
|
+
* - Texture initialization and updates - complex textures (2d array textures, cube textures, 3d textures) need multiple images
|
|
15
|
+
* `DynamicTexture` provides an API that makes it easy to provide the required data.
|
|
16
|
+
*
|
|
17
|
+
* - Texture resizing - Textures are immutable in WebGPU, meaning that they cannot be resized after creation.
|
|
18
|
+
* DynamicTexture provides a `resize()` method that internally creates a new texture with the same parameters
|
|
19
|
+
* but a different size.
|
|
20
|
+
*
|
|
21
|
+
* - Async image data initialization - It is often very convenient to be able to initialize textures with promises
|
|
22
|
+
* returned by image or data loading functions, as it allows a callback-free linear style of programming.
|
|
23
|
+
*
|
|
24
|
+
* @note GPU Textures are quite complex objects, with many subresources and modes of usage.
|
|
25
|
+
* The `DynamicTexture` class allows luma.gl to provide some support for working with textures
|
|
26
|
+
* without accumulating excessive complexity in the core Texture class which is designed as an immutable nature of GPU resource.
|
|
27
|
+
*/
|
|
28
|
+
export class DynamicTexture {
|
|
29
|
+
device;
|
|
30
|
+
id;
|
|
31
|
+
/** Props with defaults resolved (except `data` which is processed separately) */
|
|
32
|
+
props;
|
|
33
|
+
/** Created resources */
|
|
34
|
+
_texture = null;
|
|
35
|
+
_sampler = null;
|
|
36
|
+
_view = null;
|
|
37
|
+
/** Ready when GPU texture has been created and data (if any) uploaded */
|
|
38
|
+
ready;
|
|
39
|
+
isReady = false;
|
|
40
|
+
destroyed = false;
|
|
41
|
+
resolveReady = () => { };
|
|
42
|
+
rejectReady = () => { };
|
|
43
|
+
get texture() {
|
|
44
|
+
if (!this._texture)
|
|
45
|
+
throw new Error('Texture not initialized yet');
|
|
46
|
+
return this._texture;
|
|
47
|
+
}
|
|
48
|
+
get sampler() {
|
|
49
|
+
if (!this._sampler)
|
|
50
|
+
throw new Error('Sampler not initialized yet');
|
|
51
|
+
return this._sampler;
|
|
52
|
+
}
|
|
53
|
+
get view() {
|
|
54
|
+
if (!this._view)
|
|
55
|
+
throw new Error('View not initialized yet');
|
|
56
|
+
return this._view;
|
|
57
|
+
}
|
|
58
|
+
get [Symbol.toStringTag]() {
|
|
59
|
+
return 'DynamicTexture';
|
|
60
|
+
}
|
|
61
|
+
toString() {
|
|
62
|
+
return `DynamicTexture:"${this.id}":${this.texture.width}x${this.texture.height}px:(${this.isReady ? 'ready' : 'loading...'})`;
|
|
63
|
+
}
|
|
64
|
+
constructor(device, props) {
|
|
65
|
+
this.device = device;
|
|
66
|
+
const id = uid('dynamic-texture');
|
|
67
|
+
// NOTE: We avoid holding on to data to make sure it can be garbage collected.
|
|
68
|
+
const originalPropsWithAsyncData = props;
|
|
69
|
+
this.props = { ...DynamicTexture.defaultProps, id, ...props, data: null };
|
|
70
|
+
this.id = this.props.id;
|
|
71
|
+
this.ready = new Promise((resolve, reject) => {
|
|
72
|
+
this.resolveReady = resolve;
|
|
73
|
+
this.rejectReady = reject;
|
|
74
|
+
});
|
|
75
|
+
this.initAsync(originalPropsWithAsyncData);
|
|
76
|
+
}
|
|
77
|
+
/** @note Fire and forget; caller can await `ready` */
|
|
78
|
+
async initAsync(originalPropsWithAsyncData) {
|
|
79
|
+
try {
|
|
80
|
+
// TODO - Accept URL string for 2D: turn into ExternalImage promise
|
|
81
|
+
// const dataProps =
|
|
82
|
+
// typeof props.data === 'string' && (props.dimension ?? '2d') === '2d'
|
|
83
|
+
// ? ({dimension: '2d', data: loadImageBitmap(props.data)} as const)
|
|
84
|
+
// : {};
|
|
85
|
+
const propsWithSyncData = await this._loadAllData(originalPropsWithAsyncData);
|
|
86
|
+
this._checkNotDestroyed();
|
|
87
|
+
const subresources = propsWithSyncData.data
|
|
88
|
+
? getTextureSubresources({
|
|
89
|
+
...propsWithSyncData,
|
|
90
|
+
width: originalPropsWithAsyncData.width,
|
|
91
|
+
height: originalPropsWithAsyncData.height,
|
|
92
|
+
format: originalPropsWithAsyncData.format
|
|
93
|
+
})
|
|
94
|
+
: [];
|
|
95
|
+
const userProvidedFormat = 'format' in originalPropsWithAsyncData && originalPropsWithAsyncData.format !== undefined;
|
|
96
|
+
const userProvidedUsage = 'usage' in originalPropsWithAsyncData && originalPropsWithAsyncData.usage !== undefined;
|
|
97
|
+
// Deduce size when not explicitly provided
|
|
98
|
+
// TODO - what about depth?
|
|
99
|
+
const deduceSize = () => {
|
|
100
|
+
if (this.props.width && this.props.height) {
|
|
101
|
+
return { width: this.props.width, height: this.props.height };
|
|
102
|
+
}
|
|
103
|
+
const size = getTextureSizeFromData(propsWithSyncData);
|
|
104
|
+
if (size) {
|
|
105
|
+
return size;
|
|
106
|
+
}
|
|
107
|
+
return { width: this.props.width || 1, height: this.props.height || 1 };
|
|
108
|
+
};
|
|
109
|
+
const size = deduceSize();
|
|
110
|
+
if (!size || size.width <= 0 || size.height <= 0) {
|
|
111
|
+
throw new Error(`${this} size could not be determined or was zero`);
|
|
112
|
+
}
|
|
113
|
+
// Normalize caller-provided subresources into one validated mip chain description.
|
|
114
|
+
const textureData = analyzeTextureSubresources(this.device, subresources, size, {
|
|
115
|
+
format: userProvidedFormat ? originalPropsWithAsyncData.format : undefined
|
|
116
|
+
});
|
|
117
|
+
const resolvedFormat = textureData.format ?? this.props.format;
|
|
118
|
+
// Create a minimal TextureProps and validate via `satisfies`
|
|
119
|
+
const baseTextureProps = {
|
|
120
|
+
...this.props,
|
|
121
|
+
...size,
|
|
122
|
+
format: resolvedFormat,
|
|
123
|
+
mipLevels: 1, // temporary; updated below
|
|
124
|
+
data: undefined
|
|
125
|
+
};
|
|
126
|
+
if (this.device.isTextureFormatCompressed(resolvedFormat) && !userProvidedUsage) {
|
|
127
|
+
baseTextureProps.usage = Texture.SAMPLE | Texture.COPY_DST;
|
|
128
|
+
}
|
|
129
|
+
// Explicit mip arrays take ownership of the mip chain; otherwise we may auto-generate it.
|
|
130
|
+
const shouldGenerateMipmaps = this.props.mipmaps &&
|
|
131
|
+
!textureData.hasExplicitMipChain &&
|
|
132
|
+
!this.device.isTextureFormatCompressed(resolvedFormat);
|
|
133
|
+
if (this.device.type === 'webgpu' && shouldGenerateMipmaps) {
|
|
134
|
+
const requiredUsage = this.props.dimension === '3d'
|
|
135
|
+
? Texture.SAMPLE | Texture.STORAGE | Texture.COPY_DST | Texture.COPY_SRC
|
|
136
|
+
: Texture.SAMPLE | Texture.RENDER | Texture.COPY_DST | Texture.COPY_SRC;
|
|
137
|
+
baseTextureProps.usage |= requiredUsage;
|
|
138
|
+
}
|
|
139
|
+
// Compute mip levels (auto clamps to max)
|
|
140
|
+
const maxMips = this.device.getMipLevelCount(baseTextureProps.width, baseTextureProps.height);
|
|
141
|
+
const desired = textureData.hasExplicitMipChain
|
|
142
|
+
? textureData.mipLevels
|
|
143
|
+
: this.props.mipLevels === 'auto'
|
|
144
|
+
? maxMips
|
|
145
|
+
: Math.max(1, Math.min(maxMips, this.props.mipLevels ?? 1));
|
|
146
|
+
const finalTextureProps = { ...baseTextureProps, mipLevels: desired };
|
|
147
|
+
this._texture = this.device.createTexture(finalTextureProps);
|
|
148
|
+
this._sampler = this.texture.sampler;
|
|
149
|
+
this._view = this.texture.view;
|
|
150
|
+
// Upload data if provided
|
|
151
|
+
if (textureData.subresources.length) {
|
|
152
|
+
this._setTextureSubresources(textureData.subresources);
|
|
153
|
+
}
|
|
154
|
+
if (this.props.mipmaps && !textureData.hasExplicitMipChain && !shouldGenerateMipmaps) {
|
|
155
|
+
log.warn(`${this} skipping auto-generated mipmaps for compressed texture format`)();
|
|
156
|
+
}
|
|
157
|
+
if (shouldGenerateMipmaps) {
|
|
158
|
+
this.generateMipmaps();
|
|
159
|
+
}
|
|
160
|
+
this.isReady = true;
|
|
161
|
+
this.resolveReady(this.texture);
|
|
162
|
+
log.info(0, `${this} created`)();
|
|
163
|
+
}
|
|
164
|
+
catch (e) {
|
|
165
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
166
|
+
this.rejectReady(err);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
destroy() {
|
|
170
|
+
if (this._texture) {
|
|
171
|
+
this._texture.destroy();
|
|
172
|
+
this._texture = null;
|
|
173
|
+
this._sampler = null;
|
|
174
|
+
this._view = null;
|
|
175
|
+
}
|
|
176
|
+
this.destroyed = true;
|
|
177
|
+
}
|
|
178
|
+
generateMipmaps() {
|
|
179
|
+
if (this.device.type === 'webgl') {
|
|
180
|
+
this.texture.generateMipmapsWebGL();
|
|
181
|
+
}
|
|
182
|
+
else if (this.device.type === 'webgpu') {
|
|
183
|
+
this.device.generateMipmapsWebGPU(this.texture);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
log.warn(`${this} mipmaps not supported on ${this.device.type}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** Set sampler or create one from props */
|
|
190
|
+
setSampler(sampler = {}) {
|
|
191
|
+
this._checkReady();
|
|
192
|
+
const s = sampler instanceof Sampler ? sampler : this.device.createSampler(sampler);
|
|
193
|
+
this.texture.setSampler(s);
|
|
194
|
+
this._sampler = s;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Copies texture contents into a GPU buffer and waits until the copy is complete.
|
|
198
|
+
* The caller owns the returned buffer and must destroy it when finished.
|
|
199
|
+
*/
|
|
200
|
+
async readBuffer(options = {}) {
|
|
201
|
+
if (!this.isReady) {
|
|
202
|
+
await this.ready;
|
|
203
|
+
}
|
|
204
|
+
const width = options.width ?? this.texture.width;
|
|
205
|
+
const height = options.height ?? this.texture.height;
|
|
206
|
+
const depthOrArrayLayers = options.depthOrArrayLayers ?? this.texture.depth;
|
|
207
|
+
const layout = this.texture.computeMemoryLayout({ width, height, depthOrArrayLayers });
|
|
208
|
+
const buffer = this.device.createBuffer({
|
|
209
|
+
byteLength: layout.byteLength,
|
|
210
|
+
usage: Buffer.COPY_DST | Buffer.MAP_READ
|
|
211
|
+
});
|
|
212
|
+
this.texture.readBuffer({
|
|
213
|
+
...options,
|
|
214
|
+
width,
|
|
215
|
+
height,
|
|
216
|
+
depthOrArrayLayers
|
|
217
|
+
}, buffer);
|
|
218
|
+
const fence = this.device.createFence();
|
|
219
|
+
await fence.signaled;
|
|
220
|
+
fence.destroy();
|
|
221
|
+
return buffer;
|
|
222
|
+
}
|
|
223
|
+
/** Reads texture contents back to CPU memory. */
|
|
224
|
+
async readAsync(options = {}) {
|
|
225
|
+
if (!this.isReady) {
|
|
226
|
+
await this.ready;
|
|
227
|
+
}
|
|
228
|
+
const width = options.width ?? this.texture.width;
|
|
229
|
+
const height = options.height ?? this.texture.height;
|
|
230
|
+
const depthOrArrayLayers = options.depthOrArrayLayers ?? this.texture.depth;
|
|
231
|
+
const layout = this.texture.computeMemoryLayout({ width, height, depthOrArrayLayers });
|
|
232
|
+
const buffer = await this.readBuffer(options);
|
|
233
|
+
const data = await buffer.readAsync(0, layout.byteLength);
|
|
234
|
+
buffer.destroy();
|
|
235
|
+
return data.buffer;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Resize by cloning the underlying immutable texture.
|
|
239
|
+
* Does not copy contents; caller may need to re-upload and/or regenerate mips.
|
|
240
|
+
*/
|
|
241
|
+
resize(size) {
|
|
242
|
+
this._checkReady();
|
|
243
|
+
if (size.width === this.texture.width && size.height === this.texture.height) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
const prev = this.texture;
|
|
247
|
+
this._texture = prev.clone(size);
|
|
248
|
+
this._sampler = this.texture.sampler;
|
|
249
|
+
this._view = this.texture.view;
|
|
250
|
+
prev.destroy();
|
|
251
|
+
log.info(`${this} resized`);
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
/** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
|
|
255
|
+
getCubeFaceIndex(face) {
|
|
256
|
+
const index = TEXTURE_CUBE_FACE_MAP[face];
|
|
257
|
+
if (index === undefined)
|
|
258
|
+
throw new Error(`Invalid cube face: ${face}`);
|
|
259
|
+
return index;
|
|
260
|
+
}
|
|
261
|
+
/** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
|
|
262
|
+
getCubeArrayFaceIndex(cubeIndex, face) {
|
|
263
|
+
return 6 * cubeIndex + this.getCubeFaceIndex(face);
|
|
264
|
+
}
|
|
265
|
+
/** @note experimental: Set multiple mip levels (1D) */
|
|
266
|
+
setTexture1DData(data) {
|
|
267
|
+
this._checkReady();
|
|
268
|
+
if (this.texture.props.dimension !== '1d') {
|
|
269
|
+
throw new Error(`${this} is not 1d`);
|
|
270
|
+
}
|
|
271
|
+
const subresources = getTexture1DSubresources(data);
|
|
272
|
+
this._setTextureSubresources(subresources);
|
|
273
|
+
}
|
|
274
|
+
/** @note experimental: Set multiple mip levels (2D), optionally at `z`, slice (depth/array level) index */
|
|
275
|
+
setTexture2DData(lodData, z = 0) {
|
|
276
|
+
this._checkReady();
|
|
277
|
+
if (this.texture.props.dimension !== '2d') {
|
|
278
|
+
throw new Error(`${this} is not 2d`);
|
|
279
|
+
}
|
|
280
|
+
const subresources = getTexture2DSubresources(z, lodData);
|
|
281
|
+
this._setTextureSubresources(subresources);
|
|
282
|
+
}
|
|
283
|
+
/** 3D: multiple depth slices, each may carry multiple mip levels */
|
|
284
|
+
setTexture3DData(data) {
|
|
285
|
+
if (this.texture.props.dimension !== '3d') {
|
|
286
|
+
throw new Error(`${this} is not 3d`);
|
|
287
|
+
}
|
|
288
|
+
const subresources = getTexture3DSubresources(data);
|
|
289
|
+
this._setTextureSubresources(subresources);
|
|
290
|
+
}
|
|
291
|
+
/** 2D array: multiple layers, each may carry multiple mip levels */
|
|
292
|
+
setTextureArrayData(data) {
|
|
293
|
+
if (this.texture.props.dimension !== '2d-array') {
|
|
294
|
+
throw new Error(`${this} is not 2d-array`);
|
|
295
|
+
}
|
|
296
|
+
const subresources = getTextureArraySubresources(data);
|
|
297
|
+
this._setTextureSubresources(subresources);
|
|
298
|
+
}
|
|
299
|
+
/** Cube: 6 faces, each may carry multiple mip levels */
|
|
300
|
+
setTextureCubeData(data) {
|
|
301
|
+
if (this.texture.props.dimension !== 'cube') {
|
|
302
|
+
throw new Error(`${this} is not cube`);
|
|
303
|
+
}
|
|
304
|
+
const subresources = getTextureCubeSubresources(data);
|
|
305
|
+
this._setTextureSubresources(subresources);
|
|
306
|
+
}
|
|
307
|
+
/** Cube array: multiple cubes (faces×layers), each face may carry multiple mips */
|
|
308
|
+
setTextureCubeArrayData(data) {
|
|
309
|
+
if (this.texture.props.dimension !== 'cube-array') {
|
|
310
|
+
throw new Error(`${this} is not cube-array`);
|
|
311
|
+
}
|
|
312
|
+
const subresources = getTextureCubeArraySubresources(data);
|
|
313
|
+
this._setTextureSubresources(subresources);
|
|
314
|
+
}
|
|
315
|
+
/** Sets multiple mip levels on different `z` slices (depth/array index) */
|
|
316
|
+
_setTextureSubresources(subresources) {
|
|
317
|
+
// If user supplied multiple mip levels, warn if auto-mips also requested
|
|
318
|
+
// if (lodArray.length > 1 && this.props.mipmaps !== false) {
|
|
319
|
+
// log.warn(
|
|
320
|
+
// `Texture ${this.id}: provided multiple LODs and also requested mipmap generation.`
|
|
321
|
+
// )();
|
|
322
|
+
// }
|
|
323
|
+
for (const subresource of subresources) {
|
|
324
|
+
const { z, mipLevel } = subresource;
|
|
325
|
+
switch (subresource.type) {
|
|
326
|
+
case 'external-image':
|
|
327
|
+
const { image, flipY } = subresource;
|
|
328
|
+
this.texture.copyExternalImage({ image, z, mipLevel, flipY });
|
|
329
|
+
break;
|
|
330
|
+
case 'texture-data':
|
|
331
|
+
const { data, textureFormat } = subresource;
|
|
332
|
+
if (textureFormat && textureFormat !== this.texture.format) {
|
|
333
|
+
throw new Error(`${this} mip level ${mipLevel} uses format "${textureFormat}" but texture format is "${this.texture.format}"`);
|
|
334
|
+
}
|
|
335
|
+
this.texture.writeData(data.data, {
|
|
336
|
+
x: 0,
|
|
337
|
+
y: 0,
|
|
338
|
+
z,
|
|
339
|
+
width: data.width,
|
|
340
|
+
height: data.height,
|
|
341
|
+
depthOrArrayLayers: 1,
|
|
342
|
+
mipLevel
|
|
343
|
+
});
|
|
344
|
+
break;
|
|
345
|
+
default:
|
|
346
|
+
throw new Error('Unsupported 2D mip-level payload');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// ------------------ helpers ------------------
|
|
351
|
+
/** Recursively resolve all promises in data structures */
|
|
352
|
+
async _loadAllData(props) {
|
|
353
|
+
const syncData = await awaitAllPromises(props.data);
|
|
354
|
+
const dimension = (props.dimension ?? '2d');
|
|
355
|
+
return { dimension, data: syncData ?? null };
|
|
356
|
+
}
|
|
357
|
+
_checkNotDestroyed() {
|
|
358
|
+
if (this.destroyed) {
|
|
359
|
+
log.warn(`${this} already destroyed`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
_checkReady() {
|
|
363
|
+
if (!this.isReady) {
|
|
364
|
+
log.warn(`${this} Cannot perform this operation before ready`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
static defaultProps = {
|
|
368
|
+
...Texture.defaultProps,
|
|
369
|
+
dimension: '2d',
|
|
370
|
+
data: null,
|
|
371
|
+
mipmaps: false
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
// Flatten dimension-specific texture data into one list of uploadable subresources.
|
|
375
|
+
function getTextureSubresources(props) {
|
|
376
|
+
if (!props.data) {
|
|
377
|
+
return [];
|
|
378
|
+
}
|
|
379
|
+
const baseLevelSize = props.width && props.height ? { width: props.width, height: props.height } : undefined;
|
|
380
|
+
const textureFormat = 'format' in props ? props.format : undefined;
|
|
381
|
+
switch (props.dimension) {
|
|
382
|
+
case '1d':
|
|
383
|
+
return getTexture1DSubresources(props.data);
|
|
384
|
+
case '2d':
|
|
385
|
+
return getTexture2DSubresources(0, props.data, baseLevelSize, textureFormat);
|
|
386
|
+
case '3d':
|
|
387
|
+
return getTexture3DSubresources(props.data);
|
|
388
|
+
case '2d-array':
|
|
389
|
+
return getTextureArraySubresources(props.data);
|
|
390
|
+
case 'cube':
|
|
391
|
+
return getTextureCubeSubresources(props.data);
|
|
392
|
+
case 'cube-array':
|
|
393
|
+
return getTextureCubeArraySubresources(props.data);
|
|
394
|
+
default:
|
|
395
|
+
throw new Error(`Unhandled dimension ${props.dimension}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Resolve a consistent texture format and the longest mip chain valid across all slices.
|
|
399
|
+
function analyzeTextureSubresources(device, subresources, size, options) {
|
|
400
|
+
if (subresources.length === 0) {
|
|
401
|
+
return {
|
|
402
|
+
subresources,
|
|
403
|
+
mipLevels: 1,
|
|
404
|
+
format: options.format,
|
|
405
|
+
hasExplicitMipChain: false
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
const groupedSubresources = new Map();
|
|
409
|
+
for (const subresource of subresources) {
|
|
410
|
+
const group = groupedSubresources.get(subresource.z) ?? [];
|
|
411
|
+
group.push(subresource);
|
|
412
|
+
groupedSubresources.set(subresource.z, group);
|
|
413
|
+
}
|
|
414
|
+
const hasExplicitMipChain = subresources.some(subresource => subresource.mipLevel > 0);
|
|
415
|
+
let resolvedFormat = options.format;
|
|
416
|
+
let resolvedMipLevels = Number.POSITIVE_INFINITY;
|
|
417
|
+
const validSubresources = [];
|
|
418
|
+
for (const [z, sliceSubresources] of groupedSubresources) {
|
|
419
|
+
// Validate each slice independently, then keep only the mip levels that are valid everywhere.
|
|
420
|
+
const sortedSubresources = [...sliceSubresources].sort((left, right) => left.mipLevel - right.mipLevel);
|
|
421
|
+
const baseLevel = sortedSubresources[0];
|
|
422
|
+
if (!baseLevel || baseLevel.mipLevel !== 0) {
|
|
423
|
+
throw new Error(`DynamicTexture: slice ${z} is missing mip level 0`);
|
|
424
|
+
}
|
|
425
|
+
const baseSize = getTextureSubresourceSize(device, baseLevel);
|
|
426
|
+
if (baseSize.width !== size.width || baseSize.height !== size.height) {
|
|
427
|
+
throw new Error(`DynamicTexture: slice ${z} base level dimensions ${baseSize.width}x${baseSize.height} do not match expected ${size.width}x${size.height}`);
|
|
428
|
+
}
|
|
429
|
+
const baseFormat = getTextureSubresourceFormat(baseLevel);
|
|
430
|
+
if (baseFormat) {
|
|
431
|
+
if (resolvedFormat && resolvedFormat !== baseFormat) {
|
|
432
|
+
throw new Error(`DynamicTexture: slice ${z} base level format "${baseFormat}" does not match texture format "${resolvedFormat}"`);
|
|
433
|
+
}
|
|
434
|
+
resolvedFormat = baseFormat;
|
|
435
|
+
}
|
|
436
|
+
const mipLevelLimit = resolvedFormat && device.isTextureFormatCompressed(resolvedFormat)
|
|
437
|
+
? // Block-compressed formats cannot have mips smaller than a single compression block.
|
|
438
|
+
getMaxCompressedMipLevels(device, baseSize.width, baseSize.height, resolvedFormat)
|
|
439
|
+
: device.getMipLevelCount(baseSize.width, baseSize.height);
|
|
440
|
+
let validMipLevelsForSlice = 0;
|
|
441
|
+
for (let expectedMipLevel = 0; expectedMipLevel < sortedSubresources.length; expectedMipLevel++) {
|
|
442
|
+
const subresource = sortedSubresources[expectedMipLevel];
|
|
443
|
+
// Stop at the first gap so callers can provide extra trailing data without breaking creation.
|
|
444
|
+
if (!subresource || subresource.mipLevel !== expectedMipLevel) {
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
if (expectedMipLevel >= mipLevelLimit) {
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
const subresourceSize = getTextureSubresourceSize(device, subresource);
|
|
451
|
+
const expectedWidth = Math.max(1, baseSize.width >> expectedMipLevel);
|
|
452
|
+
const expectedHeight = Math.max(1, baseSize.height >> expectedMipLevel);
|
|
453
|
+
if (subresourceSize.width !== expectedWidth || subresourceSize.height !== expectedHeight) {
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
const subresourceFormat = getTextureSubresourceFormat(subresource);
|
|
457
|
+
if (subresourceFormat) {
|
|
458
|
+
if (!resolvedFormat) {
|
|
459
|
+
resolvedFormat = subresourceFormat;
|
|
460
|
+
}
|
|
461
|
+
// Later mip levels must stay on the same format as the validated base level.
|
|
462
|
+
if (subresourceFormat !== resolvedFormat) {
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
validMipLevelsForSlice++;
|
|
467
|
+
validSubresources.push(subresource);
|
|
468
|
+
}
|
|
469
|
+
resolvedMipLevels = Math.min(resolvedMipLevels, validMipLevelsForSlice);
|
|
470
|
+
}
|
|
471
|
+
const mipLevels = Number.isFinite(resolvedMipLevels) ? Math.max(1, resolvedMipLevels) : 1;
|
|
472
|
+
return {
|
|
473
|
+
// Keep every slice trimmed to the same mip count so the texture shape stays internally consistent.
|
|
474
|
+
subresources: validSubresources.filter(subresource => subresource.mipLevel < mipLevels),
|
|
475
|
+
mipLevels,
|
|
476
|
+
format: resolvedFormat,
|
|
477
|
+
hasExplicitMipChain
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
// Read the per-level format using the transitional textureFormat -> format fallback rules.
|
|
481
|
+
function getTextureSubresourceFormat(subresource) {
|
|
482
|
+
if (subresource.type !== 'texture-data') {
|
|
483
|
+
return undefined;
|
|
484
|
+
}
|
|
485
|
+
return subresource.textureFormat ?? resolveTextureImageFormat(subresource.data);
|
|
486
|
+
}
|
|
487
|
+
// Resolve dimensions from either raw bytes or external-image subresources.
|
|
488
|
+
function getTextureSubresourceSize(device, subresource) {
|
|
489
|
+
switch (subresource.type) {
|
|
490
|
+
case 'external-image':
|
|
491
|
+
return device.getExternalImageSize(subresource.image);
|
|
492
|
+
case 'texture-data':
|
|
493
|
+
return { width: subresource.data.width, height: subresource.data.height };
|
|
494
|
+
default:
|
|
495
|
+
throw new Error('Unsupported texture subresource');
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
// Count the mip levels that stay at or above one compression block in each dimension.
|
|
499
|
+
function getMaxCompressedMipLevels(device, baseWidth, baseHeight, format) {
|
|
500
|
+
const { blockWidth = 1, blockHeight = 1 } = device.getTextureFormatInfo(format);
|
|
501
|
+
let mipLevels = 1;
|
|
502
|
+
for (let mipLevel = 1;; mipLevel++) {
|
|
503
|
+
const width = Math.max(1, baseWidth >> mipLevel);
|
|
504
|
+
const height = Math.max(1, baseHeight >> mipLevel);
|
|
505
|
+
if (width < blockWidth || height < blockHeight) {
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
mipLevels++;
|
|
509
|
+
}
|
|
510
|
+
return mipLevels;
|
|
511
|
+
}
|
|
512
|
+
// HELPERS
|
|
513
|
+
/** Resolve all promises in a nested data structure */
|
|
514
|
+
async function awaitAllPromises(x) {
|
|
515
|
+
x = await x;
|
|
516
|
+
if (Array.isArray(x)) {
|
|
517
|
+
return await Promise.all(x.map(awaitAllPromises));
|
|
518
|
+
}
|
|
519
|
+
if (x && typeof x === 'object' && x.constructor === Object) {
|
|
520
|
+
const object = x;
|
|
521
|
+
const values = await Promise.all(Object.values(object).map(awaitAllPromises));
|
|
522
|
+
const keys = Object.keys(object);
|
|
523
|
+
const resolvedObject = {};
|
|
524
|
+
for (let i = 0; i < keys.length; i++) {
|
|
525
|
+
resolvedObject[keys[i]] = values[i];
|
|
526
|
+
}
|
|
527
|
+
return resolvedObject;
|
|
528
|
+
}
|
|
529
|
+
return x;
|
|
530
|
+
}
|
|
531
|
+
// /** @note experimental: Set multiple mip levels (2D), optionally at `z`, slice (depth/array level) index */
|
|
532
|
+
// setTexture2DData(lodData: Texture2DData, z: number = 0): void {
|
|
533
|
+
// this._checkReady();
|
|
534
|
+
// const lodArray = this._normalizeTexture2DData(lodData);
|
|
535
|
+
// // If user supplied multiple mip levels, warn if auto-mips also requested
|
|
536
|
+
// if (lodArray.length > 1 && this.props.mipmaps !== false) {
|
|
537
|
+
// log.warn(
|
|
538
|
+
// `Texture ${this.id}: provided multiple LODs and also requested mipmap generation.`
|
|
539
|
+
// )();
|
|
540
|
+
// }
|
|
541
|
+
// for (let mipLevel = 0; mipLevel < lodArray.length; mipLevel++) {
|
|
542
|
+
// const imageData = lodArray[mipLevel];
|
|
543
|
+
// if (this.device.isExternalImage(imageData)) {
|
|
544
|
+
// this.texture.copyExternalImage({image: imageData, z, mipLevel, flipY: true});
|
|
545
|
+
// } else if (this._isTextureImageData(imageData)) {
|
|
546
|
+
// this.texture.copyImageData({data: imageData.data, z, mipLevel});
|
|
547
|
+
// } else {
|
|
548
|
+
// throw new Error('Unsupported 2D mip-level payload');
|
|
549
|
+
// }
|
|
550
|
+
// }
|
|
551
|
+
// }
|
|
552
|
+
// /** Normalize 2D layer payload into an array of mip-level items */
|
|
553
|
+
// private _normalizeTexture2DData(data: Texture2DData): (TextureImageData | ExternalImage)[] {
|
|
554
|
+
// return Array.isArray(data) ? data : [data];
|
|
555
|
+
// }
|
|
556
|
+
//# sourceMappingURL=dynamic-texture.js.map
|