上节加载了人物模型,播放了各自的骨骼动画:

如何把骨骼动画应用到另一个呢?
首先,我们需要在女孩的模型那里把这个动画导出:

let commonAnimation = null;
let pendingResolve = null;
export function waitForCommonAnimation() {
if (commonAnimation) {
return Promise.resolve(commonAnimation);
}
return new Promise((resolve) => {
pendingResolve = resolve;
});
}

const clip0 = gltf.animations[0];
if (clip0) {
// 存为公共动画,给其他模型重定向使用
commonAnimation = clip0;
if (pendingResolve) {
pendingResolve(clip0);
pendingResolve = null;
}
// 自己也播放这个动画
const clipAction = mixer.clipAction(clip0);
clipAction.play();
}
在另一个模型那里引入,播放一下:

waitForCommonAnimation().then((clip) => {
const clipAction = mixer.clipAction(clip);
clipAction.play();
});

这样就可以了。
当然,这里是因为两个人物的骨骼是一致的,所以可以直接用。
有时候骨骼动画直接在另一个模型播放有问题的时候,就要用 SkecthUtils.retargetClip 了
在 mesh2.js 里导出 mesh 和动画:

export let sourceRoot = null;
export let sourceClip = null;
function findSkinnedMesh(root) {
let skinned = null;
root.traverse((obj) => {
if (!skinned && obj.isSkinnedMesh) {
skinned = obj;
}
});
return skinned;
}
// 源:Michelle 的 SkinnedMesh + 第 0 个动画
sourceSkin = findSkinnedMesh(gltf.scene);
sourceClip = gltf.animations[0] || null;
if (sourceClip) {
const clipAction = mixer.clipAction(sourceClip);
clipAction.play();
}
在 mesh.js 用重定向的方式来修改这个动画:


function findSkinnedMesh(root) {
let skinned = null;
root.traverse((obj) => {
if (!skinned && obj.isSkinnedMesh) {
skinned = obj;
}
});
return skinned;
}
function tryPlayRetarget() {
if (!soldierSkin || !sourceSkin || !sourceClip) {
requestAnimationFrame(tryPlayRetarget);
return;
}
try {
const retargetedClip = SkeletonUtils.retargetClip(
sourceSkin, // 源:Michelle 的 SkinnedMesh
soldierSkin, // 目标:Soldier 的 SkinnedMesh
sourceClip,
{
hip: 'mixamorigHips',
scale: 1,
getBoneName: (bone) => bone.name,
},
);
danceMixer = new THREE.AnimationMixer(soldierSkin);
const danceAction = danceMixer.clipAction(retargetedClip);
danceAction.setLoop(THREE.LoopRepeat);
danceAction.play();
} catch (err) {
console.warn('跳舞动画重定向失败:', err);
}
}
tryPlayRetarget();
当然,因为现在动画没啥错乱的,所以直接播放另一个模型的动画也行。
如果骨骼动画有错乱就可以用这种方式来做一些局部修改。
案例代码上传了小册仓库。
总结
这节我们实现了骨骼动画的复用。
如果两者骨骼模型一样,那可以直接用另一个模型播放这个模型的骨骼动画。
如果播放的时候有一些错乱,就需要用 SkeletonUtils.retargetClip 做一些局部修改了。