retargetClip static method
AnimationClip
retargetClip(
- Object3D target,
- dynamic input,
- AnimationClip clip, [
- SkeletonUtilsOptions? options,
Implementation
static AnimationClip retargetClip(Object3D target, input, AnimationClip clip, [SkeletonUtilsOptions? options]) {
late SkeletonHelper source;
options = options ?? SkeletonUtilsOptions();
options.useFirstFramePosition = options.useFirstFramePosition;
options.fps = options.fps;
options.names = options.names;
if (input is Skeleton) {
source = getHelperFromSkeleton(input);
}
else if(input is SkeletonHelper){
source = input;
}
else{
source = getHelperFromSkeleton(input.skeleton!);
}
final numFrames = (clip.duration * (options.fps / 1000) * 1000).round(),
delta = clip.duration / ( numFrames - 1 ),
convertedTracks = <KeyframeTrack>[],
mixer = AnimationMixer(source),
bones = target is SkeletonHelper?target.bones:getBones(target.skeleton),
boneDatas = [];
Vector3? positionOffset;
Bone? bone, boneTo;
Map<String,dynamic>? boneData;
String name;
mixer.clipAction(clip)?.play();
int start = 0, end = numFrames;
if ( options.trim != null ) {
start = ( options.trim![ 0 ] * options.fps ).round();
end = math.min<int>( ( options.trim![ 1 ] * options.fps ).round(), numFrames ) - start;
mixer.update( options.trim![ 0 ] );
}
else {
mixer.update( 0 );
}
source.updateMatrixWorld();
//
for (int frame = 0; frame < end; ++ frame ) {
final time = frame * delta;
retarget( target, source, options );
for ( int j = 0; j < bones.length; ++ j ) {
bone = bones[ j ];
name = getBoneName( bone, options ) ?? bone.name;
boneTo = getBoneByName( name, source.skeleton! );
if ( boneTo != null) {
boneData = boneDatas[ j ] = boneDatas[ j ] ?? { bone: bone };
if ( options.hip == name ) {
if (boneData?['pos'] == null) {
boneData?['pos'] = {
'times': new Float32List( end ),
'values': new Float32List( end * 3 )
};
}
if ( options.useFirstFramePosition ) {
if ( frame == 0 ) {
positionOffset = bone.position.clone();
}
bone.position.sub( positionOffset! );
}
boneData?['pos']['times'][ frame ] = time;
bone.position.copyIntoArray( boneData!['pos']['values'], frame * 3 );
}
if (boneData?['quat'] == null) {
boneData?['quat'] = {
'times': new Float32List( end ),
'values': new Float32List( end * 4 )
};
}
boneData?['quat']['times'][ frame ] = time;
bone.quaternion.toArray( boneData!['quat']['values'], frame * 4 );
}
}
if ( frame == end - 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 (int i = 0; i < boneDatas.length; ++ i ) {
boneData = boneDatas[ i ];
if ( boneData != null) {
if ( boneData['pos'] != null) {
convertedTracks.add( new VectorKeyframeTrack(
'.bones[${boneData['bone']['name']}].position',
boneData['pos']['times'],
boneData['pos']['values']
) );
}
convertedTracks.add( new QuaternionKeyframeTrack(
'.bones[${boneData['bone']['name']}].quaternion',
boneData['quat']['times'],
boneData['quat']['values']
) );
}
}
mixer.uncacheAction( clip );
return AnimationClip( clip.name, - 1, convertedTracks );
}