@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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
705
|
-
reader.PerfLogger.log(`[Perf]
|
|
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) => {
|