Hey wudao
I used VRoid studio to export the model. And then, I imported it on blender with a VRM blender extension. After that, I had some animations laying around, which I had to retarget to the VRM rig. I did this with the Rokoko blender plugin. After I’m done working on the model in blender, I export the model with the babylonJS blender plugin - this generates a .babylon file which I then import with SceneLoader.ImportMeshAsync.
The mouth movement is being done with the VRM model’s morph targets. To be more specific, I’m just using the “A” mouth morph target all the time. I have a talk() function that is called when there is sound available. Here’s what it looks like:
talk() {
if (this.isTalking) {
return;
}
const maxTime = 0.5;
const minTime = 0.2;
const aTarget = this.getMorphTargetByName("Face.M_F00_000_00_Fcl_MTH_A");
const vowelAnimEnd = () => {
playMorphTargetAnim(
"aSoundAnim",
[0, random(minTime, maxTime), random(minTime, maxTime)],
[0, 1, 0],
aTarget,
vowelAnimEnd,
this.scene
);
};
this.isTalking = true;
playMorphTargetAnim(
"aSoundAnim",
[0, random(minTime, maxTime), random(minTime, maxTime)],
[0, 1, 0],
aTarget,
vowelAnimEnd,
this.scene
);
}
The playMorphTargetAnim looks like this:
export const playMorphTargetAnim = (
name: string,
durationsSeconds: number[],
values: number[],
morphTarget: MorphTarget,
endCallback: () => void,
scene: Scene
) => {
const keyFrames: IAnimationKey[] = [];
const framesPerSecond = 60;
let previousFrame = 0;
for (let i = 0; i < values.length; i++) {
const currentFrame = previousFrame + durationsSeconds[i] * framesPerSecond;
keyFrames.push({
frame: currentFrame,
value: values[i],
});
previousFrame = currentFrame;
}
const lastFrame = keyFrames.at(-1);
var morphAnimation = new Animation(
name,
"influence",
framesPerSecond,
Animation.ANIMATIONTYPE_FLOAT
);
morphAnimation.setKeys(keyFrames);
morphTarget.animations = [];
morphTarget.animations.push(morphAnimation);
scene.beginAnimation(morphTarget, 0, lastFrame.frame, false, 1, endCallback);
};
The eye opening / closing is also done using morph targets. However, everything else are “regular” bone animations.
Does this answer your question? If not, let me know.