using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using VrmLib; namespace UniVRM10 { [Serializable] public sealed class VRM10ControllerExpression : IDisposable { public static IExpressionValidatorFactory ExpressionValidatorFactory = new DefaultExpressionValidator.Factory(); [SerializeField] public VRM10ExpressionAvatar ExpressionAvatar; 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() { _merger?.RestoreMaterialInitialValues(); } internal void Setup(Transform transform, ILookAtEyeDirectionProvider eyeDirectionProvider, ILookAtEyeDirectionApplicable eyeDirectionApplicable) { if (ExpressionAvatar == 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)) { _inputWeights[expressionKey] = weight; } } Apply(); } public void SetWeight(ExpressionKey expressionKey, float weight) { if (_inputWeights.ContainsKey(expressionKey)) { _inputWeights[expressionKey] = weight; } Apply(); } /// /// 入力 Weight を基に、Validation を行い実際にモデルに適用される Weights を計算し、Merger を介して適用する。 /// この際、LookAt の情報を pull してそれも適用する。 /// private void 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; } } }