retargetClip static method

dynamic retargetClip(
  1. dynamic target,
  2. dynamic source,
  3. AnimationClip clip, [
  4. SkeletonUtilsOptions? options,
])

Implementation

static retargetClip(target, source, AnimationClip clip, [SkeletonUtilsOptions? options]) {
  options = options ?? SkeletonUtilsOptions();

  options.useFirstFramePosition = options.useFirstFramePosition;
  options.fps = options.fps;
  options.names = options.names;

  if (source is! Object3D) {
    source = getHelperFromSkeleton(source);
  }

  final numFrames = (clip.duration * (options.fps / 1000) * 1000).round(),
      delta = 1 / options.fps,
      convertedTracks = <KeyframeTrack>[],
      mixer = AnimationMixer(source),
      bones = getBones(target.skeleton),
      boneDatas = [];

  Vector3? positionOffset;
  Bone? bone, boneTo;
  Map? boneData;
  String name;

  mixer.clipAction(clip)?.play();
  mixer.update(0);

  source.updateMatrixWorld();

  for (int i = 0; i < numFrames; ++i) {
    double time = i * delta;

    retarget(target, source, options);

    for (int j = 0; j < bones.length; ++j) {
      name = options.names[bones[j].name] ?? bones[j].name;

      boneTo = getBoneFromSkeleton(name, source.skeleton!);

      if (boneTo != null) {
        bone = bones[j];
        boneData = boneDatas[j] = boneDatas[j] ?? {"bone": bone};

        if (options.hip == name) {
          if (boneData!['pos'] == null) {
            boneData['pos'] = {"times": Float32List(numFrames), "values": Float32List(numFrames * 3)};
          }

          if (options.useFirstFramePosition) {
            if (i == 0) {
              positionOffset = bone.position.clone();
            }

            bone.position.sub(positionOffset!);
          }

          boneData['pos']['times'][i] = time;

          bone.position.copyIntoArray(boneData['pos']['values'], i * 3);
        }

        if (boneData!['quat'] == null) {
          boneData['quat'] = {"times": Float32List(numFrames), "values": Float32List(numFrames * 4)};
        }

        boneData['quat']['times'][i] = time;

        bone.quaternion.toArray(boneData['quat']['values'], i * 4);
      }
    }

    mixer.update(delta);

    source.updateMatrixWorld();
  }

  for (int i = 0; i < boneDatas.length; ++i) {
    boneData = boneDatas[i];

    if (boneData != null) {
      if (boneData['pos']) {
        convertedTracks.add(VectorKeyframeTrack('.bones[${boneData['bone']['name']}].position', boneData['pos']['times'], boneData['pos']['values'], null));
      }

      convertedTracks.add(QuaternionKeyframeTrack('.bones[${boneData['bone']['name']}].quaternion', boneData['quat']['times'], boneData['quat']['values'], null));
    }
  }

  mixer.uncacheAction(clip);

  return AnimationClip(clip.name, -1, convertedTracks);
}