retarget static method

dynamic retarget(
  1. dynamic target,
  2. dynamic source, [
  3. dynamic options
])

Implementation

static retarget(target, source, [options]) {
  var pos = Vector3(),
      quat = Quaternion(),
      scale = Vector3(),
      bindBoneMatrix = Matrix4(),
      relativeMatrix = Matrix4(),
      globalMatrix = Matrix4();

  options = options ?? {};

  options.preserveMatrix = options.preserveMatrix ?? true;
  options.preservePosition = options.preservePosition ?? true;
  options.preserveHipPosition = options.preserveHipPosition ?? false;
  options.useTargetMatrix = options.useTargetMatrix ?? false;
  options.hip = options.hip ?? 'hip';
  options.names = options.names ?? {};

  var sourceBones = source.isObject3D ? source.skeleton.bones : getBones(source),
      bones = target.isObject3D ? target.skeleton.bones : getBones(target);

  var 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 (var 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 (var i = 0; i < target.children.length; ++i) {
      target.children[i].updateMatrixWorld(true);
    }
  }

  if (options.offsets) {
    bindBones = [];

    for (var i = 0; i < bones.length; ++i) {
      bone = bones[i];
      name = options.names[bone.name] || bone.name;

      if (options.offsets && 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 (var 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) {
        var 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 (var 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);
  }
}