retargetClip static method

AnimationClip retargetClip(
  1. Object3D target,
  2. dynamic input,
  3. AnimationClip clip, [
  4. 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 );
}