@damienmortini/three 0.1.131

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.
@@ -0,0 +1,783 @@
1
+ import {
2
+ CompressedTexture,
3
+ FileLoader,
4
+ LinearFilter,
5
+ LinearMipmapLinearFilter,
6
+ Loader,
7
+ RGBAFormat,
8
+ RGBA_ASTC_4x4_Format,
9
+ RGBA_BPTC_Format,
10
+ RGBA_ETC2_EAC_Format,
11
+ RGBA_PVRTC_4BPPV1_Format,
12
+ RGBA_S3TC_DXT5_Format,
13
+ RGB_ETC1_Format,
14
+ RGB_ETC2_Format,
15
+ RGB_PVRTC_4BPPV1_Format,
16
+ RGB_S3TC_DXT1_Format,
17
+ UnsignedByteType
18
+ } from 'three';
19
+
20
+ /**
21
+ * Loader for Basis Universal GPU Texture Codec.
22
+ *
23
+ * Basis Universal is a "supercompressed" GPU texture and texture video
24
+ * compression system that outputs a highly compressed intermediate file format
25
+ * (.basis) that can be quickly transcoded to a wide variety of GPU texture
26
+ * compression formats.
27
+ *
28
+ * This loader parallelizes the transcoding process across a configurable number
29
+ * of web workers, before transferring the transcoded compressed texture back
30
+ * to the main thread.
31
+ */
32
+
33
+ const _taskCache = new WeakMap();
34
+
35
+ class BasisTextureLoader extends Loader {
36
+
37
+ constructor( manager ) {
38
+
39
+ super( manager );
40
+
41
+ this.transcoderPath = '';
42
+ this.transcoderBinary = null;
43
+ this.transcoderPending = null;
44
+
45
+ this.workerLimit = 4;
46
+ this.workerPool = [];
47
+ this.workerNextTaskID = 1;
48
+ this.workerSourceURL = '';
49
+ this.workerConfig = null;
50
+
51
+ }
52
+
53
+ setTranscoderPath( path ) {
54
+
55
+ this.transcoderPath = path;
56
+
57
+ return this;
58
+
59
+ }
60
+
61
+ setWorkerLimit( workerLimit ) {
62
+
63
+ this.workerLimit = workerLimit;
64
+
65
+ return this;
66
+
67
+ }
68
+
69
+ detectSupport( renderer ) {
70
+
71
+ this.workerConfig = {
72
+ astcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_astc' ),
73
+ etc1Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc1' ),
74
+ etc2Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc' ),
75
+ dxtSupported: renderer.extensions.has( 'WEBGL_compressed_texture_s3tc' ),
76
+ bptcSupported: renderer.extensions.has( 'EXT_texture_compression_bptc' ),
77
+ pvrtcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_pvrtc' )
78
+ || renderer.extensions.has( 'WEBKIT_WEBGL_compressed_texture_pvrtc' )
79
+ };
80
+
81
+ return this;
82
+
83
+ }
84
+
85
+ load( url, onLoad, onProgress, onError ) {
86
+
87
+ const loader = new FileLoader( this.manager );
88
+
89
+ loader.setResponseType( 'arraybuffer' );
90
+ loader.setWithCredentials( this.withCredentials );
91
+
92
+ const texture = new CompressedTexture();
93
+
94
+ loader.load( url, ( buffer ) => {
95
+
96
+ // Check for an existing task using this buffer. A transferred buffer cannot be transferred
97
+ // again from this thread.
98
+ if ( _taskCache.has( buffer ) ) {
99
+
100
+ const cachedTask = _taskCache.get( buffer );
101
+
102
+ return cachedTask.promise.then( onLoad ).catch( onError );
103
+
104
+ }
105
+
106
+ this._createTexture( [ buffer ] )
107
+ .then( function ( _texture ) {
108
+
109
+ texture.copy( _texture );
110
+ texture.needsUpdate = true;
111
+
112
+ if ( onLoad ) onLoad( texture );
113
+
114
+ } )
115
+ .catch( onError );
116
+
117
+ }, onProgress, onError );
118
+
119
+ return texture;
120
+
121
+ }
122
+
123
+ /** Low-level transcoding API, exposed for use by KTX2Loader. */
124
+ parseInternalAsync( options ) {
125
+
126
+ const { levels } = options;
127
+
128
+ const buffers = new Set();
129
+
130
+ for ( let i = 0; i < levels.length; i ++ ) {
131
+
132
+ buffers.add( levels[ i ].data.buffer );
133
+
134
+ }
135
+
136
+ return this._createTexture( Array.from( buffers ), { ...options, lowLevel: true } );
137
+
138
+ }
139
+
140
+ /**
141
+ * @param {ArrayBuffer[]} buffers
142
+ * @param {object?} config
143
+ * @return {Promise<CompressedTexture>}
144
+ */
145
+ _createTexture( buffers, config = {} ) {
146
+
147
+ let worker;
148
+ let taskID;
149
+
150
+ const taskConfig = config;
151
+ let taskCost = 0;
152
+
153
+ for ( let i = 0; i < buffers.length; i ++ ) {
154
+
155
+ taskCost += buffers[ i ].byteLength;
156
+
157
+ }
158
+
159
+ const texturePending = this._allocateWorker( taskCost )
160
+ .then( ( _worker ) => {
161
+
162
+ worker = _worker;
163
+ taskID = this.workerNextTaskID ++;
164
+
165
+ return new Promise( ( resolve, reject ) => {
166
+
167
+ worker._callbacks[ taskID ] = { resolve, reject };
168
+
169
+ worker.postMessage( { type: 'transcode', id: taskID, buffers: buffers, taskConfig: taskConfig }, buffers );
170
+
171
+ } );
172
+
173
+ } )
174
+ .then( ( message ) => {
175
+
176
+ const { mipmaps, width, height, format } = message;
177
+
178
+ const texture = new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );
179
+ texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
180
+ texture.magFilter = LinearFilter;
181
+ texture.generateMipmaps = false;
182
+ texture.needsUpdate = true;
183
+
184
+ return texture;
185
+
186
+ } );
187
+
188
+ // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
189
+ texturePending
190
+ .catch( () => true )
191
+ .then( () => {
192
+
193
+ if ( worker && taskID ) {
194
+
195
+ worker._taskLoad -= taskCost;
196
+ delete worker._callbacks[ taskID ];
197
+
198
+ }
199
+
200
+ } );
201
+
202
+ // Cache the task result.
203
+ _taskCache.set( buffers[ 0 ], { promise: texturePending } );
204
+
205
+ return texturePending;
206
+
207
+ }
208
+
209
+ _initTranscoder() {
210
+
211
+ if ( ! this.transcoderPending ) {
212
+
213
+ // Load transcoder wrapper.
214
+ const jsLoader = new FileLoader( this.manager );
215
+ jsLoader.setPath( this.transcoderPath );
216
+ jsLoader.setWithCredentials( this.withCredentials );
217
+ const jsContent = new Promise( ( resolve, reject ) => {
218
+
219
+ jsLoader.load( 'basis_transcoder.js', resolve, undefined, reject );
220
+
221
+ } );
222
+
223
+ // Load transcoder WASM binary.
224
+ const binaryLoader = new FileLoader( this.manager );
225
+ binaryLoader.setPath( this.transcoderPath );
226
+ binaryLoader.setResponseType( 'arraybuffer' );
227
+ binaryLoader.setWithCredentials( this.withCredentials );
228
+ const binaryContent = new Promise( ( resolve, reject ) => {
229
+
230
+ binaryLoader.load( 'basis_transcoder.wasm', resolve, undefined, reject );
231
+
232
+ } );
233
+
234
+ this.transcoderPending = Promise.all( [ jsContent, binaryContent ] )
235
+ .then( ( [ jsContent, binaryContent ] ) => {
236
+
237
+ const fn = BasisTextureLoader.BasisWorker.toString();
238
+
239
+ const body = [
240
+ '/* constants */',
241
+ 'let _EngineFormat = ' + JSON.stringify( BasisTextureLoader.EngineFormat ),
242
+ 'let _TranscoderFormat = ' + JSON.stringify( BasisTextureLoader.TranscoderFormat ),
243
+ 'let _BasisFormat = ' + JSON.stringify( BasisTextureLoader.BasisFormat ),
244
+ '/* basis_transcoder.js */',
245
+ jsContent,
246
+ '/* worker */',
247
+ fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
248
+ ].join( '\n' );
249
+
250
+ this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
251
+ this.transcoderBinary = binaryContent;
252
+
253
+ } );
254
+
255
+ }
256
+
257
+ return this.transcoderPending;
258
+
259
+ }
260
+
261
+ _allocateWorker( taskCost ) {
262
+
263
+ return this._initTranscoder().then( () => {
264
+
265
+ if ( this.workerPool.length < this.workerLimit ) {
266
+
267
+ const worker = new Worker( this.workerSourceURL );
268
+
269
+ worker._callbacks = {};
270
+ worker._taskLoad = 0;
271
+
272
+ worker.postMessage( {
273
+ type: 'init',
274
+ config: this.workerConfig,
275
+ transcoderBinary: this.transcoderBinary,
276
+ } );
277
+
278
+ worker.onmessage = function ( e ) {
279
+
280
+ const message = e.data;
281
+
282
+ switch ( message.type ) {
283
+
284
+ case 'transcode':
285
+ worker._callbacks[ message.id ].resolve( message );
286
+ break;
287
+
288
+ case 'error':
289
+ worker._callbacks[ message.id ].reject( message );
290
+ break;
291
+
292
+ default:
293
+ console.error( 'THREE.BasisTextureLoader: Unexpected message, "' + message.type + '"' );
294
+
295
+ }
296
+
297
+ };
298
+
299
+ this.workerPool.push( worker );
300
+
301
+ } else {
302
+
303
+ this.workerPool.sort( function ( a, b ) {
304
+
305
+ return a._taskLoad > b._taskLoad ? - 1 : 1;
306
+
307
+ } );
308
+
309
+ }
310
+
311
+ const worker = this.workerPool[ this.workerPool.length - 1 ];
312
+
313
+ worker._taskLoad += taskCost;
314
+
315
+ return worker;
316
+
317
+ } );
318
+
319
+ }
320
+
321
+ dispose() {
322
+
323
+ for ( let i = 0; i < this.workerPool.length; i ++ ) {
324
+
325
+ this.workerPool[ i ].terminate();
326
+
327
+ }
328
+
329
+ this.workerPool.length = 0;
330
+
331
+ return this;
332
+
333
+ }
334
+
335
+ }
336
+
337
+ /* CONSTANTS */
338
+
339
+ BasisTextureLoader.BasisFormat = {
340
+ ETC1S: 0,
341
+ UASTC_4x4: 1,
342
+ };
343
+
344
+ BasisTextureLoader.TranscoderFormat = {
345
+ ETC1: 0,
346
+ ETC2: 1,
347
+ BC1: 2,
348
+ BC3: 3,
349
+ BC4: 4,
350
+ BC5: 5,
351
+ BC7_M6_OPAQUE_ONLY: 6,
352
+ BC7_M5: 7,
353
+ PVRTC1_4_RGB: 8,
354
+ PVRTC1_4_RGBA: 9,
355
+ ASTC_4x4: 10,
356
+ ATC_RGB: 11,
357
+ ATC_RGBA_INTERPOLATED_ALPHA: 12,
358
+ RGBA32: 13,
359
+ RGB565: 14,
360
+ BGR565: 15,
361
+ RGBA4444: 16,
362
+ };
363
+
364
+ BasisTextureLoader.EngineFormat = {
365
+ RGBAFormat: RGBAFormat,
366
+ RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format,
367
+ RGBA_BPTC_Format: RGBA_BPTC_Format,
368
+ RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format,
369
+ RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format,
370
+ RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format,
371
+ RGB_ETC1_Format: RGB_ETC1_Format,
372
+ RGB_ETC2_Format: RGB_ETC2_Format,
373
+ RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format,
374
+ RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format,
375
+ };
376
+
377
+
378
+ /* WEB WORKER */
379
+
380
+ BasisTextureLoader.BasisWorker = function () {
381
+
382
+ let config;
383
+ let transcoderPending;
384
+ let BasisModule;
385
+
386
+ const EngineFormat = _EngineFormat; // eslint-disable-line no-undef
387
+ const TranscoderFormat = _TranscoderFormat; // eslint-disable-line no-undef
388
+ const BasisFormat = _BasisFormat; // eslint-disable-line no-undef
389
+
390
+ onmessage = function ( e ) {
391
+
392
+ const message = e.data;
393
+
394
+ switch ( message.type ) {
395
+
396
+ case 'init':
397
+ config = message.config;
398
+ init( message.transcoderBinary );
399
+ break;
400
+
401
+ case 'transcode':
402
+ transcoderPending.then( () => {
403
+
404
+ try {
405
+
406
+ const { width, height, hasAlpha, mipmaps, format } = message.taskConfig.lowLevel
407
+ ? transcodeLowLevel( message.taskConfig )
408
+ : transcode( message.buffers[ 0 ] );
409
+
410
+ const buffers = [];
411
+
412
+ for ( let i = 0; i < mipmaps.length; ++ i ) {
413
+
414
+ buffers.push( mipmaps[ i ].data.buffer );
415
+
416
+ }
417
+
418
+ self.postMessage( { type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format }, buffers );
419
+
420
+ } catch ( error ) {
421
+
422
+ console.error( error );
423
+
424
+ self.postMessage( { type: 'error', id: message.id, error: error.message } );
425
+
426
+ }
427
+
428
+ } );
429
+ break;
430
+
431
+ }
432
+
433
+ };
434
+
435
+ function init( wasmBinary ) {
436
+
437
+ transcoderPending = new Promise( ( resolve ) => {
438
+
439
+ BasisModule = { wasmBinary, onRuntimeInitialized: resolve };
440
+ BASIS( BasisModule ); // eslint-disable-line no-undef
441
+
442
+ } ).then( () => {
443
+
444
+ BasisModule.initializeBasis();
445
+
446
+ } );
447
+
448
+ }
449
+
450
+ function transcodeLowLevel( taskConfig ) {
451
+
452
+ const { basisFormat, width, height, hasAlpha } = taskConfig;
453
+
454
+ const { transcoderFormat, engineFormat } = getTranscoderFormat( basisFormat, width, height, hasAlpha );
455
+
456
+ const blockByteLength = BasisModule.getBytesPerBlockOrPixel( transcoderFormat );
457
+
458
+ assert( BasisModule.isFormatSupported( transcoderFormat ), 'THREE.BasisTextureLoader: Unsupported format.' );
459
+
460
+ const mipmaps = [];
461
+
462
+ if ( basisFormat === BasisFormat.ETC1S ) {
463
+
464
+ const transcoder = new BasisModule.LowLevelETC1SImageTranscoder();
465
+
466
+ const { endpointCount, endpointsData, selectorCount, selectorsData, tablesData } = taskConfig.globalData;
467
+
468
+ try {
469
+
470
+ let ok;
471
+
472
+ ok = transcoder.decodePalettes( endpointCount, endpointsData, selectorCount, selectorsData );
473
+
474
+ assert( ok, 'THREE.BasisTextureLoader: decodePalettes() failed.' );
475
+
476
+ ok = transcoder.decodeTables( tablesData );
477
+
478
+ assert( ok, 'THREE.BasisTextureLoader: decodeTables() failed.' );
479
+
480
+ for ( let i = 0; i < taskConfig.levels.length; i ++ ) {
481
+
482
+ const level = taskConfig.levels[ i ];
483
+ const imageDesc = taskConfig.globalData.imageDescs[ i ];
484
+
485
+ const dstByteLength = getTranscodedImageByteLength( transcoderFormat, level.width, level.height );
486
+ const dst = new Uint8Array( dstByteLength );
487
+
488
+ ok = transcoder.transcodeImage(
489
+ transcoderFormat,
490
+ dst, dstByteLength / blockByteLength,
491
+ level.data,
492
+ getWidthInBlocks( transcoderFormat, level.width ),
493
+ getHeightInBlocks( transcoderFormat, level.height ),
494
+ level.width, level.height, level.index,
495
+ imageDesc.rgbSliceByteOffset, imageDesc.rgbSliceByteLength,
496
+ imageDesc.alphaSliceByteOffset, imageDesc.alphaSliceByteLength,
497
+ imageDesc.imageFlags,
498
+ hasAlpha,
499
+ false,
500
+ 0, 0
501
+ );
502
+
503
+ assert( ok, 'THREE.BasisTextureLoader: transcodeImage() failed for level ' + level.index + '.' );
504
+
505
+ mipmaps.push( { data: dst, width: level.width, height: level.height } );
506
+
507
+ }
508
+
509
+ } finally {
510
+
511
+ transcoder.delete();
512
+
513
+ }
514
+
515
+ } else {
516
+
517
+ for ( let i = 0; i < taskConfig.levels.length; i ++ ) {
518
+
519
+ const level = taskConfig.levels[ i ];
520
+
521
+ const dstByteLength = getTranscodedImageByteLength( transcoderFormat, level.width, level.height );
522
+ const dst = new Uint8Array( dstByteLength );
523
+
524
+ const ok = BasisModule.transcodeUASTCImage(
525
+ transcoderFormat,
526
+ dst, dstByteLength / blockByteLength,
527
+ level.data,
528
+ getWidthInBlocks( transcoderFormat, level.width ),
529
+ getHeightInBlocks( transcoderFormat, level.height ),
530
+ level.width, level.height, level.index,
531
+ 0,
532
+ level.data.byteLength,
533
+ 0,
534
+ hasAlpha,
535
+ false,
536
+ 0, 0,
537
+ - 1, - 1
538
+ );
539
+
540
+ assert( ok, 'THREE.BasisTextureLoader: transcodeUASTCImage() failed for level ' + level.index + '.' );
541
+
542
+ mipmaps.push( { data: dst, width: level.width, height: level.height } );
543
+
544
+ }
545
+
546
+ }
547
+
548
+ return { width, height, hasAlpha, mipmaps, format: engineFormat };
549
+
550
+ }
551
+
552
+ function transcode( buffer ) {
553
+
554
+ const basisFile = new BasisModule.BasisFile( new Uint8Array( buffer ) );
555
+
556
+ const basisFormat = basisFile.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
557
+ const width = basisFile.getImageWidth( 0, 0 );
558
+ const height = basisFile.getImageHeight( 0, 0 );
559
+ const levels = basisFile.getNumLevels( 0 );
560
+ const hasAlpha = basisFile.getHasAlpha();
561
+
562
+ function cleanup() {
563
+
564
+ basisFile.close();
565
+ basisFile.delete();
566
+
567
+ }
568
+
569
+ const { transcoderFormat, engineFormat } = getTranscoderFormat( basisFormat, width, height, hasAlpha );
570
+
571
+ if ( ! width || ! height || ! levels ) {
572
+
573
+ cleanup();
574
+ throw new Error( 'THREE.BasisTextureLoader: Invalid texture' );
575
+
576
+ }
577
+
578
+ if ( ! basisFile.startTranscoding() ) {
579
+
580
+ cleanup();
581
+ throw new Error( 'THREE.BasisTextureLoader: .startTranscoding failed' );
582
+
583
+ }
584
+
585
+ const mipmaps = [];
586
+
587
+ for ( let mip = 0; mip < levels; mip ++ ) {
588
+
589
+ const mipWidth = basisFile.getImageWidth( 0, mip );
590
+ const mipHeight = basisFile.getImageHeight( 0, mip );
591
+ const dst = new Uint8Array( basisFile.getImageTranscodedSizeInBytes( 0, mip, transcoderFormat ) );
592
+
593
+ const status = basisFile.transcodeImage(
594
+ dst,
595
+ 0,
596
+ mip,
597
+ transcoderFormat,
598
+ 0,
599
+ hasAlpha
600
+ );
601
+
602
+ if ( ! status ) {
603
+
604
+ cleanup();
605
+ throw new Error( 'THREE.BasisTextureLoader: .transcodeImage failed.' );
606
+
607
+ }
608
+
609
+ mipmaps.push( { data: dst, width: mipWidth, height: mipHeight } );
610
+
611
+ }
612
+
613
+ cleanup();
614
+
615
+ return { width, height, hasAlpha, mipmaps, format: engineFormat };
616
+
617
+ }
618
+
619
+ //
620
+
621
+ // Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC),
622
+ // device capabilities, and texture dimensions. The list below ranks the formats separately
623
+ // for ETC1S and UASTC.
624
+ //
625
+ // In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at
626
+ // significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently
627
+ // chooses RGBA32 only as a last resort and does not expose that option to the caller.
628
+ const FORMAT_OPTIONS = [
629
+ {
630
+ if: 'astcSupported',
631
+ basisFormat: [ BasisFormat.UASTC_4x4 ],
632
+ transcoderFormat: [ TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4 ],
633
+ engineFormat: [ EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format ],
634
+ priorityETC1S: Infinity,
635
+ priorityUASTC: 1,
636
+ needsPowerOfTwo: false,
637
+ },
638
+ {
639
+ if: 'bptcSupported',
640
+ basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
641
+ transcoderFormat: [ TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5 ],
642
+ engineFormat: [ EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format ],
643
+ priorityETC1S: 3,
644
+ priorityUASTC: 2,
645
+ needsPowerOfTwo: false,
646
+ },
647
+ {
648
+ if: 'dxtSupported',
649
+ basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
650
+ transcoderFormat: [ TranscoderFormat.BC1, TranscoderFormat.BC3 ],
651
+ engineFormat: [ EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format ],
652
+ priorityETC1S: 4,
653
+ priorityUASTC: 5,
654
+ needsPowerOfTwo: false,
655
+ },
656
+ {
657
+ if: 'etc2Supported',
658
+ basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
659
+ transcoderFormat: [ TranscoderFormat.ETC1, TranscoderFormat.ETC2 ],
660
+ engineFormat: [ EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format ],
661
+ priorityETC1S: 1,
662
+ priorityUASTC: 3,
663
+ needsPowerOfTwo: false,
664
+ },
665
+ {
666
+ if: 'etc1Supported',
667
+ basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
668
+ transcoderFormat: [ TranscoderFormat.ETC1, TranscoderFormat.ETC1 ],
669
+ engineFormat: [ EngineFormat.RGB_ETC1_Format, EngineFormat.RGB_ETC1_Format ],
670
+ priorityETC1S: 2,
671
+ priorityUASTC: 4,
672
+ needsPowerOfTwo: false,
673
+ },
674
+ {
675
+ if: 'pvrtcSupported',
676
+ basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
677
+ transcoderFormat: [ TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA ],
678
+ engineFormat: [ EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format ],
679
+ priorityETC1S: 5,
680
+ priorityUASTC: 6,
681
+ needsPowerOfTwo: true,
682
+ },
683
+ ];
684
+
685
+ const ETC1S_OPTIONS = FORMAT_OPTIONS.sort( function ( a, b ) {
686
+
687
+ return a.priorityETC1S - b.priorityETC1S;
688
+
689
+ } );
690
+ const UASTC_OPTIONS = FORMAT_OPTIONS.sort( function ( a, b ) {
691
+
692
+ return a.priorityUASTC - b.priorityUASTC;
693
+
694
+ } );
695
+
696
+ function getTranscoderFormat( basisFormat, width, height, hasAlpha ) {
697
+
698
+ let transcoderFormat;
699
+ let engineFormat;
700
+
701
+ const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS;
702
+
703
+ for ( let i = 0; i < options.length; i ++ ) {
704
+
705
+ const opt = options[ i ];
706
+
707
+ if ( ! config[ opt.if ] ) continue;
708
+ if ( ! opt.basisFormat.includes( basisFormat ) ) continue;
709
+ if ( opt.needsPowerOfTwo && ! ( isPowerOfTwo( width ) && isPowerOfTwo( height ) ) ) continue;
710
+
711
+ transcoderFormat = opt.transcoderFormat[ hasAlpha ? 1 : 0 ];
712
+ engineFormat = opt.engineFormat[ hasAlpha ? 1 : 0 ];
713
+
714
+ return { transcoderFormat, engineFormat };
715
+
716
+ }
717
+
718
+ console.warn( 'THREE.BasisTextureLoader: No suitable compressed texture format found. Decoding to RGBA32.' );
719
+
720
+ transcoderFormat = TranscoderFormat.RGBA32;
721
+ engineFormat = EngineFormat.RGBAFormat;
722
+
723
+ return { transcoderFormat, engineFormat };
724
+
725
+ }
726
+
727
+ function assert( ok, message ) {
728
+
729
+ if ( ! ok ) throw new Error( message );
730
+
731
+ }
732
+
733
+ function getWidthInBlocks( transcoderFormat, width ) {
734
+
735
+ return Math.ceil( width / BasisModule.getFormatBlockWidth( transcoderFormat ) );
736
+
737
+ }
738
+
739
+ function getHeightInBlocks( transcoderFormat, height ) {
740
+
741
+ return Math.ceil( height / BasisModule.getFormatBlockHeight( transcoderFormat ) );
742
+
743
+ }
744
+
745
+ function getTranscodedImageByteLength( transcoderFormat, width, height ) {
746
+
747
+ const blockByteLength = BasisModule.getBytesPerBlockOrPixel( transcoderFormat );
748
+
749
+ if ( BasisModule.formatIsUncompressed( transcoderFormat ) ) {
750
+
751
+ return width * height * blockByteLength;
752
+
753
+ }
754
+
755
+ if ( transcoderFormat === TranscoderFormat.PVRTC1_4_RGB
756
+ || transcoderFormat === TranscoderFormat.PVRTC1_4_RGBA ) {
757
+
758
+ // GL requires extra padding for very small textures:
759
+ // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
760
+ const paddedWidth = ( width + 3 ) & ~ 3;
761
+ const paddedHeight = ( height + 3 ) & ~ 3;
762
+
763
+ return ( Math.max( 8, paddedWidth ) * Math.max( 8, paddedHeight ) * 4 + 7 ) / 8;
764
+
765
+ }
766
+
767
+ return ( getWidthInBlocks( transcoderFormat, width )
768
+ * getHeightInBlocks( transcoderFormat, height )
769
+ * blockByteLength );
770
+
771
+ }
772
+
773
+ function isPowerOfTwo( value ) {
774
+
775
+ if ( value <= 2 ) return true;
776
+
777
+ return ( value & ( value - 1 ) ) === 0 && value !== 0;
778
+
779
+ }
780
+
781
+ };
782
+
783
+ export { BasisTextureLoader };