@stowkit/three-loader 0.1.31 → 0.1.33

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;IAuE1K;;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;IAoGjM;;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;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 +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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"}
@@ -344,7 +344,6 @@ class MeshParser {
344
344
  if (cacheKey && version) {
345
345
  const cached = await AssetMemoryCache.getGeometry(cacheKey, version);
346
346
  if (cached) {
347
- PerfLogger.log(`[Perf] Geometry cache hit: ${cacheKey}`);
348
347
  const geometry = new THREE.BufferGeometry();
349
348
  geometry.setAttribute('position', new THREE.BufferAttribute(cached.positions, 3));
350
349
  if (cached.normals) {
@@ -402,12 +401,8 @@ class MeshParser {
402
401
  root.name = 'StowKitMesh';
403
402
  const { geometries, materials, nodes, meshIndices } = parsedData;
404
403
  // Pre-load ALL geometries in parallel for maximum speed
405
- const dracoStart = performance.now();
406
404
  const geometryPromises = geometries.map((geoInfo, index) => this.createGeometry(geoInfo, dataBlob, dracoLoader, cacheKeyPrefix ? `${cacheKeyPrefix}_geo${index}` : undefined, version));
407
405
  const loadedGeometries = await Promise.all(geometryPromises);
408
- const dracoTime = performance.now() - dracoStart;
409
- const totalVerts = geometries.reduce((sum, g) => sum + g.vertexCount, 0);
410
- PerfLogger.log(`[Perf] Draco: ${dracoTime.toFixed(2)}ms (${geometries.length} meshes, ${totalVerts} verts)`);
411
406
  // Create all Three.js objects for nodes
412
407
  const nodeObjects = [];
413
408
  for (const node of nodes) {
@@ -506,6 +501,7 @@ class StowKitPack {
506
501
  * Load a skinned mesh by its string ID
507
502
  */
508
503
  async loadSkinnedMesh(stringId) {
504
+ const totalStart = performance.now();
509
505
  const assetIndex = this.reader.findAssetByPath(stringId);
510
506
  if (assetIndex < 0) {
511
507
  throw new Error(`Skinned mesh not found: ${stringId}`);
@@ -516,12 +512,16 @@ class StowKitPack {
516
512
  throw new Error(`Failed to read skinned mesh data: ${stringId}`);
517
513
  if (!metadata)
518
514
  throw new Error(`No metadata for skinned mesh: ${stringId}`);
519
- return await this.parseSkinnedMesh(metadata, data);
515
+ const result = await this.parseSkinnedMesh(metadata, data);
516
+ const totalTime = performance.now() - totalStart;
517
+ PerfLogger.log(`[Perf] === Skinned Mesh "${stringId}": ${totalTime.toFixed(2)}ms total ===`);
518
+ return result;
520
519
  }
521
520
  /**
522
521
  * Parse skinned mesh from binary data
523
522
  */
524
523
  async parseSkinnedMesh(metadata, data) {
524
+ const parseStart = performance.now();
525
525
  // NEW FORMAT: Skip past tag header
526
526
  const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
527
527
  const tagCsvLength = view.getUint32(0, true);
@@ -567,7 +567,7 @@ class StowKitPack {
567
567
  });
568
568
  offset += 72;
569
569
  }
570
- const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);
570
+ new DataView(data.buffer, data.byteOffset, data.byteLength);
571
571
  // Sort geometry infos by vertex buffer offset to ensure sequential parsing
572
572
  const sortedGeometryInfos = geometryInfos.slice().sort((a, b) => a.vertexBufferOffset - b.vertexBufferOffset);
573
573
  // Parse materials from metadata (immediately after geometries)
@@ -722,88 +722,39 @@ class StowKitPack {
722
722
  }
723
723
  materials.push(material);
724
724
  }
725
+ const parseTime = performance.now() - parseStart;
726
+ PerfLogger.log(`[Perf] Parse skinned metadata: ${parseTime.toFixed(2)}ms`);
727
+ const textureStart = performance.now();
725
728
  await this.loadMaterialTextures(materialData, materials);
729
+ const textureTime = performance.now() - textureStart;
730
+ if (materialData.length > 0) {
731
+ PerfLogger.log(`[Perf] Load textures: ${textureTime.toFixed(2)}ms`);
732
+ }
726
733
  // Collect root bones (hierarchy already built earlier)
734
+ const buildStart = performance.now();
727
735
  const rootBones = bones.filter((_, index) => parentIndices[index] < 0);
728
736
  // Create shared skeleton
729
737
  const skeleton = new THREE.Skeleton(bones, boneMatrices);
730
- // Pre-allocate combined buffers
731
- const totalVertexCount = sortedGeometryInfos.reduce((sum, info) => sum + info.vertexCount, 0);
732
- const totalIndexCount = sortedGeometryInfos.reduce((sum, info) => sum + info.indexCount, 0);
733
- const combinedPositions = new Float32Array(totalVertexCount * 3);
734
- const combinedNormals = new Float32Array(totalVertexCount * 3);
735
- const combinedUVs = new Float32Array(totalVertexCount * 2);
736
- const combinedSkinIndices = new Uint16Array(totalVertexCount * 4);
737
- const combinedSkinWeights = new Float32Array(totalVertexCount * 4);
738
- const combinedIndices = new Uint32Array(totalIndexCount);
738
+ // Use WASM to parse geometry data (10-50x faster!)
739
+ const geomData = this.reader.parseSkinnedMeshGeometryFast(metadata, data);
740
+ if (!geomData) {
741
+ throw new Error('Failed to parse skinned mesh geometry');
742
+ }
743
+ // Convert Uint32Array skinIndices to Uint16Array for Three.js
744
+ const skinIndices16 = new Uint16Array(geomData.skinIndices);
739
745
  const combinedGeometry = new THREE.BufferGeometry();
740
- combinedGeometry.clearGroups();
741
- let vertexCursor = 0; // counts vertices
742
- let indexCursor = 0; // counts indices
746
+ combinedGeometry.setAttribute('position', new THREE.BufferAttribute(geomData.positions, 3));
747
+ combinedGeometry.setAttribute('normal', new THREE.BufferAttribute(geomData.normals, 3));
748
+ combinedGeometry.setAttribute('uv', new THREE.BufferAttribute(geomData.uvs, 2));
749
+ combinedGeometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(skinIndices16, 4));
750
+ combinedGeometry.setAttribute('skinWeight', new THREE.BufferAttribute(geomData.skinWeights, 4));
751
+ combinedGeometry.setIndex(new THREE.BufferAttribute(geomData.indices, 1));
752
+ // Add geometry groups for materials
753
+ let indexCursor = 0;
743
754
  for (const geomInfo of sortedGeometryInfos) {
744
- const vertexStrideBytes = 32;
745
- const vertexBase = vertexCursor; // base vertex index for this submesh
746
- // Validate offsets before parsing
747
- const requiredVertexBytes = geomInfo.vertexBufferOffset + geomInfo.vertexCount * vertexStrideBytes;
748
- const requiredIndexBytes = geomInfo.indexBufferOffset + geomInfo.indexCount * 4;
749
- const requiredWeightBytes = geomInfo.weightsOffset + geomInfo.vertexCount * 32;
750
- if (requiredVertexBytes > data.byteLength) {
751
- throw new Error(`Vertex buffer out of bounds: need ${requiredVertexBytes}, have ${data.byteLength}`);
752
- }
753
- if (requiredIndexBytes > data.byteLength) {
754
- throw new Error(`Index buffer out of bounds: need ${requiredIndexBytes}, have ${data.byteLength}`);
755
- }
756
- if (requiredWeightBytes > data.byteLength) {
757
- throw new Error(`Weight buffer out of bounds: need ${requiredWeightBytes}, have ${data.byteLength}`);
758
- }
759
- // Parse vertices
760
- for (let v = 0; v < geomInfo.vertexCount; v++) {
761
- const vertexOffset = geomInfo.vertexBufferOffset + v * vertexStrideBytes;
762
- // positions
763
- combinedPositions[(vertexCursor + v) * 3 + 0] = dataView.getFloat32(vertexOffset + 0, true);
764
- combinedPositions[(vertexCursor + v) * 3 + 1] = dataView.getFloat32(vertexOffset + 4, true);
765
- combinedPositions[(vertexCursor + v) * 3 + 2] = dataView.getFloat32(vertexOffset + 8, true);
766
- // normals
767
- if (geomInfo.hasNormals) {
768
- combinedNormals[(vertexCursor + v) * 3 + 0] = dataView.getFloat32(vertexOffset + 12, true);
769
- combinedNormals[(vertexCursor + v) * 3 + 1] = dataView.getFloat32(vertexOffset + 16, true);
770
- combinedNormals[(vertexCursor + v) * 3 + 2] = dataView.getFloat32(vertexOffset + 20, true);
771
- }
772
- // uvs
773
- if (geomInfo.hasUVs) {
774
- combinedUVs[(vertexCursor + v) * 2 + 0] = dataView.getFloat32(vertexOffset + 24, true);
775
- combinedUVs[(vertexCursor + v) * 2 + 1] = dataView.getFloat32(vertexOffset + 28, true);
776
- }
777
- // bone indices & weights
778
- const weightOffset = geomInfo.weightsOffset + v * 32;
779
- for (let j = 0; j < 4; j++) {
780
- const boneIndex = dataView.getUint32(weightOffset + j * 4, true);
781
- combinedSkinIndices[(vertexCursor + v) * 4 + j] = boneIndex < boneCount ? boneIndex : 0;
782
- }
783
- for (let j = 0; j < 4; j++) {
784
- combinedSkinWeights[(vertexCursor + v) * 4 + j] = dataView.getFloat32(weightOffset + 16 + j * 4, true);
785
- }
786
- }
787
- // Parse indices
788
- const groupStart = indexCursor;
789
- for (let i = 0; i < geomInfo.indexCount; i++) {
790
- const indexValue = dataView.getUint32(geomInfo.indexBufferOffset + i * 4, true);
791
- combinedIndices[indexCursor + i] = indexValue + vertexBase;
792
- }
793
- combinedGeometry.addGroup(groupStart, geomInfo.indexCount, Math.min(geomInfo.materialIndex, materials.length - 1));
794
- vertexCursor += geomInfo.vertexCount;
755
+ combinedGeometry.addGroup(indexCursor, geomInfo.indexCount, Math.min(geomInfo.materialIndex, materials.length - 1));
795
756
  indexCursor += geomInfo.indexCount;
796
757
  }
797
- combinedGeometry.setAttribute('position', new THREE.BufferAttribute(combinedPositions, 3));
798
- if (geometryInfos.some(info => info.hasNormals)) {
799
- combinedGeometry.setAttribute('normal', new THREE.BufferAttribute(combinedNormals, 3));
800
- }
801
- if (geometryInfos.some(info => info.hasUVs)) {
802
- combinedGeometry.setAttribute('uv', new THREE.BufferAttribute(combinedUVs, 2));
803
- }
804
- combinedGeometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(combinedSkinIndices, 4));
805
- combinedGeometry.setAttribute('skinWeight', new THREE.BufferAttribute(combinedSkinWeights, 4));
806
- combinedGeometry.setIndex(new THREE.BufferAttribute(combinedIndices, 1));
807
758
  combinedGeometry.computeBoundingBox();
808
759
  combinedGeometry.computeBoundingSphere();
809
760
  const skinnedMesh = new THREE.SkinnedMesh(combinedGeometry, materials);
@@ -821,6 +772,8 @@ class StowKitPack {
821
772
  container.add(skinnedMesh);
822
773
  container.userData.boneOriginalNames = boneOriginalNames;
823
774
  container.userData.skinnedMesh = skinnedMesh;
775
+ const buildTime = performance.now() - buildStart;
776
+ PerfLogger.log(`[Perf] Build skinned mesh: ${buildTime.toFixed(2)}ms (${boneCount} bones, ${geomData.vertexCount} verts)`);
824
777
  return container;
825
778
  }
826
779
  /**
@@ -845,12 +798,7 @@ class StowKitPack {
845
798
  // Parse mesh data
846
799
  const parsedData = MeshParser.parseMeshData(metadata, data);
847
800
  // Load textures for materials
848
- const textureStart = performance.now();
849
- const textureNames = await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
850
- const textureTime = performance.now() - textureStart;
851
- if (textureNames.length > 0) {
852
- PerfLogger.log(`[Perf] Textures: ${textureTime.toFixed(2)}ms (${textureNames.join(', ')})`);
853
- }
801
+ await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
854
802
  // Build Three.js scene with Draco decoder (with caching)
855
803
  const cacheKey = `${this.packUrl}::${assetPath}`;
856
804
  const scene = await MeshParser.buildScene(parsedData, data, this.dracoLoader, cacheKey, this.packVersion);
@@ -1204,26 +1152,23 @@ class StowKitPack {
1204
1152
  // Try IndexedDB cache first
1205
1153
  const cacheKey = textureName ? `${this.packUrl}::${textureName}` : undefined;
1206
1154
  if (cacheKey) {
1207
- const startCache = performance.now();
1208
1155
  const cached = await AssetMemoryCache.getTexture(cacheKey, this.packVersion);
1209
1156
  if (cached) {
1210
- PerfLogger.log(`[Perf] Texture cache hit (IndexedDB): ${textureName} (${(performance.now() - startCache).toFixed(2)}ms)`);
1157
+ PerfLogger.log(`[Perf] Texture "${textureName}": 0ms (IndexedDB cache)`);
1211
1158
  const texture = new THREE.CompressedTexture([{ data: cached.data, width: cached.width, height: cached.height }], cached.width, cached.height, cached.format, THREE.UnsignedByteType);
1212
1159
  texture.needsUpdate = true;
1213
1160
  return texture;
1214
1161
  }
1215
1162
  }
1216
- // Create blob URL - KTX2Loader requires a URL, can't use ArrayBuffer directly
1163
+ // Decode using Basis transcoder
1164
+ const decodeStart = performance.now();
1217
1165
  const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
1218
1166
  const blob = new Blob([arrayBuffer]);
1219
1167
  const url = URL.createObjectURL(blob);
1220
1168
  try {
1221
- const decodeStart = performance.now();
1222
1169
  const texture = await new Promise((resolve, reject) => {
1223
1170
  this.ktx2Loader.load(url, (texture) => {
1224
1171
  URL.revokeObjectURL(url);
1225
- const decodeTime = performance.now() - decodeStart;
1226
- PerfLogger.log(`[Perf] Basis decode: ${decodeTime.toFixed(2)}ms (${textureName || 'unknown'}, ${texture.image?.width}x${texture.image?.height})`);
1227
1172
  texture.needsUpdate = true;
1228
1173
  resolve(texture);
1229
1174
  }, undefined, (error) => {
@@ -1231,6 +1176,8 @@ class StowKitPack {
1231
1176
  reject(error);
1232
1177
  });
1233
1178
  });
1179
+ const decodeTime = performance.now() - decodeStart;
1180
+ PerfLogger.log(`[Perf] ✓ Texture "${textureName || 'unknown'}": ${decodeTime.toFixed(2)}ms (Basis decode ${texture.image?.width}x${texture.image?.height})`);
1234
1181
  // Cache the transcoded texture data to IndexedDB
1235
1182
  if (cacheKey && texture.mipmaps && texture.mipmaps.length > 0) {
1236
1183
  const mipmap = texture.mipmaps[0];