@stowkit/three-loader 0.1.26 → 0.1.28

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.
@@ -341,6 +341,7 @@ class MeshParser {
341
341
  */
342
342
  class StowKitPack {
343
343
  constructor(reader, ktx2Loader, dracoLoader) {
344
+ this.textureCache = new Map();
344
345
  this.reader = reader;
345
346
  this.ktx2Loader = ktx2Loader;
346
347
  this.dracoLoader = dracoLoader;
@@ -695,8 +696,8 @@ class StowKitPack {
695
696
  reader.PerfLogger.log(`[Perf] Parse mesh metadata: ${(performance.now() - parseStart).toFixed(2)}ms`);
696
697
  // Load textures for materials
697
698
  const textureStart = performance.now();
698
- await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
699
- reader.PerfLogger.log(`[Perf] Load textures: ${(performance.now() - textureStart).toFixed(2)}ms`);
699
+ const textureNames = await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
700
+ reader.PerfLogger.log(`[Perf] Load textures: ${(performance.now() - textureStart).toFixed(2)}ms (${textureNames.length > 0 ? textureNames.join(', ') : 'none'})`);
700
701
  // Build Three.js scene with Draco decoder
701
702
  const buildStart = performance.now();
702
703
  const scene = await MeshParser.buildScene(parsedData, data, this.dracoLoader);
@@ -708,18 +709,28 @@ class StowKitPack {
708
709
  * Load a texture by its canonical path/name
709
710
  */
710
711
  async loadTexture(assetPath) {
711
- // Find asset by path
712
- const assetIndex = this.reader.findAssetByPath(assetPath);
713
- if (assetIndex < 0) {
714
- throw new Error(`Texture not found: ${assetPath}`);
712
+ // Check cache first
713
+ if (this.textureCache.has(assetPath)) {
714
+ reader.PerfLogger.log(`[Perf] Texture cache hit: ${assetPath}`);
715
+ return this.textureCache.get(assetPath);
715
716
  }
716
- // Read texture data
717
- const data = this.reader.readAssetData(assetIndex);
718
- if (!data) {
719
- throw new Error(`Failed to read texture data: ${assetPath}`);
720
- }
721
- // Load as KTX2
722
- return await this.loadKTX2Texture(data);
717
+ // Cache the promise to avoid duplicate loads
718
+ const loadPromise = (async () => {
719
+ // Find asset by path
720
+ const assetIndex = this.reader.findAssetByPath(assetPath);
721
+ if (assetIndex < 0) {
722
+ throw new Error(`Texture not found: ${assetPath}`);
723
+ }
724
+ // Read texture data
725
+ const data = this.reader.readAssetData(assetIndex);
726
+ if (!data) {
727
+ throw new Error(`Failed to read texture data: ${assetPath}`);
728
+ }
729
+ // Load as KTX2
730
+ return await this.loadKTX2Texture(data);
731
+ })();
732
+ this.textureCache.set(assetPath, loadPromise);
733
+ return loadPromise;
723
734
  }
724
735
  /**
725
736
  * Load audio by its canonical path/name
@@ -998,27 +1009,44 @@ class StowKitPack {
998
1009
  * Close the pack and free resources
999
1010
  */
1000
1011
  dispose() {
1012
+ this.textureCache.clear();
1001
1013
  this.reader.close();
1002
1014
  }
1003
1015
  // Private methods moved from StowKitLoader
1004
1016
  async loadMaterialTextures(materialData, materials) {
1017
+ // Collect all texture load promises to load in parallel
1018
+ const textureLoads = [];
1019
+ const uniqueTextures = new Set();
1005
1020
  for (let i = 0; i < materialData.length; i++) {
1006
1021
  const matData = materialData[i];
1007
- const material = materials[i];
1008
1022
  for (const prop of matData.properties) {
1009
- // Trim and check for truly non-empty texture ID
1010
1023
  const textureId = prop.textureId?.trim();
1011
1024
  if (textureId && textureId.length > 0) {
1012
- try {
1013
- const texture = await this.loadTexture(textureId);
1014
- this.applyTextureToMaterial(material, prop.fieldName, texture);
1015
- }
1016
- catch (error) {
1017
- console.error(`Failed to load texture "${textureId}":`, error);
1018
- }
1025
+ uniqueTextures.add(textureId);
1026
+ textureLoads.push({
1027
+ materialIndex: i,
1028
+ propertyName: prop.fieldName,
1029
+ textureId: textureId,
1030
+ texturePromise: this.loadTexture(textureId).catch(error => {
1031
+ console.error(`Failed to load texture "${textureId}":`, error);
1032
+ throw error;
1033
+ })
1034
+ });
1019
1035
  }
1020
1036
  }
1021
1037
  }
1038
+ // Load all textures in parallel
1039
+ const results = await Promise.allSettled(textureLoads.map(load => load.texturePromise));
1040
+ // Apply successfully loaded textures
1041
+ for (let i = 0; i < textureLoads.length; i++) {
1042
+ const result = results[i];
1043
+ if (result.status === 'fulfilled') {
1044
+ const load = textureLoads[i];
1045
+ const material = materials[load.materialIndex];
1046
+ this.applyTextureToMaterial(material, load.propertyName, result.value);
1047
+ }
1048
+ }
1049
+ return Array.from(uniqueTextures);
1022
1050
  }
1023
1051
  async loadKTX2Texture(data) {
1024
1052
  performance.now();