@stowkit/three-loader 0.1.35 → 0.1.39
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/StowKitPack.d.ts
CHANGED
|
@@ -101,6 +101,10 @@ export declare class StowKitPack {
|
|
|
101
101
|
action: THREE.AnimationAction;
|
|
102
102
|
clip: THREE.AnimationClip;
|
|
103
103
|
}>;
|
|
104
|
+
/**
|
|
105
|
+
* Retarget animation clip to match skeleton bone names
|
|
106
|
+
*/
|
|
107
|
+
private retargetAnimationClip;
|
|
104
108
|
/**
|
|
105
109
|
* Load animation clip by index
|
|
106
110
|
*/
|
|
@@ -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;IAsB7D;;OAEG;YACW,gBAAgB;
|
|
1
|
+
{"version":3,"file":"StowKitPack.d.ts","sourceRoot":"","sources":["../src/StowKitPack.ts"],"names":[],"mappings":"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"}
|
|
@@ -590,11 +590,19 @@ class StowKitPack {
|
|
|
590
590
|
materialData.push({ name, schemaId, propertyCount, properties: [] });
|
|
591
591
|
metadataOffset += 196;
|
|
592
592
|
}
|
|
593
|
-
//
|
|
594
|
-
// Node struct:
|
|
593
|
+
// Parse Node array to calculate total mesh indices
|
|
594
|
+
// Node struct: name[64] + parent_index[4] + position[12] + rotation[16] + scale[12] + mesh_count[4] + first_mesh_index[4] = 116 bytes
|
|
595
595
|
const NODE_STRIDE = 116;
|
|
596
|
+
let totalMeshIndices = 0;
|
|
597
|
+
for (let n = 0; n < nodeCount; n++) {
|
|
598
|
+
const nodeOffset = metadataOffset + n * NODE_STRIDE;
|
|
599
|
+
// mesh_count is at offset 64 + 4 + 12 + 16 + 12 = 108 within Node struct
|
|
600
|
+
const meshCount = metaView.getUint32(nodeOffset + 108, true);
|
|
601
|
+
totalMeshIndices += meshCount;
|
|
602
|
+
}
|
|
596
603
|
metadataOffset += nodeCount * NODE_STRIDE;
|
|
597
|
-
|
|
604
|
+
// Skip node_mesh_indices array (totalMeshIndices entries, not nodeCount!)
|
|
605
|
+
metadataOffset += totalMeshIndices * 4;
|
|
598
606
|
// Parse material properties
|
|
599
607
|
const PROPERTY_STRIDE = 144;
|
|
600
608
|
for (let i = 0; i < materialCount; i++) {
|
|
@@ -672,7 +680,8 @@ class StowKitPack {
|
|
|
672
680
|
const bone = bones[i];
|
|
673
681
|
const bindPose = bindPoses[i];
|
|
674
682
|
const parentIndex = parentIndices[i];
|
|
675
|
-
if (parentIndex < 0) {
|
|
683
|
+
if (parentIndex < 0 || parentIndex >= bindPoses.length) {
|
|
684
|
+
// Root bone or invalid parent - use bind pose directly
|
|
676
685
|
const pos = new THREE.Vector3();
|
|
677
686
|
const quat = new THREE.Quaternion();
|
|
678
687
|
const scale = new THREE.Vector3();
|
|
@@ -702,7 +711,9 @@ class StowKitPack {
|
|
|
702
711
|
const material = new THREE.MeshStandardMaterial({
|
|
703
712
|
side: THREE.DoubleSide,
|
|
704
713
|
name: mat.name || `Material_${i}`,
|
|
705
|
-
color: new THREE.Color(0.8, 0.8, 0.8) // Default gray
|
|
714
|
+
color: new THREE.Color(0.8, 0.8, 0.8), // Default gray
|
|
715
|
+
metalness: 0.5,
|
|
716
|
+
roughness: 0.5
|
|
706
717
|
});
|
|
707
718
|
// Apply non-texture properties
|
|
708
719
|
for (const prop of mat.properties) {
|
|
@@ -769,6 +780,8 @@ class StowKitPack {
|
|
|
769
780
|
skinnedMesh.bind(skeleton, skinnedMesh.matrixWorld);
|
|
770
781
|
skinnedMesh.normalizeSkinWeights();
|
|
771
782
|
skinnedMesh.updateMatrixWorld(true);
|
|
783
|
+
// Ensure frustum culling doesn't hide the mesh if bounds are wrong
|
|
784
|
+
skinnedMesh.frustumCulled = false;
|
|
772
785
|
const container = new THREE.Group();
|
|
773
786
|
container.name = stringId || 'SkinnedMesh';
|
|
774
787
|
container.add(boneContainer);
|
|
@@ -975,22 +988,99 @@ class StowKitPack {
|
|
|
975
988
|
*/
|
|
976
989
|
async loadAnimation(skinnedMeshGroup, animationPath) {
|
|
977
990
|
const clip = await this.loadAnimationClip(animationPath);
|
|
991
|
+
const retargetedClip = this.retargetAnimationClip(clip, skinnedMeshGroup);
|
|
978
992
|
const mixer = new THREE.AnimationMixer(skinnedMeshGroup);
|
|
979
|
-
const action = mixer.clipAction(
|
|
993
|
+
const action = mixer.clipAction(retargetedClip);
|
|
980
994
|
action.setLoop(THREE.LoopRepeat, Infinity);
|
|
981
995
|
action.play();
|
|
982
|
-
return { mixer, action, clip };
|
|
996
|
+
return { mixer, action, clip: retargetedClip };
|
|
983
997
|
}
|
|
984
998
|
/**
|
|
985
999
|
* Load and play animation by index
|
|
986
1000
|
*/
|
|
987
1001
|
async loadAnimationByIndex(skinnedMeshGroup, animationIndex) {
|
|
988
1002
|
const clip = await this.loadAnimationClipByIndex(animationIndex);
|
|
1003
|
+
const retargetedClip = this.retargetAnimationClip(clip, skinnedMeshGroup);
|
|
989
1004
|
const mixer = new THREE.AnimationMixer(skinnedMeshGroup);
|
|
990
|
-
const action = mixer.clipAction(
|
|
1005
|
+
const action = mixer.clipAction(retargetedClip);
|
|
991
1006
|
action.setLoop(THREE.LoopRepeat, Infinity);
|
|
992
1007
|
action.play();
|
|
993
|
-
return { mixer, action, clip };
|
|
1008
|
+
return { mixer, action, clip: retargetedClip };
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Retarget animation clip to match skeleton bone names
|
|
1012
|
+
*/
|
|
1013
|
+
retargetAnimationClip(clip, skinnedMeshGroup) {
|
|
1014
|
+
// Collect all bone names from the skeleton
|
|
1015
|
+
const skeletonBoneNames = new Set();
|
|
1016
|
+
skinnedMeshGroup.traverse((child) => {
|
|
1017
|
+
if (child.isBone) {
|
|
1018
|
+
skeletonBoneNames.add(child.name);
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
// Build a mapping from animation bone names to skeleton bone names
|
|
1022
|
+
// Try to match by suffix (e.g., "CharacterRig_MainHipJ" -> "MainHipJ")
|
|
1023
|
+
const boneNameMap = new Map();
|
|
1024
|
+
for (const track of clip.tracks) {
|
|
1025
|
+
const trackParts = track.name.split('.');
|
|
1026
|
+
const animBoneName = trackParts[0];
|
|
1027
|
+
if (skeletonBoneNames.has(animBoneName)) {
|
|
1028
|
+
// Exact match - no remapping needed
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
// Try to find a match by suffix
|
|
1032
|
+
for (const skelBoneName of skeletonBoneNames) {
|
|
1033
|
+
// Check if animation bone name ends with skeleton bone name
|
|
1034
|
+
// e.g., "CharacterRig_MainHipJ" contains "MainHipJ"
|
|
1035
|
+
if (animBoneName.endsWith(skelBoneName) ||
|
|
1036
|
+
animBoneName.endsWith('_' + skelBoneName) ||
|
|
1037
|
+
skelBoneName.endsWith(animBoneName) ||
|
|
1038
|
+
skelBoneName.endsWith('_' + animBoneName)) {
|
|
1039
|
+
boneNameMap.set(animBoneName, skelBoneName);
|
|
1040
|
+
break;
|
|
1041
|
+
}
|
|
1042
|
+
// Also try matching without common prefixes
|
|
1043
|
+
const animSuffix = animBoneName.replace(/^.*_/, '');
|
|
1044
|
+
const skelSuffix = skelBoneName.replace(/^.*_/, '');
|
|
1045
|
+
if (animSuffix === skelSuffix && animSuffix.length > 2) {
|
|
1046
|
+
boneNameMap.set(animBoneName, skelBoneName);
|
|
1047
|
+
break;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
// If no remapping needed, return original clip
|
|
1052
|
+
if (boneNameMap.size === 0) {
|
|
1053
|
+
return clip;
|
|
1054
|
+
}
|
|
1055
|
+
console.log(`[StowKit] Retargeting animation: ${boneNameMap.size} bones remapped`);
|
|
1056
|
+
// Create new tracks with remapped names
|
|
1057
|
+
const newTracks = [];
|
|
1058
|
+
for (const track of clip.tracks) {
|
|
1059
|
+
const trackParts = track.name.split('.');
|
|
1060
|
+
const animBoneName = trackParts[0];
|
|
1061
|
+
const property = trackParts.slice(1).join('.');
|
|
1062
|
+
const mappedBoneName = boneNameMap.get(animBoneName) || animBoneName;
|
|
1063
|
+
const newTrackName = `${mappedBoneName}.${property}`;
|
|
1064
|
+
// Clone the track with the new name
|
|
1065
|
+
// Use generic KeyframeTrack constructor to avoid type issues with specific track types
|
|
1066
|
+
const times = track.times;
|
|
1067
|
+
const values = track.values;
|
|
1068
|
+
let newTrack;
|
|
1069
|
+
if (track instanceof THREE.VectorKeyframeTrack) {
|
|
1070
|
+
newTrack = new THREE.VectorKeyframeTrack(newTrackName, times, values);
|
|
1071
|
+
}
|
|
1072
|
+
else if (track instanceof THREE.QuaternionKeyframeTrack) {
|
|
1073
|
+
newTrack = new THREE.QuaternionKeyframeTrack(newTrackName, times, values);
|
|
1074
|
+
}
|
|
1075
|
+
else if (track instanceof THREE.NumberKeyframeTrack) {
|
|
1076
|
+
newTrack = new THREE.NumberKeyframeTrack(newTrackName, times, values);
|
|
1077
|
+
}
|
|
1078
|
+
else {
|
|
1079
|
+
newTrack = new THREE.KeyframeTrack(newTrackName, times, values);
|
|
1080
|
+
}
|
|
1081
|
+
newTracks.push(newTrack);
|
|
1082
|
+
}
|
|
1083
|
+
return new THREE.AnimationClip(clip.name, clip.duration, newTracks);
|
|
994
1084
|
}
|
|
995
1085
|
/**
|
|
996
1086
|
* Load animation clip by index
|
|
@@ -1172,6 +1262,8 @@ class StowKitPack {
|
|
|
1172
1262
|
URL.revokeObjectURL(url);
|
|
1173
1263
|
PerfLogger.log(`[Perf] ✓ Texture "${textureName}": 0ms (IndexedDB cache)`);
|
|
1174
1264
|
const texture = new THREE.CompressedTexture([{ data: cached.data, width: cached.width, height: cached.height }], cached.width, cached.height, cached.format, THREE.UnsignedByteType);
|
|
1265
|
+
// All textures default to sRGB. Data textures are set to linear in applyTextureToMaterial.
|
|
1266
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
1175
1267
|
texture.needsUpdate = true;
|
|
1176
1268
|
return texture;
|
|
1177
1269
|
}
|
|
@@ -1179,6 +1271,9 @@ class StowKitPack {
|
|
|
1179
1271
|
const texture = await new Promise((resolve, reject) => {
|
|
1180
1272
|
this.ktx2Loader.load(url, (texture) => {
|
|
1181
1273
|
URL.revokeObjectURL(url);
|
|
1274
|
+
// All textures default to sRGB. Data textures (normal, metallic, roughness)
|
|
1275
|
+
// are set back to linear in applyTextureToMaterial.
|
|
1276
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
1182
1277
|
texture.needsUpdate = true;
|
|
1183
1278
|
resolve(texture);
|
|
1184
1279
|
}, undefined, (error) => {
|
|
@@ -1212,18 +1307,26 @@ class StowKitPack {
|
|
|
1212
1307
|
const propNameLower = propertyName.toLowerCase();
|
|
1213
1308
|
if (propNameLower === 'maintex' || propNameLower.includes('diffuse') ||
|
|
1214
1309
|
propNameLower.includes('albedo') || propNameLower.includes('base')) {
|
|
1310
|
+
// Color texture - keep sRGB (default)
|
|
1215
1311
|
material.map = texture;
|
|
1216
1312
|
}
|
|
1217
1313
|
else if (propNameLower.includes('normal')) {
|
|
1314
|
+
// Data texture - use linear
|
|
1315
|
+
texture.colorSpace = THREE.LinearSRGBColorSpace;
|
|
1218
1316
|
material.normalMap = texture;
|
|
1219
1317
|
}
|
|
1220
1318
|
else if (propNameLower.includes('metallic')) {
|
|
1319
|
+
// Data texture - use linear
|
|
1320
|
+
texture.colorSpace = THREE.LinearSRGBColorSpace;
|
|
1221
1321
|
material.metalnessMap = texture;
|
|
1222
1322
|
}
|
|
1223
1323
|
else if (propNameLower.includes('roughness')) {
|
|
1324
|
+
// Data texture - use linear
|
|
1325
|
+
texture.colorSpace = THREE.LinearSRGBColorSpace;
|
|
1224
1326
|
material.roughnessMap = texture;
|
|
1225
1327
|
}
|
|
1226
1328
|
else {
|
|
1329
|
+
// Default to color texture - keep sRGB
|
|
1227
1330
|
material.map = texture;
|
|
1228
1331
|
}
|
|
1229
1332
|
material.needsUpdate = true;
|