@stowkit/three-loader 0.1.32 → 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.
|
@@ -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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"}
|
|
@@ -501,6 +501,7 @@ class StowKitPack {
|
|
|
501
501
|
* Load a skinned mesh by its string ID
|
|
502
502
|
*/
|
|
503
503
|
async loadSkinnedMesh(stringId) {
|
|
504
|
+
const totalStart = performance.now();
|
|
504
505
|
const assetIndex = this.reader.findAssetByPath(stringId);
|
|
505
506
|
if (assetIndex < 0) {
|
|
506
507
|
throw new Error(`Skinned mesh not found: ${stringId}`);
|
|
@@ -511,12 +512,16 @@ class StowKitPack {
|
|
|
511
512
|
throw new Error(`Failed to read skinned mesh data: ${stringId}`);
|
|
512
513
|
if (!metadata)
|
|
513
514
|
throw new Error(`No metadata for skinned mesh: ${stringId}`);
|
|
514
|
-
|
|
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;
|
|
515
519
|
}
|
|
516
520
|
/**
|
|
517
521
|
* Parse skinned mesh from binary data
|
|
518
522
|
*/
|
|
519
523
|
async parseSkinnedMesh(metadata, data) {
|
|
524
|
+
const parseStart = performance.now();
|
|
520
525
|
// NEW FORMAT: Skip past tag header
|
|
521
526
|
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
522
527
|
const tagCsvLength = view.getUint32(0, true);
|
|
@@ -562,7 +567,7 @@ class StowKitPack {
|
|
|
562
567
|
});
|
|
563
568
|
offset += 72;
|
|
564
569
|
}
|
|
565
|
-
|
|
570
|
+
new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
566
571
|
// Sort geometry infos by vertex buffer offset to ensure sequential parsing
|
|
567
572
|
const sortedGeometryInfos = geometryInfos.slice().sort((a, b) => a.vertexBufferOffset - b.vertexBufferOffset);
|
|
568
573
|
// Parse materials from metadata (immediately after geometries)
|
|
@@ -717,88 +722,39 @@ class StowKitPack {
|
|
|
717
722
|
}
|
|
718
723
|
materials.push(material);
|
|
719
724
|
}
|
|
725
|
+
const parseTime = performance.now() - parseStart;
|
|
726
|
+
PerfLogger.log(`[Perf] Parse skinned metadata: ${parseTime.toFixed(2)}ms`);
|
|
727
|
+
const textureStart = performance.now();
|
|
720
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
|
+
}
|
|
721
733
|
// Collect root bones (hierarchy already built earlier)
|
|
734
|
+
const buildStart = performance.now();
|
|
722
735
|
const rootBones = bones.filter((_, index) => parentIndices[index] < 0);
|
|
723
736
|
// Create shared skeleton
|
|
724
737
|
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);
|
|
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);
|
|
734
745
|
const combinedGeometry = new THREE.BufferGeometry();
|
|
735
|
-
combinedGeometry.
|
|
736
|
-
|
|
737
|
-
|
|
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;
|
|
738
754
|
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;
|
|
755
|
+
combinedGeometry.addGroup(indexCursor, geomInfo.indexCount, Math.min(geomInfo.materialIndex, materials.length - 1));
|
|
790
756
|
indexCursor += geomInfo.indexCount;
|
|
791
757
|
}
|
|
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
758
|
combinedGeometry.computeBoundingBox();
|
|
803
759
|
combinedGeometry.computeBoundingSphere();
|
|
804
760
|
const skinnedMesh = new THREE.SkinnedMesh(combinedGeometry, materials);
|
|
@@ -816,6 +772,8 @@ class StowKitPack {
|
|
|
816
772
|
container.add(skinnedMesh);
|
|
817
773
|
container.userData.boneOriginalNames = boneOriginalNames;
|
|
818
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)`);
|
|
819
777
|
return container;
|
|
820
778
|
}
|
|
821
779
|
/**
|