diff --git a/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs b/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs index 57d3fd57b..e77875e49 100644 --- a/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs +++ b/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs @@ -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(x.Key, x.Value))); + m_target.Expression.SetWeights(m_expressionKeyWeights); } } diff --git a/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs new file mode 100644 index 000000000..de6aa8de0 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs @@ -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 inputWeights, IDictionary 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); + } + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs.meta b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs.meta new file mode 100644 index 000000000..0e09d4c4c --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 50fc6f6edac946548edb751de64c1cb1 +timeCreated: 1611916405 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs b/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs index b671cc0ba..2c345b432 100644 --- a/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs +++ b/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs @@ -9,7 +9,7 @@ namespace UniVRM10 /// /// ブレンドシェイプを蓄えてまとめて適用するクラス /// - class ExpressionMerger + internal sealed class ExpressionMerger { /// /// Key から Expression を得る @@ -35,15 +35,6 @@ namespace UniVRM10 m_materialValueBindingMerger = new MaterialValueBindingMerger(m_clipMap, root); } - /// - /// 蓄積した値を適用する - /// - public void Apply() - { - m_morphTargetBindingMerger.Apply(); - m_materialValueBindingMerger.Apply(); - } - /// /// まとめて反映する。1フレームに1回呼び出されることを想定 /// @@ -54,14 +45,12 @@ namespace UniVRM10 { AccumulateValue(kv.Key, kv.Value); } + + m_morphTargetBindingMerger.Apply(); + m_materialValueBindingMerger.Apply(); } - /// - /// 即時に反映しない。後にApplyによって反映する - /// - /// - /// - public void AccumulateValue(ExpressionKey key, float value) + private void AccumulateValue(ExpressionKey key, float value) { m_valueMap[key] = value; diff --git a/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs new file mode 100644 index 000000000..f417ce01d --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace UniVRM10 +{ + /// + /// Validate Expression constraints (ex. overrideBlink) + /// + public interface IExpressionValidator + { + /// + /// Validate input weights with Expression constraints. + /// + void Validate(IReadOnlyDictionary inputWeights, IDictionary actualWeights, + LookAtEyeDirection inputEyeDirection, out LookAtEyeDirection actualEyeDirection, + out float blinkOverrideRate, out float lookAtOverrideRate, out float mouthOverrideRate); + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs.meta b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs.meta new file mode 100644 index 000000000..aa7e7bed7 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1de32ec5101c48298bb78d6835b5d4e2 +timeCreated: 1611916321 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/IExpressionValidatorFactory.cs b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidatorFactory.cs new file mode 100644 index 000000000..2f4a523cc --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidatorFactory.cs @@ -0,0 +1,7 @@ +namespace UniVRM10 +{ + public interface IExpressionValidatorFactory + { + IExpressionValidator Create(VRM10ExpressionAvatar expressionAvatar); + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/IExpressionValidatorFactory.cs.meta b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidatorFactory.cs.meta new file mode 100644 index 000000000..6e71a2826 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidatorFactory.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5a46e9573ca04d098b71780a849cd30e +timeCreated: 1611916369 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionApplicable.cs b/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionApplicable.cs new file mode 100644 index 000000000..3c2924369 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionApplicable.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace UniVRM10 +{ + /// + /// Receive LooAtEyeDirection, and Apply to bone transforms. + /// + internal interface ILookAtEyeDirectionApplicable + { + IEnumerable> Apply(LookAtEyeDirection eyeDirection); + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionApplicable.cs.meta b/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionApplicable.cs.meta new file mode 100644 index 000000000..ce20e63cb --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionApplicable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 099d76e017bc4b229ed2f852990b18ea +timeCreated: 1612166872 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionProvider.cs b/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionProvider.cs new file mode 100644 index 000000000..9bd701a85 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionProvider.cs @@ -0,0 +1,7 @@ +namespace UniVRM10 +{ + internal interface ILookAtEyeDirectionProvider + { + LookAtEyeDirection EyeDirection { get; } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionProvider.cs.meta b/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionProvider.cs.meta new file mode 100644 index 000000000..2eb9fb869 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f4729047efb44254af696cb9eb01be01 +timeCreated: 1612173634 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/LookAtEyeDirection.cs b/Assets/VRM10/Runtime/Components/Expression/LookAtEyeDirection.cs new file mode 100644 index 000000000..276ae3149 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/LookAtEyeDirection.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/LookAtEyeDirection.cs.meta b/Assets/VRM10/Runtime/Components/Expression/LookAtEyeDirection.cs.meta new file mode 100644 index 000000000..3d50ce6d7 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/LookAtEyeDirection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3a10e9a01fd446dc98513b556999ba67 +timeCreated: 1612163300 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs b/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs index f8a3e9822..3a507492e 100644 --- a/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs +++ b/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs @@ -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 /// diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs index 2126029ae..33a4ed166 100644 --- a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs +++ b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs @@ -7,7 +7,7 @@ namespace UniVRM10 /// /// A.Value * A.Weight + B.Value * B.Weight ... /// - class MorphTargetBindingMerger + internal sealed class MorphTargetBindingMerger { class DictionaryKeyMorphTargetBindingComparer : IEqualityComparer { diff --git a/Assets/VRM10/Runtime/Components/Expression/Preview/PreviewMeshItem.cs b/Assets/VRM10/Runtime/Components/Expression/Preview/PreviewMeshItem.cs index 5ff577908..e5a4348ae 100644 --- a/Assets/VRM10/Runtime/Components/Expression/Preview/PreviewMeshItem.cs +++ b/Assets/VRM10/Runtime/Components/Expression/Preview/PreviewMeshItem.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; namespace UniVRM10 { [Serializable] - public class PreviewMeshItem + public sealed class PreviewMeshItem { public string Path { diff --git a/Assets/VRM10/Runtime/Components/Expression/Preview/PreviewSceneManager.cs b/Assets/VRM10/Runtime/Components/Expression/Preview/PreviewSceneManager.cs index 66cd2b2a7..2760242ba 100644 --- a/Assets/VRM10/Runtime/Components/Expression/Preview/PreviewSceneManager.cs +++ b/Assets/VRM10/Runtime/Components/Expression/Preview/PreviewSceneManager.cs @@ -9,7 +9,7 @@ namespace UniVRM10 /// /// プレビュー向けのシーンを管理する /// - public class PreviewSceneManager : MonoBehaviour + public sealed class PreviewSceneManager : MonoBehaviour { public GameObject Prefab; diff --git a/Assets/VRM10/Runtime/Components/Expression/PreviewMaterialItem.cs b/Assets/VRM10/Runtime/Components/Expression/PreviewMaterialItem.cs index 923284020..52944f3f0 100644 --- a/Assets/VRM10/Runtime/Components/Expression/PreviewMaterialItem.cs +++ b/Assets/VRM10/Runtime/Components/Expression/PreviewMaterialItem.cs @@ -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 /// /// [Serializable] - public class PreviewMaterialItem + public sealed class PreviewMaterialItem { public readonly Material Material; diff --git a/Assets/VRM10/Runtime/Components/Expression/VRM10Expression.cs b/Assets/VRM10/Runtime/Components/Expression/VRM10Expression.cs index f5d61dfec..a1a8a835d 100644 --- a/Assets/VRM10/Runtime/Components/Expression/VRM10Expression.cs +++ b/Assets/VRM10/Runtime/Components/Expression/VRM10Expression.cs @@ -5,7 +5,7 @@ using UnityEngine; namespace UniVRM10 { [CreateAssetMenu(menuName = "VRM10/Expression")] - public class VRM10Expression : ScriptableObject + public sealed class VRM10Expression : ScriptableObject { #if UNITY_EDITOR /// diff --git a/Assets/VRM10/Runtime/Components/Expression/VRM10ExpressionAvatar.cs b/Assets/VRM10/Runtime/Components/Expression/VRM10ExpressionAvatar.cs index 4f1ec660e..98e5f2098 100644 --- a/Assets/VRM10/Runtime/Components/Expression/VRM10ExpressionAvatar.cs +++ b/Assets/VRM10/Runtime/Components/Expression/VRM10ExpressionAvatar.cs @@ -11,7 +11,7 @@ using UnityEditor; namespace UniVRM10 { [CreateAssetMenu(menuName = "VRM10/ExpressionAvatar")] - public class VRM10ExpressionAvatar : ScriptableObject + public sealed class VRM10ExpressionAvatar : ScriptableObject { [SerializeField] public List Clips = new List(); diff --git a/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs new file mode 100644 index 000000000..2eed15cad --- /dev/null +++ b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs @@ -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; + } + + /// + /// LeftEyeボーンとRightEyeボーンに回転を適用する + /// + public IEnumerable> 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; + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs.meta b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs.meta new file mode 100644 index 000000000..86e954bb2 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0c973408bdfe4936ac9847d0a55855a2 +timeCreated: 1612167497 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToExpression.cs b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToExpression.cs new file mode 100644 index 000000000..573494e12 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToExpression.cs @@ -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> Apply(LookAtEyeDirection eyeDirection) + { + var yaw = eyeDirection.LeftYaw; + var pitch = eyeDirection.LeftPitch; + + if (yaw < 0) + { + // Left + yield return new KeyValuePair(_lookRightKey, 0); + yield return new KeyValuePair(_lookLeftKey, Mathf.Clamp(_horizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f)); + } + else + { + // Right + yield return new KeyValuePair(_lookRightKey, Mathf.Clamp(_horizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f)); + yield return new KeyValuePair(_lookLeftKey, 0); + } + + if (pitch < 0) + { + // Down + yield return new KeyValuePair(_lookUpKey, 0); + yield return new KeyValuePair(_lookDownKey, Mathf.Clamp(_verticalDown.Map(Mathf.Abs(pitch)), 0, 1.0f)); + } + else + { + // Up + yield return new KeyValuePair(_lookUpKey, Mathf.Clamp(_verticalUp.Map(Mathf.Abs(pitch)), 0, 1.0f)); + yield return new KeyValuePair(_lookDownKey, 0); + } + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToExpression.cs.meta b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToExpression.cs.meta new file mode 100644 index 000000000..7517e0893 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToExpression.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1d8a407ebe89449e9b03e1ed8a759f60 +timeCreated: 1612171380 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/LookAt/OffsetOnTransform.cs b/Assets/VRM10/Runtime/Components/LookAt/OffsetOnTransform.cs deleted file mode 100644 index 48950e39c..000000000 --- a/Assets/VRM10/Runtime/Components/LookAt/OffsetOnTransform.cs +++ /dev/null @@ -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; - } - } -} diff --git a/Assets/VRM10/Runtime/Components/LookAt/OffsetOnTransform.cs.meta b/Assets/VRM10/Runtime/Components/LookAt/OffsetOnTransform.cs.meta deleted file mode 100644 index 2acf715bf..000000000 --- a/Assets/VRM10/Runtime/Components/LookAt/OffsetOnTransform.cs.meta +++ /dev/null @@ -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: diff --git a/Assets/VRM10/Runtime/Components/VRM10Controller.cs b/Assets/VRM10/Runtime/Components/VRM10Controller.cs index a08fb544e..5456ddf0b 100644 --- a/Assets/VRM10/Runtime/Components/VRM10Controller.cs +++ b/Assets/VRM10/Runtime/Components/VRM10Controller.cs @@ -96,17 +96,14 @@ namespace UniVRM10 } } - private void Start() + public void Setup() { - Expression.OnStart(transform); - - // get lookat origin var animator = GetComponent(); - 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); } /// @@ -118,7 +115,7 @@ namespace UniVRM10 /// * Expression /// /// - 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(); } } diff --git a/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs b/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs index bce2e11e1..b6472d6a9 100644 --- a/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs +++ b/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs @@ -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 _keys = new List(); + private Dictionary _inputWeights = new Dictionary(); + private Dictionary _actualWeights = new Dictionary(); + private ExpressionMerger _merger; + private IExpressionValidator _validator; + private LookAtEyeDirection _inputEyeDirection; + private LookAtEyeDirection _actualEyeDirection; + private ILookAtEyeDirectionProvider _eyeDirectionProvider; + private ILookAtEyeDirectionApplicable _eyeDirectionApplicable; + public IReadOnlyList ExpressionKeys => _keys; + public IReadOnlyDictionary 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 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> 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() + /// + /// 入力 Weight を基に、Validation を行い実際にモデルに適用される Weights を計算し、Merger を介して適用する。 + /// この際、LookAt の情報を pull してそれも適用する。 + /// + 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; } } } diff --git a/Assets/VRM10/Runtime/Components/VRM10ControllerLookAt.cs b/Assets/VRM10/Runtime/Components/VRM10ControllerLookAt.cs index dda4df4c5..16cfd9d95 100644 --- a/Assets/VRM10/Runtime/Components/VRM10ControllerLookAt.cs +++ b/Assets/VRM10/Runtime/Components/VRM10ControllerLookAt.cs @@ -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 /// @@ -107,7 +115,7 @@ namespace UniVRM10 /// LookAtTargetType に応じた yaw, pitch を得る /// /// Headボーンのforwardに対するyaw角(度), pitch角(度) - public (float, float) GetLookAtYawPitch(Transform head) + private (float, float) GetLookAtYawPitch(Transform head) { switch (LookAtTargetType) { @@ -123,108 +131,37 @@ namespace UniVRM10 throw new NotImplementedException(); } - /// - /// LeftEyeボーンとRightEyeボーンに回転を適用する - /// - 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); - - /// - /// Expression による LookAt を処理する(関連する Expression の Weight を変更する) - /// - /// - /// - 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); } } }