@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.
- package/LICENSE +5 -0
- package/copyExamples.js +15 -0
- package/ecs/THREEView.js +31 -0
- package/examples/loaders/BasisTextureLoader.js +783 -0
- package/examples/loaders/DRACOLoader.js +587 -0
- package/examples/loaders/GLTFLoader.js +4237 -0
- package/examples/objects/Lensflare.js +389 -0
- package/examples/utils/BufferGeometryUtils.js +943 -0
- package/gpgpu/THREEGPGPUSystem.js +175 -0
- package/loader/THREELoader.js +112 -0
- package/loader/meshoptimizerdecoder/THREE.EXT_meshopt_compression.js +41 -0
- package/loader/meshoptimizerdecoder/meshopt_decoder.js +129 -0
- package/material/THREEBaseMaterial.js +68 -0
- package/material/THREEPBRMaterial.js +107 -0
- package/material/THREEShaderMaterial.js +95 -0
- package/object/THREELine.js +113 -0
- package/object/THREEMotionVectorObject.js +284 -0
- package/object/THREERibbon.js +49 -0
- package/object/THREESky.js +279 -0
- package/object/THREESprite.js +96 -0
- package/object/THREESpriteAnimation.js +103 -0
- package/object/THREEText.js +240 -0
- package/package.json +33 -0
- package/renderer/THREERenderer.js +121 -0
- package/renderer/THREEStereoRenderer.js +60 -0
- package/renderer/THREEWebGLRenderTarget2D.js +63 -0
- package/shader/THREEBaseShader.js +33 -0
|
@@ -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 };
|