using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using VrmLib; namespace UniVRM10 { [Serializable] public sealed class VRM10ObjectExpression { [SerializeField] public List Clips = new List(); /// /// NullのClipを削除して詰める /// public void RemoveNullClip() { if (Clips == null) { return; } for (int i = Clips.Count - 1; i >= 0; --i) { if (Clips[i] == null) { Clips.RemoveAt(i); } } } /// /// Unknown以外で存在しないものを全て作る /// public void CreateDefaultPreset() { foreach (var preset in ((UniGLTF.Extensions.VRMC_vrm.ExpressionPreset[])Enum.GetValues(typeof(UniGLTF.Extensions.VRMC_vrm.ExpressionPreset))) .Where(x => x != UniGLTF.Extensions.VRMC_vrm.ExpressionPreset.custom) ) { CreateDefaultPreset(preset); } } void CreateDefaultPreset(UniGLTF.Extensions.VRMC_vrm.ExpressionPreset preset) { var clip = GetClip(new ExpressionKey(preset)); if (clip != null) return; clip = ScriptableObject.CreateInstance(); clip.name = preset.ToString(); clip.ExpressionName = preset.ToString(); clip.Preset = preset; Clips.Add(clip); } public void SetClip(ExpressionKey key, VRM10Expression clip) { int index = -1; try { index = Clips.FindIndex(x => key.Match(x)); } catch (Exception) { } if (index == -1) { Clips.Add(clip); } else { Clips[index] = clip; } } public VRM10Expression GetClip(ExpressionKey key) { if (Clips == null) return null; return Clips.FirstOrDefault(x => key.Match(x)); } public static IExpressionValidatorFactory ExpressionValidatorFactory = new DefaultExpressionValidator.Factory(); 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; } int m_debugCount; internal void Setup(VRM10Controller target, ILookAtEyeDirectionProvider eyeDirectionProvider, ILookAtEyeDirectionApplicable eyeDirectionApplicable) { Restore(); _merger = new ExpressionMerger(Clips, target.transform); _keys = Clips.Select(ExpressionKey.CreateFromClip).ToList(); var oldInputWeights = _inputWeights; _inputWeights = _keys.ToDictionary(x => x, x => 0f); foreach (var key in _keys) { // remain user input weights. if (oldInputWeights.ContainsKey(key)) _inputWeights[key] = oldInputWeights[key]; } _actualWeights = _keys.ToDictionary(x => x, x => 0f); _validator = ExpressionValidatorFactory.Create(this); _eyeDirectionProvider = eyeDirectionProvider; _eyeDirectionApplicable = eyeDirectionApplicable; } internal void Restore() { _merger?.RestoreMaterialInitialValues(); _merger = null; _eyeDirectionApplicable?.Restore(); _eyeDirectionApplicable = null; } 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 ?? default; // 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). _eyeDirectionApplicable?.Apply(_actualEyeDirection, _actualWeights); // 4. Set actual weights to raw blendshapes. _merger.SetValues(_actualWeights); BlinkOverrideRate = blink; LookAtOverrideRate = lookAt; MouthOverrideRate = mouth; } } }