mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-09 03:18:18 +00:00
refac
This commit is contained in:
parent
d989375b48
commit
99a50e8c1b
1 changed files with 52 additions and 25 deletions
|
|
@ -59,15 +59,49 @@ export async function createAvatarRenderer(
|
|||
vrm.humanoid.getNormalizedBoneNode(name);
|
||||
const head = bone('head');
|
||||
const spine = bone('spine');
|
||||
const chest = bone('chest') ?? bone('upperChest');
|
||||
const leftArm = bone('leftUpperArm');
|
||||
const rightArm = bone('rightUpperArm');
|
||||
// VRM's normalized skeleton gives both VRM 0 and 1 a predictable T-pose.
|
||||
if (leftArm) leftArm.rotation.z = -1.22;
|
||||
if (rightArm) rightArm.rotation.z = 1.22;
|
||||
const leftElbow = bone('leftLowerArm');
|
||||
const rightElbow = bone('rightLowerArm');
|
||||
if (leftElbow) leftElbow.rotation.y = -0.12;
|
||||
if (rightElbow) rightElbow.rotation.y = 0.12;
|
||||
// Start all built-in states from a relaxed, slightly asymmetric stance.
|
||||
// Normalized VRM bones share T-pose axes across VRM 0 and 1.
|
||||
const restingRotations: Partial<Record<Parameters<typeof bone>[0], [number, number, number]>> = {
|
||||
spine: [0.012, 0.025, -0.016],
|
||||
chest: [-0.018, -0.012, 0.022],
|
||||
head: [0.006, -0.012, -0.006],
|
||||
leftShoulder: [0, 0, -0.025],
|
||||
rightShoulder: [0, 0, 0.035],
|
||||
leftUpperArm: [0.025, -0.06, -1.38],
|
||||
rightUpperArm: [-0.015, 0.035, 1.34],
|
||||
leftLowerArm: [0, -0.24, -0.035],
|
||||
rightLowerArm: [0, 0.19, 0.025],
|
||||
leftHand: [0.025, 0, -0.055],
|
||||
rightHand: [-0.015, 0, 0.035],
|
||||
leftUpperLeg: [-0.025, 0, 0.02],
|
||||
rightUpperLeg: [-0.045, 0, -0.025],
|
||||
leftLowerLeg: [0.05, 0, 0],
|
||||
rightLowerLeg: [0.09, 0, 0],
|
||||
leftFoot: [-0.025, 0.045, -0.02],
|
||||
rightFoot: [-0.045, -0.035, 0.025]
|
||||
};
|
||||
for (const [name, rotation] of Object.entries(restingRotations))
|
||||
bone(name as Parameters<typeof bone>[0])?.rotation.set(...rotation);
|
||||
// Resting fingers curl mainly at the first two joints, with a softer index finger.
|
||||
for (const side of ['left', 'right'] as const) {
|
||||
const sign = side === 'left' ? -1 : 1;
|
||||
for (const [i, finger] of (['Index', 'Middle', 'Ring', 'Little'] as const).entries()) {
|
||||
const curl = 0.18 + i * 0.035 + (side === 'right' ? 0.025 : 0);
|
||||
for (const [joint, amount] of [
|
||||
['Proximal', curl],
|
||||
['Intermediate', curl * 0.8],
|
||||
['Distal', 0.04]
|
||||
] as const) {
|
||||
const node = bone(`${side}${finger}${joint}`);
|
||||
if (node) node.rotation.z = sign * amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
vrm.update(0);
|
||||
vrm.scene.updateMatrixWorld(true);
|
||||
const bounds = new THREE.Box3().setFromObject(vrm.scene);
|
||||
|
|
@ -85,8 +119,6 @@ export async function createAvatarRenderer(
|
|||
!!expressions?.getExpression('blinkLeft') && !!expressions?.getExpression('blinkRight');
|
||||
let speaking = 0,
|
||||
listening = 0,
|
||||
speechTime = 0,
|
||||
listenTime = 0,
|
||||
mouthWeight = 0,
|
||||
elapsed = 0,
|
||||
nextBlink = 2.8,
|
||||
|
|
@ -220,31 +252,26 @@ export async function createAvatarRenderer(
|
|||
speaking += ((input.active && input.speaking ? 1 : 0) - speaking) * smooth;
|
||||
listening +=
|
||||
((input.active && input.listening && !input.speaking ? 1 : 0) - listening) * smooth;
|
||||
speechTime = input.active && input.speaking ? speechTime + dt : 0;
|
||||
listenTime = input.active && input.listening ? listenTime + dt : 0;
|
||||
// Keep the built-in body relaxed; authored clips supply intentional gestures.
|
||||
const amount = reducedMotion ? 0 : Math.sqrt(0.35);
|
||||
const breath = Math.sin(elapsed * 1.5);
|
||||
// One small acknowledgment every few seconds, with stillness between nods.
|
||||
const nodPhase = listenTime % 5;
|
||||
const nod = nodPhase < 1.2 ? Math.sin((nodPhase / 1.2) * Math.PI) : 0;
|
||||
const breath = Math.sin(elapsed * 1.1) * 0.9 + Math.sin(elapsed * 0.47) * 0.1;
|
||||
if (spine) {
|
||||
spine.rotation.x = amount * (0.012 * breath + 0.065 * listening);
|
||||
spine.rotation.y = amount * speaking * 0.01 * Math.sin(speechTime * 0.6);
|
||||
spine.rotation.x += amount * (0.006 * breath + 0.015 * listening);
|
||||
spine.rotation.y += amount * speaking * 0.008 * Math.sin(elapsed * 0.6);
|
||||
}
|
||||
if (chest) chest.rotation.x -= amount * 0.008 * breath;
|
||||
if (head) {
|
||||
head.rotation.x =
|
||||
head.rotation.x +=
|
||||
amount *
|
||||
(0.018 * Math.sin(elapsed * 0.7) +
|
||||
speaking * 0.02 * Math.sin(speechTime * 0.9) +
|
||||
listening * (0.08 + 0.12 * nod));
|
||||
head.rotation.y =
|
||||
amount *
|
||||
(0.025 * Math.sin(elapsed * 0.47) + speaking * 0.015 * Math.sin(speechTime * 0.6));
|
||||
head.rotation.z = amount * (0.02 * Math.sin(elapsed * 0.63) + listening * 0.18);
|
||||
(0.006 * breath + speaking * 0.012 * Math.sin(elapsed * 0.85) + listening * 0.025);
|
||||
head.rotation.y +=
|
||||
amount * (0.012 * Math.sin(elapsed * 0.32) + speaking * 0.008 * Math.sin(elapsed * 0.6));
|
||||
head.rotation.z += amount * (0.006 * Math.sin(elapsed * 0.27) + listening * 0.025);
|
||||
}
|
||||
if (leftArm) leftArm.rotation.z = -1.22 + amount * 0.015 * breath;
|
||||
if (rightArm) rightArm.rotation.z = 1.22 - amount * 0.015 * breath;
|
||||
if (leftArm) leftArm.rotation.z += amount * 0.004 * breath;
|
||||
if (rightArm) rightArm.rotation.z -= amount * 0.003 * Math.sin(elapsed * 1.1 - 0.2);
|
||||
if (leftElbow) leftElbow.rotation.y -= amount * 0.006 * Math.sin(elapsed * 1.1 - 0.3);
|
||||
if (rightElbow) rightElbow.rotation.y += amount * 0.004 * Math.sin(elapsed * 1.1 - 0.5);
|
||||
if (current && (bodyWeight > 0 || authored)) {
|
||||
for (const pose of poses) {
|
||||
pose.q.copy(pose.node.quaternion);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue