@stowkit/three-loader 0.1.31 → 0.1.33
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;IAsE1K;;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":"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"}
|
|
@@ -344,7 +344,6 @@ class MeshParser {
|
|
|
344
344
|
if (cacheKey && version) {
|
|
345
345
|
const cached = await AssetMemoryCache.getGeometry(cacheKey, version);
|
|
346
346
|
if (cached) {
|
|
347
|
-
PerfLogger.log(`[Perf] Geometry cache hit: ${cacheKey}`);
|
|
348
347
|
const geometry = new THREE.BufferGeometry();
|
|
349
348
|
geometry.setAttribute('position', new THREE.BufferAttribute(cached.positions, 3));
|
|
350
349
|
if (cached.normals) {
|
|
@@ -402,12 +401,8 @@ class MeshParser {
|
|
|
402
401
|
root.name = 'StowKitMesh';
|
|
403
402
|
const { geometries, materials, nodes, meshIndices } = parsedData;
|
|
404
403
|
// Pre-load ALL geometries in parallel for maximum speed
|
|
405
|
-
const dracoStart = performance.now();
|
|
406
404
|
const geometryPromises = geometries.map((geoInfo, index) => this.createGeometry(geoInfo, dataBlob, dracoLoader, cacheKeyPrefix ? `${cacheKeyPrefix}_geo${index}` : undefined, version));
|
|
407
405
|
const loadedGeometries = await Promise.all(geometryPromises);
|
|
408
|
-
const dracoTime = performance.now() - dracoStart;
|
|
409
|
-
const totalVerts = geometries.reduce((sum, g) => sum + g.vertexCount, 0);
|
|
410
|
-
PerfLogger.log(`[Perf] Draco: ${dracoTime.toFixed(2)}ms (${geometries.length} meshes, ${totalVerts} verts)`);
|
|
411
406
|
// Create all Three.js objects for nodes
|
|
412
407
|
const nodeObjects = [];
|
|
413
408
|
for (const node of nodes) {
|
|
@@ -506,6 +501,7 @@ class StowKitPack {
|
|
|
506
501
|
* Load a skinned mesh by its string ID
|
|
507
502
|
*/
|
|
508
503
|
async loadSkinnedMesh(stringId) {
|
|
504
|
+
const totalStart = performance.now();
|
|
509
505
|
const assetIndex = this.reader.findAssetByPath(stringId);
|
|
510
506
|
if (assetIndex < 0) {
|
|
511
507
|
throw new Error(`Skinned mesh not found: ${stringId}`);
|
|
@@ -516,12 +512,16 @@ class StowKitPack {
|
|
|
516
512
|
throw new Error(`Failed to read skinned mesh data: ${stringId}`);
|
|
517
513
|
if (!metadata)
|
|
518
514
|
throw new Error(`No metadata for skinned mesh: ${stringId}`);
|
|
519
|
-
|
|
515
|
+
const result = await this.parseSkinnedMesh(metadata, data);
|
|
516
|
+
const totalTime = performance.now() - totalStart;
|
|
517
|
+
PerfLogger.log(`[Perf] === Skinned Mesh "${stringId}": ${totalTime.toFixed(2)}ms total ===`);
|
|
518
|
+
return result;
|
|
520
519
|
}
|
|
521
520
|
/**
|
|
522
521
|
* Parse skinned mesh from binary data
|
|
523
522
|
*/
|
|
524
523
|
async parseSkinnedMesh(metadata, data) {
|
|
524
|
+
const parseStart = performance.now();
|
|
525
525
|
// NEW FORMAT: Skip past tag header
|
|
526
526
|
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
527
527
|
const tagCsvLength = view.getUint32(0, true);
|
|
@@ -567,7 +567,7 @@ class StowKitPack {
|
|
|
567
567
|
});
|
|
568
568
|
offset += 72;
|
|
569
569
|
}
|
|
570
|
-
|
|
570
|
+
new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
571
571
|
// Sort geometry infos by vertex buffer offset to ensure sequential parsing
|
|
572
572
|
const sortedGeometryInfos = geometryInfos.slice().sort((a, b) => a.vertexBufferOffset - b.vertexBufferOffset);
|
|
573
573
|
// Parse materials from metadata (immediately after geometries)
|
|
@@ -722,88 +722,39 @@ class StowKitPack {
|
|
|
722
722
|
}
|
|
723
723
|
materials.push(material);
|
|
724
724
|
}
|
|
725
|
+
const parseTime = performance.now() - parseStart;
|
|
726
|
+
PerfLogger.log(`[Perf] Parse skinned metadata: ${parseTime.toFixed(2)}ms`);
|
|
727
|
+
const textureStart = performance.now();
|
|
725
728
|
await this.loadMaterialTextures(materialData, materials);
|
|
729
|
+
const textureTime = performance.now() - textureStart;
|
|
730
|
+
if (materialData.length > 0) {
|
|
731
|
+
PerfLogger.log(`[Perf] Load textures: ${textureTime.toFixed(2)}ms`);
|
|
732
|
+
}
|
|
726
733
|
// Collect root bones (hierarchy already built earlier)
|
|
734
|
+
const buildStart = performance.now();
|
|
727
735
|
const rootBones = bones.filter((_, index) => parentIndices[index] < 0);
|
|
728
736
|
// Create shared skeleton
|
|
729
737
|
const skeleton = new THREE.Skeleton(bones, boneMatrices);
|
|
730
|
-
//
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
const
|
|
737
|
-
const combinedSkinWeights = new Float32Array(totalVertexCount * 4);
|
|
738
|
-
const combinedIndices = new Uint32Array(totalIndexCount);
|
|
738
|
+
// Use WASM to parse geometry data (10-50x faster!)
|
|
739
|
+
const geomData = this.reader.parseSkinnedMeshGeometryFast(metadata, data);
|
|
740
|
+
if (!geomData) {
|
|
741
|
+
throw new Error('Failed to parse skinned mesh geometry');
|
|
742
|
+
}
|
|
743
|
+
// Convert Uint32Array skinIndices to Uint16Array for Three.js
|
|
744
|
+
const skinIndices16 = new Uint16Array(geomData.skinIndices);
|
|
739
745
|
const combinedGeometry = new THREE.BufferGeometry();
|
|
740
|
-
combinedGeometry.
|
|
741
|
-
|
|
742
|
-
|
|
746
|
+
combinedGeometry.setAttribute('position', new THREE.BufferAttribute(geomData.positions, 3));
|
|
747
|
+
combinedGeometry.setAttribute('normal', new THREE.BufferAttribute(geomData.normals, 3));
|
|
748
|
+
combinedGeometry.setAttribute('uv', new THREE.BufferAttribute(geomData.uvs, 2));
|
|
749
|
+
combinedGeometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(skinIndices16, 4));
|
|
750
|
+
combinedGeometry.setAttribute('skinWeight', new THREE.BufferAttribute(geomData.skinWeights, 4));
|
|
751
|
+
combinedGeometry.setIndex(new THREE.BufferAttribute(geomData.indices, 1));
|
|
752
|
+
// Add geometry groups for materials
|
|
753
|
+
let indexCursor = 0;
|
|
743
754
|
for (const geomInfo of sortedGeometryInfos) {
|
|
744
|
-
|
|
745
|
-
const vertexBase = vertexCursor; // base vertex index for this submesh
|
|
746
|
-
// Validate offsets before parsing
|
|
747
|
-
const requiredVertexBytes = geomInfo.vertexBufferOffset + geomInfo.vertexCount * vertexStrideBytes;
|
|
748
|
-
const requiredIndexBytes = geomInfo.indexBufferOffset + geomInfo.indexCount * 4;
|
|
749
|
-
const requiredWeightBytes = geomInfo.weightsOffset + geomInfo.vertexCount * 32;
|
|
750
|
-
if (requiredVertexBytes > data.byteLength) {
|
|
751
|
-
throw new Error(`Vertex buffer out of bounds: need ${requiredVertexBytes}, have ${data.byteLength}`);
|
|
752
|
-
}
|
|
753
|
-
if (requiredIndexBytes > data.byteLength) {
|
|
754
|
-
throw new Error(`Index buffer out of bounds: need ${requiredIndexBytes}, have ${data.byteLength}`);
|
|
755
|
-
}
|
|
756
|
-
if (requiredWeightBytes > data.byteLength) {
|
|
757
|
-
throw new Error(`Weight buffer out of bounds: need ${requiredWeightBytes}, have ${data.byteLength}`);
|
|
758
|
-
}
|
|
759
|
-
// Parse vertices
|
|
760
|
-
for (let v = 0; v < geomInfo.vertexCount; v++) {
|
|
761
|
-
const vertexOffset = geomInfo.vertexBufferOffset + v * vertexStrideBytes;
|
|
762
|
-
// positions
|
|
763
|
-
combinedPositions[(vertexCursor + v) * 3 + 0] = dataView.getFloat32(vertexOffset + 0, true);
|
|
764
|
-
combinedPositions[(vertexCursor + v) * 3 + 1] = dataView.getFloat32(vertexOffset + 4, true);
|
|
765
|
-
combinedPositions[(vertexCursor + v) * 3 + 2] = dataView.getFloat32(vertexOffset + 8, true);
|
|
766
|
-
// normals
|
|
767
|
-
if (geomInfo.hasNormals) {
|
|
768
|
-
combinedNormals[(vertexCursor + v) * 3 + 0] = dataView.getFloat32(vertexOffset + 12, true);
|
|
769
|
-
combinedNormals[(vertexCursor + v) * 3 + 1] = dataView.getFloat32(vertexOffset + 16, true);
|
|
770
|
-
combinedNormals[(vertexCursor + v) * 3 + 2] = dataView.getFloat32(vertexOffset + 20, true);
|
|
771
|
-
}
|
|
772
|
-
// uvs
|
|
773
|
-
if (geomInfo.hasUVs) {
|
|
774
|
-
combinedUVs[(vertexCursor + v) * 2 + 0] = dataView.getFloat32(vertexOffset + 24, true);
|
|
775
|
-
combinedUVs[(vertexCursor + v) * 2 + 1] = dataView.getFloat32(vertexOffset + 28, true);
|
|
776
|
-
}
|
|
777
|
-
// bone indices & weights
|
|
778
|
-
const weightOffset = geomInfo.weightsOffset + v * 32;
|
|
779
|
-
for (let j = 0; j < 4; j++) {
|
|
780
|
-
const boneIndex = dataView.getUint32(weightOffset + j * 4, true);
|
|
781
|
-
combinedSkinIndices[(vertexCursor + v) * 4 + j] = boneIndex < boneCount ? boneIndex : 0;
|
|
782
|
-
}
|
|
783
|
-
for (let j = 0; j < 4; j++) {
|
|
784
|
-
combinedSkinWeights[(vertexCursor + v) * 4 + j] = dataView.getFloat32(weightOffset + 16 + j * 4, true);
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
// Parse indices
|
|
788
|
-
const groupStart = indexCursor;
|
|
789
|
-
for (let i = 0; i < geomInfo.indexCount; i++) {
|
|
790
|
-
const indexValue = dataView.getUint32(geomInfo.indexBufferOffset + i * 4, true);
|
|
791
|
-
combinedIndices[indexCursor + i] = indexValue + vertexBase;
|
|
792
|
-
}
|
|
793
|
-
combinedGeometry.addGroup(groupStart, geomInfo.indexCount, Math.min(geomInfo.materialIndex, materials.length - 1));
|
|
794
|
-
vertexCursor += geomInfo.vertexCount;
|
|
755
|
+
combinedGeometry.addGroup(indexCursor, geomInfo.indexCount, Math.min(geomInfo.materialIndex, materials.length - 1));
|
|
795
756
|
indexCursor += geomInfo.indexCount;
|
|
796
757
|
}
|
|
797
|
-
combinedGeometry.setAttribute('position', new THREE.BufferAttribute(combinedPositions, 3));
|
|
798
|
-
if (geometryInfos.some(info => info.hasNormals)) {
|
|
799
|
-
combinedGeometry.setAttribute('normal', new THREE.BufferAttribute(combinedNormals, 3));
|
|
800
|
-
}
|
|
801
|
-
if (geometryInfos.some(info => info.hasUVs)) {
|
|
802
|
-
combinedGeometry.setAttribute('uv', new THREE.BufferAttribute(combinedUVs, 2));
|
|
803
|
-
}
|
|
804
|
-
combinedGeometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(combinedSkinIndices, 4));
|
|
805
|
-
combinedGeometry.setAttribute('skinWeight', new THREE.BufferAttribute(combinedSkinWeights, 4));
|
|
806
|
-
combinedGeometry.setIndex(new THREE.BufferAttribute(combinedIndices, 1));
|
|
807
758
|
combinedGeometry.computeBoundingBox();
|
|
808
759
|
combinedGeometry.computeBoundingSphere();
|
|
809
760
|
const skinnedMesh = new THREE.SkinnedMesh(combinedGeometry, materials);
|
|
@@ -821,6 +772,8 @@ class StowKitPack {
|
|
|
821
772
|
container.add(skinnedMesh);
|
|
822
773
|
container.userData.boneOriginalNames = boneOriginalNames;
|
|
823
774
|
container.userData.skinnedMesh = skinnedMesh;
|
|
775
|
+
const buildTime = performance.now() - buildStart;
|
|
776
|
+
PerfLogger.log(`[Perf] Build skinned mesh: ${buildTime.toFixed(2)}ms (${boneCount} bones, ${geomData.vertexCount} verts)`);
|
|
824
777
|
return container;
|
|
825
778
|
}
|
|
826
779
|
/**
|
|
@@ -845,12 +798,7 @@ class StowKitPack {
|
|
|
845
798
|
// Parse mesh data
|
|
846
799
|
const parsedData = MeshParser.parseMeshData(metadata, data);
|
|
847
800
|
// Load textures for materials
|
|
848
|
-
|
|
849
|
-
const textureNames = await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
|
|
850
|
-
const textureTime = performance.now() - textureStart;
|
|
851
|
-
if (textureNames.length > 0) {
|
|
852
|
-
PerfLogger.log(`[Perf] Textures: ${textureTime.toFixed(2)}ms (${textureNames.join(', ')})`);
|
|
853
|
-
}
|
|
801
|
+
await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
|
|
854
802
|
// Build Three.js scene with Draco decoder (with caching)
|
|
855
803
|
const cacheKey = `${this.packUrl}::${assetPath}`;
|
|
856
804
|
const scene = await MeshParser.buildScene(parsedData, data, this.dracoLoader, cacheKey, this.packVersion);
|
|
@@ -1204,26 +1152,23 @@ class StowKitPack {
|
|
|
1204
1152
|
// Try IndexedDB cache first
|
|
1205
1153
|
const cacheKey = textureName ? `${this.packUrl}::${textureName}` : undefined;
|
|
1206
1154
|
if (cacheKey) {
|
|
1207
|
-
const startCache = performance.now();
|
|
1208
1155
|
const cached = await AssetMemoryCache.getTexture(cacheKey, this.packVersion);
|
|
1209
1156
|
if (cached) {
|
|
1210
|
-
PerfLogger.log(`[Perf] Texture
|
|
1157
|
+
PerfLogger.log(`[Perf] ✓ Texture "${textureName}": 0ms (IndexedDB cache)`);
|
|
1211
1158
|
const texture = new THREE.CompressedTexture([{ data: cached.data, width: cached.width, height: cached.height }], cached.width, cached.height, cached.format, THREE.UnsignedByteType);
|
|
1212
1159
|
texture.needsUpdate = true;
|
|
1213
1160
|
return texture;
|
|
1214
1161
|
}
|
|
1215
1162
|
}
|
|
1216
|
-
//
|
|
1163
|
+
// Decode using Basis transcoder
|
|
1164
|
+
const decodeStart = performance.now();
|
|
1217
1165
|
const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
1218
1166
|
const blob = new Blob([arrayBuffer]);
|
|
1219
1167
|
const url = URL.createObjectURL(blob);
|
|
1220
1168
|
try {
|
|
1221
|
-
const decodeStart = performance.now();
|
|
1222
1169
|
const texture = await new Promise((resolve, reject) => {
|
|
1223
1170
|
this.ktx2Loader.load(url, (texture) => {
|
|
1224
1171
|
URL.revokeObjectURL(url);
|
|
1225
|
-
const decodeTime = performance.now() - decodeStart;
|
|
1226
|
-
PerfLogger.log(`[Perf] Basis decode: ${decodeTime.toFixed(2)}ms (${textureName || 'unknown'}, ${texture.image?.width}x${texture.image?.height})`);
|
|
1227
1172
|
texture.needsUpdate = true;
|
|
1228
1173
|
resolve(texture);
|
|
1229
1174
|
}, undefined, (error) => {
|
|
@@ -1231,6 +1176,8 @@ class StowKitPack {
|
|
|
1231
1176
|
reject(error);
|
|
1232
1177
|
});
|
|
1233
1178
|
});
|
|
1179
|
+
const decodeTime = performance.now() - decodeStart;
|
|
1180
|
+
PerfLogger.log(`[Perf] ✓ Texture "${textureName || 'unknown'}": ${decodeTime.toFixed(2)}ms (Basis decode ${texture.image?.width}x${texture.image?.height})`);
|
|
1234
1181
|
// Cache the transcoded texture data to IndexedDB
|
|
1235
1182
|
if (cacheKey && texture.mipmaps && texture.mipmaps.length > 0) {
|
|
1236
1183
|
const mipmap = texture.mipmaps[0];
|