met4citizen / TalkingHead

Talking Head (3D): A JavaScript class for real-time lip-sync using Ready Player Me full-body 3D avatars.
MIT License
346 stars 106 forks source link

Thinking pose/mood #48

Closed Eonasdan closed 4 months ago

Eonasdan commented 4 months ago

Hello. Back again 😄

I'm trying to get a thinking animation from mixamo or a "mood". I'm hoping that I can make this animation/mood play while waiting for the GenAI to return after getting a user request.

Unfortunately, when I try to play the animation, the characters' position completely changes and goes "off-screen".

Do you have any ideas?

I'm happy to create a sample app, and I'd be willing to donate for your time.

P.S. I need to catch up to some changes you've made since last time and push my typescript rewrite somewhere for you to take a look at.

met4citizen commented 4 months ago

Hi. I found only one thinking animation from Mixamo and it worked fine without changing the avatar's position. However, Mixamo animations don't come with facial expressions, so it doesn't really look like thinking. Furthermore, if you are going to repeat the same animation at each prompt, users might get tired of seeing the same movement over and over.

If you want to make a custom pose, mood, gesture, or emoji, take a look at README Appendix D. - Below is an example of how you could create a custom "thinking" emoji + gesture combination in your app and then play it using the new playGesturemethod. You can put this in your app and fine-tune the values if you decide to go this route. You can also create a custom mood by following the instructions in the appendix, but building a mood might take a bit more effort.

head.gestureTemplates["🤔"] = {
  'LeftShoulder.rotation':{x:1.615, y:-0.064, z:-1.53}, 'LeftArm.rotation':{x:1.002, y:0, z:0.818}, 'LeftForeArm.rotation':{x:0.242, y:0.585, z:2.128}, 'LeftHand.rotation':{x:0.029, y:-0.298, z:0.346}, 'LeftHandThumb1.rotation':{x:0.208, y:-0.189, z:0.685}, 'LeftHandThumb2.rotation':{x:0.129, y:-0.285, z:-0.163}, 'LeftHandThumb3.rotation':{x:-0.047, y:0.068, z:0.401}, 'LeftHandIndex1.rotation':{x:0.247, y:-0.011, z:-0.084}, 'LeftHandIndex2.rotation':{x:0.006, y:0, z:0}, 'LeftHandIndex3.rotation':{x:-0.047, y:0, z:0.004}, 'LeftHandMiddle1.rotation':{x:1.424, y:-0.103, z:-0.12}, 'LeftHandMiddle2.rotation':{x:1.919, y:-0.162, z:-0.114}, 'LeftHandMiddle3.rotation':{x:0.44, y:-0.012, z:-0.051}, 'LeftHandRing1.rotation':{x:1.619, y:-0.127, z:-0.053}, 'LeftHandRing2.rotation':{x:1.898, y:-0.16, z:-0.115}, 'LeftHandRing3.rotation':{x:0.262, y:-0.004, z:-0.031}, 'LeftHandPinky1.rotation':{x:1.661, y:-0.131, z:-0.016}, 'LeftHandPinky2.rotation':{x:1.715, y:-0.067, z:-0.13}, 'LeftHandPinky3.rotation':{x:0.627, y:-0.023, z:-0.071}
};
head.animEmojis["🤔"] = { dt: [300,2000], vs: { browDownLeft: [0.7], browDownRight: [0.7], eyeSquintRight: [0.7], eyesRotateX: [-0.5], eyesRotateY: [-0.5], headRotateX: [-0.05], mouthPressLeft: [0.3], mouthPressRight: [1], mouthRollLower: [0.2], mouthRollUpper: [0.2], mouthShrugLower: [0.5], mouthShrugUpper: [0.5], noseSneerLeft: [1], noseSneerRight: [1] } };
head.playGesture("🤔",2);

Now, the emoji/gesture approach suffers, of course, from the same problem as before: if you repeat the same facial expression many times, users will quickly get tired of it. I don't have a good solution for this, but in one of my apps I made the avatar turn and look at the element where the results will eventually appear, as if waiting for them, and added some randomness to the mix. Here is the general idea if you want to try it:

// Turn head to look at the DIV element
const rect = element.getBoundingClientRect();
let x = window.scrollX + rect.left + (rect.right - rect.left) / 2 - Math.random() * 100;
let y = window.scrollY + rect.top + (rect.bottom - rect.top) / 2 + Math.random() * 100;
let dur = 100 + Math.random() * 500;
head.lookAt(x,y,dur);
Eonasdan commented 4 months ago

Thank you for this.