refactor: adjust mouth clamping, jaw rotation, and viseme mappings while removing redundant mesh double-sided rendering logic
Browse files
ui/vrm.py
CHANGED
|
@@ -252,11 +252,9 @@ def avatar_html(vrm_urls: str | list[str]) -> str:
|
|
| 252 |
if (persistent) persistentEmotion = name;
|
| 253 |
}}
|
| 254 |
|
| 255 |
-
const MAX_MOUTH_WEIGHT = 0.7; // Cap mouth open to prevent chin mesh stretching artifacts
|
| 256 |
-
|
| 257 |
function setMouth(weight, viseme = 'ih') {{
|
| 258 |
if (!vrm) return;
|
| 259 |
-
const clamped = Math.max(0, Math.min(
|
| 260 |
let preset = VISEME_MAP[viseme] ?? viseme ?? 'ih';
|
| 261 |
if (preset === 'aa') preset = 'ih'; // Disable 'aa' completely due to chin artifact
|
| 262 |
let usedExpression = false;
|
|
@@ -267,10 +265,16 @@ def avatar_html(vrm_urls: str | list[str]) -> str:
|
|
| 267 |
usedExpression = true;
|
| 268 |
}}
|
| 269 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
}}
|
| 271 |
if (!usedExpression) {{
|
| 272 |
const jaw = vrm.humanoid?.getNormalizedBoneNode?.('jaw') || vrm.humanoid?.getRawBoneNode?.('jaw');
|
| 273 |
-
if (jaw) jaw.rotation.x = clamped * 0.
|
| 274 |
}}
|
| 275 |
}}
|
| 276 |
|
|
@@ -716,18 +720,7 @@ def avatar_html(vrm_urls: str | list[str]) -> str:
|
|
| 716 |
vrm = gltf.userData.vrm;
|
| 717 |
window._aikoVrm = vrm;
|
| 718 |
VRMUtils.removeUnnecessaryVertices(vrm.scene);
|
| 719 |
-
vrm.scene.traverse(o => {{
|
| 720 |
-
if (o.frustumCulled) o.frustumCulled = false;
|
| 721 |
-
// Fix white chin artifacts: force double-sided rendering so
|
| 722 |
-
// back-faces of stretched mouth/chin geometry render correctly
|
| 723 |
-
// instead of showing as bright white patches.
|
| 724 |
-
if (o.isMesh && o.material) {{
|
| 725 |
-
const mats = Array.isArray(o.material) ? o.material : [o.material];
|
| 726 |
-
for (const mat of mats) {{
|
| 727 |
-
mat.side = THREE.DoubleSide;
|
| 728 |
-
}}
|
| 729 |
-
}}
|
| 730 |
-
}});
|
| 731 |
vrm.scene.rotation.y = 0;
|
| 732 |
scene.add(vrm.scene);
|
| 733 |
|
|
@@ -765,12 +758,12 @@ def avatar_html(vrm_urls: str | list[str]) -> str:
|
|
| 765 |
const audioMouth = speaking ? getAudioMouth() : null;
|
| 766 |
|
| 767 |
if (audioMouth !== null) {{
|
| 768 |
-
setMouth(audioMouth, textMouth?.viseme ?? '
|
| 769 |
}} else if (textMouth) {{
|
| 770 |
setMouth(textMouth.weight, textMouth.viseme);
|
| 771 |
}} else if (speaking) {{
|
| 772 |
-
mouth = 0.
|
| 773 |
-
setMouth(mouth, '
|
| 774 |
}} else {{
|
| 775 |
smoothedAudioMouth = 0;
|
| 776 |
clearMouth();
|
|
|
|
| 252 |
if (persistent) persistentEmotion = name;
|
| 253 |
}}
|
| 254 |
|
|
|
|
|
|
|
| 255 |
function setMouth(weight, viseme = 'ih') {{
|
| 256 |
if (!vrm) return;
|
| 257 |
+
const clamped = Math.max(0, Math.min(1, Number(weight) || 0));
|
| 258 |
let preset = VISEME_MAP[viseme] ?? viseme ?? 'ih';
|
| 259 |
if (preset === 'aa') preset = 'ih'; // Disable 'aa' completely due to chin artifact
|
| 260 |
let usedExpression = false;
|
|
|
|
| 265 |
usedExpression = true;
|
| 266 |
}}
|
| 267 |
}}
|
| 268 |
+
try {{
|
| 269 |
+
const em = vrm.expressionManager;
|
| 270 |
+
if (em._expressions) {{
|
| 271 |
+
em._expressions.forEach(expr => expr.applyWeight({{ multiplier: 1 }}));
|
| 272 |
+
}}
|
| 273 |
+
}} catch (_) {{}}
|
| 274 |
}}
|
| 275 |
if (!usedExpression) {{
|
| 276 |
const jaw = vrm.humanoid?.getNormalizedBoneNode?.('jaw') || vrm.humanoid?.getRawBoneNode?.('jaw');
|
| 277 |
+
if (jaw) jaw.rotation.x = clamped * 0.5;
|
| 278 |
}}
|
| 279 |
}}
|
| 280 |
|
|
|
|
| 720 |
vrm = gltf.userData.vrm;
|
| 721 |
window._aikoVrm = vrm;
|
| 722 |
VRMUtils.removeUnnecessaryVertices(vrm.scene);
|
| 723 |
+
vrm.scene.traverse(o => {{ if (o.frustumCulled) o.frustumCulled = false; }});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 724 |
vrm.scene.rotation.y = 0;
|
| 725 |
scene.add(vrm.scene);
|
| 726 |
|
|
|
|
| 758 |
const audioMouth = speaking ? getAudioMouth() : null;
|
| 759 |
|
| 760 |
if (audioMouth !== null) {{
|
| 761 |
+
setMouth(audioMouth, textMouth?.viseme ?? 'aa');
|
| 762 |
}} else if (textMouth) {{
|
| 763 |
setMouth(textMouth.weight, textMouth.viseme);
|
| 764 |
}} else if (speaking) {{
|
| 765 |
+
mouth = 0.12 + Math.abs(Math.sin(now / 110)) * 0.65;
|
| 766 |
+
setMouth(mouth, 'aa');
|
| 767 |
}} else {{
|
| 768 |
smoothedAudioMouth = 0;
|
| 769 |
clearMouth();
|