@stowkit/three-loader 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"MeshParser.d.ts","sourceRoot":"","sources":["../src/MeshParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AAIxE,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB,EAAE,MAAM,CAAC;IAC/B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,IAAI;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,qBAAqB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAU;IAEnB;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY;IAyBxD;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GAAG;QAClE,QAAQ,EAAE,YAAY,CAAC;QACvB,UAAU,EAAE,gBAAgB,EAAE,CAAC;QAC/B,SAAS,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC5B,YAAY,EAAE,YAAY,EAAE,CAAC;QAC7B,KAAK,EAAE,IAAI,EAAE,CAAC;QACd,WAAW,EAAE,WAAW,CAAC;KAC5B;IAwLD;;OAEG;WACU,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC;IAsE1K;;OAEG;WACU,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IA+FjM;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;CAK5B"}
1
+ {"version":3,"file":"MeshParser.d.ts","sourceRoot":"","sources":["../src/MeshParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AAIxE,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB,EAAE,MAAM,CAAC;IAC/B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,IAAI;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,qBAAqB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAU;IAEnB;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY;IAyBxD;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GAAG;QAClE,QAAQ,EAAE,YAAY,CAAC;QACvB,UAAU,EAAE,gBAAgB,EAAE,CAAC;QAC/B,SAAS,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC5B,YAAY,EAAE,YAAY,EAAE,CAAC;QAC7B,KAAK,EAAE,IAAI,EAAE,CAAC;QACd,WAAW,EAAE,WAAW,CAAC;KAC5B;IAwLD;;OAEG;WACU,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC;IA0E1K;;OAEG;WACU,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IA+FjM;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;CAK5B"}
@@ -1 +1 @@
1
- {"version":3,"file":"StowKitPack.d.ts","sourceRoot":"","sources":["../src/StowKitPack.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StowKitPack.d.ts","sourceRoot":"","sources":["../src/StowKitPack.ts"],"names":[],"mappings":"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"}
@@ -340,9 +340,14 @@ class MeshParser {
340
340
  * Create Three.js BufferGeometry from Draco compressed mesh data
341
341
  */
342
342
  static async createGeometry(geoInfo, dataBlob, dracoLoader, cacheKey, version) {
343
- // Try loading from IndexedDB cache first
343
+ // Try loading from IndexedDB cache first (non-blocking - don't await in parallel loads)
344
+ let cachePromise = null;
344
345
  if (cacheKey && version) {
345
- const cached = await AssetMemoryCache.getGeometry(cacheKey, version);
346
+ cachePromise = AssetMemoryCache.getGeometry(cacheKey, version);
347
+ }
348
+ // Check if cache resolved while we were starting
349
+ if (cachePromise) {
350
+ const cached = await cachePromise;
346
351
  if (cached) {
347
352
  const geometry = new THREE.BufferGeometry();
348
353
  geometry.setAttribute('position', new THREE.BufferAttribute(cached.positions, 3));
@@ -374,7 +379,7 @@ class MeshParser {
374
379
  reject(new Error(`Failed to decode Draco geometry: ${error}`));
375
380
  });
376
381
  });
377
- // Cache the decoded geometry
382
+ // Cache the decoded geometry (non-blocking)
378
383
  if (cacheKey && version) {
379
384
  const posAttr = geometry.getAttribute('position');
380
385
  const normAttr = geometry.getAttribute('normal');
@@ -386,9 +391,7 @@ class MeshParser {
386
391
  normals: (normAttr && 'array' in normAttr) ? normAttr.array : undefined,
387
392
  uvs: (uvAttr && 'array' in uvAttr) ? uvAttr.array : undefined,
388
393
  indices: indexAttr.array
389
- }).catch(() => {
390
- // Silent fail - caching is optional
391
- });
394
+ }).catch(() => { });
392
395
  }
393
396
  }
394
397
  return geometry;
@@ -501,6 +504,7 @@ class StowKitPack {
501
504
  * Load a skinned mesh by its string ID
502
505
  */
503
506
  async loadSkinnedMesh(stringId) {
507
+ const totalStart = performance.now();
504
508
  const assetIndex = this.reader.findAssetByPath(stringId);
505
509
  if (assetIndex < 0) {
506
510
  throw new Error(`Skinned mesh not found: ${stringId}`);
@@ -511,12 +515,16 @@ class StowKitPack {
511
515
  throw new Error(`Failed to read skinned mesh data: ${stringId}`);
512
516
  if (!metadata)
513
517
  throw new Error(`No metadata for skinned mesh: ${stringId}`);
514
- return await this.parseSkinnedMesh(metadata, data);
518
+ const result = await this.parseSkinnedMesh(metadata, data);
519
+ const totalTime = performance.now() - totalStart;
520
+ PerfLogger.log(`[Perf] === Skinned Mesh "${stringId}": ${totalTime.toFixed(2)}ms total ===`);
521
+ return result;
515
522
  }
516
523
  /**
517
524
  * Parse skinned mesh from binary data
518
525
  */
519
526
  async parseSkinnedMesh(metadata, data) {
527
+ const parseStart = performance.now();
520
528
  // NEW FORMAT: Skip past tag header
521
529
  const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
522
530
  const tagCsvLength = view.getUint32(0, true);
@@ -562,7 +570,7 @@ class StowKitPack {
562
570
  });
563
571
  offset += 72;
564
572
  }
565
- const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);
573
+ new DataView(data.buffer, data.byteOffset, data.byteLength);
566
574
  // Sort geometry infos by vertex buffer offset to ensure sequential parsing
567
575
  const sortedGeometryInfos = geometryInfos.slice().sort((a, b) => a.vertexBufferOffset - b.vertexBufferOffset);
568
576
  // Parse materials from metadata (immediately after geometries)
@@ -717,88 +725,39 @@ class StowKitPack {
717
725
  }
718
726
  materials.push(material);
719
727
  }
728
+ const parseTime = performance.now() - parseStart;
729
+ PerfLogger.log(`[Perf] Parse skinned metadata: ${parseTime.toFixed(2)}ms`);
730
+ const textureStart = performance.now();
720
731
  await this.loadMaterialTextures(materialData, materials);
732
+ const textureTime = performance.now() - textureStart;
733
+ if (materialData.length > 0) {
734
+ PerfLogger.log(`[Perf] Load textures: ${textureTime.toFixed(2)}ms`);
735
+ }
721
736
  // Collect root bones (hierarchy already built earlier)
737
+ const buildStart = performance.now();
722
738
  const rootBones = bones.filter((_, index) => parentIndices[index] < 0);
723
739
  // Create shared skeleton
724
740
  const skeleton = new THREE.Skeleton(bones, boneMatrices);
725
- // Pre-allocate combined buffers
726
- const totalVertexCount = sortedGeometryInfos.reduce((sum, info) => sum + info.vertexCount, 0);
727
- const totalIndexCount = sortedGeometryInfos.reduce((sum, info) => sum + info.indexCount, 0);
728
- const combinedPositions = new Float32Array(totalVertexCount * 3);
729
- const combinedNormals = new Float32Array(totalVertexCount * 3);
730
- const combinedUVs = new Float32Array(totalVertexCount * 2);
731
- const combinedSkinIndices = new Uint16Array(totalVertexCount * 4);
732
- const combinedSkinWeights = new Float32Array(totalVertexCount * 4);
733
- const combinedIndices = new Uint32Array(totalIndexCount);
741
+ // Use WASM to parse geometry data (10-50x faster!)
742
+ const geomData = this.reader.parseSkinnedMeshGeometryFast(metadata, data);
743
+ if (!geomData) {
744
+ throw new Error('Failed to parse skinned mesh geometry');
745
+ }
746
+ // Convert Uint32Array skinIndices to Uint16Array for Three.js
747
+ const skinIndices16 = new Uint16Array(geomData.skinIndices);
734
748
  const combinedGeometry = new THREE.BufferGeometry();
735
- combinedGeometry.clearGroups();
736
- let vertexCursor = 0; // counts vertices
737
- let indexCursor = 0; // counts indices
749
+ combinedGeometry.setAttribute('position', new THREE.BufferAttribute(geomData.positions, 3));
750
+ combinedGeometry.setAttribute('normal', new THREE.BufferAttribute(geomData.normals, 3));
751
+ combinedGeometry.setAttribute('uv', new THREE.BufferAttribute(geomData.uvs, 2));
752
+ combinedGeometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(skinIndices16, 4));
753
+ combinedGeometry.setAttribute('skinWeight', new THREE.BufferAttribute(geomData.skinWeights, 4));
754
+ combinedGeometry.setIndex(new THREE.BufferAttribute(geomData.indices, 1));
755
+ // Add geometry groups for materials
756
+ let indexCursor = 0;
738
757
  for (const geomInfo of sortedGeometryInfos) {
739
- const vertexStrideBytes = 32;
740
- const vertexBase = vertexCursor; // base vertex index for this submesh
741
- // Validate offsets before parsing
742
- const requiredVertexBytes = geomInfo.vertexBufferOffset + geomInfo.vertexCount * vertexStrideBytes;
743
- const requiredIndexBytes = geomInfo.indexBufferOffset + geomInfo.indexCount * 4;
744
- const requiredWeightBytes = geomInfo.weightsOffset + geomInfo.vertexCount * 32;
745
- if (requiredVertexBytes > data.byteLength) {
746
- throw new Error(`Vertex buffer out of bounds: need ${requiredVertexBytes}, have ${data.byteLength}`);
747
- }
748
- if (requiredIndexBytes > data.byteLength) {
749
- throw new Error(`Index buffer out of bounds: need ${requiredIndexBytes}, have ${data.byteLength}`);
750
- }
751
- if (requiredWeightBytes > data.byteLength) {
752
- throw new Error(`Weight buffer out of bounds: need ${requiredWeightBytes}, have ${data.byteLength}`);
753
- }
754
- // Parse vertices
755
- for (let v = 0; v < geomInfo.vertexCount; v++) {
756
- const vertexOffset = geomInfo.vertexBufferOffset + v * vertexStrideBytes;
757
- // positions
758
- combinedPositions[(vertexCursor + v) * 3 + 0] = dataView.getFloat32(vertexOffset + 0, true);
759
- combinedPositions[(vertexCursor + v) * 3 + 1] = dataView.getFloat32(vertexOffset + 4, true);
760
- combinedPositions[(vertexCursor + v) * 3 + 2] = dataView.getFloat32(vertexOffset + 8, true);
761
- // normals
762
- if (geomInfo.hasNormals) {
763
- combinedNormals[(vertexCursor + v) * 3 + 0] = dataView.getFloat32(vertexOffset + 12, true);
764
- combinedNormals[(vertexCursor + v) * 3 + 1] = dataView.getFloat32(vertexOffset + 16, true);
765
- combinedNormals[(vertexCursor + v) * 3 + 2] = dataView.getFloat32(vertexOffset + 20, true);
766
- }
767
- // uvs
768
- if (geomInfo.hasUVs) {
769
- combinedUVs[(vertexCursor + v) * 2 + 0] = dataView.getFloat32(vertexOffset + 24, true);
770
- combinedUVs[(vertexCursor + v) * 2 + 1] = dataView.getFloat32(vertexOffset + 28, true);
771
- }
772
- // bone indices & weights
773
- const weightOffset = geomInfo.weightsOffset + v * 32;
774
- for (let j = 0; j < 4; j++) {
775
- const boneIndex = dataView.getUint32(weightOffset + j * 4, true);
776
- combinedSkinIndices[(vertexCursor + v) * 4 + j] = boneIndex < boneCount ? boneIndex : 0;
777
- }
778
- for (let j = 0; j < 4; j++) {
779
- combinedSkinWeights[(vertexCursor + v) * 4 + j] = dataView.getFloat32(weightOffset + 16 + j * 4, true);
780
- }
781
- }
782
- // Parse indices
783
- const groupStart = indexCursor;
784
- for (let i = 0; i < geomInfo.indexCount; i++) {
785
- const indexValue = dataView.getUint32(geomInfo.indexBufferOffset + i * 4, true);
786
- combinedIndices[indexCursor + i] = indexValue + vertexBase;
787
- }
788
- combinedGeometry.addGroup(groupStart, geomInfo.indexCount, Math.min(geomInfo.materialIndex, materials.length - 1));
789
- vertexCursor += geomInfo.vertexCount;
758
+ combinedGeometry.addGroup(indexCursor, geomInfo.indexCount, Math.min(geomInfo.materialIndex, materials.length - 1));
790
759
  indexCursor += geomInfo.indexCount;
791
760
  }
792
- combinedGeometry.setAttribute('position', new THREE.BufferAttribute(combinedPositions, 3));
793
- if (geometryInfos.some(info => info.hasNormals)) {
794
- combinedGeometry.setAttribute('normal', new THREE.BufferAttribute(combinedNormals, 3));
795
- }
796
- if (geometryInfos.some(info => info.hasUVs)) {
797
- combinedGeometry.setAttribute('uv', new THREE.BufferAttribute(combinedUVs, 2));
798
- }
799
- combinedGeometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(combinedSkinIndices, 4));
800
- combinedGeometry.setAttribute('skinWeight', new THREE.BufferAttribute(combinedSkinWeights, 4));
801
- combinedGeometry.setIndex(new THREE.BufferAttribute(combinedIndices, 1));
802
761
  combinedGeometry.computeBoundingBox();
803
762
  combinedGeometry.computeBoundingSphere();
804
763
  const skinnedMesh = new THREE.SkinnedMesh(combinedGeometry, materials);
@@ -816,6 +775,8 @@ class StowKitPack {
816
775
  container.add(skinnedMesh);
817
776
  container.userData.boneOriginalNames = boneOriginalNames;
818
777
  container.userData.skinnedMesh = skinnedMesh;
778
+ const buildTime = performance.now() - buildStart;
779
+ PerfLogger.log(`[Perf] Build skinned mesh: ${buildTime.toFixed(2)}ms (${boneCount} bones, ${geomData.vertexCount} verts)`);
819
780
  return container;
820
781
  }
821
782
  /**
@@ -1191,22 +1152,29 @@ class StowKitPack {
1191
1152
  return Array.from(uniqueTextures);
1192
1153
  }
1193
1154
  async loadKTX2Texture(data, textureName) {
1194
- // Try IndexedDB cache first
1155
+ // Start cache check but don't block on it
1195
1156
  const cacheKey = textureName ? `${this.packUrl}::${textureName}` : undefined;
1157
+ let cachePromise = null;
1196
1158
  if (cacheKey) {
1197
- const cached = await AssetMemoryCache.getTexture(cacheKey, this.packVersion);
1198
- if (cached) {
1199
- PerfLogger.log(`[Perf] ✓ Texture "${textureName}": 0ms (IndexedDB cache)`);
1200
- const texture = new THREE.CompressedTexture([{ data: cached.data, width: cached.width, height: cached.height }], cached.width, cached.height, cached.format, THREE.UnsignedByteType);
1201
- texture.needsUpdate = true;
1202
- return texture;
1203
- }
1159
+ cachePromise = AssetMemoryCache.getTexture(cacheKey, this.packVersion);
1204
1160
  }
1205
- // Decode using Basis transcoder
1206
- const decodeStart = performance.now();
1161
+ // Check cache while starting decode
1207
1162
  const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
1208
1163
  const blob = new Blob([arrayBuffer]);
1209
1164
  const url = URL.createObjectURL(blob);
1165
+ const decodeStart = performance.now();
1166
+ // Race cache check vs decode start
1167
+ const [cached, _] = await Promise.all([
1168
+ cachePromise || Promise.resolve(null),
1169
+ Promise.resolve() // Start decode immediately
1170
+ ]);
1171
+ if (cached) {
1172
+ URL.revokeObjectURL(url);
1173
+ PerfLogger.log(`[Perf] ✓ Texture "${textureName}": 0ms (IndexedDB cache)`);
1174
+ const texture = new THREE.CompressedTexture([{ data: cached.data, width: cached.width, height: cached.height }], cached.width, cached.height, cached.format, THREE.UnsignedByteType);
1175
+ texture.needsUpdate = true;
1176
+ return texture;
1177
+ }
1210
1178
  try {
1211
1179
  const texture = await new Promise((resolve, reject) => {
1212
1180
  this.ktx2Loader.load(url, (texture) => {
@@ -1220,7 +1188,7 @@ class StowKitPack {
1220
1188
  });
1221
1189
  const decodeTime = performance.now() - decodeStart;
1222
1190
  PerfLogger.log(`[Perf] ✓ Texture "${textureName || 'unknown'}": ${decodeTime.toFixed(2)}ms (Basis decode ${texture.image?.width}x${texture.image?.height})`);
1223
- // Cache the transcoded texture data to IndexedDB
1191
+ // Cache the transcoded texture data to IndexedDB (non-blocking)
1224
1192
  if (cacheKey && texture.mipmaps && texture.mipmaps.length > 0) {
1225
1193
  const mipmap = texture.mipmaps[0];
1226
1194
  const dataArray = new Uint8Array(mipmap.data.buffer, mipmap.data.byteOffset, mipmap.data.byteLength);
@@ -1231,9 +1199,7 @@ class StowKitPack {
1231
1199
  format: texture.format,
1232
1200
  internalFormat: texture.format,
1233
1201
  compressed: true
1234
- }).catch(() => {
1235
- // Silent fail - caching is optional
1236
- });
1202
+ }).catch(() => { });
1237
1203
  }
1238
1204
  return texture;
1239
1205
  }