@stowkit/three-loader 0.1.29 → 0.1.30

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.
@@ -212,34 +212,24 @@ class MeshParser {
212
212
  * Create Three.js BufferGeometry from Draco compressed mesh data
213
213
  */
214
214
  static async createGeometry(geoInfo, dataBlob, dracoLoader) {
215
- const startTime = performance.now();
216
215
  // Extract the Draco compressed buffer
217
216
  if (geoInfo.compressedBufferOffset + geoInfo.compressedBufferSize > dataBlob.length) {
218
217
  throw new Error(`Compressed buffer out of bounds: offset=${geoInfo.compressedBufferOffset}, size=${geoInfo.compressedBufferSize}, dataLength=${dataBlob.length}`);
219
218
  }
220
219
  const compressedData = dataBlob.slice(geoInfo.compressedBufferOffset, geoInfo.compressedBufferOffset + geoInfo.compressedBufferSize);
221
- const extractTime = performance.now();
222
- reader.PerfLogger.log(`[Perf] Draco extract: ${(extractTime - startTime).toFixed(2)}ms (${geoInfo.compressedBufferSize} bytes)`);
223
220
  // Decode using Draco
224
- // Create a blob URL for the Draco data (DRACOLoader expects a URL)
225
221
  const arrayBuffer = compressedData.buffer.slice(compressedData.byteOffset, compressedData.byteOffset + compressedData.byteLength);
226
222
  const blob = new Blob([arrayBuffer]);
227
223
  const url = URL.createObjectURL(blob);
228
- const decodeStart = performance.now();
229
224
  const geometry = await new Promise((resolve, reject) => {
230
225
  dracoLoader.load(url, (decoded) => {
231
226
  URL.revokeObjectURL(url);
232
- const decodeEnd = performance.now();
233
- reader.PerfLogger.log(`[Perf] Draco decode: ${(decodeEnd - decodeStart).toFixed(2)}ms (vertices: ${geoInfo.vertexCount}, indices: ${geoInfo.indexCount})`);
234
227
  resolve(decoded);
235
228
  }, undefined, (error) => {
236
229
  URL.revokeObjectURL(url);
237
230
  reject(new Error(`Failed to decode Draco geometry: ${error}`));
238
231
  });
239
232
  });
240
- // Bounds will be computed automatically by Three.js when needed
241
- const totalTime = performance.now();
242
- reader.PerfLogger.log(`[Perf] Total geometry creation: ${(totalTime - startTime).toFixed(2)}ms`);
243
233
  return geometry;
244
234
  }
245
235
  /**
@@ -250,10 +240,12 @@ class MeshParser {
250
240
  root.name = 'StowKitMesh';
251
241
  const { geometries, materials, nodes, meshIndices } = parsedData;
252
242
  // Pre-load ALL geometries in parallel for maximum speed
253
- reader.PerfLogger.log(`[Perf] Starting parallel Draco decode of ${geometries.length} geometries...`);
243
+ const dracoStart = performance.now();
254
244
  const geometryPromises = geometries.map(geoInfo => this.createGeometry(geoInfo, dataBlob, dracoLoader));
255
245
  const loadedGeometries = await Promise.all(geometryPromises);
256
- reader.PerfLogger.log(`[Perf] All Draco decodes complete`);
246
+ const dracoTime = performance.now() - dracoStart;
247
+ const totalVerts = geometries.reduce((sum, g) => sum + g.vertexCount, 0);
248
+ reader.PerfLogger.log(`[Perf] Draco decode: ${dracoTime.toFixed(2)}ms (${geometries.length} meshes, ${totalVerts} total vertices)`);
257
249
  // Create all Three.js objects for nodes
258
250
  const nodeObjects = [];
259
251
  for (const node of nodes) {
@@ -673,17 +665,13 @@ class StowKitPack {
673
665
  async loadMesh(assetPath) {
674
666
  const totalStart = performance.now();
675
667
  // Find asset by path
676
- const findStart = performance.now();
677
668
  const assetIndex = this.reader.findAssetByPath(assetPath);
678
669
  if (assetIndex < 0) {
679
670
  throw new Error(`Mesh not found: ${assetPath}`);
680
671
  }
681
- reader.PerfLogger.log(`[Perf] Find asset: ${(performance.now() - findStart).toFixed(2)}ms`);
682
672
  // Read mesh data and metadata
683
- const readStart = performance.now();
684
673
  const data = this.reader.readAssetData(assetIndex);
685
674
  const metadata = this.reader.readAssetMetadata(assetIndex);
686
- reader.PerfLogger.log(`[Perf] Read mesh data: ${(performance.now() - readStart).toFixed(2)}ms (data: ${data?.byteLength || 0} bytes, metadata: ${metadata?.byteLength || 0} bytes)`);
687
675
  if (!data) {
688
676
  throw new Error(`Failed to read mesh data: ${assetPath}`);
689
677
  }
@@ -691,18 +679,18 @@ class StowKitPack {
691
679
  throw new Error(`No metadata available: ${assetPath}`);
692
680
  }
693
681
  // Parse mesh data
694
- const parseStart = performance.now();
695
682
  const parsedData = MeshParser.parseMeshData(metadata, data);
696
- reader.PerfLogger.log(`[Perf] Parse mesh metadata: ${(performance.now() - parseStart).toFixed(2)}ms`);
697
683
  // Load textures for materials
698
684
  const textureStart = performance.now();
699
685
  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'})`);
686
+ const textureTime = performance.now() - textureStart;
687
+ if (textureNames.length > 0) {
688
+ reader.PerfLogger.log(`[Perf] Textures: ${textureTime.toFixed(2)}ms (${textureNames.join(', ')})`);
689
+ }
701
690
  // Build Three.js scene with Draco decoder
702
- const buildStart = performance.now();
703
691
  const scene = await MeshParser.buildScene(parsedData, data, this.dracoLoader);
704
- reader.PerfLogger.log(`[Perf] Build scene: ${(performance.now() - buildStart).toFixed(2)}ms`);
705
- reader.PerfLogger.log(`[Perf] ===== Total mesh load: ${(performance.now() - totalStart).toFixed(2)}ms =====`);
692
+ const totalTime = performance.now() - totalStart;
693
+ reader.PerfLogger.log(`[Perf] === Mesh "${assetPath}": ${totalTime.toFixed(2)}ms total ===`);
706
694
  return scene;
707
695
  }
708
696
  /**
@@ -711,7 +699,6 @@ class StowKitPack {
711
699
  async loadTexture(assetPath) {
712
700
  // Check cache first
713
701
  if (this.textureCache.has(assetPath)) {
714
- reader.PerfLogger.log(`[Perf] Texture cache hit: ${assetPath}`);
715
702
  return this.textureCache.get(assetPath);
716
703
  }
717
704
  // Cache the promise to avoid duplicate loads
@@ -1049,18 +1036,14 @@ class StowKitPack {
1049
1036
  return Array.from(uniqueTextures);
1050
1037
  }
1051
1038
  async loadKTX2Texture(data, textureName) {
1052
- reader.PerfLogger.log(`[Perf] KTX2/Basis texture size: ${data.byteLength} bytes${textureName ? ` (${textureName})` : ''}`);
1053
1039
  // Create blob URL - KTX2Loader requires a URL, can't use ArrayBuffer directly
1054
1040
  const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
1055
1041
  const blob = new Blob([arrayBuffer]);
1056
1042
  const url = URL.createObjectURL(blob);
1057
1043
  try {
1058
1044
  return await new Promise((resolve, reject) => {
1059
- const loadStart = performance.now();
1060
1045
  this.ktx2Loader.load(url, (texture) => {
1061
1046
  URL.revokeObjectURL(url);
1062
- const loadEnd = performance.now();
1063
- reader.PerfLogger.log(`[Perf] Basis transcode: ${(loadEnd - loadStart).toFixed(2)}ms (${textureName || 'unknown'}, resolution: ${texture.image?.width || '?'}x${texture.image?.height || '?'})`);
1064
1047
  texture.needsUpdate = true;
1065
1048
  resolve(texture);
1066
1049
  }, undefined, (error) => {