@xeokit/xeokit-sdk 2.3.0-beta-11 → 2.3.0-beta-16

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.
@@ -15299,7 +15299,7 @@ class RenderBufferManager {
15299
15299
  /**
15300
15300
  * @private
15301
15301
  */
15302
- function getExtension$1 (gl, name) {
15302
+ function getExtension (gl, name) {
15303
15303
  if (gl._cachedExtensions === undefined) {
15304
15304
  gl._cachedExtensions = {};
15305
15305
  }
@@ -15365,12 +15365,12 @@ const Renderer = function (scene, options) {
15365
15365
  this._occlusionTester = null; // Lazy-created in #addMarker()
15366
15366
 
15367
15367
  this.capabilities = {
15368
- astcSupported: !!getExtension$1(gl, 'WEBGL_compressed_texture_astc'),
15368
+ astcSupported: !!getExtension(gl, 'WEBGL_compressed_texture_astc'),
15369
15369
  etc1Supported: true, // WebGL2
15370
- etc2Supported: !!getExtension$1(gl, 'WEBGL_compressed_texture_etc'),
15371
- dxtSupported: !!getExtension$1(gl, 'WEBGL_compressed_texture_s3tc'),
15372
- bptcSupported: !!getExtension$1(gl, 'EXT_texture_compression_bptc'),
15373
- pvrtcSupported: !!(getExtension$1(gl, 'WEBGL_compressed_texture_pvrtc') || getExtension$1(gl, 'WEBKIT_WEBGL_compressed_texture_pvrtc'))
15370
+ etc2Supported: !!getExtension(gl, 'WEBGL_compressed_texture_etc'),
15371
+ dxtSupported: !!getExtension(gl, 'WEBGL_compressed_texture_s3tc'),
15372
+ bptcSupported: !!getExtension(gl, 'EXT_texture_compression_bptc'),
15373
+ pvrtcSupported: !!(getExtension(gl, 'WEBGL_compressed_texture_pvrtc') || getExtension(gl, 'WEBKIT_WEBGL_compressed_texture_pvrtc'))
15374
15374
  };
15375
15375
 
15376
15376
  this.setTransparentEnabled = function (enabled) {
@@ -15999,22 +15999,6 @@ const Renderer = function (scene, options) {
15999
15999
  gl.depthMask(false);
16000
16000
  }
16001
16001
 
16002
- // Transparent X-ray edges
16003
-
16004
- if (xrayEdgesTransparentBinLen > 0) {
16005
- for (i = 0; i < xrayEdgesTransparentBinLen; i++) {
16006
- xrayEdgesTransparentBin[i].drawEdgesXRayed(frameCtx);
16007
- }
16008
- }
16009
-
16010
- // Transparent X-ray fill
16011
-
16012
- if (xrayedFillTransparentBinLen > 0) {
16013
- for (i = 0; i < xrayedFillTransparentBinLen; i++) {
16014
- xrayedFillTransparentBin[i].drawSilhouetteXRayed(frameCtx);
16015
- }
16016
- }
16017
-
16018
16002
  // Transparent color edges
16019
16003
 
16020
16004
  if (normalFillTransparentBinLen > 0 || normalEdgesTransparentBinLen > 0) {
@@ -16035,6 +16019,23 @@ const Renderer = function (scene, options) {
16035
16019
  drawable.drawColorTransparent(frameCtx);
16036
16020
  }
16037
16021
  }
16022
+
16023
+ // Transparent X-ray edges
16024
+
16025
+ if (xrayEdgesTransparentBinLen > 0) {
16026
+ for (i = 0; i < xrayEdgesTransparentBinLen; i++) {
16027
+ xrayEdgesTransparentBin[i].drawEdgesXRayed(frameCtx);
16028
+ }
16029
+ }
16030
+
16031
+ // Transparent X-ray fill
16032
+
16033
+ if (xrayedFillTransparentBinLen > 0) {
16034
+ for (i = 0; i < xrayedFillTransparentBinLen; i++) {
16035
+ xrayedFillTransparentBin[i].drawSilhouetteXRayed(frameCtx);
16036
+ }
16037
+ }
16038
+
16038
16039
  gl.disable(gl.BLEND);
16039
16040
  if (!alphaDepthMask) {
16040
16041
  gl.depthMask(true);
@@ -27801,6 +27802,7 @@ const ClampToEdgeWrapping = 1001;
27801
27802
  const MirroredRepeatWrapping = 1002;
27802
27803
  const NearestFilter = 1003;
27803
27804
  const NearestMipMapNearestFilter = 1004;
27805
+ const NearestMipmapNearestFilter = 1004;
27804
27806
  const NearestMipmapLinearFilter = 1005;
27805
27807
  const NearestMipMapLinearFilter = 1005;
27806
27808
  const LinearFilter = 1006;
@@ -46505,29 +46507,29 @@ function transformAndOctEncodeNormals(worldNormalMatrix, normals, lenNormals, co
46505
46507
  function octEncodeNormals(normals) { // http://jcgt.org/published/0003/02/01/
46506
46508
  const lenNormals = normals.length;
46507
46509
  const compressedNormals = new Int8Array(lenNormals);
46508
- let oct, best, currentCos, bestCos;
46510
+ let oct, dec, best, currentCos, bestCos;
46509
46511
  for (let i = 0; i < lenNormals; i += 3) {
46510
46512
  // Test various combinations of ceil and floor to minimize rounding errors
46511
46513
  best = oct = octEncodeNormal(normals, i, "floor", "floor");
46512
- octDecodeVec2(oct);
46513
- currentCos = bestCos = dot(normals, i);
46514
+ dec = octDecodeVec2(oct);
46515
+ currentCos = bestCos = dot(normals, i, dec);
46514
46516
  oct = octEncodeNormal(normals, i, "ceil", "floor");
46515
- octDecodeVec2(oct);
46516
- currentCos = dot(normals, i);
46517
+ dec = octDecodeVec2(oct);
46518
+ currentCos = dot(normals, i, dec);
46517
46519
  if (currentCos > bestCos) {
46518
46520
  best = oct;
46519
46521
  bestCos = currentCos;
46520
46522
  }
46521
46523
  oct = octEncodeNormal(normals, i, "floor", "ceil");
46522
- octDecodeVec2(oct);
46523
- currentCos = dot(normals, i);
46524
+ dec = octDecodeVec2(oct);
46525
+ currentCos = dot(normals, i, dec);
46524
46526
  if (currentCos > bestCos) {
46525
46527
  best = oct;
46526
46528
  bestCos = currentCos;
46527
46529
  }
46528
46530
  oct = octEncodeNormal(normals, i, "ceil", "ceil");
46529
- octDecodeVec2(oct);
46530
- currentCos = dot(normals, i);
46531
+ dec = octDecodeVec2(oct);
46532
+ currentCos = dot(normals, i, dec);
46531
46533
  if (currentCos > bestCos) {
46532
46534
  best = oct;
46533
46535
  bestCos = currentCos;
@@ -46601,8 +46603,8 @@ function octDecodeVec2(oct) { // Decode an oct-encoded normal
46601
46603
  /**
46602
46604
  * @private
46603
46605
  */
46604
- function dot(p, vec3) { // Dot product of a normal in an array against a candidate decoding
46605
- return p[0] * vec3[0] + p[1] * vec3[1] + p[2] * vec3[2];
46606
+ function dot(array, i, vec3) {
46607
+ return array[i] * vec3[0] + array[i + 1] * vec3[1] + array[i + 2] * vec3[2];
46606
46608
  }
46607
46609
 
46608
46610
  const tempMat4$2 = math.mat4();
@@ -64417,7 +64419,7 @@ function convertConstant(gl, constantVal, encoding = null) {
64417
64419
 
64418
64420
  if (p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) {
64419
64421
  if (encoding === sRGBEncoding) {
64420
- const extension = getExtension$1(gl, 'WEBGL_compressed_texture_s3tc_srgb');
64422
+ const extension = getExtension(gl, 'WEBGL_compressed_texture_s3tc_srgb');
64421
64423
  if (extension !== null) {
64422
64424
  if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT;
64423
64425
  if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
@@ -64427,7 +64429,7 @@ function convertConstant(gl, constantVal, encoding = null) {
64427
64429
  return null;
64428
64430
  }
64429
64431
  } else {
64430
- extension = getExtension$1(gl, 'WEBGL_compressed_texture_s3tc');
64432
+ extension = getExtension(gl, 'WEBGL_compressed_texture_s3tc');
64431
64433
  if (extension !== null) {
64432
64434
  if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
64433
64435
  if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
@@ -64442,7 +64444,7 @@ function convertConstant(gl, constantVal, encoding = null) {
64442
64444
  // PVRTC
64443
64445
 
64444
64446
  if (p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format) {
64445
- const extension = getExtension$1(gl, 'WEBGL_compressed_texture_pvrtc');
64447
+ const extension = getExtension(gl, 'WEBGL_compressed_texture_pvrtc');
64446
64448
  if (extension !== null) {
64447
64449
  if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
64448
64450
  if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
@@ -64456,7 +64458,7 @@ function convertConstant(gl, constantVal, encoding = null) {
64456
64458
  // ETC1
64457
64459
 
64458
64460
  if (p === RGB_ETC1_Format) {
64459
- const extension = getExtension$1(gl, 'WEBGL_compressed_texture_etc1');
64461
+ const extension = getExtension(gl, 'WEBGL_compressed_texture_etc1');
64460
64462
  if (extension !== null) {
64461
64463
  return extension.COMPRESSED_RGB_ETC1_WEBGL;
64462
64464
  } else {
@@ -64467,7 +64469,7 @@ function convertConstant(gl, constantVal, encoding = null) {
64467
64469
  // ETC2
64468
64470
 
64469
64471
  if (p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format) {
64470
- const extension = getExtension$1(gl, 'WEBGL_compressed_texture_etc');
64472
+ const extension = getExtension(gl, 'WEBGL_compressed_texture_etc');
64471
64473
  if (extension !== null) {
64472
64474
  if (p === RGB_ETC2_Format) return (encoding === sRGBEncoding) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2;
64473
64475
  if (p === RGBA_ETC2_EAC_Format) return (encoding === sRGBEncoding) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC;
@@ -64483,7 +64485,7 @@ function convertConstant(gl, constantVal, encoding = null) {
64483
64485
  p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format ||
64484
64486
  p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format ||
64485
64487
  p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format) {
64486
- const extension = getExtension$1(gl, 'WEBGL_compressed_texture_astc');
64488
+ const extension = getExtension(gl, 'WEBGL_compressed_texture_astc');
64487
64489
  if (extension !== null) {
64488
64490
  if (p === RGBA_ASTC_4x4_Format) return (encoding === sRGBEncoding) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR;
64489
64491
  if (p === RGBA_ASTC_5x4_Format) return (encoding === sRGBEncoding) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR;
@@ -64507,7 +64509,7 @@ function convertConstant(gl, constantVal, encoding = null) {
64507
64509
  // BPTC
64508
64510
 
64509
64511
  if (p === RGBA_BPTC_Format) {
64510
- const extension = getExtension$1(gl, 'EXT_texture_compression_bptc');
64512
+ const extension = getExtension(gl, 'EXT_texture_compression_bptc');
64511
64513
  if (extension !== null) {
64512
64514
  if (p === RGBA_BPTC_Format) {
64513
64515
  return (encoding === sRGBEncoding) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT;
@@ -64540,9 +64542,6 @@ function convertConstant(gl, constantVal, encoding = null) {
64540
64542
  if (p === LinearMipMapLinearFilter) {
64541
64543
  return gl.LINEAR_MIPMAP_LINEAR;
64542
64544
  }
64543
- if (p === NearestMipMapLinearFilter) {
64544
- return gl.NEAREST_MIPMAP_LINEAR;
64545
- }
64546
64545
  if (p === NearestFilter) {
64547
64546
  return gl.NEAREST;
64548
64547
  }
@@ -64580,7 +64579,7 @@ class Texture2D {
64580
64579
  this.texture = gl.createTexture();
64581
64580
 
64582
64581
  if (preloadColor) {
64583
- this.setPreloadColor(preloadColor || [0, 0, 0, 1]); // Prevents "there is no texture bound to the unit 0" error
64582
+ this.setPreloadColor(preloadColor); // Prevents "there is no texture bound to the unit 0" error
64584
64583
  }
64585
64584
 
64586
64585
  this.allocated = true;
@@ -64599,9 +64598,6 @@ class Texture2D {
64599
64598
  color$2[3] = Math.floor((value[3] !== undefined ? value[3] : 1) * 255);
64600
64599
  }
64601
64600
  const gl = this.gl;
64602
- const glFormat = convertConstant(gl, this.format, this.encoding);
64603
- const glType = convertConstant(gl, this.type);
64604
- const glInternalFormat = getInternalFormat(gl, this.internalFormat, glFormat, glType, this.encoding, false);
64605
64601
  gl.bindTexture(this.target, this.texture);
64606
64602
  if (this.target === gl.TEXTURE_CUBE_MAP) {
64607
64603
  const faces = [
@@ -64613,10 +64609,10 @@ class Texture2D {
64613
64609
  gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
64614
64610
  ];
64615
64611
  for (let i = 0, len = faces.length; i < len; i++) {
64616
- gl.texImage2D(faces[i], 0, glInternalFormat, 1, 1, 0, glFormat, glType, color$2);
64612
+ gl.texImage2D(faces[i], 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, color$2);
64617
64613
  }
64618
64614
  } else {
64619
- gl.texImage2D(this.target, 0, glInternalFormat, 1, 1, 0, glFormat, glType, color$2);
64615
+ gl.texImage2D(this.target, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, color$2);
64620
64616
  }
64621
64617
  gl.bindTexture(this.target, null);
64622
64618
  }
@@ -64776,44 +64772,38 @@ class Texture2D {
64776
64772
  gl.activeTexture(gl.TEXTURE0 + 0);
64777
64773
  gl.bindTexture(this.target, this.texture);
64778
64774
 
64779
- let generateMipMap = false;
64775
+ let supportsMips = mipmaps.length > 1;
64780
64776
 
64781
64777
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.flipY);
64782
64778
  gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha);
64783
64779
  gl.pixelStorei(gl.UNPACK_ALIGNMENT, this.unpackAlignment);
64784
64780
  gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
64785
64781
 
64786
- {
64787
-
64788
- const minFilter = convertConstant(gl, this.minFilter);
64789
- gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, minFilter);
64790
- if (minFilter === gl.NEAREST_MIPMAP_NEAREST || minFilter === gl.LINEAR_MIPMAP_NEAREST || minFilter === gl.NEAREST_MIPMAP_LINEAR || minFilter === gl.LINEAR_MIPMAP_LINEAR) {
64791
- generateMipMap = true;
64792
- }
64793
-
64794
- const magFilter = convertConstant(gl, this.magFilter);
64795
- if (magFilter) {
64796
- gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, magFilter);
64797
- }
64782
+ const wrapS = convertConstant(gl, this.wrapS);
64783
+ if (wrapS) {
64784
+ gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, wrapS);
64785
+ }
64798
64786
 
64799
- const wrapS = convertConstant(gl, this.wrapS);
64800
- if (wrapS) {
64801
- gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, wrapS);
64802
- }
64787
+ const wrapT = convertConstant(gl, this.wrapT);
64788
+ if (wrapT) {
64789
+ gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, wrapT);
64790
+ }
64803
64791
 
64804
- const wrapT = convertConstant(gl, this.wrapT);
64805
- if (wrapT) {
64806
- gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, wrapT);
64792
+ if (this.type === gl.TEXTURE_3D || this.type === gl.TEXTURE_2D_ARRAY) {
64793
+ const wrapR = convertConstant(gl, this.wrapR);
64794
+ if (wrapR) {
64795
+ gl.texParameteri(this.target, gl.TEXTURE_WRAP_R, wrapR);
64807
64796
  }
64797
+ gl.texParameteri(this.type, gl.TEXTURE_WRAP_R, wrapR);
64798
+ }
64808
64799
 
64809
- if (this.type === gl.TEXTURE_3D || this.type === gl.TEXTURE_2D_ARRAY) {
64810
- const wrapR = convertConstant(gl, this.wrapR);
64811
- if (wrapR) {
64812
- gl.texParameteri(this.target, gl.TEXTURE_WRAP_R, wrapR);
64813
- }
64814
- gl.texParameteri(this.type, gl.TEXTURE_WRAP_R, wrapR);
64815
- }
64800
+ if (supportsMips) {
64801
+ gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, filterFallback(gl, this.minFilter));
64802
+ gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, filterFallback(gl, this.magFilter));
64816
64803
 
64804
+ } else {
64805
+ gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, convertConstant(gl, this.minFilter));
64806
+ gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, convertConstant(gl, this.magFilter));
64817
64807
  }
64818
64808
 
64819
64809
  const glFormat = convertConstant(gl, this.format, this.encoding);
@@ -64833,13 +64823,13 @@ class Texture2D {
64833
64823
  console.warn('Attempt to load unsupported compressed texture format in .setCompressedData()');
64834
64824
  }
64835
64825
  } else {
64836
- gl.texSubImage2D(gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data);
64826
+ gl.texSubImage2D(gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data);
64837
64827
  }
64838
64828
  }
64839
64829
 
64840
- if (generateMipMap) {
64841
- gl.generateMipmap(this.target);
64842
- }
64830
+ // if (generateMipMap) {
64831
+ // // gl.generateMipmap(this.target); // Only for roughness textures?
64832
+ // }
64843
64833
 
64844
64834
  gl.bindTexture(this.target, null);
64845
64835
  }
@@ -64966,6 +64956,14 @@ function getInternalFormat(gl, internalFormatName, glFormat, glType, encoding, i
64966
64956
  return internalFormat;
64967
64957
  }
64968
64958
 
64959
+ function filterFallback(gl, f) {
64960
+ if (f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter) {
64961
+ return gl.NEAREST;
64962
+ }
64963
+ return gl.LINEAR;
64964
+
64965
+ }
64966
+
64969
64967
  /**
64970
64968
  * Instantiated by VBOSceneModel#createTexture
64971
64969
  *
@@ -65886,6 +65884,1059 @@ class SceneModel {
65886
65884
  }
65887
65885
  }
65888
65886
 
65887
+ class LoadingManager {
65888
+
65889
+ constructor(onLoad, onProgress, onError) {
65890
+
65891
+ this.isLoading = false;
65892
+ this.itemsLoaded = 0;
65893
+ this.itemsTotal = 0;
65894
+ this.urlModifier = undefined;
65895
+ this.handlers = [];
65896
+
65897
+ this.onStart = undefined;
65898
+ this.onLoad = onLoad;
65899
+ this.onProgress = onProgress;
65900
+ this.onError = onError;
65901
+ }
65902
+
65903
+ itemStart(url) {
65904
+ this.itemsTotal++;
65905
+ if (this.isLoading === false) {
65906
+ if (this.onStart !== undefined) {
65907
+ this.onStart(url, this.itemsLoaded, this.itemsTotal);
65908
+ }
65909
+ }
65910
+ this.isLoading = true;
65911
+ }
65912
+
65913
+ itemEnd(url) {
65914
+ this.itemsLoaded++;
65915
+ if (this.onProgress !== undefined) {
65916
+ this.onProgress(url, this.itemsLoaded, this.itemsTotal);
65917
+ }
65918
+ if (this.itemsLoaded === this.itemsTotal) {
65919
+ this.isLoading = false;
65920
+ if (this.onLoad !== undefined) {
65921
+ this.onLoad();
65922
+ }
65923
+ }
65924
+ }
65925
+
65926
+ itemError(url) {
65927
+ if (this.onError !== undefined) {
65928
+ this.onError(url);
65929
+ }
65930
+ }
65931
+
65932
+ resolveURL(url) {
65933
+ if (this.urlModifier) {
65934
+ return this.urlModifier(url);
65935
+ }
65936
+ return url;
65937
+ }
65938
+
65939
+ setURLModifier(transform) {
65940
+ this.urlModifier = transform;
65941
+ return this;
65942
+ }
65943
+
65944
+ addHandler(regex, loader) {
65945
+ this.handlers.push(regex, loader);
65946
+ return this;
65947
+ }
65948
+
65949
+ removeHandler(regex) {
65950
+ const index = this.handlers.indexOf(regex);
65951
+ if (index !== -1) {
65952
+ this.handlers.splice(index, 2);
65953
+ }
65954
+ return this;
65955
+ }
65956
+
65957
+ getHandler(file) {
65958
+ for (let i = 0, l = this.handlers.length; i < l; i += 2) {
65959
+ const regex = this.handlers[i];
65960
+ const loader = this.handlers[i + 1];
65961
+ if (regex.global) regex.lastIndex = 0; // see #17920
65962
+ if (regex.test(file)) {
65963
+ return loader;
65964
+ }
65965
+ }
65966
+ return null;
65967
+ }
65968
+ }
65969
+
65970
+ const DefaultLoadingManager = new LoadingManager();
65971
+
65972
+ class Loader {
65973
+
65974
+ constructor(manager) {
65975
+
65976
+ this.manager = (manager !== undefined) ? manager : DefaultLoadingManager;
65977
+
65978
+ this.crossOrigin = 'anonymous';
65979
+ this.withCredentials = false;
65980
+ this.path = '';
65981
+ this.resourcePath = '';
65982
+ this.requestHeader = {};
65983
+ }
65984
+
65985
+ load( /* url, onLoad, onProgress, onError */) {
65986
+ }
65987
+
65988
+ loadAsync(url, onProgress) {
65989
+ const scope = this;
65990
+ return new Promise(function (resolve, reject) {
65991
+ scope.load(url, resolve, onProgress, reject);
65992
+ });
65993
+ }
65994
+
65995
+ parse( /* data */) {
65996
+ }
65997
+
65998
+ setCrossOrigin(crossOrigin) {
65999
+ this.crossOrigin = crossOrigin;
66000
+ return this;
66001
+ }
66002
+
66003
+ setWithCredentials(value) {
66004
+ this.withCredentials = value;
66005
+ return this;
66006
+ }
66007
+
66008
+ setPath(path) {
66009
+ this.path = path;
66010
+ return this;
66011
+ }
66012
+
66013
+ setResourcePath(resourcePath) {
66014
+ this.resourcePath = resourcePath;
66015
+ return this;
66016
+ }
66017
+
66018
+ setRequestHeader(requestHeader) {
66019
+ this.requestHeader = requestHeader;
66020
+ return this;
66021
+ }
66022
+ }
66023
+
66024
+ /**
66025
+ * @author Deepkolos / https://github.com/deepkolos
66026
+ */
66027
+
66028
+ class WorkerPool$1 {
66029
+
66030
+ constructor(pool = 4) {
66031
+ this.pool = pool;
66032
+ this.queue = [];
66033
+ this.workers = [];
66034
+ this.workersResolve = [];
66035
+ this.workerStatus = 0;
66036
+ }
66037
+
66038
+ _initWorker(workerId) {
66039
+ if (!this.workers[workerId]) {
66040
+ const worker = this.workerCreator();
66041
+ worker.addEventListener('message', this._onMessage.bind(this, workerId));
66042
+ this.workers[workerId] = worker;
66043
+ }
66044
+ }
66045
+
66046
+ _getIdleWorker() {
66047
+ for (let i = 0; i < this.pool; i++)
66048
+ if (!(this.workerStatus & (1 << i))) return i;
66049
+ return -1;
66050
+ }
66051
+
66052
+ _onMessage(workerId, msg) {
66053
+ const resolve = this.workersResolve[workerId];
66054
+ resolve && resolve(msg);
66055
+ if (this.queue.length) {
66056
+ const {resolve, msg, transfer} = this.queue.shift();
66057
+ this.workersResolve[workerId] = resolve;
66058
+ this.workers[workerId].postMessage(msg, transfer);
66059
+ } else {
66060
+ this.workerStatus ^= 1 << workerId;
66061
+ }
66062
+ }
66063
+
66064
+ setWorkerCreator(workerCreator) {
66065
+ this.workerCreator = workerCreator;
66066
+ }
66067
+
66068
+ setWorkerLimit(pool) {
66069
+ this.pool = pool;
66070
+ }
66071
+
66072
+ postMessage(msg, transfer) {
66073
+ return new Promise((resolve) => {
66074
+ const workerId = this._getIdleWorker();
66075
+ if (workerId !== -1) {
66076
+ this._initWorker(workerId);
66077
+ this.workerStatus |= 1 << workerId;
66078
+ this.workersResolve[workerId] = resolve;
66079
+ this.workers[workerId].postMessage(msg, transfer);
66080
+ } else {
66081
+ this.queue.push({resolve, msg, transfer});
66082
+ }
66083
+ });
66084
+ }
66085
+
66086
+ destroy() {
66087
+
66088
+ this.workers.forEach((worker) => worker.terminate());
66089
+ this.workersResolve.length = 0;
66090
+ this.workers.length = 0;
66091
+ this.queue.length = 0;
66092
+ this.workerStatus = 0;
66093
+
66094
+ }
66095
+
66096
+ }
66097
+
66098
+ /**
66099
+ * Transcodes texture data.
66100
+ *
66101
+ * A {@link SceneModel} configured with an appropriate TextureTranscoder will allow us to add textures from
66102
+ * transcoded buffers or files. For a concrete example, see {@link VBOSceneModel}, which can be configured with
66103
+ * a {@link KTX2TextureTranscoder}, which allows us to add textures from KTX2 buffers and files.
66104
+ *
66105
+ * @interface
66106
+ */
66107
+ class TextureTranscoder {
66108
+
66109
+ /**
66110
+ * Transcodes texture data from transcoded buffers into a {@link Texture2D}.
66111
+ *
66112
+ * @param {ArrayBuffer[]} buffers Transcoded texture data. Given as an array of buffers so that we can support
66113
+ * multi-image textures, such as cube maps.
66114
+ * @param {*} config Transcoding options.
66115
+ * @param {Texture2D} texture The texture to load.
66116
+ * @returns {Promise} Resolves when the texture has loaded.
66117
+ */
66118
+ transcode(buffers, texture, config = {}) {
66119
+ }
66120
+
66121
+ /**
66122
+ * Destroys this transcoder.
66123
+ */
66124
+ destroy() {
66125
+ }
66126
+ }
66127
+
66128
+ const Cache = {
66129
+
66130
+ enabled: false,
66131
+ files: {},
66132
+
66133
+ add: function (key, file) {
66134
+ if (this.enabled === false) {
66135
+ return;
66136
+ }
66137
+ this.files[key] = file;
66138
+ },
66139
+
66140
+ get: function (key) {
66141
+ if (this.enabled === false) {
66142
+ return;
66143
+ }
66144
+ return this.files[key];
66145
+ },
66146
+
66147
+ remove: function (key) {
66148
+ delete this.files[key];
66149
+ },
66150
+
66151
+ clear: function () {
66152
+ this.files = {};
66153
+ }
66154
+ };
66155
+
66156
+ const loading = {};
66157
+
66158
+ class FileLoader extends Loader {
66159
+
66160
+ constructor(manager) {
66161
+ super(manager);
66162
+ }
66163
+
66164
+ load(url, onLoad, onProgress, onError) {
66165
+ if (url === undefined) {
66166
+ url = '';
66167
+ }
66168
+ if (this.path !== undefined) {
66169
+ url = this.path + url;
66170
+ }
66171
+ url = this.manager.resolveURL(url);
66172
+ const cached = Cache.get(url);
66173
+ if (cached !== undefined) {
66174
+ this.manager.itemStart(url);
66175
+ setTimeout(() => {
66176
+ if (onLoad) {
66177
+ onLoad(cached);
66178
+ }
66179
+ this.manager.itemEnd(url);
66180
+ }, 0);
66181
+ return cached;
66182
+ }
66183
+ if (loading[url] !== undefined) {
66184
+ loading[url].push({onLoad, onProgress, onError});
66185
+ return;
66186
+ }
66187
+ loading[url] = [];
66188
+ loading[url].push({onLoad, onProgress, onError});
66189
+ const req = new Request(url, {
66190
+ headers: new Headers(this.requestHeader),
66191
+ credentials: this.withCredentials ? 'include' : 'same-origin'
66192
+ });
66193
+ const mimeType = this.mimeType;
66194
+ const responseType = this.responseType;
66195
+ fetch(req).then(response => {
66196
+ if (response.status === 200 || response.status === 0) {
66197
+ // Some browsers return HTTP Status 0 when using non-http protocol
66198
+ // e.g. 'file://' or 'data://'. Handle as success.
66199
+ if (response.status === 0) {
66200
+ console.warn('FileLoader: HTTP Status 0 received.');
66201
+ }
66202
+ if (typeof ReadableStream === 'undefined' || response.body.getReader === undefined) {
66203
+ return response;
66204
+ }
66205
+ const callbacks = loading[url];
66206
+ const reader = response.body.getReader();
66207
+ const contentLength = response.headers.get('Content-Length');
66208
+ const total = contentLength ? parseInt(contentLength) : 0;
66209
+ const lengthComputable = total !== 0;
66210
+ let loaded = 0;
66211
+ const stream = new ReadableStream({
66212
+ start(controller) {
66213
+ readData();
66214
+
66215
+ function readData() {
66216
+ reader.read().then(({done, value}) => {
66217
+ if (done) {
66218
+ controller.close();
66219
+ } else {
66220
+ loaded += value.byteLength;
66221
+ const event = new ProgressEvent('progress', {lengthComputable, loaded, total});
66222
+ for (let i = 0, il = callbacks.length; i < il; i++) {
66223
+ const callback = callbacks[i];
66224
+ if (callback.onProgress) {
66225
+ callback.onProgress(event);
66226
+ }
66227
+ }
66228
+ controller.enqueue(value);
66229
+ readData();
66230
+ }
66231
+ });
66232
+ }
66233
+ }
66234
+ });
66235
+ return new Response(stream);
66236
+ } else {
66237
+ throw Error(`fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`);
66238
+ }
66239
+ }).then(response => {
66240
+ switch (responseType) {
66241
+ case 'arraybuffer':
66242
+ return response.arrayBuffer();
66243
+ case 'blob':
66244
+ return response.blob();
66245
+ case 'document':
66246
+ return response.text()
66247
+ .then(text => {
66248
+ const parser = new DOMParser();
66249
+ return parser.parseFromString(text, mimeType);
66250
+ });
66251
+ case 'json':
66252
+ return response.json();
66253
+ default:
66254
+ if (mimeType === undefined) {
66255
+ return response.text();
66256
+ } else {
66257
+ // sniff encoding
66258
+ const re = /charset="?([^;"\s]*)"?/i;
66259
+ const exec = re.exec(mimeType);
66260
+ const label = exec && exec[1] ? exec[1].toLowerCase() : undefined;
66261
+ const decoder = new TextDecoder(label);
66262
+ return response.arrayBuffer().then(ab => decoder.decode(ab));
66263
+ }
66264
+ }
66265
+ }).then(data => {
66266
+ // Add to cache only on HTTP success, so that we do not cache
66267
+ // error response bodies as proper responses to requests.
66268
+ Cache.add(url, data);
66269
+ const callbacks = loading[url];
66270
+ delete loading[url];
66271
+ for (let i = 0, il = callbacks.length; i < il; i++) {
66272
+ const callback = callbacks[i];
66273
+ if (callback.onLoad) {
66274
+ callback.onLoad(data);
66275
+ }
66276
+ }
66277
+ }).catch(err => {
66278
+ // Abort errors and other errors are handled the same
66279
+ const callbacks = loading[url];
66280
+ if (callbacks === undefined) {
66281
+ // When onLoad was called and url was deleted in `loading`
66282
+ this.manager.itemError(url);
66283
+ throw err;
66284
+
66285
+ }
66286
+ delete loading[url];
66287
+ for (let i = 0, il = callbacks.length; i < il; i++) {
66288
+ const callback = callbacks[i];
66289
+ if (callback.onError) {
66290
+ callback.onError(err);
66291
+ }
66292
+ }
66293
+ this.manager.itemError(url);
66294
+ }).finally(() => {
66295
+ this.manager.itemEnd(url);
66296
+ });
66297
+ this.manager.itemStart(url);
66298
+ }
66299
+
66300
+ setResponseType(value) {
66301
+ this.responseType = value;
66302
+ return this;
66303
+ }
66304
+
66305
+ setMimeType(value) {
66306
+ this.mimeType = value;
66307
+ return this;
66308
+ }
66309
+ }
66310
+
66311
+ const KTX2TransferSRGB = 2;
66312
+ const KTX2_ALPHA_PREMULTIPLIED = 1;
66313
+
66314
+ let activeTranscoders = 0;
66315
+
66316
+ /**
66317
+ * Transcodes texture data from KTX2.
66318
+ *
66319
+ * ## Overview
66320
+ *
66321
+ * * Uses the [Basis Universal GPU Texture Codec](https://github.com/BinomialLLC/basis_universal) to
66322
+ * transcode [KTX2](https://github.khronos.org/KTX-Specification/) textures.
66323
+ * * {@link XKTLoaderPlugin} uses a KTX2TextureTranscoder to load textures in XKT files.
66324
+ * * {@link VBOSceneModel} uses a KTX2TextureTranscoder to enable us to add KTX2-encoded textures.
66325
+ * * Loads the Basis Codec from [CDN](https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/) by default, but can
66326
+ * also be configured to load the Codec from local files.
66327
+ * * We also bundle the Basis Codec with the xeokit-sdk npm package, and in the [repository](https://github.com/xeokit/xeokit-sdk/tree/master/dist/basis).
66328
+ *
66329
+ * ## What is KTX2?
66330
+ *
66331
+ * A [KTX2](https://github.khronos.org/KTX-Specification/) file stores GPU texture data in the Khronos Texture 2.0 (KTX2) container format. It contains image data for
66332
+ * a texture asset compressed with Basis Universal (BasisU) supercompression that can be transcoded to different formats
66333
+ * depending on the support provided by the target devices. KTX2 provides a lightweight format for distributing texture
66334
+ * assets to GPUs. Due to BasisU compression, KTX2 files can store any image format supported by GPUs.
66335
+ *
66336
+ * ## Loading XKT files containing KTX2 textures
66337
+ *
66338
+ * {@link XKTLoaderPlugin} uses a KTX2TextureTranscoder to load textures in XKT files. An XKTLoaderPlugin has its own
66339
+ * default KTX2TextureTranscoder, configured to load the Basis Codec from the [CDN](https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/). If we wish, we can override that with our own
66340
+ * KTX2TextureTranscoder, configured to load the Codec locally.
66341
+ *
66342
+ * In the example below, we'll create a {@link Viewer} and add an {@link XKTLoaderPlugin}
66343
+ * configured with a KTX2TextureTranscoder. Then we'll use the XKTLoaderPlugin to load an
66344
+ * XKT file that contains KTX2 textures, which the plugin will transcode using
66345
+ * its KTX2TextureTranscoder.
66346
+ *
66347
+ * We'll configure our KTX2TextureTranscoder to load the Basis Codec from a local directory. If we were happy with loading the
66348
+ * Codec from our [CDN](https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/) (ie. our app will always have an Internet connection) then we could just leave out the
66349
+ * KTX2TextureTranscoder altogether, and let the XKTLoaderPlugin use its internal default KTX2TextureTranscoder, which is configured to
66350
+ * load the Codec from the CDN. We'll stick with loading our own Codec, in case we want to run our app without an Internet connection.
66351
+ *
66352
+ * <a href="https://xeokit.github.io/xeokit-sdk/examples/#loading_XKT_Textures_HousePlan"><img src="https://xeokit.github.io/xeokit-sdk/assets/images/xktWithTextures.png"></a>
66353
+ *
66354
+ * * [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/#loading_XKT_Textures_HousePlan)]
66355
+ *
66356
+ * ````javascript
66357
+ * const viewer = new Viewer({
66358
+ * canvasId: "myCanvas",
66359
+ * transparent: true
66360
+ * });
66361
+ *
66362
+ * viewer.camera.eye = [-2.56, 8.38, 8.27];
66363
+ * viewer.camera.look = [13.44, 3.31, -14.83];
66364
+ * viewer.camera.up = [0.10, 0.98, -0.14];
66365
+ *
66366
+ * const textureTranscoder = new KTX2TextureTranscoder({
66367
+ * viewer,
66368
+ * transcoderPath: "./../dist/basis/" // <------ Path to Basis Universal transcoder
66369
+ * });
66370
+ *
66371
+ * const xktLoader = new XKTLoaderPlugin(viewer, {
66372
+ * textureTranscoder // <<------------- Transcodes KTX2 textures in XKT files
66373
+ * });
66374
+ *
66375
+ * const sceneModel = xktLoader.load({
66376
+ * id: "myModel",
66377
+ * src: "./HousePlan.xkt" // <<------ XKT file with KTX2 textures
66378
+ * });
66379
+ * ````
66380
+ *
66381
+ * ## Loading KTX2 files into a VBOSceneModel
66382
+ *
66383
+ * A {@link SceneModel} that is configured with a KTX2TextureTranscoder will
66384
+ * allow us to load textures into it from KTX2-transcoded buffers or files.
66385
+ *
66386
+ * In the example below, we'll create a {@link Viewer}, containing a {@link VBOSceneModel} configured with a
66387
+ * KTX2TextureTranscoder.
66388
+ *
66389
+ * We'll then programmatically create a simple object within the VBOSceneModel, consisting of
66390
+ * a single box mesh with a texture loaded from a KTX2 file, which our VBOSceneModel internally transcodes, using
66391
+ * its KTX2TextureTranscoder.
66392
+ *
66393
+ * As in the previous example, we'll configure our KTX2TextureTranscoder to load the Basis Codec from a local directory.
66394
+ *
66395
+ * * [Run a similar example](http://localhost:8080/examples/sceneRepresentation_VBOSceneModel_batching_textures_ktx2.html)
66396
+ *
66397
+ * ````javascript
66398
+ * const viewer = new Viewer({
66399
+ * canvasId: "myCanvas",
66400
+ * transparent: true
66401
+ * });
66402
+ *
66403
+ * viewer.scene.camera.eye = [-21.80, 4.01, 6.56];
66404
+ * viewer.scene.camera.look = [0, -5.75, 0];
66405
+ * viewer.scene.camera.up = [0.37, 0.91, -0.11];
66406
+ *
66407
+ * const textureTranscoder = new KTX2TextureTranscoder({
66408
+ * viewer,
66409
+ * transcoderPath: "./../dist/basis/" // <------ Path to BasisU transcoder module
66410
+ * });
66411
+ *
66412
+ * const vboSceneModel = new VBOSceneModel(viewer.scene, {
66413
+ * id: "myModel",
66414
+ * textureTranscoder // <<-------------------- Configure model with our transcoder
66415
+ * });
66416
+ *
66417
+ * vboSceneModel.createTexture({
66418
+ * id: "myColorTexture",
66419
+ * src: "../assets/textures/compressed/sample_uastc_zstd.ktx2" // <<----- KTX2 texture asset
66420
+ * });
66421
+ *
66422
+ * vboSceneModel.createTexture({
66423
+ * id: "myMetallicRoughnessTexture",
66424
+ * src: "../assets/textures/alpha/crosshatchAlphaMap.jpg" // <<----- JPEG texture asset
66425
+ * });
66426
+ *
66427
+ * vboSceneModel.createTextureSet({
66428
+ * id: "myTextureSet",
66429
+ * colorTextureId: "myColorTexture",
66430
+ * metallicRoughnessTextureId: "myMetallicRoughnessTexture"
66431
+ * });
66432
+ *
66433
+ * vboSceneModel.createMesh({
66434
+ * id: "myMesh",
66435
+ * textureSetId: "myTextureSet",
66436
+ * primitive: "triangles",
66437
+ * positions: [1, 1, 1, ...],
66438
+ * normals: [0, 0, 1, 0, ...],
66439
+ * uv: [1, 0, 0, ...],
66440
+ * indices: [0, 1, 2, ...],
66441
+ * });
66442
+ *
66443
+ * vboSceneModel.createEntity({
66444
+ * id: "myEntity",
66445
+ * meshIds: ["myMesh"]
66446
+ * });
66447
+ *
66448
+ * vboSceneModel.finalize();
66449
+ * ````
66450
+ *
66451
+ * ## Loading KTX2 ArrayBuffers into a VBOSceneModel
66452
+ *
66453
+ * A {@link SceneModel} that is configured with a KTX2TextureTranscoder will also allow us to load textures into
66454
+ * it from KTX2 ArrayBuffers.
66455
+ *
66456
+ * In the example below, we'll create a {@link Viewer}, containing a {@link VBOSceneModel} configured with a
66457
+ * KTX2TextureTranscoder.
66458
+ *
66459
+ * We'll then programmatically create a simple object within the VBOSceneModel, consisting of
66460
+ * a single mesh with a texture loaded from a KTX2 ArrayBuffer, which our VBOSceneModel internally transcodes, using
66461
+ * its KTX2TextureTranscoder.
66462
+ *
66463
+ * ````javascript
66464
+ * const viewer = new Viewer({
66465
+ * canvasId: "myCanvas",
66466
+ * transparent: true
66467
+ * });
66468
+ *
66469
+ * viewer.scene.camera.eye = [-21.80, 4.01, 6.56];
66470
+ * viewer.scene.camera.look = [0, -5.75, 0];
66471
+ * viewer.scene.camera.up = [0.37, 0.91, -0.11];
66472
+ *
66473
+ * const textureTranscoder = new KTX2TextureTranscoder({
66474
+ * viewer,
66475
+ * transcoderPath: "./../dist/basis/" // <------ Path to BasisU transcoder module
66476
+ * });
66477
+ *
66478
+ * const vboSceneModel = new VBOSceneModel(viewer.scene, {
66479
+ * id: "myModel",
66480
+ * textureTranscoder // <<-------------------- Configure model with our transcoder
66481
+ * });
66482
+ *
66483
+ * utils.loadArraybuffer("../assets/textures/compressed/sample_uastc_zstd.ktx2",(arrayBuffer) => {
66484
+ *
66485
+ * vboSceneModel.createTexture({
66486
+ * id: "myColorTexture",
66487
+ * buffers: [arrayBuffer] // <<----- KTX2 texture asset
66488
+ * });
66489
+ *
66490
+ * vboSceneModel.createTexture({
66491
+ * id: "myMetallicRoughnessTexture",
66492
+ * src: "../assets/textures/alpha/crosshatchAlphaMap.jpg" // <<----- JPEG texture asset
66493
+ * });
66494
+ *
66495
+ * vboSceneModel.createTextureSet({
66496
+ * id: "myTextureSet",
66497
+ * colorTextureId: "myColorTexture",
66498
+ * metallicRoughnessTextureId: "myMetallicRoughnessTexture"
66499
+ * });
66500
+ *
66501
+ * vboSceneModel.createMesh({
66502
+ * id: "myMesh",
66503
+ * textureSetId: "myTextureSet",
66504
+ * primitive: "triangles",
66505
+ * positions: [1, 1, 1, ...],
66506
+ * normals: [0, 0, 1, 0, ...],
66507
+ * uv: [1, 0, 0, ...],
66508
+ * indices: [0, 1, 2, ...],
66509
+ * });
66510
+ *
66511
+ * vboSceneModel.createEntity({
66512
+ * id: "myEntity",
66513
+ * meshIds: ["myMesh"]
66514
+ * });
66515
+ *
66516
+ * vboSceneModel.finalize();
66517
+ * });
66518
+ * ````
66519
+ *
66520
+ * @implements {TextureTranscoder}
66521
+ */
66522
+ class KTX2TextureTranscoder {
66523
+
66524
+ /**
66525
+ * Creates a new KTX2TextureTranscoder.
66526
+ *
66527
+ * @param {Viewer} viewer The Viewer that our KTX2TextureTranscoder will be used with. This KTX2TextureTranscoder
66528
+ * must only be used to transcode textures for this Viewer. This is because the Viewer's capabilities will decide
66529
+ * what target GPU formats this KTX2TextureTranscoder will transcode to.
66530
+ * @param {String} [transcoderPath="https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/"] Path to the Basis
66531
+ * transcoder module that internally does the heavy lifting for our KTX2TextureTranscoder. If we omit this configuration,
66532
+ * then our KTX2TextureTranscoder will load it from ````https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/```` by
66533
+ * default. Therefore, make sure your application is connected to the internet if you wish to use the default transcoder path.
66534
+ * @param {Number} [workerLimit] The maximum number of Workers to use for transcoding.
66535
+ */
66536
+ constructor({viewer, transcoderPath, workerLimit}) {
66537
+
66538
+ this._transcoderPath = transcoderPath || "https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/";
66539
+ this._transcoderBinary = null;
66540
+ this._transcoderPending = null;
66541
+ this._workerPool = new WorkerPool$1();
66542
+ this._workerSourceURL = '';
66543
+
66544
+ if (workerLimit) {
66545
+ this._workerPool.setWorkerLimit(workerLimit);
66546
+ }
66547
+
66548
+ const viewerCapabilities = viewer.capabilities;
66549
+
66550
+ this._workerConfig = {
66551
+ astcSupported: viewerCapabilities.astcSupported,
66552
+ etc1Supported: viewerCapabilities.etc1Supported,
66553
+ etc2Supported: viewerCapabilities.etc2Supported,
66554
+ dxtSupported: viewerCapabilities.dxtSupported,
66555
+ bptcSupported: viewerCapabilities.bptcSupported,
66556
+ pvrtcSupported: viewerCapabilities.pvrtcSupported
66557
+ };
66558
+
66559
+ this._supportedFileTypes = ["xkt2"];
66560
+ }
66561
+
66562
+ _init() {
66563
+ if (!this._transcoderPending) {
66564
+ const jsLoader = new FileLoader();
66565
+ jsLoader.setPath(this._transcoderPath);
66566
+ jsLoader.setWithCredentials(this.withCredentials);
66567
+ const jsContent = jsLoader.loadAsync('basis_transcoder.js');
66568
+ const binaryLoader = new FileLoader();
66569
+ binaryLoader.setPath(this._transcoderPath);
66570
+ binaryLoader.setResponseType('arraybuffer');
66571
+ binaryLoader.setWithCredentials(this.withCredentials);
66572
+ const binaryContent = binaryLoader.loadAsync('basis_transcoder.wasm');
66573
+ this._transcoderPending = Promise.all([jsContent, binaryContent])
66574
+ .then(([jsContent, binaryContent]) => {
66575
+ const fn = KTX2TextureTranscoder.BasisWorker.toString();
66576
+ const body = [
66577
+ '/* constants */',
66578
+ 'let _EngineFormat = ' + JSON.stringify(KTX2TextureTranscoder.EngineFormat),
66579
+ 'let _TranscoderFormat = ' + JSON.stringify(KTX2TextureTranscoder.TranscoderFormat),
66580
+ 'let _BasisFormat = ' + JSON.stringify(KTX2TextureTranscoder.BasisFormat),
66581
+ '/* basis_transcoder.js */',
66582
+ jsContent,
66583
+ '/* worker */',
66584
+ fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}'))
66585
+ ].join('\n');
66586
+ this._workerSourceURL = URL.createObjectURL(new Blob([body]));
66587
+ this._transcoderBinary = binaryContent;
66588
+ this._workerPool.setWorkerCreator(() => {
66589
+ const worker = new Worker(this._workerSourceURL);
66590
+ const transcoderBinary = this._transcoderBinary.slice(0);
66591
+ worker.postMessage({
66592
+ type: 'init',
66593
+ config: this._workerConfig,
66594
+ transcoderBinary
66595
+ }, [transcoderBinary]);
66596
+ return worker;
66597
+ });
66598
+ });
66599
+ if (activeTranscoders > 0) {
66600
+ console.warn('KTX2TextureTranscoder: Multiple active KTX2TextureTranscoder may cause performance issues.' + ' Use a single KTX2TextureTranscoder instance, or call .dispose() on old instances.');
66601
+ }
66602
+ activeTranscoders++;
66603
+ }
66604
+ return this._transcoderPending;
66605
+ }
66606
+
66607
+ /**
66608
+ * Transcodes texture data from transcoded buffers into a {@link Texture2D}.
66609
+ *
66610
+ * @param {ArrayBuffer[]} buffers Transcoded texture data. Given as an array of buffers so that we can support multi-image textures, such as cube maps.
66611
+ * @param {*} config Transcoding options.
66612
+ * @param {Texture2D} texture The texture to load.
66613
+ * @returns {Promise} Resolves when the texture has loaded.
66614
+ */
66615
+ transcode(buffers, texture, config = {}) {
66616
+ return new Promise((resolve, reject) => {
66617
+ const taskConfig = config;
66618
+ this._init().then(() => {
66619
+ return this._workerPool.postMessage({
66620
+ type: 'transcode',
66621
+ buffers,
66622
+ taskConfig: taskConfig
66623
+ }, buffers);
66624
+ }).then((e) => {
66625
+ const transcodeResult = e.data;
66626
+ const {mipmaps, width, height, format, type, error, dfdTransferFn, dfdFlags} = transcodeResult;
66627
+ if (type === 'error') {
66628
+ return reject(error);
66629
+ }
66630
+ texture.setCompressedData({
66631
+ mipmaps,
66632
+ props: {
66633
+ format: format,
66634
+ minFilter: mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter,
66635
+ magFilter: mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter,
66636
+ encoding: dfdTransferFn === KTX2TransferSRGB ? sRGBEncoding : LinearEncoding,
66637
+ premultiplyAlpha: !!(dfdFlags & KTX2_ALPHA_PREMULTIPLIED)
66638
+ }
66639
+ });
66640
+ resolve();
66641
+ });
66642
+ });
66643
+ }
66644
+
66645
+ /**
66646
+ * Destroys this KTX2TextureTranscoder
66647
+ */
66648
+ destroy() {
66649
+ URL.revokeObjectURL(this._workerSourceURL);
66650
+ this._workerPool.destroy();
66651
+ activeTranscoders--;
66652
+ }
66653
+ }
66654
+
66655
+ /**
66656
+ * @private
66657
+ */
66658
+ KTX2TextureTranscoder.BasisFormat = {
66659
+ ETC1S: 0,
66660
+ UASTC_4x4: 1
66661
+ };
66662
+
66663
+ /**
66664
+ * @private
66665
+ */
66666
+ KTX2TextureTranscoder.TranscoderFormat = {
66667
+ ETC1: 0,
66668
+ ETC2: 1,
66669
+ BC1: 2,
66670
+ BC3: 3,
66671
+ BC4: 4,
66672
+ BC5: 5,
66673
+ BC7_M6_OPAQUE_ONLY: 6,
66674
+ BC7_M5: 7,
66675
+ PVRTC1_4_RGB: 8,
66676
+ PVRTC1_4_RGBA: 9,
66677
+ ASTC_4x4: 10,
66678
+ ATC_RGB: 11,
66679
+ ATC_RGBA_INTERPOLATED_ALPHA: 12,
66680
+ RGBA32: 13,
66681
+ RGB565: 14,
66682
+ BGR565: 15,
66683
+ RGBA4444: 16
66684
+ };
66685
+
66686
+ /**
66687
+ * @private
66688
+ */
66689
+ KTX2TextureTranscoder.EngineFormat = {
66690
+ RGBAFormat: RGBAFormat,
66691
+ RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format,
66692
+ RGBA_BPTC_Format: RGBA_BPTC_Format,
66693
+ RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format,
66694
+ RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format,
66695
+ RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format,
66696
+ RGB_ETC1_Format: RGB_ETC1_Format,
66697
+ RGB_ETC2_Format: RGB_ETC2_Format,
66698
+ RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format,
66699
+ RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format
66700
+ };
66701
+
66702
+ /* WEB WORKER */
66703
+
66704
+ /**
66705
+ * @private
66706
+ * @constructor
66707
+ */
66708
+ KTX2TextureTranscoder.BasisWorker = function () {
66709
+
66710
+ let config;
66711
+ let transcoderPending;
66712
+ let BasisModule;
66713
+
66714
+ const EngineFormat = _EngineFormat; // eslint-disable-line no-undef
66715
+ const TranscoderFormat = _TranscoderFormat; // eslint-disable-line no-undef
66716
+ const BasisFormat = _BasisFormat; // eslint-disable-line no-undef
66717
+
66718
+ self.addEventListener('message', function (e) {
66719
+ const message = e.data;
66720
+ switch (message.type) {
66721
+ case 'init':
66722
+ config = message.config;
66723
+ init(message.transcoderBinary);
66724
+ break;
66725
+ case 'transcode':
66726
+ transcoderPending.then(() => {
66727
+ try {
66728
+ const {
66729
+ width,
66730
+ height,
66731
+ hasAlpha,
66732
+ mipmaps,
66733
+ format,
66734
+ dfdTransferFn,
66735
+ dfdFlags
66736
+ } = transcode(message.buffers[0]);
66737
+ const buffers = [];
66738
+ for (let i = 0; i < mipmaps.length; ++i) {
66739
+ buffers.push(mipmaps[i].data.buffer);
66740
+ }
66741
+ self.postMessage({
66742
+ type: 'transcode',
66743
+ id: message.id,
66744
+ width,
66745
+ height,
66746
+ hasAlpha,
66747
+ mipmaps,
66748
+ format,
66749
+ dfdTransferFn,
66750
+ dfdFlags
66751
+ }, buffers);
66752
+ } catch (error) {
66753
+ console.error(`[KTX2TextureTranscoder.BasisWorker]: ${error}`);
66754
+ self.postMessage({type: 'error', id: message.id, error: error.message});
66755
+ }
66756
+ });
66757
+ break;
66758
+ }
66759
+ });
66760
+
66761
+ function init(wasmBinary) {
66762
+ transcoderPending = new Promise(resolve => {
66763
+ BasisModule = {
66764
+ wasmBinary,
66765
+ onRuntimeInitialized: resolve
66766
+ };
66767
+ BASIS(BasisModule); // eslint-disable-line no-undef
66768
+ }).then(() => {
66769
+ BasisModule.initializeBasis();
66770
+ if (BasisModule.KTX2File === undefined) {
66771
+ console.warn('KTX2TextureTranscoder: Please update Basis Universal transcoder.');
66772
+ }
66773
+ });
66774
+ }
66775
+
66776
+ function transcode(buffer) {
66777
+ const ktx2File = new BasisModule.KTX2File(new Uint8Array(buffer));
66778
+
66779
+ function cleanup() {
66780
+ ktx2File.close();
66781
+ ktx2File.delete();
66782
+ }
66783
+
66784
+ if (!ktx2File.isValid()) {
66785
+ cleanup();
66786
+ throw new Error('KTX2TextureTranscoder: Invalid or unsupported .ktx2 file');
66787
+ }
66788
+ const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
66789
+ const width = ktx2File.getWidth();
66790
+ const height = ktx2File.getHeight();
66791
+ const levels = ktx2File.getLevels();
66792
+ const hasAlpha = ktx2File.getHasAlpha();
66793
+ const dfdTransferFn = ktx2File.getDFDTransferFunc();
66794
+ const dfdFlags = ktx2File.getDFDFlags();
66795
+ const {transcoderFormat, engineFormat} = getTranscoderFormat(basisFormat, width, height, hasAlpha);
66796
+ if (!width || !height || !levels) {
66797
+ cleanup();
66798
+ throw new Error('KTX2TextureTranscoder: Invalid texture');
66799
+ }
66800
+ if (!ktx2File.startTranscoding()) {
66801
+ cleanup();
66802
+ throw new Error('KTX2TextureTranscoder: .startTranscoding failed');
66803
+ }
66804
+ const mipmaps = [];
66805
+ for (let mip = 0; mip < levels; mip++) {
66806
+ const levelInfo = ktx2File.getImageLevelInfo(mip, 0, 0);
66807
+ const mipWidth = levelInfo.origWidth;
66808
+ const mipHeight = levelInfo.origHeight;
66809
+ const dst = new Uint8Array(ktx2File.getImageTranscodedSizeInBytes(mip, 0, 0, transcoderFormat));
66810
+ const status = ktx2File.transcodeImage(dst, mip, 0, 0, transcoderFormat, 0, -1, -1);
66811
+ if (!status) {
66812
+ cleanup();
66813
+ throw new Error('KTX2TextureTranscoder: .transcodeImage failed.');
66814
+ }
66815
+ mipmaps.push({data: dst, width: mipWidth, height: mipHeight});
66816
+ }
66817
+ cleanup();
66818
+ return {width, height, hasAlpha, mipmaps, format: engineFormat, dfdTransferFn, dfdFlags};
66819
+ }
66820
+
66821
+ // Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC),
66822
+ // device capabilities, and texture dimensions. The list below ranks the formats separately
66823
+ // for ETC1S and UASTC.
66824
+ //
66825
+ // In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at
66826
+ // significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently
66827
+ // chooses RGBA32 only as a last resort and does not expose that option to the caller.
66828
+
66829
+ const FORMAT_OPTIONS = [{
66830
+ if: 'astcSupported',
66831
+ basisFormat: [BasisFormat.UASTC_4x4],
66832
+ transcoderFormat: [TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4],
66833
+ engineFormat: [EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format],
66834
+ priorityETC1S: Infinity,
66835
+ priorityUASTC: 1,
66836
+ needsPowerOfTwo: false
66837
+ }, {
66838
+ if: 'bptcSupported',
66839
+ basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
66840
+ transcoderFormat: [TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5],
66841
+ engineFormat: [EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format],
66842
+ priorityETC1S: 3,
66843
+ priorityUASTC: 2,
66844
+ needsPowerOfTwo: false
66845
+ }, {
66846
+ if: 'dxtSupported',
66847
+ basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
66848
+ transcoderFormat: [TranscoderFormat.BC1, TranscoderFormat.BC3],
66849
+ engineFormat: [EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format],
66850
+ priorityETC1S: 4,
66851
+ priorityUASTC: 5,
66852
+ needsPowerOfTwo: false
66853
+ }, {
66854
+ if: 'etc2Supported',
66855
+ basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
66856
+ transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC2],
66857
+ engineFormat: [EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format],
66858
+ priorityETC1S: 1,
66859
+ priorityUASTC: 3,
66860
+ needsPowerOfTwo: false
66861
+ }, {
66862
+ if: 'etc1Supported',
66863
+ basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
66864
+ transcoderFormat: [TranscoderFormat.ETC1],
66865
+ engineFormat: [EngineFormat.RGB_ETC1_Format],
66866
+ priorityETC1S: 2,
66867
+ priorityUASTC: 4,
66868
+ needsPowerOfTwo: false
66869
+ }, {
66870
+ if: 'pvrtcSupported',
66871
+ basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
66872
+ transcoderFormat: [TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA],
66873
+ engineFormat: [EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format],
66874
+ priorityETC1S: 5,
66875
+ priorityUASTC: 6,
66876
+ needsPowerOfTwo: true
66877
+ }];
66878
+ const ETC1S_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) {
66879
+ return a.priorityETC1S - b.priorityETC1S;
66880
+ });
66881
+ const UASTC_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) {
66882
+ return a.priorityUASTC - b.priorityUASTC;
66883
+ });
66884
+
66885
+ function getTranscoderFormat(basisFormat, width, height, hasAlpha) {
66886
+ let transcoderFormat;
66887
+ let engineFormat;
66888
+ const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS;
66889
+ for (let i = 0; i < options.length; i++) {
66890
+ const opt = options[i];
66891
+ if (!config[opt.if]) continue;
66892
+ if (!opt.basisFormat.includes(basisFormat)) continue;
66893
+ if (hasAlpha && opt.transcoderFormat.length < 2) continue;
66894
+ if (opt.needsPowerOfTwo && !(isPowerOfTwo(width) && isPowerOfTwo(height))) continue;
66895
+ transcoderFormat = opt.transcoderFormat[hasAlpha ? 1 : 0];
66896
+ engineFormat = opt.engineFormat[hasAlpha ? 1 : 0];
66897
+ return {
66898
+ transcoderFormat,
66899
+ engineFormat
66900
+ };
66901
+ }
66902
+ console.warn('KTX2TextureTranscoder: No suitable compressed texture format found. Decoding to RGBA32.');
66903
+ transcoderFormat = TranscoderFormat.RGBA32;
66904
+ engineFormat = EngineFormat.RGBAFormat;
66905
+ return {
66906
+ transcoderFormat,
66907
+ engineFormat
66908
+ };
66909
+ }
66910
+
66911
+ function isPowerOfTwo(value) {
66912
+ if (value <= 2) return true;
66913
+ return (value & value - 1) === 0 && value !== 0;
66914
+ }
66915
+ };
66916
+
66917
+ const cachedTranscoders = {};
66918
+
66919
+ /**
66920
+ * Returns a new {@link KTX2TextureTranscoder}.
66921
+ *
66922
+ * The ````transcoderPath```` config will be set to: "https://cdn.jsdelivr.net/npm/@xeokit/xeokit-sdk/dist/basis/"
66923
+ *
66924
+ * @private
66925
+ */
66926
+ function getKTX2TextureTranscoder(viewer) {
66927
+ const sceneId = viewer.scene.id;
66928
+ let transcoder = cachedTranscoders[sceneId];
66929
+ if (!transcoder) {
66930
+ transcoder = new KTX2TextureTranscoder({viewer});
66931
+ cachedTranscoders[sceneId] = transcoder;
66932
+ viewer.scene.on("destroyed", () => {
66933
+ delete cachedTranscoders[sceneId];
66934
+ transcoder.destroy();
66935
+ });
66936
+ }
66937
+ return transcoder;
66938
+ }
66939
+
65889
66940
  const tempVec3a$9 = math.vec3();
65890
66941
  const tempMat4$1 = math.mat4();
65891
66942
 
@@ -66961,7 +68012,7 @@ class VBOSceneModel extends Component {
66961
68012
 
66962
68013
  super(owner, cfg);
66963
68014
 
66964
- this._textureTranscoder = cfg.textureTranscoder;
68015
+ this._textureTranscoder = cfg.textureTranscoder || getKTX2TextureTranscoder(this.scene.viewer);
66965
68016
 
66966
68017
  this._maxGeometryBatchSize = cfg.maxGeometryBatchSize;
66967
68018
 
@@ -67937,7 +68988,7 @@ class VBOSceneModel extends Component {
67937
68988
  * @param {String} [cfg.src] Image file for the texture. Assumed to be transcoded if not having a recognized image file
67938
68989
  * extension (jpg, jpeg, png etc.). If transcoded, then assumes ````VBOSceneModel```` is configured with a {@link TextureTranscoder}.
67939
68990
  * @param {ArrayBuffer[]} [cfg.buffers] Transcoded texture data. Assumes ````VBOSceneModel```` is
67940
- * configured with a {@link TextureTranscoder}. Given as an array of buffers so we can potentially support multi-image textures, such as cube maps.
68991
+ * configured with a {@link TextureTranscoder}. This parameter is given as an array of buffers so we can potentially support multi-image textures, such as cube maps.
67941
68992
  * @param {HTMLImageElement} [cfg.image] HTML Image object to load into this texture. Overrides ````src```` and ````buffers````. Never transcoded.
67942
68993
  * @param {Number} [cfg.minFilter=LinearMipmapLinearFilter] How the texture is sampled when a texel covers less than one pixel.
67943
68994
  * Supported values are {@link LinearMipmapLinearFilter}, {@link LinearMipMapNearestFilter}, {@link NearestMipMapNearestFilter}, {@link NearestMipMapLinearFilter} and {@link LinearMipMapLinearFilter}.
@@ -67957,8 +69008,8 @@ class VBOSceneModel extends Component {
67957
69008
  this.error("Texture already created: " + textureId);
67958
69009
  return;
67959
69010
  }
67960
- if (!cfg.src && !cfg.image) {
67961
- this.error("Param expected: `src` or `image'");
69011
+ if (!cfg.src && !cfg.image && !cfg.buffers) {
69012
+ this.error("Param expected: `src`, `image' or 'buffers'");
67962
69013
  return null;
67963
69014
  }
67964
69015
  let minFilter = cfg.minFilter || LinearMipmapLinearFilter;
@@ -74059,7 +75110,7 @@ class WorkerThread {
74059
75110
 
74060
75111
  }
74061
75112
 
74062
- class WorkerPool$1 {
75113
+ class WorkerPool {
74063
75114
  constructor(props) {
74064
75115
  _defineProperty(this, "name", 'unnamed');
74065
75116
 
@@ -74267,7 +75318,7 @@ class WorkerFarm {
74267
75318
  let workerPool = this.workerPools.get(name);
74268
75319
 
74269
75320
  if (!workerPool) {
74270
- workerPool = new WorkerPool$1({
75321
+ workerPool = new WorkerPool({
74271
75322
  name,
74272
75323
  source,
74273
75324
  url
@@ -100446,7 +101497,7 @@ function inflate(deflatedData) {
100446
101497
 
100447
101498
  return {
100448
101499
  metadata: JSON.parse(pako.inflate(deflatedData.metadata, {to: 'string'})),
100449
- textureData: new Uint8ClampedArray(inflate(deflatedData.textureData)),
101500
+ textureData: new Uint8Array(inflate(deflatedData.textureData)),
100450
101501
  eachTextureDataPortion: new Uint32Array(inflate(deflatedData.eachTextureDataPortion)),
100451
101502
  eachTextureDimensions: new Uint16Array(inflate(deflatedData.eachTextureDimensions)),
100452
101503
  positions: new Uint16Array(inflate(deflatedData.positions)),
@@ -100486,7 +101537,7 @@ const decompressColor = (function () {
100486
101537
  };
100487
101538
  })();
100488
101539
 
100489
- const imagDataToImage = (function () {
101540
+ ((function () {
100490
101541
  const canvas = document.createElement('canvas');
100491
101542
  const context = canvas.getContext('2d');
100492
101543
  return function (imagedata) {
@@ -100495,14 +101546,14 @@ const imagDataToImage = (function () {
100495
101546
  context.putImageData(imagedata, 0, 0);
100496
101547
  return canvas.toDataURL();
100497
101548
  };
100498
- })();
101549
+ }))();
100499
101550
 
100500
101551
  function load(viewer, options, inflatedData, sceneModel) {
100501
101552
 
100502
101553
  const metadata = inflatedData.metadata;
100503
101554
  const textureData = inflatedData.textureData;
100504
101555
  const eachTextureDataPortion = inflatedData.eachTextureDataPortion;
100505
- const eachTextureDimensions = inflatedData.eachTextureDimensions;
101556
+ inflatedData.eachTextureDimensions;
100506
101557
  const positions = inflatedData.positions;
100507
101558
  const normals = inflatedData.normals;
100508
101559
  const colors = inflatedData.colors;
@@ -100563,20 +101614,13 @@ function load(viewer, options, inflatedData, sceneModel) {
100563
101614
  const textureDataPortionSize = textureDataPortionEnd - textureDataPortionStart;
100564
101615
  const textureDataPortionExists = (textureDataPortionSize > 0);
100565
101616
  if (textureDataPortionExists) {
100566
- const imageDataSubarray = textureData.subarray(textureDataPortionStart, textureDataPortionEnd);
100567
- const width = eachTextureDimensions[textureIndex * 2 + 0];
100568
- const height = eachTextureDimensions[textureIndex * 2 + 1];
100569
- const imageData = new ImageData(imageDataSubarray, width, height);
100570
-
100571
- //////////////////////////////////////////////////////////////
100572
- // TODO: This is super inefficient - replace with Basis:
100573
- //////////////////////////////////////////////////////////////
100574
101617
 
100575
- const image = imagDataToImage(imageData);
101618
+ const imageDataSubarray = new Uint8Array(textureData.subarray(textureDataPortionStart, textureDataPortionEnd));
101619
+ const arrayBuffer = imageDataSubarray.buffer;
100576
101620
 
100577
101621
  sceneModel.createTexture({
100578
101622
  id: `texture-${textureIndex}`,
100579
- src: image
101623
+ buffers: [arrayBuffer]
100580
101624
  });
100581
101625
  }
100582
101626
  }
@@ -100998,6 +102042,7 @@ parsers[ParserV10.version] = ParserV10;
100998
102042
  * * An *````.XKT````* file is a single BLOB containing a model, compressed using geometry quantization
100999
102043
  * and [pako](https://nodeca.github.io/pako/).
101000
102044
  * * Supports double-precision coordinates.
102045
+ * * Supports compressed textures.
101001
102046
  * * Set the position, scale and rotation of each model as you load it.
101002
102047
  * * Filter which IFC types get loaded.
101003
102048
  * * Configure initial default appearances for IFC types.
@@ -101116,17 +102161,26 @@ parsers[ParserV10.version] = ParserV10;
101116
102161
  * model.destroy();
101117
102162
  * ````
101118
102163
  *
101119
- * ## Loading XKT files containing KTX2 textures
102164
+ * ## Loading XKT files containing textures
101120
102165
  *
101121
- * An XKTLoaderPlugin that is configured with a {@link KTX2TextureTranscoder} will allow us to load XKT files that
101122
- * contain KTX2 textures. If we don't configure a KTX2TextureTranscoder, then the XKTLoaderPlugin will simply ignore
101123
- * the textures in the XKT.
102166
+ * XKTLoaderPlugin uses a {@link KTX2TextureTranscoder} to load textures in XKT files (XKT v10+). An XKTLoaderPlugin has its own
102167
+ * default KTX2TextureTranscoder, configured to load the Basis Codec from the CDN. If we wish, we can override that with our own
102168
+ * KTX2TextureTranscoder instance that's configured to load the Codec locally.
101124
102169
  *
101125
102170
  * In the example below, we'll create a {@link Viewer} and add an XKTLoaderPlugin
101126
- * configured with a KTX2TextureTranscoder. Then we'll use the XKTLoaderPlugin to load an
101127
- * XKT file that contains KTX2 textures, which the plugin will transcode using
102171
+ * configured with a KTX2TextureTranscoder that finds the Codec in our local file system. Then we'll use the
102172
+ * XKTLoaderPlugin to load an XKT file that contains KTX2 textures, which the plugin will transcode using
101128
102173
  * its KTX2TextureTranscoder.
101129
102174
  *
102175
+ * We'll configure our KTX2TextureTranscoder to load the Basis Codec from a local directory. If we were happy with loading the
102176
+ * Codec from our CDN (ie. our app will always have an Internet connection) then we could just leave out the
102177
+ * KTX2TextureTranscoder altogether, and let the XKTLoaderPlugin use its internal default KTX2TextureTranscoder, which is configured to
102178
+ * load the Codec from the CDN. We'll stick with loading our own Codec, in case we want to run our app without an Internet connection.
102179
+ *
102180
+ * <a href="https://xeokit.github.io/xeokit-sdk/examples/#loading_XKT_Textures_HousePlan"><img src="https://xeokit.github.io/xeokit-sdk/assets/images/xktWithTextures.png"></a>
102181
+ *
102182
+ * * [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/#loading_XKT_Textures_HousePlan)]
102183
+ *
101130
102184
  * ````javascript
101131
102185
  * const viewer = new Viewer({
101132
102186
  * canvasId: "myCanvas",
@@ -154212,1016 +155266,6 @@ class PerformanceModel extends VBOSceneModel {
154212
155266
  }
154213
155267
  }
154214
155268
 
154215
- class LoadingManager {
154216
-
154217
- constructor(onLoad, onProgress, onError) {
154218
-
154219
- this.isLoading = false;
154220
- this.itemsLoaded = 0;
154221
- this.itemsTotal = 0;
154222
- this.urlModifier = undefined;
154223
- this.handlers = [];
154224
-
154225
- this.onStart = undefined;
154226
- this.onLoad = onLoad;
154227
- this.onProgress = onProgress;
154228
- this.onError = onError;
154229
- }
154230
-
154231
- itemStart(url) {
154232
- this.itemsTotal++;
154233
- if (this.isLoading === false) {
154234
- if (this.onStart !== undefined) {
154235
- this.onStart(url, this.itemsLoaded, this.itemsTotal);
154236
- }
154237
- }
154238
- this.isLoading = true;
154239
- }
154240
-
154241
- itemEnd(url) {
154242
- this.itemsLoaded++;
154243
- if (this.onProgress !== undefined) {
154244
- this.onProgress(url, this.itemsLoaded, this.itemsTotal);
154245
- }
154246
- if (this.itemsLoaded === this.itemsTotal) {
154247
- this.isLoading = false;
154248
- if (this.onLoad !== undefined) {
154249
- this.onLoad();
154250
- }
154251
- }
154252
- }
154253
-
154254
- itemError(url) {
154255
- if (this.onError !== undefined) {
154256
- this.onError(url);
154257
- }
154258
- }
154259
-
154260
- resolveURL(url) {
154261
- if (this.urlModifier) {
154262
- return this.urlModifier(url);
154263
- }
154264
- return url;
154265
- }
154266
-
154267
- setURLModifier(transform) {
154268
- this.urlModifier = transform;
154269
- return this;
154270
- }
154271
-
154272
- addHandler(regex, loader) {
154273
- this.handlers.push(regex, loader);
154274
- return this;
154275
- }
154276
-
154277
- removeHandler(regex) {
154278
- const index = this.handlers.indexOf(regex);
154279
- if (index !== -1) {
154280
- this.handlers.splice(index, 2);
154281
- }
154282
- return this;
154283
- }
154284
-
154285
- getHandler(file) {
154286
- for (let i = 0, l = this.handlers.length; i < l; i += 2) {
154287
- const regex = this.handlers[i];
154288
- const loader = this.handlers[i + 1];
154289
- if (regex.global) regex.lastIndex = 0; // see #17920
154290
- if (regex.test(file)) {
154291
- return loader;
154292
- }
154293
- }
154294
- return null;
154295
- }
154296
- }
154297
-
154298
- const DefaultLoadingManager = new LoadingManager();
154299
-
154300
- class Loader {
154301
-
154302
- constructor(manager) {
154303
-
154304
- this.manager = (manager !== undefined) ? manager : DefaultLoadingManager;
154305
-
154306
- this.crossOrigin = 'anonymous';
154307
- this.withCredentials = false;
154308
- this.path = '';
154309
- this.resourcePath = '';
154310
- this.requestHeader = {};
154311
- }
154312
-
154313
- load( /* url, onLoad, onProgress, onError */) {
154314
- }
154315
-
154316
- loadAsync(url, onProgress) {
154317
- const scope = this;
154318
- return new Promise(function (resolve, reject) {
154319
- scope.load(url, resolve, onProgress, reject);
154320
- });
154321
- }
154322
-
154323
- parse( /* data */) {
154324
- }
154325
-
154326
- setCrossOrigin(crossOrigin) {
154327
- this.crossOrigin = crossOrigin;
154328
- return this;
154329
- }
154330
-
154331
- setWithCredentials(value) {
154332
- this.withCredentials = value;
154333
- return this;
154334
- }
154335
-
154336
- setPath(path) {
154337
- this.path = path;
154338
- return this;
154339
- }
154340
-
154341
- setResourcePath(resourcePath) {
154342
- this.resourcePath = resourcePath;
154343
- return this;
154344
- }
154345
-
154346
- setRequestHeader(requestHeader) {
154347
- this.requestHeader = requestHeader;
154348
- return this;
154349
- }
154350
- }
154351
-
154352
- /**
154353
- * @author Deepkolos / https://github.com/deepkolos
154354
- */
154355
-
154356
- class WorkerPool {
154357
-
154358
- constructor(pool = 4) {
154359
- this.pool = pool;
154360
- this.queue = [];
154361
- this.workers = [];
154362
- this.workersResolve = [];
154363
- this.workerStatus = 0;
154364
- }
154365
-
154366
- _initWorker(workerId) {
154367
- if (!this.workers[workerId]) {
154368
- const worker = this.workerCreator();
154369
- worker.addEventListener('message', this._onMessage.bind(this, workerId));
154370
- this.workers[workerId] = worker;
154371
- }
154372
- }
154373
-
154374
- _getIdleWorker() {
154375
- for (let i = 0; i < this.pool; i++)
154376
- if (!(this.workerStatus & (1 << i))) return i;
154377
- return -1;
154378
- }
154379
-
154380
- _onMessage(workerId, msg) {
154381
- const resolve = this.workersResolve[workerId];
154382
- resolve && resolve(msg);
154383
- if (this.queue.length) {
154384
- const {resolve, msg, transfer} = this.queue.shift();
154385
- this.workersResolve[workerId] = resolve;
154386
- this.workers[workerId].postMessage(msg, transfer);
154387
- } else {
154388
- this.workerStatus ^= 1 << workerId;
154389
- }
154390
- }
154391
-
154392
- setWorkerCreator(workerCreator) {
154393
- this.workerCreator = workerCreator;
154394
- }
154395
-
154396
- setWorkerLimit(pool) {
154397
- this.pool = pool;
154398
- }
154399
-
154400
- postMessage(msg, transfer) {
154401
- return new Promise((resolve) => {
154402
- const workerId = this._getIdleWorker();
154403
- if (workerId !== -1) {
154404
- this._initWorker(workerId);
154405
- this.workerStatus |= 1 << workerId;
154406
- this.workersResolve[workerId] = resolve;
154407
- this.workers[workerId].postMessage(msg, transfer);
154408
- } else {
154409
- this.queue.push({resolve, msg, transfer});
154410
- }
154411
- });
154412
- }
154413
-
154414
- destroy() {
154415
-
154416
- this.workers.forEach((worker) => worker.terminate());
154417
- this.workersResolve.length = 0;
154418
- this.workers.length = 0;
154419
- this.queue.length = 0;
154420
- this.workerStatus = 0;
154421
-
154422
- }
154423
-
154424
- }
154425
-
154426
- /**
154427
- * Transcodes texture data.
154428
- *
154429
- * A {@link SceneModel} configured with an appropriate TextureTranscoder will allow us to add textures from
154430
- * transcoded buffers or files. For a concrete example, see {@link VBOSceneModel}, which can be configured with
154431
- * a {@link KTX2TextureTranscoder}, which allows us to add textures from KTX2 buffers and files.
154432
- *
154433
- * @interface
154434
- */
154435
- class TextureTranscoder {
154436
-
154437
- /**
154438
- * Transcodes texture data from transcoded buffers into a {@link Texture2D}.
154439
- *
154440
- * @param {ArrayBuffer[]} buffers Transcoded texture data. Given as an array of buffers so that we can support
154441
- * multi-image textures, such as cube maps.
154442
- * @param {*} config Transcoding options.
154443
- * @param {Texture2D} texture The texture to load.
154444
- * @returns {Promise} Resolves when the texture has loaded.
154445
- */
154446
- transcode(buffers, texture, config = {}) {
154447
- }
154448
-
154449
- /**
154450
- * Destroys this transcoder.
154451
- */
154452
- destroy() {
154453
- }
154454
- }
154455
-
154456
- const Cache = {
154457
-
154458
- enabled: false,
154459
- files: {},
154460
-
154461
- add: function (key, file) {
154462
- if (this.enabled === false) {
154463
- return;
154464
- }
154465
- this.files[key] = file;
154466
- },
154467
-
154468
- get: function (key) {
154469
- if (this.enabled === false) {
154470
- return;
154471
- }
154472
- return this.files[key];
154473
- },
154474
-
154475
- remove: function (key) {
154476
- delete this.files[key];
154477
- },
154478
-
154479
- clear: function () {
154480
- this.files = {};
154481
- }
154482
- };
154483
-
154484
- const loading = {};
154485
-
154486
- class FileLoader extends Loader {
154487
-
154488
- constructor(manager) {
154489
- super(manager);
154490
- }
154491
-
154492
- load(url, onLoad, onProgress, onError) {
154493
- if (url === undefined) {
154494
- url = '';
154495
- }
154496
- if (this.path !== undefined) {
154497
- url = this.path + url;
154498
- }
154499
- url = this.manager.resolveURL(url);
154500
- const cached = Cache.get(url);
154501
- if (cached !== undefined) {
154502
- this.manager.itemStart(url);
154503
- setTimeout(() => {
154504
- if (onLoad) {
154505
- onLoad(cached);
154506
- }
154507
- this.manager.itemEnd(url);
154508
- }, 0);
154509
- return cached;
154510
- }
154511
- if (loading[url] !== undefined) {
154512
- loading[url].push({onLoad, onProgress, onError});
154513
- return;
154514
- }
154515
- loading[url] = [];
154516
- loading[url].push({onLoad, onProgress, onError});
154517
- const req = new Request(url, {
154518
- headers: new Headers(this.requestHeader),
154519
- credentials: this.withCredentials ? 'include' : 'same-origin'
154520
- });
154521
- const mimeType = this.mimeType;
154522
- const responseType = this.responseType;
154523
- fetch(req).then(response => {
154524
- if (response.status === 200 || response.status === 0) {
154525
- // Some browsers return HTTP Status 0 when using non-http protocol
154526
- // e.g. 'file://' or 'data://'. Handle as success.
154527
- if (response.status === 0) {
154528
- console.warn('FileLoader: HTTP Status 0 received.');
154529
- }
154530
- if (typeof ReadableStream === 'undefined' || response.body.getReader === undefined) {
154531
- return response;
154532
- }
154533
- const callbacks = loading[url];
154534
- const reader = response.body.getReader();
154535
- const contentLength = response.headers.get('Content-Length');
154536
- const total = contentLength ? parseInt(contentLength) : 0;
154537
- const lengthComputable = total !== 0;
154538
- let loaded = 0;
154539
- const stream = new ReadableStream({
154540
- start(controller) {
154541
- readData();
154542
-
154543
- function readData() {
154544
- reader.read().then(({done, value}) => {
154545
- if (done) {
154546
- controller.close();
154547
- } else {
154548
- loaded += value.byteLength;
154549
- const event = new ProgressEvent('progress', {lengthComputable, loaded, total});
154550
- for (let i = 0, il = callbacks.length; i < il; i++) {
154551
- const callback = callbacks[i];
154552
- if (callback.onProgress) {
154553
- callback.onProgress(event);
154554
- }
154555
- }
154556
- controller.enqueue(value);
154557
- readData();
154558
- }
154559
- });
154560
- }
154561
- }
154562
- });
154563
- return new Response(stream);
154564
- } else {
154565
- throw Error(`fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`);
154566
- }
154567
- }).then(response => {
154568
- switch (responseType) {
154569
- case 'arraybuffer':
154570
- return response.arrayBuffer();
154571
- case 'blob':
154572
- return response.blob();
154573
- case 'document':
154574
- return response.text()
154575
- .then(text => {
154576
- const parser = new DOMParser();
154577
- return parser.parseFromString(text, mimeType);
154578
- });
154579
- case 'json':
154580
- return response.json();
154581
- default:
154582
- if (mimeType === undefined) {
154583
- return response.text();
154584
- } else {
154585
- // sniff encoding
154586
- const re = /charset="?([^;"\s]*)"?/i;
154587
- const exec = re.exec(mimeType);
154588
- const label = exec && exec[1] ? exec[1].toLowerCase() : undefined;
154589
- const decoder = new TextDecoder(label);
154590
- return response.arrayBuffer().then(ab => decoder.decode(ab));
154591
- }
154592
- }
154593
- }).then(data => {
154594
- // Add to cache only on HTTP success, so that we do not cache
154595
- // error response bodies as proper responses to requests.
154596
- Cache.add(url, data);
154597
- const callbacks = loading[url];
154598
- delete loading[url];
154599
- for (let i = 0, il = callbacks.length; i < il; i++) {
154600
- const callback = callbacks[i];
154601
- if (callback.onLoad) {
154602
- callback.onLoad(data);
154603
- }
154604
- }
154605
- }).catch(err => {
154606
- // Abort errors and other errors are handled the same
154607
- const callbacks = loading[url];
154608
- if (callbacks === undefined) {
154609
- // When onLoad was called and url was deleted in `loading`
154610
- this.manager.itemError(url);
154611
- throw err;
154612
-
154613
- }
154614
- delete loading[url];
154615
- for (let i = 0, il = callbacks.length; i < il; i++) {
154616
- const callback = callbacks[i];
154617
- if (callback.onError) {
154618
- callback.onError(err);
154619
- }
154620
- }
154621
- this.manager.itemError(url);
154622
- }).finally(() => {
154623
- this.manager.itemEnd(url);
154624
- });
154625
- this.manager.itemStart(url);
154626
- }
154627
-
154628
- setResponseType(value) {
154629
- this.responseType = value;
154630
- return this;
154631
- }
154632
-
154633
- setMimeType(value) {
154634
- this.mimeType = value;
154635
- return this;
154636
- }
154637
- }
154638
-
154639
- const KTX2TransferSRGB = 2;
154640
- const KTX2_ALPHA_PREMULTIPLIED = 1;
154641
-
154642
- let activeTranscoders = 0;
154643
-
154644
- /**
154645
- * Transcodes texture data from KTX2.
154646
- *
154647
- * ## Overview
154648
- *
154649
- * * Uses the Basis Universal codec to transcode KTX2 texture assets.
154650
- * * An {@link XKTLoaderPlugin} that is configured with a KTX2TextureTranscoder will allow us to load KTX2 textures in
154651
- * XKT files. Textures in XKT are always KTX2. If we do not configure a KTX2TextureTranscoder, the XKTLoaderPlugin will
154652
- * simply ignore the textures in the XKT.
154653
- * * A {@link SceneModel} implementation (eg. {@link VBOSceneModel}) that is configured with a KTX2TextureTranscoder will
154654
- * allow us to load textures into it from KTX2 files or ArrayBuffers.
154655
- *
154656
- * ## What is KTX2?
154657
- *
154658
- * A KTX2 file stores GPU texture data in the Khronos Texture 2.0 (KTX2) container format. It contains image data for
154659
- * a texture asset compressed with Basis Universal (BasisU) supercompression that can be transcoded to different formats
154660
- * depending on the support provided by the target devices. KTX2 provides a lightweight format for distributing texture
154661
- * assets to GPUs. Due to BasisU compression, KTX2 files can store any image format supported by GPUs.
154662
- *
154663
- * ## Loading XKT files containing KTX2 textures
154664
- *
154665
- * An {@link XKTLoaderPlugin} that is configured with a KTX2TextureTranscoder will allow us to load XKT files that
154666
- * contain KTX2 textures. If we don't configure a KTX2TextureTranscoder, then the XKTLoaderPlugin will simply ignore
154667
- * the textures in the XKT.
154668
- *
154669
- * In the example below, we'll create a {@link Viewer} and add an {@link XKTLoaderPlugin}
154670
- * configured with a KTX2TextureTranscoder. Then we'll use the XKTLoaderPlugin to load an
154671
- * XKT file that contains KTX2 textures, which the plugin will transcode using
154672
- * its KTX2TextureTranscoder.
154673
- *
154674
- * ````javascript
154675
- * const viewer = new Viewer({
154676
- * canvasId: "myCanvas",
154677
- * transparent: true
154678
- * });
154679
- *
154680
- * viewer.camera.eye = [-2.56, 8.38, 8.27];
154681
- * viewer.camera.look = [13.44, 3.31, -14.83];
154682
- * viewer.camera.up = [0.10, 0.98, -0.14];
154683
- *
154684
- * const textureTranscoder = new KTX2TextureTranscoder({
154685
- * viewer,
154686
- * transcoderPath: "./../dist/basis/" // <------ Path to Basis Universal transcoder
154687
- * });
154688
- *
154689
- * const xktLoader = new XKTLoaderPlugin(viewer, {
154690
- * textureTranscoder // <<------------- Transcodes KTX2 textures in XKT files
154691
- * });
154692
- *
154693
- * const sceneModel = xktLoader.load({
154694
- * id: "myModel",
154695
- * src: "./HousePlan.xkt" // <<------ XKT file with KTX2 textures
154696
- * });
154697
- * ````
154698
- *
154699
- * ## Loading KTX2 files into a VBOSceneModel
154700
- *
154701
- * A {@link SceneModel} that is configured with a KTX2TextureTranscoder will
154702
- * allow us to load textures into it from KTX2-transcoded buffers or files.
154703
- *
154704
- * In the example below, we'll create a {@link Viewer}, containing a {@link VBOSceneModel} configured with a
154705
- * KTX2TextureTranscoder. We'll then programmatically create a simple object within the VBOSceneModel, consisting of
154706
- * a single mesh with a texture loaded from a KTX2 file, which our VBOSceneModel internally transcodes, using
154707
- * its KTX2TextureTranscoder. Note how we configure our KTX2TextureTranscoder with a path to the Basis Universal
154708
- * transcoder WASM module.
154709
- *
154710
- * ````javascript
154711
- * const viewer = new Viewer({
154712
- * canvasId: "myCanvas",
154713
- * transparent: true
154714
- * });
154715
- *
154716
- * viewer.scene.camera.eye = [-21.80, 4.01, 6.56];
154717
- * viewer.scene.camera.look = [0, -5.75, 0];
154718
- * viewer.scene.camera.up = [0.37, 0.91, -0.11];
154719
- *
154720
- * const textureTranscoder = new KTX2TextureTranscoder({
154721
- * viewer,
154722
- * transcoderPath: "./../dist/basis/" // <------ Path to BasisU transcoder module
154723
- * });
154724
- *
154725
- * const vboSceneModel = new VBOSceneModel(viewer.scene, {
154726
- * id: "myModel",
154727
- * textureTranscoder // <<-------------------- Configure model with our transcoder
154728
- * });
154729
- *
154730
- * vboSceneModel.createTexture({
154731
- * id: "myColorTexture",
154732
- * src: "../assets/textures/compressed/sample_uastc_zstd.ktx2" // <<----- KTX2 texture asset
154733
- * });
154734
- *
154735
- * vboSceneModel.createTexture({
154736
- * id: "myMetallicRoughnessTexture",
154737
- * src: "../assets/textures/alpha/crosshatchAlphaMap.jpg" // <<----- JPEG texture asset
154738
- * });
154739
- *
154740
- * vboSceneModel.createTextureSet({
154741
- * id: "myTextureSet",
154742
- * colorTextureId: "myColorTexture",
154743
- * metallicRoughnessTextureId: "myMetallicRoughnessTexture"
154744
- * });
154745
- *
154746
- * vboSceneModel.createMesh({
154747
- * id: "myMesh",
154748
- * textureSetId: "myTextureSet",
154749
- * primitive: "triangles",
154750
- * positions: [1, 1, 1, ...],
154751
- * normals: [0, 0, 1, 0, ...],
154752
- * uv: [1, 0, 0, ...],
154753
- * indices: [0, 1, 2, ...],
154754
- * });
154755
- *
154756
- * vboSceneModel.createEntity({
154757
- * id: "myEntity",
154758
- * meshIds: ["myMesh"]
154759
- * });
154760
- *
154761
- * vboSceneModel.finalize();
154762
- * ````
154763
- *
154764
- * ## Loading KTX2 ArrayBuffers into a VBOSceneModel
154765
- *
154766
- * A {@link SceneModel} that is configured with a KTX2TextureTranscoder will allow us to load textures into
154767
- * it from KTX2 ArrayBuffers.
154768
- *
154769
- * In the example below, we'll create a {@link Viewer}, containing a {@link VBOSceneModel} configured with a
154770
- * KTX2TextureTranscoder. We'll then programmatically create a simple object within the VBOSceneModel, consisting of
154771
- * a single mesh with a texture loaded from a KTX2 ArrayBuffer, which our VBOSceneModel internally transcodes, using
154772
- * its KTX2TextureTranscoder.
154773
- *
154774
- * ````javascript
154775
- * const viewer = new Viewer({
154776
- * canvasId: "myCanvas",
154777
- * transparent: true
154778
- * });
154779
- *
154780
- * viewer.scene.camera.eye = [-21.80, 4.01, 6.56];
154781
- * viewer.scene.camera.look = [0, -5.75, 0];
154782
- * viewer.scene.camera.up = [0.37, 0.91, -0.11];
154783
- *
154784
- * const textureTranscoder = new KTX2TextureTranscoder({
154785
- * viewer,
154786
- * transcoderPath: "./../dist/basis/" // <------ Path to BasisU transcoder module
154787
- * });
154788
- *
154789
- * const vboSceneModel = new VBOSceneModel(viewer.scene, {
154790
- * id: "myModel",
154791
- * textureTranscoder // <<-------------------- Configure model with our transcoder
154792
- * });
154793
- *
154794
- * utils.loadArraybuffer("../assets/textures/compressed/sample_uastc_zstd.ktx2",(arrayBuffer) => {
154795
- *
154796
- * vboSceneModel.createTexture({
154797
- * id: "myColorTexture",
154798
- * buffers: [arrayBuffer] // <<----- KTX2 texture asset
154799
- * });
154800
- *
154801
- * vboSceneModel.createTexture({
154802
- * id: "myMetallicRoughnessTexture",
154803
- * src: "../assets/textures/alpha/crosshatchAlphaMap.jpg" // <<----- JPEG texture asset
154804
- * });
154805
- *
154806
- * vboSceneModel.createTextureSet({
154807
- * id: "myTextureSet",
154808
- * colorTextureId: "myColorTexture",
154809
- * metallicRoughnessTextureId: "myMetallicRoughnessTexture"
154810
- * });
154811
- *
154812
- * vboSceneModel.createMesh({
154813
- * id: "myMesh",
154814
- * textureSetId: "myTextureSet",
154815
- * primitive: "triangles",
154816
- * positions: [1, 1, 1, ...],
154817
- * normals: [0, 0, 1, 0, ...],
154818
- * uv: [1, 0, 0, ...],
154819
- * indices: [0, 1, 2, ...],
154820
- * });
154821
- *
154822
- * vboSceneModel.createEntity({
154823
- * id: "myEntity",
154824
- * meshIds: ["myMesh"]
154825
- * });
154826
- *
154827
- * vboSceneModel.finalize();
154828
- * });
154829
- * ````
154830
- *
154831
- * @implements {TextureTranscoder}
154832
- */
154833
- class KTX2TextureTranscoder {
154834
-
154835
- /**
154836
- * Creates a new KTX2TextureTranscoder.
154837
- *
154838
- * @param {Viewer} viewer The Viewer that our KTX2TextureTranscoder will be used with. This KTX2TextureTranscoder
154839
- * must only be used to transcode textures for this Viewer. This is because the Viewer's capabilities will decide
154840
- * what target GPU formats this KTX2TextureTranscoder will transcode to.
154841
- * @param {String} transcoderPath Path to the Basis transcoder module that internally does the heavy lifting for our KTX2TextureTranscoder.
154842
- * @param {Number} [workerLimit] The maximum number of Workers to use for transcoding.
154843
- */
154844
- constructor({viewer, transcoderPath, workerLimit}) {
154845
-
154846
- this._transcoderPath = transcoderPath;
154847
- this._transcoderBinary = null;
154848
- this._transcoderPending = null;
154849
- this._workerPool = new WorkerPool();
154850
- this._workerSourceURL = '';
154851
-
154852
- if (workerLimit) {
154853
- this._workerPool.setWorkerLimit(workerLimit);
154854
- }
154855
-
154856
- const viewerCapabilities = viewer.capabilities;
154857
-
154858
- this._workerConfig = {
154859
- astcSupported: viewerCapabilities.astcSupported,
154860
- etc1Supported: viewerCapabilities.etc1Supported,
154861
- etc2Supported: viewerCapabilities.etc2Supported,
154862
- dxtSupported: viewerCapabilities.dxtSupported,
154863
- bptcSupported: viewerCapabilities.bptcSupported,
154864
- pvrtcSupported: viewerCapabilities.pvrtcSupported
154865
- };
154866
-
154867
- this._supportedFileTypes = ["xkt2"];
154868
- }
154869
-
154870
- _init() {
154871
- if (!this._transcoderPending) {
154872
- const jsLoader = new FileLoader();
154873
- jsLoader.setPath(this._transcoderPath);
154874
- jsLoader.setWithCredentials(this.withCredentials);
154875
- const jsContent = jsLoader.loadAsync('basis_transcoder.js');
154876
- const binaryLoader = new FileLoader();
154877
- binaryLoader.setPath(this._transcoderPath);
154878
- binaryLoader.setResponseType('arraybuffer');
154879
- binaryLoader.setWithCredentials(this.withCredentials);
154880
- const binaryContent = binaryLoader.loadAsync('basis_transcoder.wasm');
154881
- this._transcoderPending = Promise.all([jsContent, binaryContent])
154882
- .then(([jsContent, binaryContent]) => {
154883
- const fn = KTX2TextureTranscoder.BasisWorker.toString();
154884
- const body = [
154885
- '/* constants */',
154886
- 'let _EngineFormat = ' + JSON.stringify(KTX2TextureTranscoder.EngineFormat),
154887
- 'let _TranscoderFormat = ' + JSON.stringify(KTX2TextureTranscoder.TranscoderFormat),
154888
- 'let _BasisFormat = ' + JSON.stringify(KTX2TextureTranscoder.BasisFormat),
154889
- '/* basis_transcoder.js */',
154890
- jsContent,
154891
- '/* worker */',
154892
- fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}'))
154893
- ].join('\n');
154894
- this._workerSourceURL = URL.createObjectURL(new Blob([body]));
154895
- this._transcoderBinary = binaryContent;
154896
- this._workerPool.setWorkerCreator(() => {
154897
- const worker = new Worker(this._workerSourceURL);
154898
- const transcoderBinary = this._transcoderBinary.slice(0);
154899
- worker.postMessage({
154900
- type: 'init',
154901
- config: this._workerConfig,
154902
- transcoderBinary
154903
- }, [transcoderBinary]);
154904
- return worker;
154905
- });
154906
- });
154907
- if (activeTranscoders > 0) {
154908
- console.warn('KTX2TextureTranscoder: Multiple active KTX2TextureTranscoder may cause performance issues.' + ' Use a single KTX2TextureTranscoder instance, or call .dispose() on old instances.');
154909
- }
154910
- activeTranscoders++;
154911
- }
154912
- return this._transcoderPending;
154913
- }
154914
-
154915
- /**
154916
- * Transcodes texture data from transcoded buffers into a {@link Texture2D}.
154917
- *
154918
- * @param {ArrayBuffer[]} buffers Transcoded texture data. Given as an array of buffers so that we can support multi-image textures, such as cube maps.
154919
- * @param {*} config Transcoding options.
154920
- * @param {Texture2D} texture The texture to load.
154921
- * @returns {Promise} Resolves when the texture has loaded.
154922
- */
154923
- transcode(buffers, texture, config = {}) {
154924
- return new Promise((resolve, reject) => {
154925
- const taskConfig = config;
154926
- this._init().then(() => {
154927
- return this._workerPool.postMessage({
154928
- type: 'transcode',
154929
- buffers,
154930
- taskConfig: taskConfig
154931
- }, buffers);
154932
- }).then((e) => {
154933
- const transcodeResult = e.data;
154934
- const {mipmaps, width, height, format, type, error, dfdTransferFn, dfdFlags} = transcodeResult;
154935
- if (type === 'error') {
154936
- return reject(error);
154937
- }
154938
- texture.setCompressedData({
154939
- mipmaps,
154940
- props: {
154941
- format: format,
154942
- minFilter: mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter,
154943
- magFilter: mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter,
154944
- encoding: dfdTransferFn === KTX2TransferSRGB ? sRGBEncoding : LinearEncoding,
154945
- premultiplyAlpha : !!(dfdFlags & KTX2_ALPHA_PREMULTIPLIED)
154946
- }
154947
- });
154948
- resolve();
154949
- });
154950
- });
154951
- }
154952
-
154953
- /**
154954
- * Destroys this KTX2TextureTranscoder
154955
- */
154956
- destroy() {
154957
- URL.revokeObjectURL(this._workerSourceURL);
154958
- this._workerPool.destroy();
154959
- activeTranscoders--;
154960
- }
154961
- }
154962
-
154963
- /**
154964
- * @private
154965
- */
154966
- KTX2TextureTranscoder.BasisFormat = {
154967
- ETC1S: 0,
154968
- UASTC_4x4: 1
154969
- };
154970
-
154971
- /**
154972
- * @private
154973
- */
154974
- KTX2TextureTranscoder.TranscoderFormat = {
154975
- ETC1: 0,
154976
- ETC2: 1,
154977
- BC1: 2,
154978
- BC3: 3,
154979
- BC4: 4,
154980
- BC5: 5,
154981
- BC7_M6_OPAQUE_ONLY: 6,
154982
- BC7_M5: 7,
154983
- PVRTC1_4_RGB: 8,
154984
- PVRTC1_4_RGBA: 9,
154985
- ASTC_4x4: 10,
154986
- ATC_RGB: 11,
154987
- ATC_RGBA_INTERPOLATED_ALPHA: 12,
154988
- RGBA32: 13,
154989
- RGB565: 14,
154990
- BGR565: 15,
154991
- RGBA4444: 16
154992
- };
154993
-
154994
- /**
154995
- * @private
154996
- */
154997
- KTX2TextureTranscoder.EngineFormat = {
154998
- RGBAFormat: RGBAFormat,
154999
- RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format,
155000
- RGBA_BPTC_Format: RGBA_BPTC_Format,
155001
- RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format,
155002
- RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format,
155003
- RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format,
155004
- RGB_ETC1_Format: RGB_ETC1_Format,
155005
- RGB_ETC2_Format: RGB_ETC2_Format,
155006
- RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format,
155007
- RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format
155008
- };
155009
-
155010
- /* WEB WORKER */
155011
-
155012
- /**
155013
- * @private
155014
- * @constructor
155015
- */
155016
- KTX2TextureTranscoder.BasisWorker = function () {
155017
-
155018
- let config;
155019
- let transcoderPending;
155020
- let BasisModule;
155021
-
155022
- const EngineFormat = _EngineFormat; // eslint-disable-line no-undef
155023
- const TranscoderFormat = _TranscoderFormat; // eslint-disable-line no-undef
155024
- const BasisFormat = _BasisFormat; // eslint-disable-line no-undef
155025
-
155026
- self.addEventListener('message', function (e) {
155027
- const message = e.data;
155028
- switch (message.type) {
155029
- case 'init':
155030
- config = message.config;
155031
- init(message.transcoderBinary);
155032
- break;
155033
- case 'transcode':
155034
- transcoderPending.then(() => {
155035
- try {
155036
- const {
155037
- width,
155038
- height,
155039
- hasAlpha,
155040
- mipmaps,
155041
- format,
155042
- dfdTransferFn,
155043
- dfdFlags
155044
- } = transcode(message.buffers[0]);
155045
- const buffers = [];
155046
- for (let i = 0; i < mipmaps.length; ++i) {
155047
- buffers.push(mipmaps[i].data.buffer);
155048
- }
155049
- self.postMessage({
155050
- type: 'transcode',
155051
- id: message.id,
155052
- width,
155053
- height,
155054
- hasAlpha,
155055
- mipmaps,
155056
- format,
155057
- dfdTransferFn,
155058
- dfdFlags
155059
- }, buffers);
155060
- } catch (error) {
155061
- console.error(`[KTX2TextureTranscoder.BasisWorker]: ${error}`);
155062
- self.postMessage({type: 'error', id: message.id, error: error.message});
155063
- }
155064
- });
155065
- break;
155066
- }
155067
- });
155068
-
155069
- function init(wasmBinary) {
155070
- transcoderPending = new Promise(resolve => {
155071
- BasisModule = {
155072
- wasmBinary,
155073
- onRuntimeInitialized: resolve
155074
- };
155075
- BASIS(BasisModule); // eslint-disable-line no-undef
155076
- }).then(() => {
155077
- BasisModule.initializeBasis();
155078
- if (BasisModule.KTX2File === undefined) {
155079
- console.warn('KTX2TextureTranscoder: Please update Basis Universal transcoder.');
155080
- }
155081
- });
155082
- }
155083
-
155084
- function transcode(buffer) {
155085
- const ktx2File = new BasisModule.KTX2File(new Uint8Array(buffer));
155086
-
155087
- function cleanup() {
155088
- ktx2File.close();
155089
- ktx2File.delete();
155090
- }
155091
-
155092
- if (!ktx2File.isValid()) {
155093
- cleanup();
155094
- throw new Error('KTX2TextureTranscoder: Invalid or unsupported .ktx2 file');
155095
- }
155096
- const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
155097
- const width = ktx2File.getWidth();
155098
- const height = ktx2File.getHeight();
155099
- const levels = ktx2File.getLevels();
155100
- const hasAlpha = ktx2File.getHasAlpha();
155101
- const dfdTransferFn = ktx2File.getDFDTransferFunc();
155102
- const dfdFlags = ktx2File.getDFDFlags();
155103
- const {transcoderFormat, engineFormat} = getTranscoderFormat(basisFormat, width, height, hasAlpha);
155104
- if (!width || !height || !levels) {
155105
- cleanup();
155106
- throw new Error('KTX2TextureTranscoder: Invalid texture');
155107
- }
155108
- if (!ktx2File.startTranscoding()) {
155109
- cleanup();
155110
- throw new Error('KTX2TextureTranscoder: .startTranscoding failed');
155111
- }
155112
- const mipmaps = [];
155113
- for (let mip = 0; mip < levels; mip++) {
155114
- const levelInfo = ktx2File.getImageLevelInfo(mip, 0, 0);
155115
- const mipWidth = levelInfo.origWidth;
155116
- const mipHeight = levelInfo.origHeight;
155117
- const dst = new Uint8Array(ktx2File.getImageTranscodedSizeInBytes(mip, 0, 0, transcoderFormat));
155118
- const status = ktx2File.transcodeImage(dst, mip, 0, 0, transcoderFormat, 0, -1, -1);
155119
- if (!status) {
155120
- cleanup();
155121
- throw new Error('KTX2TextureTranscoder: .transcodeImage failed.');
155122
- }
155123
- mipmaps.push({data: dst, width: mipWidth, height: mipHeight});
155124
- }
155125
- cleanup();
155126
- return {width, height, hasAlpha, mipmaps, format: engineFormat, dfdTransferFn, dfdFlags};
155127
- }
155128
-
155129
- // Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC),
155130
- // device capabilities, and texture dimensions. The list below ranks the formats separately
155131
- // for ETC1S and UASTC.
155132
- //
155133
- // In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at
155134
- // significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently
155135
- // chooses RGBA32 only as a last resort and does not expose that option to the caller.
155136
-
155137
- const FORMAT_OPTIONS = [{
155138
- if: 'astcSupported',
155139
- basisFormat: [BasisFormat.UASTC_4x4],
155140
- transcoderFormat: [TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4],
155141
- engineFormat: [EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format],
155142
- priorityETC1S: Infinity,
155143
- priorityUASTC: 1,
155144
- needsPowerOfTwo: false
155145
- }, {
155146
- if: 'bptcSupported',
155147
- basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
155148
- transcoderFormat: [TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5],
155149
- engineFormat: [EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format],
155150
- priorityETC1S: 3,
155151
- priorityUASTC: 2,
155152
- needsPowerOfTwo: false
155153
- }, {
155154
- if: 'dxtSupported',
155155
- basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
155156
- transcoderFormat: [TranscoderFormat.BC1, TranscoderFormat.BC3],
155157
- engineFormat: [EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format],
155158
- priorityETC1S: 4,
155159
- priorityUASTC: 5,
155160
- needsPowerOfTwo: false
155161
- }, {
155162
- if: 'etc2Supported',
155163
- basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
155164
- transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC2],
155165
- engineFormat: [EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format],
155166
- priorityETC1S: 1,
155167
- priorityUASTC: 3,
155168
- needsPowerOfTwo: false
155169
- }, {
155170
- if: 'etc1Supported',
155171
- basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
155172
- transcoderFormat: [TranscoderFormat.ETC1],
155173
- engineFormat: [EngineFormat.RGB_ETC1_Format],
155174
- priorityETC1S: 2,
155175
- priorityUASTC: 4,
155176
- needsPowerOfTwo: false
155177
- }, {
155178
- if: 'pvrtcSupported',
155179
- basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
155180
- transcoderFormat: [TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA],
155181
- engineFormat: [EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format],
155182
- priorityETC1S: 5,
155183
- priorityUASTC: 6,
155184
- needsPowerOfTwo: true
155185
- }];
155186
- const ETC1S_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) {
155187
- return a.priorityETC1S - b.priorityETC1S;
155188
- });
155189
- const UASTC_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) {
155190
- return a.priorityUASTC - b.priorityUASTC;
155191
- });
155192
-
155193
- function getTranscoderFormat(basisFormat, width, height, hasAlpha) {
155194
- let transcoderFormat;
155195
- let engineFormat;
155196
- const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS;
155197
- for (let i = 0; i < options.length; i++) {
155198
- const opt = options[i];
155199
- if (!config[opt.if]) continue;
155200
- if (!opt.basisFormat.includes(basisFormat)) continue;
155201
- if (hasAlpha && opt.transcoderFormat.length < 2) continue;
155202
- if (opt.needsPowerOfTwo && !(isPowerOfTwo(width) && isPowerOfTwo(height))) continue;
155203
- transcoderFormat = opt.transcoderFormat[hasAlpha ? 1 : 0];
155204
- engineFormat = opt.engineFormat[hasAlpha ? 1 : 0];
155205
- return {
155206
- transcoderFormat,
155207
- engineFormat
155208
- };
155209
- }
155210
- console.warn('KTX2TextureTranscoder: No suitable compressed texture format found. Decoding to RGBA32.');
155211
- transcoderFormat = TranscoderFormat.RGBA32;
155212
- engineFormat = EngineFormat.RGBAFormat;
155213
- return {
155214
- transcoderFormat,
155215
- engineFormat
155216
- };
155217
- }
155218
-
155219
- function isPowerOfTwo(value) {
155220
- if (value <= 2) return true;
155221
- return (value & value - 1) === 0 && value !== 0;
155222
- }
155223
- };
155224
-
155225
155269
  const screenPos = math.vec4();
155226
155270
  const viewPos = math.vec4();
155227
155271
 
@@ -161998,11 +162042,9 @@ class LASLoaderPlugin extends Plugin {
161998
162042
  try {
161999
162043
  parse$3(arrayBuffer, LASLoader, options).then((parsedData) => {
162000
162044
 
162001
- const loaderData = parsedData.loaderData;
162002
- const loaderDataHeader = loaderData.header;
162003
- const pointsFormatId = loaderDataHeader.pointsFormatId;
162004
-
162005
162045
  const attributes = parsedData.attributes;
162046
+ const loaderData = parsedData.loaderData;
162047
+ const pointsFormatId = loaderData.pointsFormatId !== undefined ? loaderData.pointsFormatId : -1;
162006
162048
 
162007
162049
  if (!attributes.POSITION) {
162008
162050
  sceneModel.finalize();
@@ -163333,7 +163375,7 @@ class CityJSONLoaderPlugin extends Plugin {
163333
163375
 
163334
163376
  _parseGeometrySurfacesWithSharedMaterial(ctx, geometry, objectMaterial, meshIds) {
163335
163377
 
163336
- ctx.performanceModel;
163378
+ const sceneModel = ctx.sceneModel;
163337
163379
  const sharedIndices = [];
163338
163380
  const geometryCfg = {
163339
163381
  positions: [],
@@ -163379,7 +163421,7 @@ class CityJSONLoaderPlugin extends Plugin {
163379
163421
 
163380
163422
  const meshId = "" + ctx.nextId++;
163381
163423
 
163382
- performanceModel.createMesh({
163424
+ sceneModel.createMesh({
163383
163425
  id: meshId,
163384
163426
  primitive: "triangles",
163385
163427
  positions: geometryCfg.positions,
@@ -163599,6 +163641,7 @@ exports.NearestFilter = NearestFilter;
163599
163641
  exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter;
163600
163642
  exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter;
163601
163643
  exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter;
163644
+ exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter;
163602
163645
  exports.Node = Node$1;
163603
163646
  exports.OBJLoaderPlugin = OBJLoaderPlugin;
163604
163647
  exports.ObjectsMemento = ObjectsMemento;
@@ -163672,7 +163715,7 @@ exports.VBOSceneModel = VBOSceneModel;
163672
163715
  exports.ViewCullPlugin = ViewCullPlugin;
163673
163716
  exports.Viewer = Viewer;
163674
163717
  exports.WebIFCLoaderPlugin = WebIFCLoaderPlugin;
163675
- exports.WorkerPool = WorkerPool;
163718
+ exports.WorkerPool = WorkerPool$1;
163676
163719
  exports.XKTDefaultDataSource = XKTDefaultDataSource;
163677
163720
  exports.XKTLoaderPlugin = XKTLoaderPlugin;
163678
163721
  exports.XML3DLoaderPlugin = XML3DLoaderPlugin;
@@ -163685,6 +163728,7 @@ exports.buildPlaneGeometry = buildPlaneGeometry;
163685
163728
  exports.buildSphereGeometry = buildSphereGeometry;
163686
163729
  exports.buildTorusGeometry = buildTorusGeometry;
163687
163730
  exports.buildVectorTextGeometry = buildVectorTextGeometry;
163731
+ exports.getKTX2TextureTranscoder = getKTX2TextureTranscoder;
163688
163732
  exports.load3DSGeometry = load3DSGeometry;
163689
163733
  exports.loadOBJGeometry = loadOBJGeometry;
163690
163734
  exports.math = math;