diff --git a/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirection.cs b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirection.cs index 2f36fe60b..5aa6c250a 100644 --- a/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirection.cs +++ b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirection.cs @@ -2,10 +2,19 @@ { public readonly struct LookAtEyeDirection { + /// + /// Positive is right. + /// Negative is left. + /// + /// public float Yaw { get; } /// - /// Pitch of LeftEye + /// Positive is upper. + /// Negative is lower. + /// + /// Usually in z-forward y-up left coordinates, positive is lower. + /// This is inverted because of following the vrm-1.0 specification. /// public float Pitch { get; } diff --git a/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs index 89e483c67..dee0ac4ea 100644 --- a/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs +++ b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs @@ -3,13 +3,18 @@ using UnityEngine; namespace UniVRM10 { + /// + /// Apply eye direction to bone transforms. + /// internal sealed class LookAtEyeDirectionApplicableToBone : ILookAtEyeDirectionApplicable { private readonly Transform _leftEye; - private readonly Matrix4x4 _leftInit; + private readonly Quaternion _leftEyePreMultiplyRotation; + private readonly Quaternion _leftEyePostMultiplyRotation; private readonly Transform _rightEye; - private readonly Matrix4x4 _rightInit; + private readonly Quaternion _rightEyePreMultiplyRotation; + private readonly Quaternion _rightEyePostMultiplyRotation; private readonly CurveMapper _horizontalOuter; private readonly CurveMapper _horizontalInner; @@ -20,52 +25,24 @@ namespace UniVRM10 CurveMapper horizontalOuter, CurveMapper horizontalInner, CurveMapper verticalDown, CurveMapper verticalUp) { _leftEye = leftEye; - _leftInit = Matrix4x4.Rotate(leftEye.localRotation); + var leftEyeRotation = _leftEye.rotation; + _leftEyePreMultiplyRotation = _leftEye.localRotation * Quaternion.Inverse(leftEyeRotation); + _leftEyePostMultiplyRotation = leftEyeRotation; + _rightEye = rightEye; - _rightInit = Matrix4x4.Rotate(rightEye.localRotation); + var rightEyeRotation = _rightEye.rotation; + _rightEyePreMultiplyRotation = _rightEye.localRotation * Quaternion.Inverse(rightEyeRotation); + _rightEyePostMultiplyRotation = rightEyeRotation; + _horizontalOuter = horizontalOuter; _horizontalInner = horizontalInner; _verticalDown = verticalDown; _verticalUp = verticalUp; } - /// - /// LeftEyeボーンとRightEyeボーンに回転を適用する - /// public void Apply(LookAtEyeDirection eyeDirection, Dictionary actualWeights) { - // FIXME - var yaw = eyeDirection.Yaw; - var pitch = eyeDirection.Pitch; - - // horizontal - float leftYaw, rightYaw; - if (yaw < 0) - { - leftYaw = -_horizontalOuter.Map(-yaw); - rightYaw = -_horizontalInner.Map(-yaw); - } - else - { - rightYaw = _horizontalOuter.Map(yaw); - leftYaw = _horizontalInner.Map(yaw); - } - - // vertical - if (pitch < 0) - { - pitch = -_verticalDown.Map(-pitch); - } - else - { - pitch = _verticalUp.Map(pitch); - } - - // Apply - // 現状、右目左目を個別に動かす機能は無い。 - // 特に BlendShape 型に対して実装と、Asset のセットアップが煩雑になるので見送っている。 - // 代表して左を採用。 - SetYawPitchToBones(new LookAtEyeDirection(leftYaw, pitch)); + SetYawPitchToBones(eyeDirection); } public void Restore() @@ -73,12 +50,41 @@ namespace UniVRM10 SetYawPitchToBones(new LookAtEyeDirection(0, 0)); } - private void SetYawPitchToBones(LookAtEyeDirection actualEyeDirection) + private void SetYawPitchToBones(LookAtEyeDirection eyeDirection) { - if (_leftEye != null && _rightEye != null) + float leftYaw, rightYaw; + if (eyeDirection.Yaw < 0) { - _leftEye.localRotation = _leftInit.rotation * Matrix4x4.identity.YawPitchRotation(actualEyeDirection.Yaw, actualEyeDirection.Pitch); - _rightEye.localRotation = _rightInit.rotation * Matrix4x4.identity.YawPitchRotation(actualEyeDirection.Yaw, actualEyeDirection.Pitch); + leftYaw = -_horizontalOuter.Map(-eyeDirection.Yaw); + rightYaw = -_horizontalInner.Map(-eyeDirection.Yaw); + } + else + { + leftYaw = _horizontalInner.Map(eyeDirection.Yaw); + rightYaw = _horizontalOuter.Map(eyeDirection.Yaw); + } + + float pitch; + if (eyeDirection.Pitch < 0) + { + pitch = _verticalDown.Map(-eyeDirection.Pitch); + } + else + { + pitch = -_verticalUp.Map(eyeDirection.Pitch); + } + + if (_leftEye != null) + { + _leftEye.localRotation = _leftEyePreMultiplyRotation * + Quaternion.Euler(pitch, leftYaw, 0) * + _leftEyePostMultiplyRotation; + } + if (_rightEye != null) + { + _rightEye.localRotation = _rightEyePreMultiplyRotation * + Quaternion.Euler(pitch, rightYaw, 0) * + _rightEyePostMultiplyRotation; } } }