expressionController.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import * as THREE from "three";
  2. import {
  3. VRM,
  4. VRMExpressionManager,
  5. VRMExpressionPresetName,
  6. } from "@pixiv/three-vrm";
  7. import { AutoLookAt } from "./autoLookAt";
  8. import { AutoBlink } from "./autoBlink";
  9. import { emotionNames } from "@/features/chat/messages";
  10. /**
  11. * Expressionを管理するクラス
  12. *
  13. * 主に前の表情を保持しておいて次の表情を適用する際に0に戻す作業や、
  14. * 前の表情が終わるまで待ってから表情適用する役割を持っている。
  15. */
  16. export class ExpressionController {
  17. private _autoLookAt: AutoLookAt;
  18. private _autoBlink?: AutoBlink;
  19. private _expressionManager?: VRMExpressionManager;
  20. private _currentEmotion: VRMExpressionPresetName | string;
  21. private _currentLipSync: {
  22. preset: VRMExpressionPresetName | string;
  23. value: number;
  24. } | null;
  25. constructor(vrm: VRM, camera: THREE.Object3D) {
  26. this._autoLookAt = new AutoLookAt(vrm, camera);
  27. this._currentEmotion = "neutral";
  28. this._currentLipSync = null;
  29. this.registerExpression(vrm)
  30. if (vrm.expressionManager) {
  31. this._expressionManager = vrm.expressionManager;
  32. this._autoBlink = new AutoBlink(vrm.expressionManager);
  33. }
  34. }
  35. public registerExpression(vrm: VRM) {
  36. const expressionManager = vrm.expressionManager;
  37. if (!expressionManager) return;
  38. const { expressionMap: allExpressions, presetExpressionMap: presetExpressions } = expressionManager;
  39. const oddExpressions = Object.keys(allExpressions).filter(key => !(key in presetExpressions));
  40. oddExpressions.forEach(expressionName => {
  41. const expression = allExpressions[expressionName];
  42. if (expression) expressionManager.registerExpression(expression);
  43. });
  44. const allExpressionNames = Object.keys(allExpressions);
  45. emotionNames.push(...allExpressionNames);
  46. return allExpressionNames;
  47. }
  48. public playEmotion(preset: VRMExpressionPresetName | string) {
  49. const normalizedPreset = `${preset.charAt(0).toUpperCase()}${preset.slice(1)}`;
  50. if (this._currentEmotion == preset) {
  51. return;
  52. }
  53. if (this._currentEmotion != "neutral") {
  54. this._expressionManager?.setValue(this._currentEmotion, 0);
  55. }
  56. if (preset == "neutral") {
  57. this._autoBlink?.setEnable(true);
  58. this._currentEmotion = preset;
  59. return;
  60. }
  61. const value = (normalizedPreset in VRMExpressionPresetName) ? (normalizedPreset === "Surprised" ? 0.5 : 1) : 0.5;
  62. const t = this._autoBlink?.setEnable(false) || 0;
  63. this._currentEmotion = preset;
  64. setTimeout(() => {
  65. this._expressionManager?.setValue(preset, value);
  66. }, t * 1000);
  67. }
  68. public lipSync(preset: VRMExpressionPresetName | string, value: number) {
  69. if (this._currentLipSync) {
  70. this._expressionManager?.setValue(this._currentLipSync.preset, 0);
  71. }
  72. this._currentLipSync = {
  73. preset,
  74. value,
  75. };
  76. }
  77. public update(delta: number) {
  78. if (this._autoBlink) {
  79. this._autoBlink.update(delta);
  80. }
  81. if (this._currentLipSync) {
  82. const weight =
  83. this._currentEmotion === "neutral"
  84. ? this._currentLipSync.value * 0.5
  85. : this._currentLipSync.value * 0.25;
  86. this._expressionManager?.setValue(this._currentLipSync.preset, weight);
  87. }
  88. }
  89. }