Merge pull request #713 from Santarh/expressionImpl2

Refactoring API VRM1.0 Expression & LookAt
This commit is contained in:
Masataka SUMI 2021-02-01 19:51:47 +09:00 committed by GitHub
commit 0058e261f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 434 additions and 236 deletions

View File

@ -2,7 +2,7 @@
using UnityEditor;
using UnityEngine;
using System.Linq;
using System;
using VrmLib;
namespace UniVRM10
{
@ -176,11 +176,13 @@ namespace UniVRM10
void ExpressionGUI()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("IgnoreStatus", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Override rates", EditorStyles.boldLabel);
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.Toggle("Ignore Blink", m_target.Expression.Accumulator.IgnoreBlink);
EditorGUILayout.Toggle("Ignore Look At", m_target.Expression.Accumulator.IgnoreLookAt);
EditorGUILayout.Toggle("Ignore Mouth", m_target.Expression.Accumulator.IgnoreMouth);
{
EditorGUILayout.Slider("Blink override rate", m_target.Expression.BlinkOverrideRate, 0f, 1f);
EditorGUILayout.Slider("LookAt override rate", m_target.Expression.LookAtOverrideRate, 0f, 1f);
EditorGUILayout.Slider("Mouth override rate", m_target.Expression.MouthOverrideRate, 0f, 1f);
}
EditorGUI.EndDisabledGroup();
if (!Application.isPlaying)
@ -200,7 +202,7 @@ namespace UniVRM10
{
m_expressionKeyWeights[slider.Key] = slider.Value;
}
m_target.Expression.Accumulator.SetValues(m_expressionKeyWeights.Select(x => new KeyValuePair<ExpressionKey, float>(x.Key, x.Value)));
m_target.Expression.SetWeights(m_expressionKeyWeights);
}
}

View File

@ -0,0 +1,45 @@
using System.Collections.Generic;
using VrmLib;
namespace UniVRM10
{
public sealed class DefaultExpressionValidator : IExpressionValidator
{
private DefaultExpressionValidator(VRM10ExpressionAvatar expressionAvatar)
{
}
public void Validate(IReadOnlyDictionary<ExpressionKey, float> inputWeights, IDictionary<ExpressionKey, float> actualWeights,
LookAtEyeDirection inputEyeDirection, out LookAtEyeDirection actualEyeDirection,
out float blinkOverrideRate, out float lookAtOverrideRate, out float mouthOverrideRate)
{
// weights
foreach (var (key, weight) in inputWeights)
{
if (!actualWeights.ContainsKey(key))
{
actualWeights.Add(key, weight);
}
actualWeights[key] = weight;
}
// eye direction
actualEyeDirection = inputEyeDirection;
// override rate
blinkOverrideRate = 0f;
lookAtOverrideRate = 0f;
mouthOverrideRate = 0f;
}
public sealed class Factory : IExpressionValidatorFactory
{
public IExpressionValidator Create(VRM10ExpressionAvatar expressionAvatar)
{
return new DefaultExpressionValidator(expressionAvatar);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 50fc6f6edac946548edb751de64c1cb1
timeCreated: 1611916405

View File

@ -9,7 +9,7 @@ namespace UniVRM10
/// <summary>
/// ブレンドシェイプを蓄えてまとめて適用するクラス
/// </summary>
class ExpressionMerger
internal sealed class ExpressionMerger
{
/// <summary>
/// Key から Expression を得る
@ -35,15 +35,6 @@ namespace UniVRM10
m_materialValueBindingMerger = new MaterialValueBindingMerger(m_clipMap, root);
}
/// <summary>
/// 蓄積した値を適用する
/// </summary>
public void Apply()
{
m_morphTargetBindingMerger.Apply();
m_materialValueBindingMerger.Apply();
}
/// <summary>
/// まとめて反映する。1フレームに1回呼び出されることを想定
/// </summary>
@ -54,14 +45,12 @@ namespace UniVRM10
{
AccumulateValue(kv.Key, kv.Value);
}
m_morphTargetBindingMerger.Apply();
m_materialValueBindingMerger.Apply();
}
/// <summary>
/// 即時に反映しない。後にApplyによって反映する
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AccumulateValue(ExpressionKey key, float value)
private void AccumulateValue(ExpressionKey key, float value)
{
m_valueMap[key] = value;

View File

@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace UniVRM10
{
/// <summary>
/// Validate Expression constraints (ex. overrideBlink)
/// </summary>
public interface IExpressionValidator
{
/// <summary>
/// Validate input weights with Expression constraints.
/// </summary>
void Validate(IReadOnlyDictionary<ExpressionKey, float> inputWeights, IDictionary<ExpressionKey, float> actualWeights,
LookAtEyeDirection inputEyeDirection, out LookAtEyeDirection actualEyeDirection,
out float blinkOverrideRate, out float lookAtOverrideRate, out float mouthOverrideRate);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1de32ec5101c48298bb78d6835b5d4e2
timeCreated: 1611916321

View File

@ -0,0 +1,7 @@
namespace UniVRM10
{
public interface IExpressionValidatorFactory
{
IExpressionValidator Create(VRM10ExpressionAvatar expressionAvatar);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5a46e9573ca04d098b71780a849cd30e
timeCreated: 1611916369

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace UniVRM10
{
/// <summary>
/// Receive LooAtEyeDirection, and Apply to bone transforms.
/// </summary>
internal interface ILookAtEyeDirectionApplicable
{
IEnumerable<KeyValuePair<ExpressionKey, float>> Apply(LookAtEyeDirection eyeDirection);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 099d76e017bc4b229ed2f852990b18ea
timeCreated: 1612166872

View File

@ -0,0 +1,7 @@
namespace UniVRM10
{
internal interface ILookAtEyeDirectionProvider
{
LookAtEyeDirection EyeDirection { get; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f4729047efb44254af696cb9eb01be01
timeCreated: 1612173634

View File

@ -0,0 +1,18 @@
namespace UniVRM10
{
public readonly struct LookAtEyeDirection
{
public float LeftYaw { get; }
public float LeftPitch { get; }
public float RightYaw { get; }
public float RightPitch { get; }
public LookAtEyeDirection(float leftYaw, float leftPitch, float rightYaw, float rightPitch)
{
LeftYaw = leftYaw;
LeftPitch = leftPitch;
RightYaw = rightYaw;
RightPitch = rightPitch;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a10e9a01fd446dc98513b556999ba67
timeCreated: 1612163300

View File

@ -7,7 +7,7 @@ namespace UniVRM10
///
/// Base + (A.Target - Base) * A.Weight + (B.Target - Base) * B.Weight ...
///
class MaterialValueBindingMerger
internal sealed class MaterialValueBindingMerger
{
#region MaterialMap
/// <summary>

View File

@ -7,7 +7,7 @@ namespace UniVRM10
///
/// A.Value * A.Weight + B.Value * B.Weight ...
///
class MorphTargetBindingMerger
internal sealed class MorphTargetBindingMerger
{
class DictionaryKeyMorphTargetBindingComparer : IEqualityComparer<MorphTargetBinding>
{

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace UniVRM10
{
[Serializable]
public class PreviewMeshItem
public sealed class PreviewMeshItem
{
public string Path
{

View File

@ -9,7 +9,7 @@ namespace UniVRM10
/// <summary>
/// プレビュー向けのシーンを管理する
/// </summary>
public class PreviewSceneManager : MonoBehaviour
public sealed class PreviewSceneManager : MonoBehaviour
{
public GameObject Prefab;

View File

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UniVRM10
{
@ -46,7 +48,7 @@ namespace UniVRM10
///
/// </summary>
[Serializable]
public class PreviewMaterialItem
public sealed class PreviewMaterialItem
{
public readonly Material Material;

View File

@ -5,7 +5,7 @@ using UnityEngine;
namespace UniVRM10
{
[CreateAssetMenu(menuName = "VRM10/Expression")]
public class VRM10Expression : ScriptableObject
public sealed class VRM10Expression : ScriptableObject
{
#if UNITY_EDITOR
/// <summary>

View File

@ -11,7 +11,7 @@ using UnityEditor;
namespace UniVRM10
{
[CreateAssetMenu(menuName = "VRM10/ExpressionAvatar")]
public class VRM10ExpressionAvatar : ScriptableObject
public sealed class VRM10ExpressionAvatar : ScriptableObject
{
[SerializeField]
public List<VRM10Expression> Clips = new List<VRM10Expression>();

View File

@ -0,0 +1,68 @@
using System.Collections.Generic;
using UnityEngine;
namespace UniVRM10
{
internal sealed class LookAtEyeDirectionApplicableToBone : ILookAtEyeDirectionApplicable
{
private readonly Transform _leftEye;
private readonly Transform _rightEye;
private readonly CurveMapper _horizontalOuter;
private readonly CurveMapper _horizontalInner;
private readonly CurveMapper _verticalDown;
private readonly CurveMapper _verticalUp;
public LookAtEyeDirectionApplicableToBone(Transform leftEye, Transform rightEye,
CurveMapper horizontalOuter, CurveMapper horizontalInner, CurveMapper verticalDown, CurveMapper verticalUp)
{
_leftEye = leftEye;
_rightEye = rightEye;
_horizontalOuter = horizontalOuter;
_horizontalInner = horizontalInner;
_verticalDown = verticalDown;
_verticalUp = verticalUp;
}
/// <summary>
/// LeftEyeボーンとRightEyeボーンに回転を適用する
/// </summary>
public IEnumerable<KeyValuePair<ExpressionKey, float>> Apply(LookAtEyeDirection eyeDirection)
{
var yaw = eyeDirection.LeftYaw;
var pitch = eyeDirection.LeftPitch;
// 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
if (_leftEye != null && _rightEye != null)
{
// 目に値を適用する
_leftEye.localRotation = Matrix4x4.identity.YawPitchRotation(leftYaw, pitch);
_rightEye.localRotation = Matrix4x4.identity.YawPitchRotation(rightYaw, pitch);
}
yield break;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0c973408bdfe4936ac9847d0a55855a2
timeCreated: 1612167497

View File

@ -0,0 +1,60 @@
using System.Collections.Generic;
using UnityEngine;
using VrmLib;
namespace UniVRM10
{
internal sealed class LookAtEyeDirectionApplicableToExpression : ILookAtEyeDirectionApplicable
{
private readonly CurveMapper _horizontalOuter;
private readonly CurveMapper _horizontalInner;
private readonly CurveMapper _verticalDown;
private readonly CurveMapper _verticalUp;
private readonly ExpressionKey _lookRightKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookRight);
private readonly ExpressionKey _lookLeftKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookLeft);
private readonly ExpressionKey _lookUpKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookUp);
private readonly ExpressionKey _lookDownKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookDown);
public LookAtEyeDirectionApplicableToExpression(
CurveMapper horizontalOuter, CurveMapper horizontalInner, CurveMapper verticalDown, CurveMapper verticalUp)
{
_horizontalOuter = horizontalOuter;
_horizontalInner = horizontalInner;
_verticalDown = verticalDown;
_verticalUp = verticalUp;
}
public IEnumerable<KeyValuePair<ExpressionKey, float>> Apply(LookAtEyeDirection eyeDirection)
{
var yaw = eyeDirection.LeftYaw;
var pitch = eyeDirection.LeftPitch;
if (yaw < 0)
{
// Left
yield return new KeyValuePair<ExpressionKey, float>(_lookRightKey, 0);
yield return new KeyValuePair<ExpressionKey, float>(_lookLeftKey, Mathf.Clamp(_horizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f));
}
else
{
// Right
yield return new KeyValuePair<ExpressionKey, float>(_lookRightKey, Mathf.Clamp(_horizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f));
yield return new KeyValuePair<ExpressionKey, float>(_lookLeftKey, 0);
}
if (pitch < 0)
{
// Down
yield return new KeyValuePair<ExpressionKey, float>(_lookUpKey, 0);
yield return new KeyValuePair<ExpressionKey, float>(_lookDownKey, Mathf.Clamp(_verticalDown.Map(Mathf.Abs(pitch)), 0, 1.0f));
}
else
{
// Up
yield return new KeyValuePair<ExpressionKey, float>(_lookUpKey, Mathf.Clamp(_verticalUp.Map(Mathf.Abs(pitch)), 0, 1.0f));
yield return new KeyValuePair<ExpressionKey, float>(_lookDownKey, 0);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d8a407ebe89449e9b03e1ed8a759f60
timeCreated: 1612171380

View File

@ -1,62 +0,0 @@
using System;
using UnityEngine;
namespace UniVRM10
{
[Serializable]
public struct OffsetOnTransform
{
public Transform Transform;
public Matrix4x4 OffsetRotation;
public Matrix4x4 WorldMatrix
{
get
{
if (Transform == null) return Matrix4x4.identity;
return Transform.localToWorldMatrix * OffsetRotation;
}
}
public Vector3 WorldForward
{
get
{
var m = WorldMatrix;
return m.GetColumn(2); // zaxis
}
}
Matrix4x4 m_initialLocalMatrix;
public void Setup()
{
if (Transform == null) return;
m_initialLocalMatrix = Transform.parent.worldToLocalMatrix * Transform.localToWorldMatrix;
}
public Matrix4x4 InitialWorldMatrix
{
get
{
return Transform.parent.localToWorldMatrix * m_initialLocalMatrix;
}
}
public static OffsetOnTransform Create(Transform transform)
{
var coordinate = new OffsetOnTransform
{
Transform = transform,
m_initialLocalMatrix = Matrix4x4.identity,
};
if (transform != null)
{
coordinate.OffsetRotation = transform.worldToLocalMatrix.RotationToWorldAxis();
}
return coordinate;
}
}
}

View File

@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: 53baaa47aacbd984a8361a5f9d33ea33
timeCreated: 1518347841
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -96,17 +96,14 @@ namespace UniVRM10
}
}
private void Start()
public void Setup()
{
Expression.OnStart(transform);
// get lookat origin
var animator = GetComponent<Animator>();
if (animator != null)
{
m_head = animator.GetBoneTransform(HumanBodyBones.Head);
LookAt.Setup(animator, m_head);
}
if (animator == null) return;
m_head = animator.GetBoneTransform(HumanBodyBones.Head);
LookAt.Setup(animator, m_head);
Expression.Setup(transform, LookAt, LookAt.EyeDirectionApplicable);
}
/// <summary>
@ -118,7 +115,7 @@ namespace UniVRM10
/// * Expression
///
/// </summary>
public void Apply()
public void Process()
{
//
// constraint
@ -140,19 +137,24 @@ namespace UniVRM10
//
// gaze control
//
LookAt.Process(Head, Expression.Accumulator.SetPresetValue);
LookAt.Process();
//
// expression
//
Expression.Apply();
Expression.Process();
}
private void OnEnable()
{
Setup();
}
private void Update()
{
if (Controller.UpdateType == VRM10ControllerImpl.UpdateTypes.Update)
{
Apply();
Process();
}
}
@ -160,7 +162,7 @@ namespace UniVRM10
{
if (Controller.UpdateType == VRM10ControllerImpl.UpdateTypes.LateUpdate)
{
Apply();
Process();
}
}

View File

@ -1,55 +1,138 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using VrmLib;
namespace UniVRM10
{
[Serializable]
public class VRM10ControllerExpression : IDisposable
public sealed class VRM10ControllerExpression : IDisposable
{
public static IExpressionValidatorFactory ExpressionValidatorFactory = new DefaultExpressionValidator.Factory();
[SerializeField]
public VRM10ExpressionAvatar ExpressionAvatar;
ExpressionMerger m_merger;
private List<ExpressionKey> _keys = new List<ExpressionKey>();
private Dictionary<ExpressionKey, float> _inputWeights = new Dictionary<ExpressionKey, float>();
private Dictionary<ExpressionKey, float> _actualWeights = new Dictionary<ExpressionKey, float>();
private ExpressionMerger _merger;
private IExpressionValidator _validator;
private LookAtEyeDirection _inputEyeDirection;
private LookAtEyeDirection _actualEyeDirection;
private ILookAtEyeDirectionProvider _eyeDirectionProvider;
private ILookAtEyeDirectionApplicable _eyeDirectionApplicable;
public IReadOnlyList<ExpressionKey> ExpressionKeys => _keys;
public IReadOnlyDictionary<ExpressionKey, float> ActualWeights => _actualWeights;
public LookAtEyeDirection ActualEyeDirection => _actualEyeDirection;
public float BlinkOverrideRate { get; private set; }
public float LookAtOverrideRate { get; private set; }
public float MouthOverrideRate { get; private set; }
public void Dispose()
{
if (m_merger != null)
{
m_merger.RestoreMaterialInitialValues();
}
_merger?.RestoreMaterialInitialValues();
}
IExpressionAccumulator m_accumulator;
public IExpressionAccumulator Accumulator
internal void Setup(Transform transform, ILookAtEyeDirectionProvider eyeDirectionProvider, ILookAtEyeDirectionApplicable eyeDirectionApplicable)
{
get
if (ExpressionAvatar == null)
{
if (m_accumulator == null)
Debug.LogError($"{nameof(VRM10ControllerExpression)}.{nameof(ExpressionAvatar)} is null.");
return;
}
_merger = new ExpressionMerger(ExpressionAvatar.Clips, transform);
_keys = ExpressionAvatar.Clips.Select(ExpressionKey.CreateFromClip).ToList();
_inputWeights = _keys.ToDictionary(x => x, x => 0f);
_actualWeights = _keys.ToDictionary(x => x, x => 0f);
_validator = ExpressionValidatorFactory.Create(ExpressionAvatar);
_eyeDirectionProvider = eyeDirectionProvider;
_eyeDirectionApplicable = eyeDirectionApplicable;
}
public void Process()
{
Apply();
}
public IDictionary<ExpressionKey, float> GetWeights()
{
return _inputWeights;
}
public float GetWeight(ExpressionKey expressionKey)
{
if (_inputWeights.ContainsKey(expressionKey))
{
return _inputWeights[expressionKey];
}
return 0f;
}
public LookAtEyeDirection GetEyeDirection()
{
return _inputEyeDirection;
}
public void SetWeights(IEnumerable<KeyValuePair<ExpressionKey, float>> weights)
{
foreach (var (expressionKey, weight) in weights)
{
if (_inputWeights.ContainsKey(expressionKey))
{
m_accumulator = new DefaultExpressionAccumulator();
_inputWeights[expressionKey] = weight;
}
return m_accumulator;
}
Apply();
}
public void OnStart(Transform transform)
public void SetWeight(ExpressionKey expressionKey, float weight)
{
if (ExpressionAvatar != null)
if (_inputWeights.ContainsKey(expressionKey))
{
if (m_merger == null)
{
m_merger = new ExpressionMerger(ExpressionAvatar.Clips, transform);
}
Accumulator.OnStart(ExpressionAvatar);
_inputWeights[expressionKey] = weight;
}
Apply();
}
public void Apply()
/// <summary>
/// 入力 Weight を基に、Validation を行い実際にモデルに適用される Weights を計算し、Merger を介して適用する。
/// この際、LookAt の情報を pull してそれも適用する。
/// </summary>
private void Apply()
{
m_merger.SetValues(m_accumulator.FrameExpression());
m_merger.Apply();
// 1. Get eye direction from provider.
_inputEyeDirection = _eyeDirectionProvider.EyeDirection;
// 2. Validate user input, and Output as actual weights.
_validator.Validate(_inputWeights, _actualWeights,
_inputEyeDirection, out _actualEyeDirection,
out var blink, out var lookAt, out var mouth);
// 3. Set eye direction expression weights or any other side-effects (ex. eye bone).
if (_eyeDirectionApplicable != null)
{
foreach (var (expressionKey, weight) in _eyeDirectionApplicable.Apply(_actualEyeDirection))
{
if (!_actualWeights.ContainsKey(expressionKey))
{
_actualWeights.Add(expressionKey, 0f);
}
_actualWeights[expressionKey] = weight;
}
}
// 4. Set actual weights to raw blendshapes.
_merger.SetValues(_actualWeights);
BlinkOverrideRate = blink;
LookAtOverrideRate = lookAt;
MouthOverrideRate = mouth;
}
}
}

View File

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using VrmLib;
#if UNITY_EDITOR
using UnityEditor;
#endif
@ -7,7 +9,7 @@ using UnityEditor;
namespace UniVRM10
{
[Serializable]
public class VRM10ControllerLookAt
public class VRM10ControllerLookAt : ILookAtEyeDirectionProvider
{
public enum LookAtTypes
{
@ -47,8 +49,14 @@ namespace UniVRM10
[SerializeField]
public LookAtTargetTypes LookAtTargetType;
OffsetOnTransform m_leftEye;
OffsetOnTransform m_rightEye;
private Transform m_head;
private Transform m_leftEye;
private Transform m_rightEye;
private ILookAtEyeDirectionApplicable _eyeDirectionApplicable;
internal ILookAtEyeDirectionApplicable EyeDirectionApplicable => _eyeDirectionApplicable;
public LookAtEyeDirection EyeDirection { get; private set; }
#region LookAtTargetTypes.CalcYawPitchToGaze
/// <summay>
@ -107,7 +115,7 @@ namespace UniVRM10
/// LookAtTargetType に応じた yaw, pitch を得る
/// </summary>
/// <returns>Headボーンのforwardに対するyaw角(度), pitch角(度)</returns>
public (float, float) GetLookAtYawPitch(Transform head)
private (float, float) GetLookAtYawPitch(Transform head)
{
switch (LookAtTargetType)
{
@ -123,108 +131,37 @@ namespace UniVRM10
throw new NotImplementedException();
}
/// <summary>
/// LeftEyeボーンとRightEyeボーンに回転を適用する
/// </summary>
void LookAtBone(float yaw, float pitch)
internal void Setup(Animator animator, Transform head)
{
// 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
if (m_leftEye.Transform != null && m_rightEye.Transform != null)
{
// 目に値を適用する
m_leftEye.Transform.rotation = m_leftEye.InitialWorldMatrix.ExtractRotation() * Matrix4x4.identity.YawPitchRotation(leftYaw, pitch);
m_rightEye.Transform.rotation = m_rightEye.InitialWorldMatrix.ExtractRotation() * Matrix4x4.identity.YawPitchRotation(rightYaw, pitch);
}
}
public delegate void SetPresetValue(VrmLib.ExpressionPreset preset, float weight);
/// <summary>
/// Expression による LookAt を処理する(関連する Expression の Weight を変更する)
/// </summary>
/// <param name="yaw"></param>
/// <param name="pitch"></param>
void LookAtExpression(float yaw, float pitch, SetPresetValue SetPresetValue)
{
if (yaw < 0)
{
// Left
SetPresetValue(VrmLib.ExpressionPreset.LookRight, 0); // clear first
SetPresetValue(VrmLib.ExpressionPreset.LookLeft, Mathf.Clamp(HorizontalOuter.Map(-yaw), 0, 1.0f));
}
else
{
// Right
SetPresetValue(VrmLib.ExpressionPreset.LookLeft, 0); // clear first
SetPresetValue(VrmLib.ExpressionPreset.LookRight, Mathf.Clamp(HorizontalOuter.Map(yaw), 0, 1.0f));
}
if (pitch < 0)
{
// Down
SetPresetValue(VrmLib.ExpressionPreset.LookUp, 0); // clear first
SetPresetValue(VrmLib.ExpressionPreset.LookDown, Mathf.Clamp(VerticalDown.Map(-pitch), 0, 1.0f));
}
else
{
// Up
SetPresetValue(VrmLib.ExpressionPreset.LookDown, 0); // clear first
SetPresetValue(VrmLib.ExpressionPreset.LookUp, Mathf.Clamp(VerticalUp.Map(pitch), 0, 1.0f));
}
}
public void Setup(Animator animator, Transform head)
{
m_leftEye = OffsetOnTransform.Create(animator.GetBoneTransform(HumanBodyBones.LeftEye));
m_rightEye = OffsetOnTransform.Create(animator.GetBoneTransform(HumanBodyBones.RightEye));
m_head = head;
m_leftEye = animator.GetBoneTransform(HumanBodyBones.LeftEye);
m_rightEye = animator.GetBoneTransform(HumanBodyBones.RightEye);
if (Gaze == null)
{
Gaze = new GameObject().transform;
Gaze.name = "__LOOKAT_GAZE__";
Gaze.SetParent(head);
Gaze.SetParent(m_head);
Gaze.localPosition = Vector3.forward;
}
}
public void Process(Transform head, SetPresetValue setPresetValue)
{
var (yaw, pitch) = GetLookAtYawPitch(head);
switch (LookAtType)
{
case LookAtTypes.Bone:
LookAtBone(yaw, pitch);
_eyeDirectionApplicable = new LookAtEyeDirectionApplicableToBone(m_leftEye, m_rightEye, HorizontalOuter, HorizontalInner, VerticalDown, VerticalUp);
break;
case LookAtTypes.Expression:
LookAtExpression(yaw, pitch, setPresetValue);
_eyeDirectionApplicable = new LookAtEyeDirectionApplicableToExpression(HorizontalOuter, HorizontalInner, VerticalDown, VerticalUp);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
public void Process()
{
var (yaw, pitch) = GetLookAtYawPitch(m_head);
EyeDirection = new LookAtEyeDirection(yaw, pitch, 0, 0);
}
#if UNITY_EDITOR
#region Gizmo
static void DrawMatrix(Matrix4x4 m, float size)
@ -244,10 +181,10 @@ namespace UniVRM10
{
if (DrawGizmo)
{
if (m_leftEye.Transform != null & m_rightEye.Transform != null)
if (m_leftEye != null & m_rightEye != null)
{
DrawMatrix(m_leftEye.WorldMatrix, LOOKAT_GIZMO_SIZE);
DrawMatrix(m_rightEye.WorldMatrix, LOOKAT_GIZMO_SIZE);
DrawMatrix(m_leftEye.localToWorldMatrix, LOOKAT_GIZMO_SIZE);
DrawMatrix(m_rightEye.localToWorldMatrix, LOOKAT_GIZMO_SIZE);
}
}
}