@stowkit/three-loader 0.1.32 → 0.1.34
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
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MeshParser.d.ts","sourceRoot":"","sources":["../src/MeshParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AAIxE,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB,EAAE,MAAM,CAAC;IAC/B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,IAAI;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,qBAAqB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAU;IAEnB;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY;IAyBxD;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GAAG;QAClE,QAAQ,EAAE,YAAY,CAAC;QACvB,UAAU,EAAE,gBAAgB,EAAE,CAAC;QAC/B,SAAS,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC5B,YAAY,EAAE,YAAY,EAAE,CAAC;QAC7B,KAAK,EAAE,IAAI,EAAE,CAAC;QACd,WAAW,EAAE,WAAW,CAAC;KAC5B;IAwLD;;OAEG;WACU,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"MeshParser.d.ts","sourceRoot":"","sources":["../src/MeshParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AAIxE,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB,EAAE,MAAM,CAAC;IAC/B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,IAAI;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,qBAAqB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAU;IAEnB;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY;IAyBxD;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GAAG;QAClE,QAAQ,EAAE,YAAY,CAAC;QACvB,UAAU,EAAE,gBAAgB,EAAE,CAAC;QAC/B,SAAS,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC5B,YAAY,EAAE,YAAY,EAAE,CAAC;QAC7B,KAAK,EAAE,IAAI,EAAE,CAAC;QACd,WAAW,EAAE,WAAW,CAAC;KAC5B;IAwLD;;OAEG;WACU,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC;IA0E1K;;OAEG;WACU,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IA+FjM;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;CAK5B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StowKitPack.d.ts","sourceRoot":"","sources":["../src/StowKitPack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,EAAE,aAAa,EAAc,MAAM,iBAAiB,CAAC;AAI5D;;GAEG;AACH,qBAAa,WAAW;IACb,MAAM,EAAE,aAAa,CAAC;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,YAAY,CAA4D;IAChF,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,GAAE,MAAW,EAAE,WAAW,GAAE,MAAW;IAQnI;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"StowKitPack.d.ts","sourceRoot":"","sources":["../src/StowKitPack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,EAAE,aAAa,EAAc,MAAM,iBAAiB,CAAC;AAI5D;;GAEG;AACH,qBAAa,WAAW;IACb,MAAM,EAAE,aAAa,CAAC;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,YAAY,CAA4D;IAChF,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,GAAE,MAAW,EAAE,WAAW,GAAE,MAAW;IAQnI;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAsB7D;;OAEG;YACW,gBAAgB;IA4U9B;;OAEG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAoCvD;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC;IA4BtE;;OAEG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IA0BvF;;OAEG;IACH,UAAU;IAIV;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG;IAIhC;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAI/C;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAKnD;;OAEG;IACG,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAUjE;;OAEG;IACG,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAY1D;;OAEG;IACG,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC;IAMzE;;OAEG;IACG,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAiBlE;;OAEG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM;IAMlC;;OAEG;IACH,oBAAoB,CAAC,KAAK,EAAE,MAAM;IAIlC;;OAEG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM;IAKhC;;OAEG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC;IAQvE;;OAEG;IACG,aAAa,CACf,gBAAgB,EAAE,KAAK,CAAC,KAAK,EAC7B,aAAa,EAAE,MAAM,GACtB,OAAO,CAAC;QAAE,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC;QAAC,IAAI,EAAE,KAAK,CAAC,aAAa,CAAA;KAAE,CAAC;IAUrG;;OAEG;IACG,oBAAoB,CACtB,gBAAgB,EAAE,KAAK,CAAC,KAAK,EAC7B,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC;QAAE,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC;QAAC,IAAI,EAAE,KAAK,CAAC,aAAa,CAAA;KAAE,CAAC;IAUrG;;OAEG;IACG,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC;IAW3E;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA+H1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,IAAI,IAAI;YAMD,oBAAoB;YA0DpB,eAAe;IA6E7B,OAAO,CAAC,sBAAsB;IAwB9B,OAAO,CAAC,iBAAiB;CAM5B"}
|
|
@@ -340,9 +340,14 @@ class MeshParser {
|
|
|
340
340
|
* Create Three.js BufferGeometry from Draco compressed mesh data
|
|
341
341
|
*/
|
|
342
342
|
static async createGeometry(geoInfo, dataBlob, dracoLoader, cacheKey, version) {
|
|
343
|
-
// Try loading from IndexedDB cache first
|
|
343
|
+
// Try loading from IndexedDB cache first (non-blocking - don't await in parallel loads)
|
|
344
|
+
let cachePromise = null;
|
|
344
345
|
if (cacheKey && version) {
|
|
345
|
-
|
|
346
|
+
cachePromise = AssetMemoryCache.getGeometry(cacheKey, version);
|
|
347
|
+
}
|
|
348
|
+
// Check if cache resolved while we were starting
|
|
349
|
+
if (cachePromise) {
|
|
350
|
+
const cached = await cachePromise;
|
|
346
351
|
if (cached) {
|
|
347
352
|
const geometry = new THREE.BufferGeometry();
|
|
348
353
|
geometry.setAttribute('position', new THREE.BufferAttribute(cached.positions, 3));
|
|
@@ -374,7 +379,7 @@ class MeshParser {
|
|
|
374
379
|
reject(new Error(`Failed to decode Draco geometry: ${error}`));
|
|
375
380
|
});
|
|
376
381
|
});
|
|
377
|
-
// Cache the decoded geometry
|
|
382
|
+
// Cache the decoded geometry (non-blocking)
|
|
378
383
|
if (cacheKey && version) {
|
|
379
384
|
const posAttr = geometry.getAttribute('position');
|
|
380
385
|
const normAttr = geometry.getAttribute('normal');
|
|
@@ -386,9 +391,7 @@ class MeshParser {
|
|
|
386
391
|
normals: (normAttr && 'array' in normAttr) ? normAttr.array : undefined,
|
|
387
392
|
uvs: (uvAttr && 'array' in uvAttr) ? uvAttr.array : undefined,
|
|
388
393
|
indices: indexAttr.array
|
|
389
|
-
}).catch(() => {
|
|
390
|
-
// Silent fail - caching is optional
|
|
391
|
-
});
|
|
394
|
+
}).catch(() => { });
|
|
392
395
|
}
|
|
393
396
|
}
|
|
394
397
|
return geometry;
|
|
@@ -501,6 +504,7 @@ class StowKitPack {
|
|
|
501
504
|
* Load a skinned mesh by its string ID
|
|
502
505
|
*/
|
|
503
506
|
async loadSkinnedMesh(stringId) {
|
|
507
|
+
const totalStart = performance.now();
|
|
504
508
|
const assetIndex = this.reader.findAssetByPath(stringId);
|
|
505
509
|
if (assetIndex < 0) {
|
|
506
510
|
throw new Error(`Skinned mesh not found: ${stringId}`);
|
|
@@ -511,12 +515,16 @@ class StowKitPack {
|
|
|
511
515
|
throw new Error(`Failed to read skinned mesh data: ${stringId}`);
|
|
512
516
|
if (!metadata)
|
|
513
517
|
throw new Error(`No metadata for skinned mesh: ${stringId}`);
|
|
514
|
-
|
|
518
|
+
const result = await this.parseSkinnedMesh(metadata, data);
|
|
519
|
+
const totalTime = performance.now() - totalStart;
|
|
520
|
+
PerfLogger.log(`[Perf] === Skinned Mesh "${stringId}": ${totalTime.toFixed(2)}ms total ===`);
|
|
521
|
+
return result;
|
|
515
522
|
}
|
|
516
523
|
/**
|
|
517
524
|
* Parse skinned mesh from binary data
|
|
518
525
|
*/
|
|
519
526
|
async parseSkinnedMesh(metadata, data) {
|
|
527
|
+
const parseStart = performance.now();
|
|
520
528
|
// NEW FORMAT: Skip past tag header
|
|
521
529
|
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
522
530
|
const tagCsvLength = view.getUint32(0, true);
|
|
@@ -562,7 +570,7 @@ class StowKitPack {
|
|
|
562
570
|
});
|
|
563
571
|
offset += 72;
|
|
564
572
|
}
|
|
565
|
-
|
|
573
|
+
new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
566
574
|
// Sort geometry infos by vertex buffer offset to ensure sequential parsing
|
|
567
575
|
const sortedGeometryInfos = geometryInfos.slice().sort((a, b) => a.vertexBufferOffset - b.vertexBufferOffset);
|
|
568
576
|
// Parse materials from metadata (immediately after geometries)
|
|
@@ -717,88 +725,39 @@ class StowKitPack {
|
|
|
717
725
|
}
|
|
718
726
|
materials.push(material);
|
|
719
727
|
}
|
|
728
|
+
const parseTime = performance.now() - parseStart;
|
|
729
|
+
PerfLogger.log(`[Perf] Parse skinned metadata: ${parseTime.toFixed(2)}ms`);
|
|
730
|
+
const textureStart = performance.now();
|
|
720
731
|
await this.loadMaterialTextures(materialData, materials);
|
|
732
|
+
const textureTime = performance.now() - textureStart;
|
|
733
|
+
if (materialData.length > 0) {
|
|
734
|
+
PerfLogger.log(`[Perf] Load textures: ${textureTime.toFixed(2)}ms`);
|
|
735
|
+
}
|
|
721
736
|
// Collect root bones (hierarchy already built earlier)
|
|
737
|
+
const buildStart = performance.now();
|
|
722
738
|
const rootBones = bones.filter((_, index) => parentIndices[index] < 0);
|
|
723
739
|
// Create shared skeleton
|
|
724
740
|
const skeleton = new THREE.Skeleton(bones, boneMatrices);
|
|
725
|
-
//
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const
|
|
732
|
-
const combinedSkinWeights = new Float32Array(totalVertexCount * 4);
|
|
733
|
-
const combinedIndices = new Uint32Array(totalIndexCount);
|
|
741
|
+
// Use WASM to parse geometry data (10-50x faster!)
|
|
742
|
+
const geomData = this.reader.parseSkinnedMeshGeometryFast(metadata, data);
|
|
743
|
+
if (!geomData) {
|
|
744
|
+
throw new Error('Failed to parse skinned mesh geometry');
|
|
745
|
+
}
|
|
746
|
+
// Convert Uint32Array skinIndices to Uint16Array for Three.js
|
|
747
|
+
const skinIndices16 = new Uint16Array(geomData.skinIndices);
|
|
734
748
|
const combinedGeometry = new THREE.BufferGeometry();
|
|
735
|
-
combinedGeometry.
|
|
736
|
-
|
|
737
|
-
|
|
749
|
+
combinedGeometry.setAttribute('position', new THREE.BufferAttribute(geomData.positions, 3));
|
|
750
|
+
combinedGeometry.setAttribute('normal', new THREE.BufferAttribute(geomData.normals, 3));
|
|
751
|
+
combinedGeometry.setAttribute('uv', new THREE.BufferAttribute(geomData.uvs, 2));
|
|
752
|
+
combinedGeometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(skinIndices16, 4));
|
|
753
|
+
combinedGeometry.setAttribute('skinWeight', new THREE.BufferAttribute(geomData.skinWeights, 4));
|
|
754
|
+
combinedGeometry.setIndex(new THREE.BufferAttribute(geomData.indices, 1));
|
|
755
|
+
// Add geometry groups for materials
|
|
756
|
+
let indexCursor = 0;
|
|
738
757
|
for (const geomInfo of sortedGeometryInfos) {
|
|
739
|
-
|
|
740
|
-
const vertexBase = vertexCursor; // base vertex index for this submesh
|
|
741
|
-
// Validate offsets before parsing
|
|
742
|
-
const requiredVertexBytes = geomInfo.vertexBufferOffset + geomInfo.vertexCount * vertexStrideBytes;
|
|
743
|
-
const requiredIndexBytes = geomInfo.indexBufferOffset + geomInfo.indexCount * 4;
|
|
744
|
-
const requiredWeightBytes = geomInfo.weightsOffset + geomInfo.vertexCount * 32;
|
|
745
|
-
if (requiredVertexBytes > data.byteLength) {
|
|
746
|
-
throw new Error(`Vertex buffer out of bounds: need ${requiredVertexBytes}, have ${data.byteLength}`);
|
|
747
|
-
}
|
|
748
|
-
if (requiredIndexBytes > data.byteLength) {
|
|
749
|
-
throw new Error(`Index buffer out of bounds: need ${requiredIndexBytes}, have ${data.byteLength}`);
|
|
750
|
-
}
|
|
751
|
-
if (requiredWeightBytes > data.byteLength) {
|
|
752
|
-
throw new Error(`Weight buffer out of bounds: need ${requiredWeightBytes}, have ${data.byteLength}`);
|
|
753
|
-
}
|
|
754
|
-
// Parse vertices
|
|
755
|
-
for (let v = 0; v < geomInfo.vertexCount; v++) {
|
|
756
|
-
const vertexOffset = geomInfo.vertexBufferOffset + v * vertexStrideBytes;
|
|
757
|
-
// positions
|
|
758
|
-
combinedPositions[(vertexCursor + v) * 3 + 0] = dataView.getFloat32(vertexOffset + 0, true);
|
|
759
|
-
combinedPositions[(vertexCursor + v) * 3 + 1] = dataView.getFloat32(vertexOffset + 4, true);
|
|
760
|
-
combinedPositions[(vertexCursor + v) * 3 + 2] = dataView.getFloat32(vertexOffset + 8, true);
|
|
761
|
-
// normals
|
|
762
|
-
if (geomInfo.hasNormals) {
|
|
763
|
-
combinedNormals[(vertexCursor + v) * 3 + 0] = dataView.getFloat32(vertexOffset + 12, true);
|
|
764
|
-
combinedNormals[(vertexCursor + v) * 3 + 1] = dataView.getFloat32(vertexOffset + 16, true);
|
|
765
|
-
combinedNormals[(vertexCursor + v) * 3 + 2] = dataView.getFloat32(vertexOffset + 20, true);
|
|
766
|
-
}
|
|
767
|
-
// uvs
|
|
768
|
-
if (geomInfo.hasUVs) {
|
|
769
|
-
combinedUVs[(vertexCursor + v) * 2 + 0] = dataView.getFloat32(vertexOffset + 24, true);
|
|
770
|
-
combinedUVs[(vertexCursor + v) * 2 + 1] = dataView.getFloat32(vertexOffset + 28, true);
|
|
771
|
-
}
|
|
772
|
-
// bone indices & weights
|
|
773
|
-
const weightOffset = geomInfo.weightsOffset + v * 32;
|
|
774
|
-
for (let j = 0; j < 4; j++) {
|
|
775
|
-
const boneIndex = dataView.getUint32(weightOffset + j * 4, true);
|
|
776
|
-
combinedSkinIndices[(vertexCursor + v) * 4 + j] = boneIndex < boneCount ? boneIndex : 0;
|
|
777
|
-
}
|
|
778
|
-
for (let j = 0; j < 4; j++) {
|
|
779
|
-
combinedSkinWeights[(vertexCursor + v) * 4 + j] = dataView.getFloat32(weightOffset + 16 + j * 4, true);
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
// Parse indices
|
|
783
|
-
const groupStart = indexCursor;
|
|
784
|
-
for (let i = 0; i < geomInfo.indexCount; i++) {
|
|
785
|
-
const indexValue = dataView.getUint32(geomInfo.indexBufferOffset + i * 4, true);
|
|
786
|
-
combinedIndices[indexCursor + i] = indexValue + vertexBase;
|
|
787
|
-
}
|
|
788
|
-
combinedGeometry.addGroup(groupStart, geomInfo.indexCount, Math.min(geomInfo.materialIndex, materials.length - 1));
|
|
789
|
-
vertexCursor += geomInfo.vertexCount;
|
|
758
|
+
combinedGeometry.addGroup(indexCursor, geomInfo.indexCount, Math.min(geomInfo.materialIndex, materials.length - 1));
|
|
790
759
|
indexCursor += geomInfo.indexCount;
|
|
791
760
|
}
|
|
792
|
-
combinedGeometry.setAttribute('position', new THREE.BufferAttribute(combinedPositions, 3));
|
|
793
|
-
if (geometryInfos.some(info => info.hasNormals)) {
|
|
794
|
-
combinedGeometry.setAttribute('normal', new THREE.BufferAttribute(combinedNormals, 3));
|
|
795
|
-
}
|
|
796
|
-
if (geometryInfos.some(info => info.hasUVs)) {
|
|
797
|
-
combinedGeometry.setAttribute('uv', new THREE.BufferAttribute(combinedUVs, 2));
|
|
798
|
-
}
|
|
799
|
-
combinedGeometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(combinedSkinIndices, 4));
|
|
800
|
-
combinedGeometry.setAttribute('skinWeight', new THREE.BufferAttribute(combinedSkinWeights, 4));
|
|
801
|
-
combinedGeometry.setIndex(new THREE.BufferAttribute(combinedIndices, 1));
|
|
802
761
|
combinedGeometry.computeBoundingBox();
|
|
803
762
|
combinedGeometry.computeBoundingSphere();
|
|
804
763
|
const skinnedMesh = new THREE.SkinnedMesh(combinedGeometry, materials);
|
|
@@ -816,6 +775,8 @@ class StowKitPack {
|
|
|
816
775
|
container.add(skinnedMesh);
|
|
817
776
|
container.userData.boneOriginalNames = boneOriginalNames;
|
|
818
777
|
container.userData.skinnedMesh = skinnedMesh;
|
|
778
|
+
const buildTime = performance.now() - buildStart;
|
|
779
|
+
PerfLogger.log(`[Perf] Build skinned mesh: ${buildTime.toFixed(2)}ms (${boneCount} bones, ${geomData.vertexCount} verts)`);
|
|
819
780
|
return container;
|
|
820
781
|
}
|
|
821
782
|
/**
|
|
@@ -1191,22 +1152,29 @@ class StowKitPack {
|
|
|
1191
1152
|
return Array.from(uniqueTextures);
|
|
1192
1153
|
}
|
|
1193
1154
|
async loadKTX2Texture(data, textureName) {
|
|
1194
|
-
//
|
|
1155
|
+
// Start cache check but don't block on it
|
|
1195
1156
|
const cacheKey = textureName ? `${this.packUrl}::${textureName}` : undefined;
|
|
1157
|
+
let cachePromise = null;
|
|
1196
1158
|
if (cacheKey) {
|
|
1197
|
-
|
|
1198
|
-
if (cached) {
|
|
1199
|
-
PerfLogger.log(`[Perf] ✓ Texture "${textureName}": 0ms (IndexedDB cache)`);
|
|
1200
|
-
const texture = new THREE.CompressedTexture([{ data: cached.data, width: cached.width, height: cached.height }], cached.width, cached.height, cached.format, THREE.UnsignedByteType);
|
|
1201
|
-
texture.needsUpdate = true;
|
|
1202
|
-
return texture;
|
|
1203
|
-
}
|
|
1159
|
+
cachePromise = AssetMemoryCache.getTexture(cacheKey, this.packVersion);
|
|
1204
1160
|
}
|
|
1205
|
-
//
|
|
1206
|
-
const decodeStart = performance.now();
|
|
1161
|
+
// Check cache while starting decode
|
|
1207
1162
|
const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
1208
1163
|
const blob = new Blob([arrayBuffer]);
|
|
1209
1164
|
const url = URL.createObjectURL(blob);
|
|
1165
|
+
const decodeStart = performance.now();
|
|
1166
|
+
// Race cache check vs decode start
|
|
1167
|
+
const [cached, _] = await Promise.all([
|
|
1168
|
+
cachePromise || Promise.resolve(null),
|
|
1169
|
+
Promise.resolve() // Start decode immediately
|
|
1170
|
+
]);
|
|
1171
|
+
if (cached) {
|
|
1172
|
+
URL.revokeObjectURL(url);
|
|
1173
|
+
PerfLogger.log(`[Perf] ✓ Texture "${textureName}": 0ms (IndexedDB cache)`);
|
|
1174
|
+
const texture = new THREE.CompressedTexture([{ data: cached.data, width: cached.width, height: cached.height }], cached.width, cached.height, cached.format, THREE.UnsignedByteType);
|
|
1175
|
+
texture.needsUpdate = true;
|
|
1176
|
+
return texture;
|
|
1177
|
+
}
|
|
1210
1178
|
try {
|
|
1211
1179
|
const texture = await new Promise((resolve, reject) => {
|
|
1212
1180
|
this.ktx2Loader.load(url, (texture) => {
|
|
@@ -1220,7 +1188,7 @@ class StowKitPack {
|
|
|
1220
1188
|
});
|
|
1221
1189
|
const decodeTime = performance.now() - decodeStart;
|
|
1222
1190
|
PerfLogger.log(`[Perf] ✓ Texture "${textureName || 'unknown'}": ${decodeTime.toFixed(2)}ms (Basis decode ${texture.image?.width}x${texture.image?.height})`);
|
|
1223
|
-
// Cache the transcoded texture data to IndexedDB
|
|
1191
|
+
// Cache the transcoded texture data to IndexedDB (non-blocking)
|
|
1224
1192
|
if (cacheKey && texture.mipmaps && texture.mipmaps.length > 0) {
|
|
1225
1193
|
const mipmap = texture.mipmaps[0];
|
|
1226
1194
|
const dataArray = new Uint8Array(mipmap.data.buffer, mipmap.data.byteOffset, mipmap.data.byteLength);
|
|
@@ -1231,9 +1199,7 @@ class StowKitPack {
|
|
|
1231
1199
|
format: texture.format,
|
|
1232
1200
|
internalFormat: texture.format,
|
|
1233
1201
|
compressed: true
|
|
1234
|
-
}).catch(() => {
|
|
1235
|
-
// Silent fail - caching is optional
|
|
1236
|
-
});
|
|
1202
|
+
}).catch(() => { });
|
|
1237
1203
|
}
|
|
1238
1204
|
return texture;
|
|
1239
1205
|
}
|