@stowkit/three-loader 0.1.27 → 0.1.29

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.
@@ -237,12 +237,7 @@ class MeshParser {
237
237
  reject(new Error(`Failed to decode Draco geometry: ${error}`));
238
238
  });
239
239
  });
240
- // Compute bounding volumes
241
- const boundsStart = performance.now();
242
- geometry.computeBoundingSphere();
243
- geometry.computeBoundingBox();
244
- const boundsEnd = performance.now();
245
- reader.PerfLogger.log(`[Perf] Bounds computation: ${(boundsEnd - boundsStart).toFixed(2)}ms`);
240
+ // Bounds will be computed automatically by Three.js when needed
246
241
  const totalTime = performance.now();
247
242
  reader.PerfLogger.log(`[Perf] Total geometry creation: ${(totalTime - startTime).toFixed(2)}ms`);
248
243
  return geometry;
@@ -254,6 +249,11 @@ class MeshParser {
254
249
  const root = new THREE__namespace.Group();
255
250
  root.name = 'StowKitMesh';
256
251
  const { geometries, materials, nodes, meshIndices } = parsedData;
252
+ // Pre-load ALL geometries in parallel for maximum speed
253
+ reader.PerfLogger.log(`[Perf] Starting parallel Draco decode of ${geometries.length} geometries...`);
254
+ const geometryPromises = geometries.map(geoInfo => this.createGeometry(geoInfo, dataBlob, dracoLoader));
255
+ const loadedGeometries = await Promise.all(geometryPromises);
256
+ reader.PerfLogger.log(`[Perf] All Draco decodes complete`);
257
257
  // Create all Three.js objects for nodes
258
258
  const nodeObjects = [];
259
259
  for (const node of nodes) {
@@ -269,7 +269,7 @@ class MeshParser {
269
269
  const meshIndex = meshIndices[meshIndexArrayPos];
270
270
  if (meshIndex < geometries.length) {
271
271
  const geoInfo = geometries[meshIndex];
272
- const geometry = await this.createGeometry(geoInfo, dataBlob, dracoLoader);
272
+ const geometry = loadedGeometries[meshIndex];
273
273
  // Use assigned material if valid, otherwise create default
274
274
  let material;
275
275
  if (geoInfo.materialIndex < materials.length) {
@@ -300,11 +300,11 @@ class MeshParser {
300
300
  root.add(obj);
301
301
  }
302
302
  }
303
- // If no nodes, create a single mesh from all geometries
303
+ // If no nodes, create meshes from all geometries
304
304
  if (nodes.length === 0 && geometries.length > 0) {
305
305
  for (let index = 0; index < geometries.length; index++) {
306
306
  const geoInfo = geometries[index];
307
- const geometry = await this.createGeometry(geoInfo, dataBlob, dracoLoader);
307
+ const geometry = loadedGeometries[index];
308
308
  // Use assigned material if valid, otherwise create default
309
309
  let material;
310
310
  if (geoInfo.materialIndex < materials.length) {
@@ -696,8 +696,8 @@ class StowKitPack {
696
696
  reader.PerfLogger.log(`[Perf] Parse mesh metadata: ${(performance.now() - parseStart).toFixed(2)}ms`);
697
697
  // Load textures for materials
698
698
  const textureStart = performance.now();
699
- await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
700
- 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'})`);
701
701
  // Build Three.js scene with Draco decoder
702
702
  const buildStart = performance.now();
703
703
  const scene = await MeshParser.buildScene(parsedData, data, this.dracoLoader);
@@ -727,7 +727,7 @@ class StowKitPack {
727
727
  throw new Error(`Failed to read texture data: ${assetPath}`);
728
728
  }
729
729
  // Load as KTX2
730
- return await this.loadKTX2Texture(data);
730
+ return await this.loadKTX2Texture(data, assetPath);
731
731
  })();
732
732
  this.textureCache.set(assetPath, loadPromise);
733
733
  return loadPromise;
@@ -1016,14 +1016,17 @@ class StowKitPack {
1016
1016
  async loadMaterialTextures(materialData, materials) {
1017
1017
  // Collect all texture load promises to load in parallel
1018
1018
  const textureLoads = [];
1019
+ const uniqueTextures = new Set();
1019
1020
  for (let i = 0; i < materialData.length; i++) {
1020
1021
  const matData = materialData[i];
1021
1022
  for (const prop of matData.properties) {
1022
1023
  const textureId = prop.textureId?.trim();
1023
1024
  if (textureId && textureId.length > 0) {
1025
+ uniqueTextures.add(textureId);
1024
1026
  textureLoads.push({
1025
1027
  materialIndex: i,
1026
1028
  propertyName: prop.fieldName,
1029
+ textureId: textureId,
1027
1030
  texturePromise: this.loadTexture(textureId).catch(error => {
1028
1031
  console.error(`Failed to load texture "${textureId}":`, error);
1029
1032
  throw error;
@@ -1043,20 +1046,21 @@ class StowKitPack {
1043
1046
  this.applyTextureToMaterial(material, load.propertyName, result.value);
1044
1047
  }
1045
1048
  }
1049
+ return Array.from(uniqueTextures);
1046
1050
  }
1047
- async loadKTX2Texture(data) {
1048
- performance.now();
1051
+ async loadKTX2Texture(data, textureName) {
1052
+ reader.PerfLogger.log(`[Perf] KTX2/Basis texture size: ${data.byteLength} bytes${textureName ? ` (${textureName})` : ''}`);
1053
+ // Create blob URL - KTX2Loader requires a URL, can't use ArrayBuffer directly
1049
1054
  const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
1050
1055
  const blob = new Blob([arrayBuffer]);
1051
1056
  const url = URL.createObjectURL(blob);
1052
- reader.PerfLogger.log(`[Perf] KTX2/Basis texture size: ${data.byteLength} bytes`);
1053
1057
  try {
1054
1058
  return await new Promise((resolve, reject) => {
1055
1059
  const loadStart = performance.now();
1056
1060
  this.ktx2Loader.load(url, (texture) => {
1057
1061
  URL.revokeObjectURL(url);
1058
1062
  const loadEnd = performance.now();
1059
- reader.PerfLogger.log(`[Perf] Basis transcode: ${(loadEnd - loadStart).toFixed(2)}ms (resolution: ${texture.image?.width || '?'}x${texture.image?.height || '?'})`);
1063
+ reader.PerfLogger.log(`[Perf] Basis transcode: ${(loadEnd - loadStart).toFixed(2)}ms (${textureName || 'unknown'}, resolution: ${texture.image?.width || '?'}x${texture.image?.height || '?'})`);
1060
1064
  texture.needsUpdate = true;
1061
1065
  resolve(texture);
1062
1066
  }, undefined, (error) => {