UniVRM/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeLookAt.cs
2023-02-14 22:14:48 +09:00

129 lines
5.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using UniGLTF.Extensions.VRMC_vrm;
using UnityEngine;
namespace UniVRM10
{
public sealed class Vrm10RuntimeLookAt : ILookAtEyeDirectionProvider
{
private readonly VRM10ObjectLookAt _lookAt;
private readonly Transform _head;
private readonly Vector3 _eyeTransformLocalPosition;
private readonly Quaternion _eyeTransformLocalRotation;
public float Yaw { get; private set; }
public float Pitch { get; private set; }
internal ILookAtEyeDirectionApplicable EyeDirectionApplicable { get; }
public LookAtEyeDirection EyeDirection { get; private set; }
public Transform EyeTransform { get; }
internal Vrm10RuntimeLookAt(VRM10ObjectLookAt lookAt, UniHumanoid.Humanoid humanoid, Transform head)
{
_lookAt = lookAt;
_head = head;
EyeTransform = InitializeEyePositionTransform(_head, _lookAt.OffsetFromHead);
_eyeTransformLocalPosition = EyeTransform.localPosition;
_eyeTransformLocalRotation = EyeTransform.localRotation;
var leftEyeBone = humanoid.GetBoneTransform(HumanBodyBones.LeftEye);
var rightEyeBone = humanoid.GetBoneTransform(HumanBodyBones.RightEye);
if (_lookAt.LookAtType == LookAtType.bone && leftEyeBone != null && rightEyeBone != null)
{
EyeDirectionApplicable = new LookAtEyeDirectionApplicableToBone(leftEyeBone, rightEyeBone, _lookAt.HorizontalOuter, _lookAt.HorizontalInner, _lookAt.VerticalDown, _lookAt.VerticalUp);
}
else
{
EyeDirectionApplicable = new LookAtEyeDirectionApplicableToExpression(_lookAt.HorizontalOuter, _lookAt.HorizontalInner, _lookAt.VerticalDown, _lookAt.VerticalUp);
}
}
internal void Process(VRM10ObjectLookAt.LookAtTargetTypes lookAtTargetType, Transform gazeTarget)
{
EyeTransform.localPosition = _eyeTransformLocalPosition;
EyeTransform.localRotation = _eyeTransformLocalRotation;
switch (lookAtTargetType)
{
case VRM10ObjectLookAt.LookAtTargetTypes.Auto:
// NOTE: 指定された Gaze Transform の位置を向くように Yaw/Pitch を計算して適用する
if (gazeTarget != null)
{
var value = CalculateYawPitchFromGazePosition(gazeTarget.position);
SetYawPitchManually(value.Yaw, value.Pitch);
}
break;
case VRM10ObjectLookAt.LookAtTargetTypes.Manual:
// NOTE: 直接 Set された Yaw/Pitch を使って計算する
break;
}
EyeDirection = new LookAtEyeDirection(Yaw, Pitch, 0, 0);
}
/// <summary>
/// LookAtTargetTypes.Manual 時に考慮される Yaw/Pitch 値を設定する
/// </summary>
/// <param name="yaw">Headボーンのforwardに対するyaw角(度)</param>
/// <param name="pitch">Headボーンのforwardに対するpitch角(度)</param>
public void SetYawPitchManually(float yaw, float pitch)
{
Yaw = yaw;
Pitch = pitch;
}
public (float Yaw, float Pitch) CalculateYawPitchFromGazePosition(Vector3 gazeWorldPosition)
{
var localPosition = EyeTransform.worldToLocalMatrix.MultiplyPoint(gazeWorldPosition);
Matrix4x4.identity.CalcYawPitch(localPosition, out var yaw, out var pitch);
return (yaw, pitch);
}
private static Transform InitializeEyePositionTransform(Transform head, Vector3 eyeOffsetValue)
{
if (!Application.isPlaying) return null;
// NOTE: このメソッドを実行するとき、モデル全体は初期姿勢T-Poseでなければならない。
var eyePositionTransform = new GameObject("_eye_transform_").transform;
eyePositionTransform.SetParent(head);
eyePositionTransform.localPosition = eyeOffsetValue;
eyePositionTransform.rotation = Quaternion.identity;
return eyePositionTransform;
}
#region Obsolete
[Obsolete]
public Transform GetLookAtOrigin(Transform head)
{
return EyeTransform;
}
[Obsolete]
public void SetLookAtYawPitch(float yaw, float pitch)
{
SetYawPitchManually(yaw, pitch);
}
[Obsolete]
public (float, float) GetLookAtYawPitch(
Transform head,
VRM10ObjectLookAt.LookAtTargetTypes lookAtTargetType,
Transform gaze)
{
switch (lookAtTargetType)
{
case VRM10ObjectLookAt.LookAtTargetTypes.Auto:
return CalculateYawPitchFromGazePosition(gaze.position);
case VRM10ObjectLookAt.LookAtTargetTypes.Manual:
return (Yaw, Pitch);
default:
throw new ArgumentOutOfRangeException(nameof(lookAtTargetType), lookAtTargetType, null);
}
}
#endregion
}
}