@stowkit/three-loader 0.1.25 → 0.1.27
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.
- package/dist/MeshParser.d.ts.map +1 -1
- package/dist/StowKitPack.d.ts +1 -0
- package/dist/StowKitPack.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/stowkit-three-loader.esm.js +74 -23
- package/dist/stowkit-three-loader.esm.js.map +1 -1
- package/dist/stowkit-three-loader.js +76 -21
- package/dist/stowkit-three-loader.js.map +1 -1
- package/package.json +1 -1
|
@@ -212,20 +212,25 @@ 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();
|
|
215
216
|
// Extract the Draco compressed buffer
|
|
216
217
|
if (geoInfo.compressedBufferOffset + geoInfo.compressedBufferSize > dataBlob.length) {
|
|
217
218
|
throw new Error(`Compressed buffer out of bounds: offset=${geoInfo.compressedBufferOffset}, size=${geoInfo.compressedBufferSize}, dataLength=${dataBlob.length}`);
|
|
218
219
|
}
|
|
219
220
|
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)`);
|
|
220
223
|
// Decode using Draco
|
|
221
224
|
// Create a blob URL for the Draco data (DRACOLoader expects a URL)
|
|
222
225
|
const arrayBuffer = compressedData.buffer.slice(compressedData.byteOffset, compressedData.byteOffset + compressedData.byteLength);
|
|
223
226
|
const blob = new Blob([arrayBuffer]);
|
|
224
227
|
const url = URL.createObjectURL(blob);
|
|
228
|
+
const decodeStart = performance.now();
|
|
225
229
|
const geometry = await new Promise((resolve, reject) => {
|
|
226
230
|
dracoLoader.load(url, (decoded) => {
|
|
227
231
|
URL.revokeObjectURL(url);
|
|
228
|
-
|
|
232
|
+
const decodeEnd = performance.now();
|
|
233
|
+
reader.PerfLogger.log(`[Perf] Draco decode: ${(decodeEnd - decodeStart).toFixed(2)}ms (vertices: ${geoInfo.vertexCount}, indices: ${geoInfo.indexCount})`);
|
|
229
234
|
resolve(decoded);
|
|
230
235
|
}, undefined, (error) => {
|
|
231
236
|
URL.revokeObjectURL(url);
|
|
@@ -233,8 +238,13 @@ class MeshParser {
|
|
|
233
238
|
});
|
|
234
239
|
});
|
|
235
240
|
// Compute bounding volumes
|
|
241
|
+
const boundsStart = performance.now();
|
|
236
242
|
geometry.computeBoundingSphere();
|
|
237
243
|
geometry.computeBoundingBox();
|
|
244
|
+
const boundsEnd = performance.now();
|
|
245
|
+
reader.PerfLogger.log(`[Perf] Bounds computation: ${(boundsEnd - boundsStart).toFixed(2)}ms`);
|
|
246
|
+
const totalTime = performance.now();
|
|
247
|
+
reader.PerfLogger.log(`[Perf] Total geometry creation: ${(totalTime - startTime).toFixed(2)}ms`);
|
|
238
248
|
return geometry;
|
|
239
249
|
}
|
|
240
250
|
/**
|
|
@@ -331,6 +341,7 @@ class MeshParser {
|
|
|
331
341
|
*/
|
|
332
342
|
class StowKitPack {
|
|
333
343
|
constructor(reader, ktx2Loader, dracoLoader) {
|
|
344
|
+
this.textureCache = new Map();
|
|
334
345
|
this.reader = reader;
|
|
335
346
|
this.ktx2Loader = ktx2Loader;
|
|
336
347
|
this.dracoLoader = dracoLoader;
|
|
@@ -660,14 +671,19 @@ class StowKitPack {
|
|
|
660
671
|
* Load a mesh by its canonical path/name
|
|
661
672
|
*/
|
|
662
673
|
async loadMesh(assetPath) {
|
|
674
|
+
const totalStart = performance.now();
|
|
663
675
|
// Find asset by path
|
|
676
|
+
const findStart = performance.now();
|
|
664
677
|
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
665
678
|
if (assetIndex < 0) {
|
|
666
679
|
throw new Error(`Mesh not found: ${assetPath}`);
|
|
667
680
|
}
|
|
681
|
+
reader.PerfLogger.log(`[Perf] Find asset: ${(performance.now() - findStart).toFixed(2)}ms`);
|
|
668
682
|
// Read mesh data and metadata
|
|
683
|
+
const readStart = performance.now();
|
|
669
684
|
const data = this.reader.readAssetData(assetIndex);
|
|
670
685
|
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)`);
|
|
671
687
|
if (!data) {
|
|
672
688
|
throw new Error(`Failed to read mesh data: ${assetPath}`);
|
|
673
689
|
}
|
|
@@ -675,29 +691,46 @@ class StowKitPack {
|
|
|
675
691
|
throw new Error(`No metadata available: ${assetPath}`);
|
|
676
692
|
}
|
|
677
693
|
// Parse mesh data
|
|
694
|
+
const parseStart = performance.now();
|
|
678
695
|
const parsedData = MeshParser.parseMeshData(metadata, data);
|
|
696
|
+
reader.PerfLogger.log(`[Perf] Parse mesh metadata: ${(performance.now() - parseStart).toFixed(2)}ms`);
|
|
679
697
|
// Load textures for materials
|
|
698
|
+
const textureStart = performance.now();
|
|
680
699
|
await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
|
|
700
|
+
reader.PerfLogger.log(`[Perf] Load textures: ${(performance.now() - textureStart).toFixed(2)}ms`);
|
|
681
701
|
// Build Three.js scene with Draco decoder
|
|
702
|
+
const buildStart = performance.now();
|
|
682
703
|
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 =====`);
|
|
683
706
|
return scene;
|
|
684
707
|
}
|
|
685
708
|
/**
|
|
686
709
|
* Load a texture by its canonical path/name
|
|
687
710
|
*/
|
|
688
711
|
async loadTexture(assetPath) {
|
|
689
|
-
//
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
}
|
|
694
|
-
// Read texture data
|
|
695
|
-
const data = this.reader.readAssetData(assetIndex);
|
|
696
|
-
if (!data) {
|
|
697
|
-
throw new Error(`Failed to read texture data: ${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);
|
|
698
716
|
}
|
|
699
|
-
//
|
|
700
|
-
|
|
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;
|
|
701
734
|
}
|
|
702
735
|
/**
|
|
703
736
|
* Load audio by its canonical path/name
|
|
@@ -976,36 +1009,54 @@ class StowKitPack {
|
|
|
976
1009
|
* Close the pack and free resources
|
|
977
1010
|
*/
|
|
978
1011
|
dispose() {
|
|
1012
|
+
this.textureCache.clear();
|
|
979
1013
|
this.reader.close();
|
|
980
1014
|
}
|
|
981
1015
|
// Private methods moved from StowKitLoader
|
|
982
1016
|
async loadMaterialTextures(materialData, materials) {
|
|
1017
|
+
// Collect all texture load promises to load in parallel
|
|
1018
|
+
const textureLoads = [];
|
|
983
1019
|
for (let i = 0; i < materialData.length; i++) {
|
|
984
1020
|
const matData = materialData[i];
|
|
985
|
-
const material = materials[i];
|
|
986
1021
|
for (const prop of matData.properties) {
|
|
987
|
-
// Trim and check for truly non-empty texture ID
|
|
988
1022
|
const textureId = prop.textureId?.trim();
|
|
989
1023
|
if (textureId && textureId.length > 0) {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1024
|
+
textureLoads.push({
|
|
1025
|
+
materialIndex: i,
|
|
1026
|
+
propertyName: prop.fieldName,
|
|
1027
|
+
texturePromise: this.loadTexture(textureId).catch(error => {
|
|
1028
|
+
console.error(`Failed to load texture "${textureId}":`, error);
|
|
1029
|
+
throw error;
|
|
1030
|
+
})
|
|
1031
|
+
});
|
|
997
1032
|
}
|
|
998
1033
|
}
|
|
999
1034
|
}
|
|
1035
|
+
// Load all textures in parallel
|
|
1036
|
+
const results = await Promise.allSettled(textureLoads.map(load => load.texturePromise));
|
|
1037
|
+
// Apply successfully loaded textures
|
|
1038
|
+
for (let i = 0; i < textureLoads.length; i++) {
|
|
1039
|
+
const result = results[i];
|
|
1040
|
+
if (result.status === 'fulfilled') {
|
|
1041
|
+
const load = textureLoads[i];
|
|
1042
|
+
const material = materials[load.materialIndex];
|
|
1043
|
+
this.applyTextureToMaterial(material, load.propertyName, result.value);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1000
1046
|
}
|
|
1001
1047
|
async loadKTX2Texture(data) {
|
|
1048
|
+
performance.now();
|
|
1002
1049
|
const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
1003
1050
|
const blob = new Blob([arrayBuffer]);
|
|
1004
1051
|
const url = URL.createObjectURL(blob);
|
|
1052
|
+
reader.PerfLogger.log(`[Perf] KTX2/Basis texture size: ${data.byteLength} bytes`);
|
|
1005
1053
|
try {
|
|
1006
1054
|
return await new Promise((resolve, reject) => {
|
|
1055
|
+
const loadStart = performance.now();
|
|
1007
1056
|
this.ktx2Loader.load(url, (texture) => {
|
|
1008
1057
|
URL.revokeObjectURL(url);
|
|
1058
|
+
const loadEnd = performance.now();
|
|
1059
|
+
reader.PerfLogger.log(`[Perf] Basis transcode: ${(loadEnd - loadStart).toFixed(2)}ms (resolution: ${texture.image?.width || '?'}x${texture.image?.height || '?'})`);
|
|
1009
1060
|
texture.needsUpdate = true;
|
|
1010
1061
|
resolve(texture);
|
|
1011
1062
|
}, undefined, (error) => {
|
|
@@ -1138,6 +1189,10 @@ Object.defineProperty(exports, 'AssetType', {
|
|
|
1138
1189
|
enumerable: true,
|
|
1139
1190
|
get: function () { return reader.AssetType; }
|
|
1140
1191
|
});
|
|
1192
|
+
Object.defineProperty(exports, 'PerfLogger', {
|
|
1193
|
+
enumerable: true,
|
|
1194
|
+
get: function () { return reader.PerfLogger; }
|
|
1195
|
+
});
|
|
1141
1196
|
exports.StowKitLoader = StowKitLoader;
|
|
1142
1197
|
exports.StowKitPack = StowKitPack;
|
|
1143
1198
|
//# sourceMappingURL=stowkit-three-loader.js.map
|