three-stdlib 2.13.0 → 2.14.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 };