From 144ee3fd67c8fd877ddec15bbba91ac25a6e6ad0 Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Fri, 29 Jan 2021 19:12:01 +0900 Subject: [PATCH 1/5] make Expression merger classes internal. --- .../Components/Expression/ExpressionMerger.cs | 21 +++++-------------- .../Expression/MaterialValueBindingMerger.cs | 2 +- .../Expression/MorphTargetBindingMerger.cs | 2 +- .../Components/VRM10ControllerExpression.cs | 1 - 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs b/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs index b671cc0ba..dcd1c1a18 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 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/MaterialValueBindingMerger.cs b/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs index f8a3e9822..3455c5e1d 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 class MaterialValueBindingMerger { #region MaterialMap /// diff --git a/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs b/Assets/VRM10/Runtime/Components/Expression/MorphTargetBindingMerger.cs index 2126029ae..3130fb5fc 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 class MorphTargetBindingMerger { class DictionaryKeyMorphTargetBindingComparer : IEqualityComparer { diff --git a/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs b/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs index bce2e11e1..94b0beece 100644 --- a/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs +++ b/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs @@ -49,7 +49,6 @@ namespace UniVRM10 public void Apply() { m_merger.SetValues(m_accumulator.FrameExpression()); - m_merger.Apply(); } } } From 5241f79927e26bc5d50074f1bfebf50d42f29285 Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Fri, 29 Jan 2021 22:06:46 +0900 Subject: [PATCH 2/5] make Expression classes sealed. --- .../VRM10/Runtime/Components/Expression/ExpressionMerger.cs | 2 +- .../Components/Expression/MaterialValueBindingMerger.cs | 2 +- .../Components/Expression/MorphTargetBindingMerger.cs | 2 +- .../Components/Expression/Preview/PreviewMeshItem.cs | 2 +- .../Components/Expression/Preview/PreviewSceneManager.cs | 2 +- .../Runtime/Components/Expression/PreviewMaterialItem.cs | 6 ++++-- .../VRM10/Runtime/Components/Expression/VRM10Expression.cs | 2 +- .../Runtime/Components/Expression/VRM10ExpressionAvatar.cs | 2 +- 8 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs b/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs index dcd1c1a18..2c345b432 100644 --- a/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs +++ b/Assets/VRM10/Runtime/Components/Expression/ExpressionMerger.cs @@ -9,7 +9,7 @@ namespace UniVRM10 /// /// ブレンドシェイプを蓄えてまとめて適用するクラス /// - internal class ExpressionMerger + internal sealed class ExpressionMerger { /// /// Key から Expression を得る diff --git a/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs b/Assets/VRM10/Runtime/Components/Expression/MaterialValueBindingMerger.cs index 3455c5e1d..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 ... /// - internal 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 3130fb5fc..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 ... /// - internal 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(); From a3afd483d04b0e85fbbc5b5180b0bce7b0e5472d Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Mon, 1 Feb 2021 15:54:13 +0900 Subject: [PATCH 3/5] Define ExpressionValidator instead of ExpressionAccumulator --- .../Components/VRM10ControllerEditor.cs | 14 +-- .../Expression/DefaultExpressionValidator.cs | 34 +++++++ .../DefaultExpressionValidator.cs.meta | 3 + .../Expression/IExpressionValidator.cs | 15 +++ .../Expression/IExpressionValidator.cs.meta | 3 + .../Expression/IExpressionValidatorFactory.cs | 7 ++ .../IExpressionValidatorFactory.cs.meta | 3 + .../SpringBone/VRM10SpringBoneManager.cs | 2 +- .../Runtime/Components/VRM10Controller.cs | 12 +-- .../Components/VRM10ControllerExpression.cs | 94 ++++++++++++++----- .../Components/VRM10ControllerLookAt.cs | 36 +++---- 11 files changed, 170 insertions(+), 53 deletions(-) create mode 100644 Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs create mode 100644 Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs.meta create mode 100644 Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs create mode 100644 Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs.meta create mode 100644 Assets/VRM10/Runtime/Components/Expression/IExpressionValidatorFactory.cs create mode 100644 Assets/VRM10/Runtime/Components/Expression/IExpressionValidatorFactory.cs.meta diff --git a/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs b/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs index 57d3fd57b..5190aca3a 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 weights", 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 weight", m_target.Expression.OverrideBlinkWeight, 0f, 1f); + EditorGUILayout.Slider("LookAt override weight", m_target.Expression.OverrideLookAtWeight, 0f, 1f); + EditorGUILayout.Slider("Mouth override weight", m_target.Expression.OverrideMouthWeight, 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..e06fb2381 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using VrmLib; + +namespace UniVRM10 +{ + public sealed class DefaultExpressionValidator : IExpressionValidator + { + private DefaultExpressionValidator(VRM10ExpressionAvatar expressionAvatar) + { + + } + + public void Validate(IReadOnlyDictionary inputWeights, IDictionary actualWeights) + { + foreach (var (key, weight) in inputWeights) + { + if (!actualWeights.ContainsKey(key)) + { + actualWeights.Add(key, weight); + } + + actualWeights[key] = weight; + } + } + + 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/IExpressionValidator.cs b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs new file mode 100644 index 000000000..8f6aad267 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs @@ -0,0 +1,15 @@ +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); + } +} \ 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/SpringBone/VRM10SpringBoneManager.cs b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs index 1eea53e46..527778f37 100644 --- a/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs @@ -25,7 +25,7 @@ namespace UniVRM10 /// /// 1フレームに一回呼び出す(VRM10Controllerの仕事) /// - public void Process() + internal void Process() { foreach (var spring in Springs) { diff --git a/Assets/VRM10/Runtime/Components/VRM10Controller.cs b/Assets/VRM10/Runtime/Components/VRM10Controller.cs index a08fb544e..973dfa3b2 100644 --- a/Assets/VRM10/Runtime/Components/VRM10Controller.cs +++ b/Assets/VRM10/Runtime/Components/VRM10Controller.cs @@ -98,7 +98,7 @@ namespace UniVRM10 private void Start() { - Expression.OnStart(transform); + Expression.Setup(transform); // get lookat origin var animator = GetComponent(); @@ -118,7 +118,7 @@ namespace UniVRM10 /// * Expression /// /// - public void Apply() + public void Process() { // // constraint @@ -140,19 +140,19 @@ namespace UniVRM10 // // gaze control // - LookAt.Process(Head, Expression.Accumulator.SetPresetValue); + LookAt.Process(Head, Expression.SetWeights); // // expression // - Expression.Apply(); + Expression.Process(); } private void Update() { if (Controller.UpdateType == VRM10ControllerImpl.UpdateTypes.Update) { - Apply(); + Process(); } } @@ -160,7 +160,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 94b0beece..d4ecfd489 100644 --- a/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs +++ b/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs @@ -1,54 +1,100 @@ 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; + public IReadOnlyList ExpressionKeys => _keys; + public IReadOnlyDictionary ActualWeights => _actualWeights; + + public float OverrideBlinkWeight { get; private set; } + public float OverrideLookAtWeight { get; private set; } + public float OverrideMouthWeight { 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) { - 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); + } + + internal void Process() + { + + } + + public IDictionary GetWeights() + { + return _inputWeights; + } + + public float GetWeight(ExpressionKey expressionKey) + { + if (_inputWeights.ContainsKey(expressionKey)) + { + return _inputWeights[expressionKey]; + } + + return 0f; + } + + 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 を介して適用する。 + /// + private void Apply() { - m_merger.SetValues(m_accumulator.FrameExpression()); + _validator.Validate(_inputWeights, _actualWeights); + _merger.SetValues(_actualWeights); } } } diff --git a/Assets/VRM10/Runtime/Components/VRM10ControllerLookAt.cs b/Assets/VRM10/Runtime/Components/VRM10ControllerLookAt.cs index dda4df4c5..6baa9eba6 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 @@ -49,6 +51,10 @@ namespace UniVRM10 OffsetOnTransform m_leftEye; OffsetOnTransform m_rightEye; + ExpressionKey m_lookRightKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookRight); + ExpressionKey m_lookLeftKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookLeft); + ExpressionKey m_lookUpKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookUp); + ExpressionKey m_lookDownKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookDown); #region LookAtTargetTypes.CalcYawPitchToGaze /// @@ -160,43 +166,41 @@ namespace UniVRM10 } } - public delegate void SetPresetValue(VrmLib.ExpressionPreset preset, float weight); + public delegate void SetExpressionWeights(IEnumerable> weights); /// - /// Expression による LookAt を処理する(関連する Expression の Weight を変更する) + /// Expression による LookAt の Weight を計算する /// - /// - /// - void LookAtExpression(float yaw, float pitch, SetPresetValue SetPresetValue) + private IEnumerable> GetLookAtExpressionEnumerable(float yaw, float pitch) { if (yaw < 0) { // Left - SetPresetValue(VrmLib.ExpressionPreset.LookRight, 0); // clear first - SetPresetValue(VrmLib.ExpressionPreset.LookLeft, Mathf.Clamp(HorizontalOuter.Map(-yaw), 0, 1.0f)); + yield return new KeyValuePair(m_lookRightKey, 0); + yield return new KeyValuePair(m_lookLeftKey, Mathf.Clamp(HorizontalOuter.Map(Mathf.Abs(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)); + yield return new KeyValuePair(m_lookRightKey, Mathf.Clamp(HorizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f)); + yield return new KeyValuePair(m_lookLeftKey, 0); } if (pitch < 0) { // Down - SetPresetValue(VrmLib.ExpressionPreset.LookUp, 0); // clear first - SetPresetValue(VrmLib.ExpressionPreset.LookDown, Mathf.Clamp(VerticalDown.Map(-pitch), 0, 1.0f)); + yield return new KeyValuePair(m_lookUpKey, 0); + yield return new KeyValuePair(m_lookDownKey, Mathf.Clamp(VerticalDown.Map(Mathf.Abs(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)); + yield return new KeyValuePair(m_lookUpKey, Mathf.Clamp(VerticalUp.Map(Mathf.Abs(pitch)), 0, 1.0f)); + yield return new KeyValuePair(m_lookDownKey, 0); } } - public void Setup(Animator animator, Transform head) + internal void Setup(Animator animator, Transform head) { m_leftEye = OffsetOnTransform.Create(animator.GetBoneTransform(HumanBodyBones.LeftEye)); m_rightEye = OffsetOnTransform.Create(animator.GetBoneTransform(HumanBodyBones.RightEye)); @@ -209,7 +213,7 @@ namespace UniVRM10 } } - public void Process(Transform head, SetPresetValue setPresetValue) + internal void Process(Transform head, SetExpressionWeights setExpressionWeights) { var (yaw, pitch) = GetLookAtYawPitch(head); @@ -220,7 +224,7 @@ namespace UniVRM10 break; case LookAtTypes.Expression: - LookAtExpression(yaw, pitch, setPresetValue); + setExpressionWeights(GetLookAtExpressionEnumerable(yaw, pitch)); break; } } From 349fd06645d339712f9361c31ee6c7ccd059fca5 Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Mon, 1 Feb 2021 16:04:24 +0900 Subject: [PATCH 4/5] Rename --- .../VRM10/Editor/Components/VRM10ControllerEditor.cs | 6 +++--- .../Expression/DefaultExpressionValidator.cs | 7 ++++++- .../Components/Expression/IExpressionValidator.cs | 3 ++- .../Runtime/Components/VRM10ControllerExpression.cs | 11 +++++++---- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs b/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs index 5190aca3a..5d652011e 100644 --- a/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs +++ b/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs @@ -179,9 +179,9 @@ namespace UniVRM10 EditorGUILayout.LabelField("Override weights", EditorStyles.boldLabel); EditorGUI.BeginDisabledGroup(true); { - EditorGUILayout.Slider("Blink override weight", m_target.Expression.OverrideBlinkWeight, 0f, 1f); - EditorGUILayout.Slider("LookAt override weight", m_target.Expression.OverrideLookAtWeight, 0f, 1f); - EditorGUILayout.Slider("Mouth override weight", m_target.Expression.OverrideMouthWeight, 0f, 1f); + EditorGUILayout.Slider("Blink nullify weight", m_target.Expression.BlinkNullifyWeight, 0f, 1f); + EditorGUILayout.Slider("LookAt nullify weight", m_target.Expression.LookAtNullifyWeight, 0f, 1f); + EditorGUILayout.Slider("Mouth nullify weight", m_target.Expression.MouthNullifyWeight, 0f, 1f); } EditorGUI.EndDisabledGroup(); diff --git a/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs index e06fb2381..589a39b3c 100644 --- a/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs +++ b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs @@ -10,7 +10,8 @@ namespace UniVRM10 } - public void Validate(IReadOnlyDictionary inputWeights, IDictionary actualWeights) + public void Validate(IReadOnlyDictionary inputWeights, IDictionary actualWeights, out float blinkNullifyWeight, + out float lookAtNullifyWeight, out float mouthNullifyWeight) { foreach (var (key, weight) in inputWeights) { @@ -21,6 +22,10 @@ namespace UniVRM10 actualWeights[key] = weight; } + + blinkNullifyWeight = 0f; + lookAtNullifyWeight = 0f; + mouthNullifyWeight = 0f; } public sealed class Factory : IExpressionValidatorFactory diff --git a/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs index 8f6aad267..43f69b7e9 100644 --- a/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs +++ b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs @@ -10,6 +10,7 @@ namespace UniVRM10 /// /// Validate input weights with Expression constraints. /// - void Validate(IReadOnlyDictionary inputWeights, IDictionary actualWeights); + void Validate(IReadOnlyDictionary inputWeights, IDictionary actualWeights, + out float blinkNullifyWeight, out float lookAtNullifyWeight, out float mouthNullifyWeight); } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs b/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs index d4ecfd489..447ffb83a 100644 --- a/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs +++ b/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs @@ -23,9 +23,9 @@ namespace UniVRM10 public IReadOnlyList ExpressionKeys => _keys; public IReadOnlyDictionary ActualWeights => _actualWeights; - public float OverrideBlinkWeight { get; private set; } - public float OverrideLookAtWeight { get; private set; } - public float OverrideMouthWeight { get; private set; } + public float BlinkNullifyWeight { get; private set; } + public float LookAtNullifyWeight { get; private set; } + public float MouthNullifyWeight { get; private set; } public void Dispose() { @@ -93,8 +93,11 @@ namespace UniVRM10 /// private void Apply() { - _validator.Validate(_inputWeights, _actualWeights); + _validator.Validate(_inputWeights, _actualWeights, out var blink, out var lookAt, out var mouth); _merger.SetValues(_actualWeights); + BlinkNullifyWeight = blink; + LookAtNullifyWeight = lookAt; + MouthNullifyWeight = mouth; } } } From 7986638c821f299f83a1ad82ef5aba35180988d9 Mon Sep 17 00:00:00 2001 From: Masataka SUMI Date: Mon, 1 Feb 2021 19:27:22 +0900 Subject: [PATCH 5/5] Refactoring reference between Expression and LookAt. --- .../Components/VRM10ControllerEditor.cs | 8 +- .../Expression/DefaultExpressionValidator.cs | 16 ++- .../Expression/IExpressionValidator.cs | 3 +- .../ILookAtEyeDirectionApplicable.cs | 12 ++ .../ILookAtEyeDirectionApplicable.cs.meta | 3 + .../Expression/ILookAtEyeDirectionProvider.cs | 7 + .../ILookAtEyeDirectionProvider.cs.meta | 3 + .../Expression/LookAtEyeDirection.cs | 18 +++ .../Expression/LookAtEyeDirection.cs.meta | 3 + .../LookAtEyeDirectionApplicableToBone.cs | 68 ++++++++++ ...LookAtEyeDirectionApplicableToBone.cs.meta | 3 + ...ookAtEyeDirectionApplicableToExpression.cs | 60 +++++++++ ...EyeDirectionApplicableToExpression.cs.meta | 3 + .../Components/LookAt/OffsetOnTransform.cs | 62 --------- .../LookAt/OffsetOnTransform.cs.meta | 13 -- .../SpringBone/VRM10SpringBoneManager.cs | 2 +- .../Runtime/Components/VRM10Controller.cs | 22 ++-- .../Components/VRM10ControllerExpression.cs | 59 +++++++-- .../Components/VRM10ControllerLookAt.cs | 121 ++++-------------- 19 files changed, 284 insertions(+), 202 deletions(-) create mode 100644 Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionApplicable.cs create mode 100644 Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionApplicable.cs.meta create mode 100644 Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionProvider.cs create mode 100644 Assets/VRM10/Runtime/Components/Expression/ILookAtEyeDirectionProvider.cs.meta create mode 100644 Assets/VRM10/Runtime/Components/Expression/LookAtEyeDirection.cs create mode 100644 Assets/VRM10/Runtime/Components/Expression/LookAtEyeDirection.cs.meta create mode 100644 Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs create mode 100644 Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToBone.cs.meta create mode 100644 Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToExpression.cs create mode 100644 Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirectionApplicableToExpression.cs.meta delete mode 100644 Assets/VRM10/Runtime/Components/LookAt/OffsetOnTransform.cs delete mode 100644 Assets/VRM10/Runtime/Components/LookAt/OffsetOnTransform.cs.meta diff --git a/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs b/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs index 5d652011e..e77875e49 100644 --- a/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs +++ b/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs @@ -176,12 +176,12 @@ namespace UniVRM10 void ExpressionGUI() { EditorGUILayout.Space(); - EditorGUILayout.LabelField("Override weights", EditorStyles.boldLabel); + EditorGUILayout.LabelField("Override rates", EditorStyles.boldLabel); EditorGUI.BeginDisabledGroup(true); { - EditorGUILayout.Slider("Blink nullify weight", m_target.Expression.BlinkNullifyWeight, 0f, 1f); - EditorGUILayout.Slider("LookAt nullify weight", m_target.Expression.LookAtNullifyWeight, 0f, 1f); - EditorGUILayout.Slider("Mouth nullify weight", m_target.Expression.MouthNullifyWeight, 0f, 1f); + 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(); diff --git a/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs index 589a39b3c..de6aa8de0 100644 --- a/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs +++ b/Assets/VRM10/Runtime/Components/Expression/DefaultExpressionValidator.cs @@ -10,9 +10,11 @@ namespace UniVRM10 } - public void Validate(IReadOnlyDictionary inputWeights, IDictionary actualWeights, out float blinkNullifyWeight, - out float lookAtNullifyWeight, out float mouthNullifyWeight) + 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)) @@ -22,10 +24,14 @@ namespace UniVRM10 actualWeights[key] = weight; } + + // eye direction + actualEyeDirection = inputEyeDirection; - blinkNullifyWeight = 0f; - lookAtNullifyWeight = 0f; - mouthNullifyWeight = 0f; + // override rate + blinkOverrideRate = 0f; + lookAtOverrideRate = 0f; + mouthOverrideRate = 0f; } public sealed class Factory : IExpressionValidatorFactory diff --git a/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs index 43f69b7e9..f417ce01d 100644 --- a/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs +++ b/Assets/VRM10/Runtime/Components/Expression/IExpressionValidator.cs @@ -11,6 +11,7 @@ namespace UniVRM10 /// Validate input weights with Expression constraints. /// void Validate(IReadOnlyDictionary inputWeights, IDictionary actualWeights, - out float blinkNullifyWeight, out float lookAtNullifyWeight, out float mouthNullifyWeight); + 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/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/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/SpringBone/VRM10SpringBoneManager.cs b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs index 527778f37..1eea53e46 100644 --- a/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs @@ -25,7 +25,7 @@ namespace UniVRM10 /// /// 1フレームに一回呼び出す(VRM10Controllerの仕事) /// - internal void Process() + public void Process() { foreach (var spring in Springs) { diff --git a/Assets/VRM10/Runtime/Components/VRM10Controller.cs b/Assets/VRM10/Runtime/Components/VRM10Controller.cs index 973dfa3b2..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.Setup(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); } /// @@ -140,7 +137,7 @@ namespace UniVRM10 // // gaze control // - LookAt.Process(Head, Expression.SetWeights); + LookAt.Process(); // // expression @@ -148,6 +145,11 @@ namespace UniVRM10 Expression.Process(); } + private void OnEnable() + { + Setup(); + } + private void Update() { if (Controller.UpdateType == VRM10ControllerImpl.UpdateTypes.Update) diff --git a/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs b/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs index 447ffb83a..b6472d6a9 100644 --- a/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs +++ b/Assets/VRM10/Runtime/Components/VRM10ControllerExpression.cs @@ -19,20 +19,24 @@ namespace UniVRM10 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 float BlinkNullifyWeight { get; private set; } - public float LookAtNullifyWeight { get; private set; } - public float MouthNullifyWeight { get; private set; } + 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() { _merger?.RestoreMaterialInitialValues(); } - internal void Setup(Transform transform) + internal void Setup(Transform transform, ILookAtEyeDirectionProvider eyeDirectionProvider, ILookAtEyeDirectionApplicable eyeDirectionApplicable) { if (ExpressionAvatar == null) { @@ -45,11 +49,13 @@ namespace UniVRM10 _inputWeights = _keys.ToDictionary(x => x, x => 0f); _actualWeights = _keys.ToDictionary(x => x, x => 0f); _validator = ExpressionValidatorFactory.Create(ExpressionAvatar); + _eyeDirectionProvider = eyeDirectionProvider; + _eyeDirectionApplicable = eyeDirectionApplicable; } - internal void Process() + public void Process() { - + Apply(); } public IDictionary GetWeights() @@ -67,6 +73,11 @@ namespace UniVRM10 return 0f; } + public LookAtEyeDirection GetEyeDirection() + { + return _inputEyeDirection; + } + public void SetWeights(IEnumerable> weights) { foreach (var (expressionKey, weight) in weights) @@ -87,17 +98,41 @@ namespace UniVRM10 } Apply(); } - + /// /// 入力 Weight を基に、Validation を行い実際にモデルに適用される Weights を計算し、Merger を介して適用する。 + /// この際、LookAt の情報を pull してそれも適用する。 /// private void Apply() { - _validator.Validate(_inputWeights, _actualWeights, out var blink, out var lookAt, out var mouth); + // 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); - BlinkNullifyWeight = blink; - LookAtNullifyWeight = lookAt; - MouthNullifyWeight = mouth; + + BlinkOverrideRate = blink; + LookAtOverrideRate = lookAt; + MouthOverrideRate = mouth; } } } diff --git a/Assets/VRM10/Runtime/Components/VRM10ControllerLookAt.cs b/Assets/VRM10/Runtime/Components/VRM10ControllerLookAt.cs index 6baa9eba6..16cfd9d95 100644 --- a/Assets/VRM10/Runtime/Components/VRM10ControllerLookAt.cs +++ b/Assets/VRM10/Runtime/Components/VRM10ControllerLookAt.cs @@ -9,7 +9,7 @@ using UnityEditor; namespace UniVRM10 { [Serializable] - public class VRM10ControllerLookAt + public class VRM10ControllerLookAt : ILookAtEyeDirectionProvider { public enum LookAtTypes { @@ -49,12 +49,14 @@ namespace UniVRM10 [SerializeField] public LookAtTargetTypes LookAtTargetType; - OffsetOnTransform m_leftEye; - OffsetOnTransform m_rightEye; - ExpressionKey m_lookRightKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookRight); - ExpressionKey m_lookLeftKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookLeft); - ExpressionKey m_lookUpKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookUp); - ExpressionKey m_lookDownKey = ExpressionKey.CreateFromPreset(ExpressionPreset.LookDown); + 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 /// @@ -113,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) { @@ -129,106 +131,37 @@ namespace UniVRM10 throw new NotImplementedException(); } - /// - /// LeftEyeボーンとRightEyeボーンに回転を適用する - /// - void LookAtBone(float yaw, float 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 - 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 SetExpressionWeights(IEnumerable> weights); - - /// - /// Expression による LookAt の Weight を計算する - /// - private IEnumerable> GetLookAtExpressionEnumerable(float yaw, float pitch) - { - if (yaw < 0) - { - // Left - yield return new KeyValuePair(m_lookRightKey, 0); - yield return new KeyValuePair(m_lookLeftKey, Mathf.Clamp(HorizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f)); - } - else - { - // Right - yield return new KeyValuePair(m_lookRightKey, Mathf.Clamp(HorizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f)); - yield return new KeyValuePair(m_lookLeftKey, 0); - } - - if (pitch < 0) - { - // Down - yield return new KeyValuePair(m_lookUpKey, 0); - yield return new KeyValuePair(m_lookDownKey, Mathf.Clamp(VerticalDown.Map(Mathf.Abs(pitch)), 0, 1.0f)); - } - else - { - // Up - yield return new KeyValuePair(m_lookUpKey, Mathf.Clamp(VerticalUp.Map(Mathf.Abs(pitch)), 0, 1.0f)); - yield return new KeyValuePair(m_lookDownKey, 0); - } - } - internal 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; } - } - - internal void Process(Transform head, SetExpressionWeights setExpressionWeights) - { - 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: - setExpressionWeights(GetLookAtExpressionEnumerable(yaw, pitch)); + _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) @@ -248,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); } } }