three-stdlib 2.13.0 → 2.14.1

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.
@@ -1,30 +1,36 @@
1
- import { CompressedTextureLoader, CompressedTexture, FileLoader, sRGBEncoding, LinearEncoding } from 'three';
2
- import { BasisTextureLoader } from './BasisTextureLoader.js';
1
+ import { Loader, FileLoader, CompressedTexture, UnsignedByteType, LinearFilter, LinearMipmapLinearFilter, sRGBEncoding, LinearEncoding, RGBAFormat, RGBA_ASTC_4x4_Format, RGBA_BPTC_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT5_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, FloatType, HalfFloatType, DataTexture, Data3DTexture, RGFormat, RedFormat } from 'three';
2
+ import { WorkerPool } from '../utils/WorkerPool.js';
3
+ import { KHR_DF_TRANSFER_SRGB, KHR_DF_FLAG_ALPHA_PREMULTIPLIED, read, VK_FORMAT_UNDEFINED, KHR_SUPERCOMPRESSION_NONE, KHR_SUPERCOMPRESSION_ZSTD, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8G8_SRGB, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R8_SRGB, VK_FORMAT_R8_UNORM } from 'ktx-parse';
3
4
  import { ZSTDDecoder } from 'zstddec';
4
- import { read, KTX2Model, KTX2Transfer, KTX2SupercompressionScheme, KTX2ChannelUASTC, KTX2ChannelETC1S, KTX2Flags } from 'ktx-parse';
5
5
 
6
6
  /**
7
7
  * Loader for KTX 2.0 GPU Texture containers.
8
8
  *
9
9
  * KTX 2.0 is a container format for various GPU texture formats. The loader
10
10
  * supports Basis Universal GPU textures, which can be quickly transcoded to
11
- * a wide variety of GPU texture compression formats. While KTX 2.0 also allows
12
- * other hardware-specific formats, this loader does not yet parse them.
13
- *
14
- * This loader parses the KTX 2.0 container and then relies on
15
- * THREE.BasisTextureLoader to complete the transcoding process.
11
+ * a wide variety of GPU texture compression formats, as well as some
12
+ * uncompressed DataTexture and Data3DTexture formats.
16
13
  *
17
14
  * References:
18
15
  * - KTX: http://github.khronos.org/KTX-Specification/
19
16
  * - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor
20
17
  */
21
18
 
22
- class KTX2Loader extends CompressedTextureLoader {
19
+ const _taskCache = new WeakMap();
20
+
21
+ let _activeLoaders = 0;
22
+
23
+ let _zstd;
24
+
25
+ class KTX2Loader extends Loader {
23
26
  constructor(manager) {
24
27
  super(manager);
25
- this.basisLoader = new BasisTextureLoader(manager);
26
- this.zstd = new ZSTDDecoder();
27
- this.zstd.init();
28
+ this.transcoderPath = '';
29
+ this.transcoderBinary = null;
30
+ this.transcoderPending = null;
31
+ this.workerPool = new WorkerPool();
32
+ this.workerSourceURL = '';
33
+ this.workerConfig = null;
28
34
 
29
35
  if (typeof MSC_TRANSCODER !== 'undefined') {
30
36
  console.warn('THREE.KTX2Loader: Please update to latest "basis_transcoder".' + ' "msc_basis_transcoder" is no longer supported in three.js r125+.');
@@ -32,138 +38,523 @@ class KTX2Loader extends CompressedTextureLoader {
32
38
  }
33
39
 
34
40
  setTranscoderPath(path) {
35
- this.basisLoader.setTranscoderPath(path);
41
+ this.transcoderPath = path;
36
42
  return this;
37
43
  }
38
44
 
39
- setWorkerLimit(path) {
40
- this.basisLoader.setWorkerLimit(path);
45
+ setWorkerLimit(num) {
46
+ this.workerPool.setWorkerLimit(num);
41
47
  return this;
42
48
  }
43
49
 
44
50
  detectSupport(renderer) {
45
- this.basisLoader.detectSupport(renderer);
51
+ this.workerConfig = {
52
+ astcSupported: renderer.extensions.has('WEBGL_compressed_texture_astc'),
53
+ etc1Supported: renderer.extensions.has('WEBGL_compressed_texture_etc1'),
54
+ etc2Supported: renderer.extensions.has('WEBGL_compressed_texture_etc'),
55
+ dxtSupported: renderer.extensions.has('WEBGL_compressed_texture_s3tc'),
56
+ bptcSupported: renderer.extensions.has('EXT_texture_compression_bptc'),
57
+ pvrtcSupported: renderer.extensions.has('WEBGL_compressed_texture_pvrtc') || renderer.extensions.has('WEBKIT_WEBGL_compressed_texture_pvrtc')
58
+ };
59
+
60
+ if (renderer.capabilities.isWebGL2) {
61
+ // https://github.com/mrdoob/three.js/pull/22928
62
+ this.workerConfig.etc1Supported = false;
63
+ }
64
+
46
65
  return this;
47
66
  }
48
67
 
49
- dispose() {
50
- this.basisLoader.dispose();
51
- return this;
68
+ init() {
69
+ if (!this.transcoderPending) {
70
+ // Load transcoder wrapper.
71
+ const jsLoader = new FileLoader(this.manager);
72
+ jsLoader.setPath(this.transcoderPath);
73
+ jsLoader.setWithCredentials(this.withCredentials);
74
+ const jsContent = jsLoader.loadAsync('basis_transcoder.js'); // Load transcoder WASM binary.
75
+
76
+ const binaryLoader = new FileLoader(this.manager);
77
+ binaryLoader.setPath(this.transcoderPath);
78
+ binaryLoader.setResponseType('arraybuffer');
79
+ binaryLoader.setWithCredentials(this.withCredentials);
80
+ const binaryContent = binaryLoader.loadAsync('basis_transcoder.wasm');
81
+ this.transcoderPending = Promise.all([jsContent, binaryContent]).then(([jsContent, binaryContent]) => {
82
+ const fn = KTX2Loader.BasisWorker.toString();
83
+ const body = ['/* constants */', 'let _EngineFormat = ' + JSON.stringify(KTX2Loader.EngineFormat), 'let _TranscoderFormat = ' + JSON.stringify(KTX2Loader.TranscoderFormat), 'let _BasisFormat = ' + JSON.stringify(KTX2Loader.BasisFormat), '/* basis_transcoder.js */', jsContent, '/* worker */', fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}'))].join('\n');
84
+ this.workerSourceURL = URL.createObjectURL(new Blob([body]));
85
+ this.transcoderBinary = binaryContent;
86
+ this.workerPool.setWorkerCreator(() => {
87
+ const worker = new Worker(this.workerSourceURL);
88
+ const transcoderBinary = this.transcoderBinary.slice(0);
89
+ worker.postMessage({
90
+ type: 'init',
91
+ config: this.workerConfig,
92
+ transcoderBinary
93
+ }, [transcoderBinary]);
94
+ return worker;
95
+ });
96
+ });
97
+
98
+ if (_activeLoaders > 0) {
99
+ // Each instance loads a transcoder and allocates workers, increasing network and memory cost.
100
+ console.warn('THREE.KTX2Loader: Multiple active KTX2 loaders may cause performance issues.' + ' Use a single KTX2Loader instance, or call .dispose() on old instances.');
101
+ }
102
+
103
+ _activeLoaders++;
104
+ }
105
+
106
+ return this.transcoderPending;
52
107
  }
53
108
 
54
109
  load(url, onLoad, onProgress, onError) {
55
- var scope = this;
56
- var texture = new CompressedTexture();
57
- var bufferPending = new Promise(function (resolve, reject) {
58
- new FileLoader(scope.manager).setPath(scope.path).setResponseType('arraybuffer').load(url, resolve, onProgress, reject);
59
- });
60
- bufferPending.then(function (buffer) {
61
- scope.parse(buffer, function (_texture) {
62
- texture.copy(_texture);
63
- texture.needsUpdate = true;
64
- if (onLoad) onLoad(texture);
65
- }, onError);
66
- }).catch(onError);
110
+ if (this.workerConfig === null) {
111
+ throw new Error('THREE.KTX2Loader: Missing initialization with `.detectSupport( renderer )`.');
112
+ }
113
+
114
+ const loader = new FileLoader(this.manager);
115
+ loader.setResponseType('arraybuffer');
116
+ loader.setWithCredentials(this.withCredentials);
117
+ loader.load(url, buffer => {
118
+ // Check for an existing task using this buffer. A transferred buffer cannot be transferred
119
+ // again from this thread.
120
+ if (_taskCache.has(buffer)) {
121
+ const cachedTask = _taskCache.get(buffer);
122
+
123
+ return cachedTask.promise.then(onLoad).catch(onError);
124
+ }
125
+
126
+ this._createTexture(buffer).then(texture => onLoad ? onLoad(texture) : null).catch(onError);
127
+ }, onProgress, onError);
128
+ }
129
+
130
+ _createTextureFrom(transcodeResult) {
131
+ const {
132
+ mipmaps,
133
+ width,
134
+ height,
135
+ format,
136
+ type,
137
+ error,
138
+ dfdTransferFn,
139
+ dfdFlags
140
+ } = transcodeResult;
141
+ if (type === 'error') return Promise.reject(error);
142
+ const texture = new CompressedTexture(mipmaps, width, height, format, UnsignedByteType);
143
+ texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
144
+ texture.magFilter = LinearFilter;
145
+ texture.generateMipmaps = false;
146
+ texture.needsUpdate = true;
147
+ texture.encoding = dfdTransferFn === KHR_DF_TRANSFER_SRGB ? sRGBEncoding : LinearEncoding;
148
+ texture.premultiplyAlpha = !!(dfdFlags & KHR_DF_FLAG_ALPHA_PREMULTIPLIED);
67
149
  return texture;
68
150
  }
151
+ /**
152
+ * @param {ArrayBuffer} buffer
153
+ * @param {object?} config
154
+ * @return {Promise<CompressedTexture|DataTexture|Data3DTexture>}
155
+ */
69
156
 
70
- parse(buffer, onLoad, onError) {
71
- var scope = this;
72
- var ktx = read(new Uint8Array(buffer));
73
157
 
74
- if (ktx.pixelDepth > 0) {
75
- throw new Error('THREE.KTX2Loader: Only 2D textures are currently supported.');
76
- }
158
+ _createTexture(buffer, config = {}) {
159
+ const container = read(new Uint8Array(buffer));
77
160
 
78
- if (ktx.layerCount > 1) {
79
- throw new Error('THREE.KTX2Loader: Array textures are not currently supported.');
80
- }
161
+ if (container.vkFormat !== VK_FORMAT_UNDEFINED) {
162
+ return createDataTexture(container);
163
+ } //
81
164
 
82
- if (ktx.faceCount > 1) {
83
- throw new Error('THREE.KTX2Loader: Cube textures are not currently supported.');
84
- }
85
165
 
86
- var dfd = KTX2Utils.getBasicDFD(ktx);
87
- KTX2Utils.createLevels(ktx, this.zstd).then(function (levels) {
88
- var basisFormat = dfd.colorModel === KTX2Model.UASTC ? BasisTextureLoader.BasisFormat.UASTC_4x4 : BasisTextureLoader.BasisFormat.ETC1S;
89
- var parseConfig = {
90
- levels: levels,
91
- width: ktx.pixelWidth,
92
- height: ktx.pixelHeight,
93
- basisFormat: basisFormat,
94
- hasAlpha: KTX2Utils.getAlpha(ktx)
95
- };
166
+ const taskConfig = config;
167
+ const texturePending = this.init().then(() => {
168
+ return this.workerPool.postMessage({
169
+ type: 'transcode',
170
+ buffer,
171
+ taskConfig: taskConfig
172
+ }, [buffer]);
173
+ }).then(e => this._createTextureFrom(e.data)); // Cache the task result.
96
174
 
97
- if (basisFormat === BasisTextureLoader.BasisFormat.ETC1S) {
98
- parseConfig.globalData = ktx.globalData;
99
- }
175
+ _taskCache.set(buffer, {
176
+ promise: texturePending
177
+ });
178
+
179
+ return texturePending;
180
+ }
100
181
 
101
- return scope.basisLoader.parseInternalAsync(parseConfig);
102
- }).then(function (texture) {
103
- texture.encoding = dfd.transferFunction === KTX2Transfer.SRGB ? sRGBEncoding : LinearEncoding;
104
- texture.premultiplyAlpha = KTX2Utils.getPremultiplyAlpha(ktx);
105
- onLoad(texture);
106
- }).catch(onError);
182
+ dispose() {
183
+ this.workerPool.dispose();
184
+ if (this.workerSourceURL) URL.revokeObjectURL(this.workerSourceURL);
185
+ _activeLoaders--;
107
186
  return this;
108
187
  }
109
188
 
110
189
  }
190
+ /* CONSTANTS */
191
+
192
+
193
+ KTX2Loader.BasisFormat = {
194
+ ETC1S: 0,
195
+ UASTC_4x4: 1
196
+ };
197
+ KTX2Loader.TranscoderFormat = {
198
+ ETC1: 0,
199
+ ETC2: 1,
200
+ BC1: 2,
201
+ BC3: 3,
202
+ BC4: 4,
203
+ BC5: 5,
204
+ BC7_M6_OPAQUE_ONLY: 6,
205
+ BC7_M5: 7,
206
+ PVRTC1_4_RGB: 8,
207
+ PVRTC1_4_RGBA: 9,
208
+ ASTC_4x4: 10,
209
+ ATC_RGB: 11,
210
+ ATC_RGBA_INTERPOLATED_ALPHA: 12,
211
+ RGBA32: 13,
212
+ RGB565: 14,
213
+ BGR565: 15,
214
+ RGBA4444: 16
215
+ };
216
+ KTX2Loader.EngineFormat = {
217
+ RGBAFormat: RGBAFormat,
218
+ RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format,
219
+ RGBA_BPTC_Format: RGBA_BPTC_Format,
220
+ RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format,
221
+ RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format,
222
+ RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format,
223
+ RGB_ETC1_Format: RGB_ETC1_Format,
224
+ RGB_ETC2_Format: RGB_ETC2_Format,
225
+ RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format,
226
+ RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format
227
+ };
228
+ /* WEB WORKER */
229
+
230
+ KTX2Loader.BasisWorker = function () {
231
+ let config;
232
+ let transcoderPending;
233
+ let BasisModule;
234
+ const EngineFormat = _EngineFormat; // eslint-disable-line no-undef
235
+
236
+ const TranscoderFormat = _TranscoderFormat; // eslint-disable-line no-undef
237
+
238
+ const BasisFormat = _BasisFormat; // eslint-disable-line no-undef
239
+
240
+ self.addEventListener('message', function (e) {
241
+ const message = e.data;
111
242
 
112
- var KTX2Utils = {
113
- createLevels: async function (ktx, zstd) {
114
- if (ktx.supercompressionScheme === KTX2SupercompressionScheme.ZSTD) {
115
- await zstd.init();
243
+ switch (message.type) {
244
+ case 'init':
245
+ config = message.config;
246
+ init(message.transcoderBinary);
247
+ break;
248
+
249
+ case 'transcode':
250
+ transcoderPending.then(() => {
251
+ try {
252
+ const {
253
+ width,
254
+ height,
255
+ hasAlpha,
256
+ mipmaps,
257
+ format,
258
+ dfdTransferFn,
259
+ dfdFlags
260
+ } = transcode(message.buffer);
261
+ const buffers = [];
262
+
263
+ for (let i = 0; i < mipmaps.length; ++i) {
264
+ buffers.push(mipmaps[i].data.buffer);
265
+ }
266
+
267
+ self.postMessage({
268
+ type: 'transcode',
269
+ id: message.id,
270
+ width,
271
+ height,
272
+ hasAlpha,
273
+ mipmaps,
274
+ format,
275
+ dfdTransferFn,
276
+ dfdFlags
277
+ }, buffers);
278
+ } catch (error) {
279
+ console.error(error);
280
+ self.postMessage({
281
+ type: 'error',
282
+ id: message.id,
283
+ error: error.message
284
+ });
285
+ }
286
+ });
287
+ break;
116
288
  }
289
+ });
117
290
 
118
- var levels = [];
119
- var width = ktx.pixelWidth;
120
- var height = ktx.pixelHeight;
291
+ function init(wasmBinary) {
292
+ transcoderPending = new Promise(resolve => {
293
+ BasisModule = {
294
+ wasmBinary,
295
+ onRuntimeInitialized: resolve
296
+ };
297
+ BASIS(BasisModule); // eslint-disable-line no-undef
298
+ }).then(() => {
299
+ BasisModule.initializeBasis();
121
300
 
122
- for (var levelIndex = 0; levelIndex < ktx.levels.length; levelIndex++) {
123
- var levelWidth = Math.max(1, Math.floor(width / Math.pow(2, levelIndex)));
124
- var levelHeight = Math.max(1, Math.floor(height / Math.pow(2, levelIndex)));
125
- var levelData = ktx.levels[levelIndex].levelData;
301
+ if (BasisModule.KTX2File === undefined) {
302
+ console.warn('THREE.KTX2Loader: Please update Basis Universal transcoder.');
303
+ }
304
+ });
305
+ }
306
+
307
+ function transcode(buffer) {
308
+ const ktx2File = new BasisModule.KTX2File(new Uint8Array(buffer));
126
309
 
127
- if (ktx.supercompressionScheme === KTX2SupercompressionScheme.ZSTD) {
128
- levelData = zstd.decode(levelData, ktx.levels[levelIndex].uncompressedByteLength);
310
+ function cleanup() {
311
+ ktx2File.close();
312
+ ktx2File.delete();
313
+ }
314
+
315
+ if (!ktx2File.isValid()) {
316
+ cleanup();
317
+ throw new Error('THREE.KTX2Loader: Invalid or unsupported .ktx2 file');
318
+ }
319
+
320
+ const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
321
+ const width = ktx2File.getWidth();
322
+ const height = ktx2File.getHeight();
323
+ const levels = ktx2File.getLevels();
324
+ const hasAlpha = ktx2File.getHasAlpha();
325
+ const dfdTransferFn = ktx2File.getDFDTransferFunc();
326
+ const dfdFlags = ktx2File.getDFDFlags();
327
+ const {
328
+ transcoderFormat,
329
+ engineFormat
330
+ } = getTranscoderFormat(basisFormat, width, height, hasAlpha);
331
+
332
+ if (!width || !height || !levels) {
333
+ cleanup();
334
+ throw new Error('THREE.KTX2Loader: Invalid texture');
335
+ }
336
+
337
+ if (!ktx2File.startTranscoding()) {
338
+ cleanup();
339
+ throw new Error('THREE.KTX2Loader: .startTranscoding failed');
340
+ }
341
+
342
+ const mipmaps = [];
343
+
344
+ for (let mip = 0; mip < levels; mip++) {
345
+ const levelInfo = ktx2File.getImageLevelInfo(mip, 0, 0);
346
+ const mipWidth = levelInfo.origWidth;
347
+ const mipHeight = levelInfo.origHeight;
348
+ const dst = new Uint8Array(ktx2File.getImageTranscodedSizeInBytes(mip, 0, 0, transcoderFormat));
349
+ const status = ktx2File.transcodeImage(dst, mip, 0, 0, transcoderFormat, 0, -1, -1);
350
+
351
+ if (!status) {
352
+ cleanup();
353
+ throw new Error('THREE.KTX2Loader: .transcodeImage failed.');
129
354
  }
130
355
 
131
- levels.push({
132
- index: levelIndex,
133
- width: levelWidth,
134
- height: levelHeight,
135
- data: levelData
356
+ mipmaps.push({
357
+ data: dst,
358
+ width: mipWidth,
359
+ height: mipHeight
136
360
  });
137
361
  }
138
362
 
139
- return levels;
140
- },
141
- getBasicDFD: function (ktx) {
142
- // Basic Data Format Descriptor Block is always the first DFD.
143
- return ktx.dataFormatDescriptor[0];
144
- },
145
- getAlpha: function (ktx) {
146
- var dfd = this.getBasicDFD(ktx); // UASTC
147
-
148
- if (dfd.colorModel === KTX2Model.UASTC) {
149
- if ((dfd.samples[0].channelID & 0xf) === KTX2ChannelUASTC.RGBA) {
150
- return true;
151
- }
363
+ cleanup();
364
+ return {
365
+ width,
366
+ height,
367
+ hasAlpha,
368
+ mipmaps,
369
+ format: engineFormat,
370
+ dfdTransferFn,
371
+ dfdFlags
372
+ };
373
+ } //
374
+ // Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC),
375
+ // device capabilities, and texture dimensions. The list below ranks the formats separately
376
+ // for ETC1S and UASTC.
377
+ //
378
+ // In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at
379
+ // significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently
380
+ // chooses RGBA32 only as a last resort and does not expose that option to the caller.
381
+
152
382
 
153
- return false;
154
- } // ETC1S
383
+ const FORMAT_OPTIONS = [{
384
+ if: 'astcSupported',
385
+ basisFormat: [BasisFormat.UASTC_4x4],
386
+ transcoderFormat: [TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4],
387
+ engineFormat: [EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format],
388
+ priorityETC1S: Infinity,
389
+ priorityUASTC: 1,
390
+ needsPowerOfTwo: false
391
+ }, {
392
+ if: 'bptcSupported',
393
+ basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
394
+ transcoderFormat: [TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5],
395
+ engineFormat: [EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format],
396
+ priorityETC1S: 3,
397
+ priorityUASTC: 2,
398
+ needsPowerOfTwo: false
399
+ }, {
400
+ if: 'dxtSupported',
401
+ basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
402
+ transcoderFormat: [TranscoderFormat.BC1, TranscoderFormat.BC3],
403
+ engineFormat: [EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format],
404
+ priorityETC1S: 4,
405
+ priorityUASTC: 5,
406
+ needsPowerOfTwo: false
407
+ }, {
408
+ if: 'etc2Supported',
409
+ basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
410
+ transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC2],
411
+ engineFormat: [EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format],
412
+ priorityETC1S: 1,
413
+ priorityUASTC: 3,
414
+ needsPowerOfTwo: false
415
+ }, {
416
+ if: 'etc1Supported',
417
+ basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
418
+ transcoderFormat: [TranscoderFormat.ETC1],
419
+ engineFormat: [EngineFormat.RGB_ETC1_Format],
420
+ priorityETC1S: 2,
421
+ priorityUASTC: 4,
422
+ needsPowerOfTwo: false
423
+ }, {
424
+ if: 'pvrtcSupported',
425
+ basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
426
+ transcoderFormat: [TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA],
427
+ engineFormat: [EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format],
428
+ priorityETC1S: 5,
429
+ priorityUASTC: 6,
430
+ needsPowerOfTwo: true
431
+ }];
432
+ const ETC1S_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) {
433
+ return a.priorityETC1S - b.priorityETC1S;
434
+ });
435
+ const UASTC_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) {
436
+ return a.priorityUASTC - b.priorityUASTC;
437
+ });
155
438
 
439
+ function getTranscoderFormat(basisFormat, width, height, hasAlpha) {
440
+ let transcoderFormat;
441
+ let engineFormat;
442
+ const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS;
156
443
 
157
- if (dfd.samples.length === 2 && (dfd.samples[1].channelID & 0xf) === KTX2ChannelETC1S.AAA) {
158
- return true;
444
+ for (let i = 0; i < options.length; i++) {
445
+ const opt = options[i];
446
+ if (!config[opt.if]) continue;
447
+ if (!opt.basisFormat.includes(basisFormat)) continue;
448
+ if (hasAlpha && opt.transcoderFormat.length < 2) continue;
449
+ if (opt.needsPowerOfTwo && !(isPowerOfTwo(width) && isPowerOfTwo(height))) continue;
450
+ transcoderFormat = opt.transcoderFormat[hasAlpha ? 1 : 0];
451
+ engineFormat = opt.engineFormat[hasAlpha ? 1 : 0];
452
+ return {
453
+ transcoderFormat,
454
+ engineFormat
455
+ };
159
456
  }
160
457
 
161
- return false;
162
- },
163
- getPremultiplyAlpha: function (ktx) {
164
- var dfd = this.getBasicDFD(ktx);
165
- return !!(dfd.flags & KTX2Flags.ALPHA_PREMULTIPLIED);
458
+ console.warn('THREE.KTX2Loader: No suitable compressed texture format found. Decoding to RGBA32.');
459
+ transcoderFormat = TranscoderFormat.RGBA32;
460
+ engineFormat = EngineFormat.RGBAFormat;
461
+ return {
462
+ transcoderFormat,
463
+ engineFormat
464
+ };
465
+ }
466
+
467
+ function isPowerOfTwo(value) {
468
+ if (value <= 2) return true;
469
+ return (value & value - 1) === 0 && value !== 0;
166
470
  }
471
+ }; //
472
+ // DataTexture and Data3DTexture parsing.
473
+
474
+
475
+ const FORMAT_MAP = {
476
+ [VK_FORMAT_R32G32B32A32_SFLOAT]: RGBAFormat,
477
+ [VK_FORMAT_R16G16B16A16_SFLOAT]: RGBAFormat,
478
+ [VK_FORMAT_R8G8B8A8_UNORM]: RGBAFormat,
479
+ [VK_FORMAT_R8G8B8A8_SRGB]: RGBAFormat,
480
+ [VK_FORMAT_R32G32_SFLOAT]: RGFormat,
481
+ [VK_FORMAT_R16G16_SFLOAT]: RGFormat,
482
+ [VK_FORMAT_R8G8_UNORM]: RGFormat,
483
+ [VK_FORMAT_R8G8_SRGB]: RGFormat,
484
+ [VK_FORMAT_R32_SFLOAT]: RedFormat,
485
+ [VK_FORMAT_R16_SFLOAT]: RedFormat,
486
+ [VK_FORMAT_R8_SRGB]: RedFormat,
487
+ [VK_FORMAT_R8_UNORM]: RedFormat
488
+ };
489
+ const TYPE_MAP = {
490
+ [VK_FORMAT_R32G32B32A32_SFLOAT]: FloatType,
491
+ [VK_FORMAT_R16G16B16A16_SFLOAT]: HalfFloatType,
492
+ [VK_FORMAT_R8G8B8A8_UNORM]: UnsignedByteType,
493
+ [VK_FORMAT_R8G8B8A8_SRGB]: UnsignedByteType,
494
+ [VK_FORMAT_R32G32_SFLOAT]: FloatType,
495
+ [VK_FORMAT_R16G16_SFLOAT]: HalfFloatType,
496
+ [VK_FORMAT_R8G8_UNORM]: UnsignedByteType,
497
+ [VK_FORMAT_R8G8_SRGB]: UnsignedByteType,
498
+ [VK_FORMAT_R32_SFLOAT]: FloatType,
499
+ [VK_FORMAT_R16_SFLOAT]: HalfFloatType,
500
+ [VK_FORMAT_R8_SRGB]: UnsignedByteType,
501
+ [VK_FORMAT_R8_UNORM]: UnsignedByteType
167
502
  };
503
+ const ENCODING_MAP = {
504
+ [VK_FORMAT_R8G8B8A8_SRGB]: sRGBEncoding,
505
+ [VK_FORMAT_R8G8_SRGB]: sRGBEncoding,
506
+ [VK_FORMAT_R8_SRGB]: sRGBEncoding
507
+ };
508
+
509
+ async function createDataTexture(container) {
510
+ const {
511
+ vkFormat,
512
+ pixelWidth,
513
+ pixelHeight,
514
+ pixelDepth
515
+ } = container;
516
+
517
+ if (FORMAT_MAP[vkFormat] === undefined) {
518
+ throw new Error('THREE.KTX2Loader: Unsupported vkFormat.');
519
+ } //
520
+
521
+
522
+ const level = container.levels[0];
523
+ let levelData;
524
+ let view;
525
+
526
+ if (container.supercompressionScheme === KHR_SUPERCOMPRESSION_NONE) {
527
+ levelData = level.levelData;
528
+ } else if (container.supercompressionScheme === KHR_SUPERCOMPRESSION_ZSTD) {
529
+ if (!_zstd) {
530
+ _zstd = new Promise(async resolve => {
531
+ const zstd = new ZSTDDecoder();
532
+ await zstd.init();
533
+ resolve(zstd);
534
+ });
535
+ }
536
+
537
+ levelData = (await _zstd).decode(level.levelData, level.uncompressedByteLength);
538
+ } else {
539
+ throw new Error('THREE.KTX2Loader: Unsupported supercompressionScheme.');
540
+ }
541
+
542
+ if (TYPE_MAP[vkFormat] === FloatType) {
543
+ view = new Float32Array(levelData.buffer, levelData.byteOffset, levelData.byteLength / Float32Array.BYTES_PER_ELEMENT);
544
+ } else if (TYPE_MAP[vkFormat] === HalfFloatType) {
545
+ view = new Uint16Array(levelData.buffer, levelData.byteOffset, levelData.byteLength / Uint16Array.BYTES_PER_ELEMENT);
546
+ } else {
547
+ view = levelData;
548
+ } //
549
+
550
+
551
+ const texture = pixelDepth === 0 ? new DataTexture(view, pixelWidth, pixelHeight) : new Data3DTexture(view, pixelWidth, pixelHeight, pixelDepth);
552
+ texture.type = TYPE_MAP[vkFormat];
553
+ texture.format = FORMAT_MAP[vkFormat];
554
+ texture.encoding = ENCODING_MAP[vkFormat] || LinearEncoding;
555
+ texture.needsUpdate = true; //
556
+
557
+ return Promise.resolve(texture);
558
+ }
168
559
 
169
560
  export { KTX2Loader };