pixiv / three-vrm

Use VRM on Three.js
MIT License
1.3k stars 110 forks source link

VRMExpression EYE lssues #1533

Closed yuchanahn closed 1 week ago

yuchanahn commented 1 week ago
class VRMExpressionMorphTargetBind {
  public readonly primitives: THREE.Mesh[];
  public readonly index: number;
  public readonly weight: number;

  public constructor({
    primitives,
    index,
    weight,
  }: {
    primitives: THREE.Mesh[];
    index: number;
    weight: number;
  }) {
    this.primitives = primitives;
    this.index = index;
    this.weight = weight;
  }

  public applyWeight(weight: number): void {
    this.primitives.forEach((mesh) => {
      if (mesh.morphTargetInfluences?.[this.index] != null) {
        mesh.morphTargetInfluences[this.index] += this.weight * weight;
      }
    });
  }

  public clearAppliedWeight(): void {
    this.primitives.forEach((mesh) => {
      if (mesh.morphTargetInfluences?.[this.index] != null) {
        mesh.morphTargetInfluences[this.index] = 0.0;
      }
    });
  }
}

export class ExpressionController {
  private _autoLookAt: AutoLookAt
  private _autoBlink?: AutoBlink
  private _expressionManager?: VRMExpressionManager
  private _currentEmotion: VRMExpressionPresetName
  private _vrm: VRM

  private _currentLipSync: {
    preset: VRMExpressionPresetName
    value: number
  } | null

  private emotionExpressions = [
    {
      name: "annoyance",
      targets: [
        { targetName: "Fcl_BRW_Angry", weight: 0.8 },
        { targetName: "Fcl_EYE_Close_R", weight: 0.6 },
        { targetName: "Fcl_EYE_Close_L", weight: 0.6 },
        { targetName: "Fcl_MTH_Angry", weight: 0.7 },
      ]
    },
  ];

  constructor(vrm: VRM, camera: THREE.Object3D) {
    this._vrm = vrm
    this._autoLookAt = new AutoLookAt(vrm, camera)
    this._autoLookAt.start()
    this._currentEmotion = 'neutral'
    this._currentLipSync = null
    if (vrm.expressionManager) {
      this._expressionManager = vrm.expressionManager
      this._autoBlink = new AutoBlink(vrm.expressionManager)
    }

    const expressions = this.custom_expressions(
      this.emotionExpressions
    );

    expressions.forEach(
      (x) => {this._expressionManager?.registerExpression(x);}
    );

    this.emotion_test()
  }

  public custom_expressions(
    expressionsData: Array<{ name: string; targets: Array<{ targetName: string; weight: number }> }>
  ): VRMExpression[] {
    const expressions: VRMExpression[] = [];

    expressionsData.forEach(({ name, targets }) => {
      const expression = new VRMExpression(name);

      // Face 오브젝트 찾기
      const faceObject = this._vrm?.scene.getObjectByName("Face");
      if (!faceObject) {
        console.warn("Face 오브젝트를 찾을 수 없습니다.");
        return;
      }

      // Face 오브젝트의 프리미티브 가져오기
      const primitives = faceObject.children.filter(
        (child) => child instanceof THREE.Mesh
      ) as THREE.Mesh[];

      // SkinnedMesh에서 morphTargetDictionary 가져오기
      const skinnedMesh = faceObject.children[0] as THREE.SkinnedMesh;
      const morphTargetDictionary = skinnedMesh.morphTargetDictionary;

      if (!morphTargetDictionary) {
        console.warn("morphTargetDictionary를 찾을 수 없습니다.");
        return;
      } else {
        console.log(Object.keys(morphTargetDictionary));
      }

      // 여러 target을 바인드하는 부분
      targets.forEach(({ targetName, weight }) => {
        if (morphTargetDictionary[targetName] !== undefined) {
          expression.addBind(
            new VRMExpressionMorphTargetBind({
              primitives,
              index: morphTargetDictionary[targetName],
              weight,
            })
          );
        } else {
          console.warn(`Target '${targetName}'을 찾을 수 없습니다.`);
        }
      });

      expressions.push(expression); // expression을 배열에 추가
    });

    return expressions; // 모든 Expression 객체를 배열로 반환
  }

  public emotion_test() {

    // annoyance
    // approval
    // caring
    // confusion

    const test_emotions = ['annoyance']

    for (let i = 0; i < test_emotions.length; i++) {
      setTimeout(() => {
        if((i - 1) != -1) {
          this._expressionManager?.setValue(test_emotions[i - 1], 0);
        }
        this._expressionManager?.setValue(test_emotions[i], 1);
        console.log(test_emotions[i]);
      }, i * 100000);
    }
  }

image <- three-vrm(1.0.3)

same code

image <- three-vrm(3.1.6)