@@ -65,18 +65,43 @@ export function combineSkeletons(root: THREE.Object3D): void {
65
65
}
66
66
67
67
// prepare new skeletons for each group, and bind them to the meshes
68
- for ( const { boneInverseMap, meshes } of groups ) {
68
+
69
+ // the condition to use the same skin index attribute:
70
+ // - the same skin index attribute
71
+ // - and the skeleton is same
72
+ // - and the bone set is same
73
+ const cache = new Map < string , THREE . BufferAttribute | THREE . InterleavedBufferAttribute > ( ) ;
74
+ const skinIndexDispatcher = new ObjectIndexDispatcher < THREE . BufferAttribute | THREE . InterleavedBufferAttribute > ( ) ;
75
+ const skeletonDispatcher = new ObjectIndexDispatcher < THREE . Skeleton > ( ) ;
76
+ const boneDispatcher = new ObjectIndexDispatcher < THREE . Bone > ( ) ;
77
+
78
+ for ( const group of groups ) {
79
+ const { boneInverseMap, meshes } = group ;
80
+
69
81
// create a new skeleton
70
82
const newBones = Array . from ( boneInverseMap . keys ( ) ) ;
71
83
const newBoneInverses = Array . from ( boneInverseMap . values ( ) ) ;
72
84
const newSkeleton = new THREE . Skeleton ( newBones , newBoneInverses ) ;
73
-
74
- // collect skin index attributes and corresponding bone arrays
75
- const skinIndexBonesPairSet = collectSkinIndexAttrs ( meshes ) ;
85
+ const skeletonKey = skeletonDispatcher . getOrCreate ( newSkeleton ) ;
76
86
77
87
// remap skin index attribute
78
- for ( const [ skinIndexAttr , bones ] of skinIndexBonesPairSet ) {
79
- remapSkinIndexAttribute ( skinIndexAttr , bones , newBones ) ;
88
+ for ( const mesh of meshes ) {
89
+ const skinIndexAttr = mesh . geometry . getAttribute ( 'skinIndex' ) ;
90
+ const skinIndexKey = skinIndexDispatcher . getOrCreate ( skinIndexAttr ) ;
91
+
92
+ const bones = mesh . skeleton . bones ;
93
+ const bonesKey = bones . map ( ( bone ) => boneDispatcher . getOrCreate ( bone ) ) . join ( ',' ) ;
94
+
95
+ const key = `${ skinIndexKey } ;${ skeletonKey } ;${ bonesKey } ` ;
96
+ let newSkinIndexAttr = cache . get ( key ) ;
97
+
98
+ if ( newSkinIndexAttr == null ) {
99
+ newSkinIndexAttr = skinIndexAttr . clone ( ) ;
100
+ remapSkinIndexAttribute ( newSkinIndexAttr , bones , newBones ) ;
101
+ cache . set ( key , newSkinIndexAttr ) ;
102
+ }
103
+
104
+ mesh . geometry . setAttribute ( 'skinIndex' , newSkinIndexAttr ) ;
80
105
}
81
106
82
107
// bind the new skeleton to the meshes
@@ -189,6 +214,13 @@ function boneInverseMapIsMergeable(
189
214
return true ;
190
215
}
191
216
217
+ /**
218
+ * Remap the skin index attribute from old bones to new bones.
219
+ * This function modifies the given attribute in place.
220
+ * @param attribute The skin index attribute to remap
221
+ * @param oldBones The bone array that the attribute is currently using
222
+ * @param newBones The bone array that the attribute will be using
223
+ */
192
224
function remapSkinIndexAttribute (
193
225
attribute : THREE . BufferAttribute | THREE . InterleavedBufferAttribute ,
194
226
oldBones : THREE . Bone [ ] ,
@@ -236,63 +268,22 @@ function matrixEquals(a: THREE.Matrix4, b: THREE.Matrix4, tolerance?: number) {
236
268
return true ;
237
269
}
238
270
239
- /**
240
- * Check if the contents of two arrays are equal.
241
- */
242
- function arrayEquals < T > ( a : T [ ] , b : T [ ] ) : boolean {
243
- if ( a . length !== b . length ) {
244
- return false ;
245
- }
246
-
247
- return a . every ( ( v , i ) => v === b [ i ] ) ;
248
- }
249
-
250
- /**
251
- * Collect skin index attributes and corresponding bone arrays from the given skinned meshes.
252
- * If a skin index attribute is shared among different bone sets, clone the attribute.
253
- */
254
- function collectSkinIndexAttrs (
255
- meshes : Iterable < THREE . SkinnedMesh > ,
256
- ) : Set < [ THREE . BufferAttribute | THREE . InterleavedBufferAttribute , THREE . Bone [ ] ] > {
257
- const skinIndexBonesPairSet = new Set < [ THREE . BufferAttribute | THREE . InterleavedBufferAttribute , THREE . Bone [ ] ] > ( ) ;
258
-
259
- // Collect skin index attributes
260
- // skinIndex attribute might be shared among different bone sets
261
- // If there are multiple bone sets that share the same skinIndex attribute, clone the attribute
262
- const skinIndexNewSkinIndexBonesMapMap = new Map <
263
- THREE . BufferAttribute | THREE . InterleavedBufferAttribute ,
264
- Map < THREE . BufferAttribute | THREE . InterleavedBufferAttribute , THREE . Bone [ ] >
265
- > ( ) ;
271
+ class ObjectIndexDispatcher < T > {
272
+ private _objectIndexMap = new Map < T , number > ( ) ;
273
+ private _index = 0 ;
266
274
267
- for ( const mesh of meshes ) {
268
- const skinIndexAttr = mesh . geometry . getAttribute ( 'skinIndex' ) ;
269
-
270
- // Get or create a map for the skin index attribute
271
- let newSkinIndexBonesMap = skinIndexNewSkinIndexBonesMapMap . get ( skinIndexAttr ) ;
272
- if ( newSkinIndexBonesMap == null ) {
273
- // Create a new map for the skin index attribute and register the bone array
274
- newSkinIndexBonesMap = new Map ( ) ;
275
- skinIndexNewSkinIndexBonesMapMap . set ( skinIndexAttr , newSkinIndexBonesMap ) ;
276
- newSkinIndexBonesMap . set ( skinIndexAttr , mesh . skeleton . bones ) ;
277
- skinIndexBonesPairSet . add ( [ skinIndexAttr , mesh . skeleton . bones ] ) ;
278
- continue ;
279
- }
275
+ public get ( obj : T ) : number | undefined {
276
+ return this . _objectIndexMap . get ( obj ) ;
277
+ }
280
278
281
- // Check if the bone set is already registered
282
- // If the bone set is already registered, reuse the skin index attribute
283
- let newSkinIndexAttr = Array . from ( newSkinIndexBonesMap ) . find ( ( [ _ , bones ] ) =>
284
- arrayEquals ( bones , mesh . skeleton . bones ) ,
285
- ) ?. [ 0 ] ;
286
-
287
- // If there is no matching bone set, clone the skin index attribute
288
- if ( newSkinIndexAttr == null ) {
289
- newSkinIndexAttr = skinIndexAttr . clone ( ) ;
290
- newSkinIndexBonesMap . set ( newSkinIndexAttr , mesh . skeleton . bones ) ;
291
- skinIndexBonesPairSet . add ( [ newSkinIndexAttr , mesh . skeleton . bones ] ) ;
279
+ public getOrCreate ( obj : T ) : number {
280
+ let index = this . _objectIndexMap . get ( obj ) ;
281
+ if ( index == null ) {
282
+ index = this . _index ;
283
+ this . _objectIndexMap . set ( obj , index ) ;
284
+ this . _index ++ ;
292
285
}
293
286
294
- mesh . geometry . setAttribute ( 'skinIndex' , newSkinIndexAttr ) ;
287
+ return index ;
295
288
}
296
-
297
- return skinIndexBonesPairSet ;
298
289
}
0 commit comments