425 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			425 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								import {
							 | 
						||
| 
								 | 
							
									AnimationClip,
							 | 
						||
| 
								 | 
							
									AnimationMixer,
							 | 
						||
| 
								 | 
							
									Matrix4,
							 | 
						||
| 
								 | 
							
									Quaternion,
							 | 
						||
| 
								 | 
							
									QuaternionKeyframeTrack,
							 | 
						||
| 
								 | 
							
									SkeletonHelper,
							 | 
						||
| 
								 | 
							
									Vector3,
							 | 
						||
| 
								 | 
							
									VectorKeyframeTrack
							 | 
						||
| 
								 | 
							
								} from 'three';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function retarget( target, source, options = {} ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const pos = new Vector3(),
							 | 
						||
| 
								 | 
							
										quat = new Quaternion(),
							 | 
						||
| 
								 | 
							
										scale = new Vector3(),
							 | 
						||
| 
								 | 
							
										bindBoneMatrix = new Matrix4(),
							 | 
						||
| 
								 | 
							
										relativeMatrix = new Matrix4(),
							 | 
						||
| 
								 | 
							
										globalMatrix = new Matrix4();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									options.preserveMatrix = options.preserveMatrix !== undefined ? options.preserveMatrix : true;
							 | 
						||
| 
								 | 
							
									options.preservePosition = options.preservePosition !== undefined ? options.preservePosition : true;
							 | 
						||
| 
								 | 
							
									options.preserveHipPosition = options.preserveHipPosition !== undefined ? options.preserveHipPosition : false;
							 | 
						||
| 
								 | 
							
									options.useTargetMatrix = options.useTargetMatrix !== undefined ? options.useTargetMatrix : false;
							 | 
						||
| 
								 | 
							
									options.hip = options.hip !== undefined ? options.hip : 'hip';
							 | 
						||
| 
								 | 
							
									options.names = options.names || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const sourceBones = source.isObject3D ? source.skeleton.bones : getBones( source ),
							 | 
						||
| 
								 | 
							
										bones = target.isObject3D ? target.skeleton.bones : getBones( target );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									let bindBones,
							 | 
						||
| 
								 | 
							
										bone, name, boneTo,
							 | 
						||
| 
								 | 
							
										bonesPosition;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// reset bones
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if ( target.isObject3D ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										target.skeleton.pose();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										options.useTargetMatrix = true;
							 | 
						||
| 
								 | 
							
										options.preserveMatrix = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if ( options.preservePosition ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										bonesPosition = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( let i = 0; i < bones.length; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											bonesPosition.push( bones[ i ].position.clone() );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if ( options.preserveMatrix ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// reset matrix
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										target.updateMatrixWorld();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										target.matrixWorld.identity();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// reset children matrix
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( let i = 0; i < target.children.length; ++ i ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											target.children[ i ].updateMatrixWorld( true );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if ( options.offsets ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										bindBones = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( let i = 0; i < bones.length; ++ i ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											bone = bones[ i ];
							 | 
						||
| 
								 | 
							
											name = options.names[ bone.name ] || bone.name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( options.offsets[ name ] ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												bone.matrix.multiply( options.offsets[ name ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												bone.updateMatrixWorld();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											bindBones.push( bone.matrixWorld.clone() );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for ( let i = 0; i < bones.length; ++ i ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										bone = bones[ i ];
							 | 
						||
| 
								 | 
							
										name = options.names[ bone.name ] || bone.name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										boneTo = getBoneByName( name, sourceBones );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										globalMatrix.copy( bone.matrixWorld );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( boneTo ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											boneTo.updateMatrixWorld();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( options.useTargetMatrix ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												relativeMatrix.copy( boneTo.matrixWorld );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												relativeMatrix.copy( target.matrixWorld ).invert();
							 | 
						||
| 
								 | 
							
												relativeMatrix.multiply( boneTo.matrixWorld );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// ignore scale to extract rotation
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											scale.setFromMatrixScale( relativeMatrix );
							 | 
						||
| 
								 | 
							
											relativeMatrix.scale( scale.set( 1 / scale.x, 1 / scale.y, 1 / scale.z ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// apply to global matrix
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											globalMatrix.makeRotationFromQuaternion( quat.setFromRotationMatrix( relativeMatrix ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( target.isObject3D ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												const boneIndex = bones.indexOf( bone ),
							 | 
						||
| 
								 | 
							
													wBindMatrix = bindBones ? bindBones[ boneIndex ] : bindBoneMatrix.copy( target.skeleton.boneInverses[ boneIndex ] ).invert();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												globalMatrix.multiply( wBindMatrix );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											globalMatrix.copyPosition( relativeMatrix );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( bone.parent && bone.parent.isBone ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											bone.matrix.copy( bone.parent.matrixWorld ).invert();
							 | 
						||
| 
								 | 
							
											bone.matrix.multiply( globalMatrix );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											bone.matrix.copy( globalMatrix );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( options.preserveHipPosition && name === options.hip ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											bone.matrix.setPosition( pos.set( 0, bone.position.y, 0 ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										bone.updateMatrixWorld();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if ( options.preservePosition ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( let i = 0; i < bones.length; ++ i ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											bone = bones[ i ];
							 | 
						||
| 
								 | 
							
											name = options.names[ bone.name ] || bone.name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( name !== options.hip ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												bone.position.copy( bonesPosition[ i ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if ( options.preserveMatrix ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// restore matrix
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										target.updateMatrixWorld( true );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function retargetClip( target, source, clip, options = {} ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									options.useFirstFramePosition = options.useFirstFramePosition !== undefined ? options.useFirstFramePosition : false;
							 | 
						||
| 
								 | 
							
									// Calculate the fps from the source clip based on the track with the most frames, unless fps is already provided.
							 | 
						||
| 
								 | 
							
									options.fps = options.fps !== undefined ? options.fps : ( Math.max( ...clip.tracks.map( track => track.times.length ) ) / clip.duration );
							 | 
						||
| 
								 | 
							
									options.names = options.names || [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if ( ! source.isObject3D ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										source = getHelperFromSkeleton( source );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const numFrames = Math.round( clip.duration * ( options.fps / 1000 ) * 1000 ),
							 | 
						||
| 
								 | 
							
										delta = clip.duration / ( numFrames - 1 ),
							 | 
						||
| 
								 | 
							
										convertedTracks = [],
							 | 
						||
| 
								 | 
							
										mixer = new AnimationMixer( source ),
							 | 
						||
| 
								 | 
							
										bones = getBones( target.skeleton ),
							 | 
						||
| 
								 | 
							
										boneDatas = [];
							 | 
						||
| 
								 | 
							
									let positionOffset,
							 | 
						||
| 
								 | 
							
										bone, boneTo, boneData,
							 | 
						||
| 
								 | 
							
										name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									mixer.clipAction( clip ).play();
							 | 
						||
| 
								 | 
							
									mixer.update( 0 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									source.updateMatrixWorld();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for ( let i = 0; i < numFrames; ++ i ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const time = i * delta;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										retarget( target, source, options );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( let j = 0; j < bones.length; ++ j ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											name = options.names[ bones[ j ].name ] || bones[ j ].name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											boneTo = getBoneByName( name, source.skeleton );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( boneTo ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												bone = bones[ j ];
							 | 
						||
| 
								 | 
							
												boneData = boneDatas[ j ] = boneDatas[ j ] || { bone: bone };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( options.hip === name ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													if ( ! boneData.pos ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														boneData.pos = {
							 | 
						||
| 
								 | 
							
															times: new Float32Array( numFrames ),
							 | 
						||
| 
								 | 
							
															values: new Float32Array( numFrames * 3 )
							 | 
						||
| 
								 | 
							
														};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													if ( options.useFirstFramePosition ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														if ( i === 0 ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
															positionOffset = bone.position.clone();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														bone.position.sub( positionOffset );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													boneData.pos.times[ i ] = time;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													bone.position.toArray( boneData.pos.values, i * 3 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( ! boneData.quat ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													boneData.quat = {
							 | 
						||
| 
								 | 
							
														times: new Float32Array( numFrames ),
							 | 
						||
| 
								 | 
							
														values: new Float32Array( numFrames * 4 )
							 | 
						||
| 
								 | 
							
													};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												boneData.quat.times[ i ] = time;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												bone.quaternion.toArray( boneData.quat.values, i * 4 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( i === numFrames - 2 ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// last mixer update before final loop iteration
							 | 
						||
| 
								 | 
							
											// make sure we do not go over or equal to clip duration
							 | 
						||
| 
								 | 
							
											mixer.update( delta - 0.0000001 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											mixer.update( delta );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										source.updateMatrixWorld();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for ( let i = 0; i < boneDatas.length; ++ i ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										boneData = boneDatas[ i ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( boneData ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( boneData.pos ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												convertedTracks.push( new VectorKeyframeTrack(
							 | 
						||
| 
								 | 
							
													'.bones[' + boneData.bone.name + '].position',
							 | 
						||
| 
								 | 
							
													boneData.pos.times,
							 | 
						||
| 
								 | 
							
													boneData.pos.values
							 | 
						||
| 
								 | 
							
												) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											convertedTracks.push( new QuaternionKeyframeTrack(
							 | 
						||
| 
								 | 
							
												'.bones[' + boneData.bone.name + '].quaternion',
							 | 
						||
| 
								 | 
							
												boneData.quat.times,
							 | 
						||
| 
								 | 
							
												boneData.quat.values
							 | 
						||
| 
								 | 
							
											) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									mixer.uncacheAction( clip );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return new AnimationClip( clip.name, - 1, convertedTracks );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function clone( source ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const sourceLookup = new Map();
							 | 
						||
| 
								 | 
							
									const cloneLookup = new Map();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const clone = source.clone();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									parallelTraverse( source, clone, function ( sourceNode, clonedNode ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										sourceLookup.set( clonedNode, sourceNode );
							 | 
						||
| 
								 | 
							
										cloneLookup.set( sourceNode, clonedNode );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									} );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									clone.traverse( function ( node ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( ! node.isSkinnedMesh ) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const clonedMesh = node;
							 | 
						||
| 
								 | 
							
										const sourceMesh = sourceLookup.get( node );
							 | 
						||
| 
								 | 
							
										const sourceBones = sourceMesh.skeleton.bones;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										clonedMesh.skeleton = sourceMesh.skeleton.clone();
							 | 
						||
| 
								 | 
							
										clonedMesh.bindMatrix.copy( sourceMesh.bindMatrix );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										clonedMesh.skeleton.bones = sourceBones.map( function ( bone ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											return cloneLookup.get( bone );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										} );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										clonedMesh.bind( clonedMesh.skeleton, clonedMesh.bindMatrix );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									} );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return clone;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// internal helper
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getBoneByName( name, skeleton ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for ( let i = 0, bones = getBones( skeleton ); i < bones.length; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( name === bones[ i ].name )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											return bones[ i ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getBones( skeleton ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return Array.isArray( skeleton ) ? skeleton : skeleton.bones;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getHelperFromSkeleton( skeleton ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const source = new SkeletonHelper( skeleton.bones[ 0 ] );
							 | 
						||
| 
								 | 
							
									source.skeleton = skeleton;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return source;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function parallelTraverse( a, b, callback ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									callback( a, b );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for ( let i = 0; i < a.children.length; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										parallelTraverse( a.children[ i ], b.children[ i ], callback );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export {
							 | 
						||
| 
								 | 
							
									retarget,
							 | 
						||
| 
								 | 
							
									retargetClip,
							 | 
						||
| 
								 | 
							
									clone,
							 | 
						||
| 
								 | 
							
								};
							 |