mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-09-14 06:50:19 -05:00
Merge pull request #714 from Santarh/expressionImpl3
Implements expression overriding
This commit is contained in:
@@ -136,6 +136,9 @@ namespace UniVRM10
|
||||
}
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
// Setup runtime function.
|
||||
m_target.Setup();
|
||||
|
||||
// base.OnInspectorGUI();
|
||||
switch (_tab)
|
||||
@@ -176,14 +179,6 @@ namespace UniVRM10
|
||||
void ExpressionGUI()
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Override rates", EditorStyles.boldLabel);
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -197,6 +192,9 @@ namespace UniVRM10
|
||||
|
||||
if (m_sliders != null)
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Expression Weights", EditorStyles.boldLabel);
|
||||
|
||||
var sliders = m_sliders.Select(x => x.Slider());
|
||||
foreach (var slider in sliders)
|
||||
{
|
||||
@@ -204,6 +202,16 @@ namespace UniVRM10
|
||||
}
|
||||
m_target.Expression.SetWeights(m_expressionKeyWeights);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Override rates", EditorStyles.boldLabel);
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
void OnSceneGUI()
|
||||
|
||||
@@ -1,37 +1,102 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using VrmLib;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
public sealed class DefaultExpressionValidator : IExpressionValidator
|
||||
{
|
||||
private readonly ExpressionKey[] _keys;
|
||||
private readonly Dictionary<ExpressionKey, VRM10Expression> _expressions;
|
||||
|
||||
private DefaultExpressionValidator(VRM10ExpressionAvatar expressionAvatar)
|
||||
{
|
||||
|
||||
_keys = expressionAvatar.Clips.Select(ExpressionKey.CreateFromClip).ToArray();
|
||||
_expressions = expressionAvatar.Clips.ToDictionary(ExpressionKey.CreateFromClip, x => x);
|
||||
}
|
||||
|
||||
public void Validate(IReadOnlyDictionary<ExpressionKey, float> inputWeights, IDictionary<ExpressionKey, float> actualWeights,
|
||||
LookAtEyeDirection inputEyeDirection, out LookAtEyeDirection actualEyeDirection,
|
||||
out float blinkOverrideRate, out float lookAtOverrideRate, out float mouthOverrideRate)
|
||||
{
|
||||
// weights
|
||||
foreach (var (key, weight) in inputWeights)
|
||||
{
|
||||
if (!actualWeights.ContainsKey(key))
|
||||
{
|
||||
actualWeights.Add(key, weight);
|
||||
}
|
||||
|
||||
actualWeights[key] = weight;
|
||||
}
|
||||
|
||||
// eye direction
|
||||
actualEyeDirection = inputEyeDirection;
|
||||
|
||||
// override rate
|
||||
blinkOverrideRate = 0f;
|
||||
lookAtOverrideRate = 0f;
|
||||
mouthOverrideRate = 0f;
|
||||
|
||||
// 1. Set weights and Accumulate override rates.
|
||||
foreach (var key in _keys)
|
||||
{
|
||||
// Get expression.
|
||||
if (!_expressions.ContainsKey(key)) continue;
|
||||
var expression = _expressions[key];
|
||||
|
||||
// Get weight with evaluation binary flag.
|
||||
var weight = expression.IsBinary ? Mathf.Round(inputWeights[key]) : inputWeights[key];
|
||||
|
||||
// Set weight.
|
||||
actualWeights[key] = weight;
|
||||
|
||||
// Override rate without targeting myself.
|
||||
if (!key.IsBlink)
|
||||
{
|
||||
blinkOverrideRate += GetOverrideRate(expression.OverrideBlink, weight);
|
||||
}
|
||||
if (!key.IsLookAt)
|
||||
{
|
||||
lookAtOverrideRate += GetOverrideRate(expression.OverrideLookAt, weight);
|
||||
}
|
||||
if (!key.IsMouth)
|
||||
{
|
||||
mouthOverrideRate += GetOverrideRate(expression.OverrideMouth, weight);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Saturate rate.
|
||||
blinkOverrideRate = Mathf.Clamp01(blinkOverrideRate);
|
||||
lookAtOverrideRate = Mathf.Clamp01(lookAtOverrideRate);
|
||||
mouthOverrideRate = Mathf.Clamp01(mouthOverrideRate);
|
||||
|
||||
var blinkMultiplier = 1f - blinkOverrideRate;
|
||||
var lookAtMultiplier = 1f - lookAtOverrideRate;
|
||||
var mouthMultiplier = 1f - mouthOverrideRate;
|
||||
|
||||
// 3. Set procedural key's weights.
|
||||
foreach (var key in _keys)
|
||||
{
|
||||
if (key.IsBlink)
|
||||
{
|
||||
actualWeights[key] = inputWeights[key] * blinkMultiplier;
|
||||
}
|
||||
else if (key.IsLookAt)
|
||||
{
|
||||
actualWeights[key] = inputWeights[key] * lookAtMultiplier;
|
||||
}
|
||||
else if (key.IsMouth)
|
||||
{
|
||||
actualWeights[key] = inputWeights[key] * mouthMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. eye direction
|
||||
actualEyeDirection = LookAtEyeDirection.Multiply(inputEyeDirection, 1f - lookAtOverrideRate);
|
||||
}
|
||||
|
||||
private float GetOverrideRate(ExpressionOverrideType type, float weight)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ExpressionOverrideType.None:
|
||||
return 0f;
|
||||
case ExpressionOverrideType.Block:
|
||||
return weight > 0f ? 1f : 0f;
|
||||
case ExpressionOverrideType.Blend:
|
||||
return weight;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Factory : IExpressionValidatorFactory
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
public class DefaultExpressionAccumulator : IExpressionAccumulator
|
||||
{
|
||||
Dictionary<ExpressionKey, VRM10Expression> m_clipMap;
|
||||
|
||||
Dictionary<ExpressionKey, float> expressionKeyWeights = new Dictionary<ExpressionKey, float>();
|
||||
|
||||
public bool IgnoreBlink { get; private set; }
|
||||
public bool IgnoreLookAt { get; private set; }
|
||||
public bool IgnoreMouth { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// initilaize
|
||||
/// </summary>
|
||||
/// <param name="avatar"></param>
|
||||
public void OnStart(VRM10ExpressionAvatar avatar)
|
||||
{
|
||||
m_clipMap = avatar.Clips.ToDictionary(x => ExpressionKey.CreateFromClip(x), x => x);
|
||||
expressionKeyWeights = m_clipMap.Keys.ToDictionary(x => x, x => 0.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// each frame
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<KeyValuePair<ExpressionKey, float>> FrameExpression()
|
||||
{
|
||||
IgnoreBlink = false;
|
||||
IgnoreLookAt = false;
|
||||
IgnoreMouth = false;
|
||||
|
||||
//
|
||||
// except blink, lookat, mouth
|
||||
//
|
||||
foreach (var kv in expressionKeyWeights)
|
||||
{
|
||||
if (kv.Key.IsProcedual)
|
||||
{
|
||||
// 後で
|
||||
continue;
|
||||
}
|
||||
|
||||
var expression = m_clipMap[kv.Key];
|
||||
|
||||
if (expression.OverrideBlink == VrmLib.ExpressionOverrideType.Block && kv.Value > 0)
|
||||
{
|
||||
IgnoreBlink = true;
|
||||
}
|
||||
if (expression.OverrideLookAt == VrmLib.ExpressionOverrideType.Block && kv.Value > 0)
|
||||
{
|
||||
IgnoreLookAt = true;
|
||||
}
|
||||
if (expression.OverrideMouth == VrmLib.ExpressionOverrideType.Block && kv.Value > 0)
|
||||
{
|
||||
IgnoreMouth = true;
|
||||
}
|
||||
|
||||
yield return kv;
|
||||
}
|
||||
|
||||
//
|
||||
// blink, lookat, mouth
|
||||
//
|
||||
foreach (var kv in expressionKeyWeights)
|
||||
{
|
||||
if (kv.Key.IsBlink)
|
||||
{
|
||||
if (IgnoreBlink)
|
||||
{
|
||||
// skip
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (kv.Key.IsLookAt)
|
||||
{
|
||||
if (IgnoreLookAt)
|
||||
{
|
||||
// skip
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (kv.Key.IsMouth)
|
||||
{
|
||||
if (IgnoreMouth)
|
||||
{
|
||||
// skip
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// already return
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return kv;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a expression value
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public float GetValue(ExpressionKey key)
|
||||
{
|
||||
if (expressionKeyWeights.ContainsKey(key))
|
||||
{
|
||||
return expressionKeyWeights[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<KeyValuePair<ExpressionKey, float>> GetValues()
|
||||
{
|
||||
return expressionKeyWeights.Select(x => new KeyValuePair<ExpressionKey, float>(x.Key, x.Value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SetValue
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
public void SetValue(ExpressionKey key, float value)
|
||||
{
|
||||
if (expressionKeyWeights.ContainsKey(key))
|
||||
{
|
||||
expressionKeyWeights[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValues(IEnumerable<KeyValuePair<ExpressionKey, float>> values)
|
||||
{
|
||||
foreach (var keyValue in values)
|
||||
{
|
||||
if (expressionKeyWeights.ContainsKey(keyValue.Key))
|
||||
{
|
||||
expressionKeyWeights[keyValue.Key] = keyValue.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bcb5bbef7ed54854d8041988a84fa827
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -4,28 +4,34 @@ using System.Collections.Generic;
|
||||
namespace UniVRM10
|
||||
{
|
||||
[Serializable]
|
||||
public struct ExpressionKey : IEquatable<ExpressionKey>, IComparable<ExpressionKey>
|
||||
public readonly struct ExpressionKey : IEquatable<ExpressionKey>, IComparable<ExpressionKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum.ToString() のGC回避用キャッシュ
|
||||
/// </summary>
|
||||
private static readonly Dictionary<VrmLib.ExpressionPreset, string> m_presetNameDictionary =
|
||||
private static readonly Dictionary<VrmLib.ExpressionPreset, string> PresetNameDictionary =
|
||||
new Dictionary<VrmLib.ExpressionPreset, string>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ExpressionPreset と同名の名前を持つ独自に追加した Expression を区別するための prefix
|
||||
/// </summary>
|
||||
private static readonly string UnknownPresetPrefix = "Unknown_";
|
||||
|
||||
private string m_customName;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return m_customName.ToUpper(); }
|
||||
}
|
||||
|
||||
public VrmLib.ExpressionPreset Preset;
|
||||
/// <summary>
|
||||
/// Preset of this ExpressionKey.
|
||||
/// </summary>
|
||||
public readonly VrmLib.ExpressionPreset Preset;
|
||||
|
||||
/// <summary>
|
||||
/// Custom Name of this ExpressionKey.
|
||||
/// This works if Preset was Custom.
|
||||
/// </summary>
|
||||
public readonly string Name;
|
||||
|
||||
/// <summary>
|
||||
/// Id for comparison of ExpressionKey.
|
||||
/// </summary>
|
||||
private readonly string _id;
|
||||
|
||||
public bool IsBlink
|
||||
{
|
||||
@@ -77,61 +83,31 @@ namespace UniVRM10
|
||||
|
||||
public bool IsProcedual => IsBlink || IsLookAt || IsMouth;
|
||||
|
||||
string m_id;
|
||||
|
||||
string ID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_id))
|
||||
{
|
||||
// Unknown was deleted
|
||||
if (Preset != VrmLib.ExpressionPreset.Custom)
|
||||
{
|
||||
if (m_presetNameDictionary.ContainsKey(Preset))
|
||||
{
|
||||
m_id = m_presetNameDictionary[Preset];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_presetNameDictionary.Add(Preset, Preset.ToString());
|
||||
m_id = m_presetNameDictionary[Preset];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_id = UnknownPresetPrefix + m_customName;
|
||||
}
|
||||
}
|
||||
|
||||
return m_id;
|
||||
}
|
||||
}
|
||||
|
||||
public ExpressionKey(VrmLib.ExpressionPreset preset, string customName = null)
|
||||
{
|
||||
Preset = preset;
|
||||
m_customName = customName;
|
||||
|
||||
if (Preset != VrmLib.ExpressionPreset.Custom)
|
||||
{
|
||||
if (m_presetNameDictionary.ContainsKey((Preset)))
|
||||
if (PresetNameDictionary.ContainsKey((Preset)))
|
||||
{
|
||||
m_id = m_presetNameDictionary[Preset];
|
||||
_id = Name = PresetNameDictionary[Preset];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_presetNameDictionary.Add(Preset, Preset.ToString());
|
||||
m_id = m_presetNameDictionary[Preset];
|
||||
PresetNameDictionary.Add(Preset, Preset.ToString());
|
||||
_id = Name = PresetNameDictionary[Preset];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_customName))
|
||||
if (string.IsNullOrEmpty(customName))
|
||||
{
|
||||
throw new ArgumentException("name is required for VrmLib.ExpressionPreset.Custom");
|
||||
}
|
||||
m_id = UnknownPresetPrefix + m_customName;
|
||||
|
||||
_id = $"{UnknownPresetPrefix}{customName}";
|
||||
Name = customName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,12 +133,12 @@ namespace UniVRM10
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ID.Replace(UnknownPresetPrefix, "").ToUpper();
|
||||
return _id.Replace(UnknownPresetPrefix, "");
|
||||
}
|
||||
|
||||
public bool Equals(ExpressionKey other)
|
||||
{
|
||||
return ID == other.ID;
|
||||
return _id == other._id;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
@@ -179,7 +155,7 @@ namespace UniVRM10
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
return _id.GetHashCode();
|
||||
}
|
||||
|
||||
public bool Match(VRM10Expression clip)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using VrmLib;
|
||||
|
||||
|
||||
namespace UniVRM10
|
||||
@@ -38,12 +39,12 @@ namespace UniVRM10
|
||||
/// <summary>
|
||||
/// まとめて反映する。1フレームに1回呼び出されることを想定
|
||||
/// </summary>
|
||||
/// <param name="values"></param>
|
||||
public void SetValues(IEnumerable<KeyValuePair<ExpressionKey, float>> values)
|
||||
/// <param name="expressionWeights"></param>
|
||||
public void SetValues(Dictionary<ExpressionKey, float> expressionWeights)
|
||||
{
|
||||
foreach (var kv in values)
|
||||
foreach (var (key, weight) in expressionWeights)
|
||||
{
|
||||
AccumulateValue(kv.Key, kv.Value);
|
||||
AccumulateValue(key, weight);
|
||||
}
|
||||
|
||||
m_morphTargetBindingMerger.Apply();
|
||||
@@ -60,11 +61,6 @@ namespace UniVRM10
|
||||
return;
|
||||
}
|
||||
|
||||
if (clip.IsBinary)
|
||||
{
|
||||
value = Mathf.Round(value);
|
||||
}
|
||||
|
||||
m_morphTargetBindingMerger.AccumulateValue(clip, value);
|
||||
m_materialValueBindingMerger.AccumulateValue(clip, value);
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
/// <summary>
|
||||
/// 1フレーム分の Expression を蓄える
|
||||
/// </summary>
|
||||
public interface IExpressionAccumulator
|
||||
{
|
||||
/// <summary>
|
||||
/// 開始時に初期化する
|
||||
/// </summary>
|
||||
/// <param name="avatar"></param>
|
||||
void OnStart(VRM10ExpressionAvatar avatar);
|
||||
|
||||
/// <summary>
|
||||
/// 蓄えて処理(ignoreフラグなど)した結果を得る
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerable<KeyValuePair<ExpressionKey, float>> FrameExpression();
|
||||
|
||||
void SetValue(ExpressionKey key, float value);
|
||||
void SetValues(IEnumerable<KeyValuePair<ExpressionKey, float>> values);
|
||||
float GetValue(ExpressionKey key);
|
||||
IEnumerable<KeyValuePair<ExpressionKey, float>> GetValues();
|
||||
|
||||
bool IgnoreBlink { get; }
|
||||
bool IgnoreLookAt { get; }
|
||||
bool IgnoreMouth { get; }
|
||||
}
|
||||
|
||||
public static class ExpressionAccumulatorExtensions
|
||||
{
|
||||
public static float GetPresetValue(this IExpressionAccumulator self, VrmLib.ExpressionPreset key)
|
||||
{
|
||||
var expressionKey = new ExpressionKey(key);
|
||||
return self.GetValue(expressionKey);
|
||||
}
|
||||
|
||||
public static float GetCustomValue(this IExpressionAccumulator self, string key)
|
||||
{
|
||||
var expressionKey = ExpressionKey.CreateCustom(key);
|
||||
return self.GetValue(expressionKey);
|
||||
}
|
||||
|
||||
public static void SetPresetValue(this IExpressionAccumulator self, VrmLib.ExpressionPreset key, float value)
|
||||
{
|
||||
var expressionKey = new ExpressionKey(key);
|
||||
self.SetValue(expressionKey, value);
|
||||
}
|
||||
|
||||
public static void SetCustomValue(this IExpressionAccumulator self, string key, float value)
|
||||
{
|
||||
var expressionKey = ExpressionKey.CreateCustom(key);
|
||||
self.SetValue(expressionKey, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdd7cdf6d59574348bb7336272ef5edf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -7,6 +7,7 @@ namespace UniVRM10
|
||||
/// </summary>
|
||||
internal interface ILookAtEyeDirectionApplicable
|
||||
{
|
||||
IEnumerable<KeyValuePair<ExpressionKey, float>> Apply(LookAtEyeDirection eyeDirection);
|
||||
void Apply(LookAtEyeDirection eyeDirection, Dictionary<ExpressionKey, float> actualWeights);
|
||||
void Restore();
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,24 @@
|
||||
{
|
||||
public readonly struct LookAtEyeDirection
|
||||
{
|
||||
/// <summary>
|
||||
/// Yaw of LeftEye
|
||||
/// </summary>
|
||||
public float LeftYaw { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Pitch of LeftEye
|
||||
/// </summary>
|
||||
public float LeftPitch { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Yaw of RightEye
|
||||
/// </summary>
|
||||
public float RightYaw { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Pitch of RightEye
|
||||
/// </summary>
|
||||
public float RightPitch { get; }
|
||||
|
||||
public LookAtEyeDirection(float leftYaw, float leftPitch, float rightYaw, float rightPitch)
|
||||
@@ -14,5 +29,15 @@
|
||||
RightYaw = rightYaw;
|
||||
RightPitch = rightPitch;
|
||||
}
|
||||
|
||||
public static LookAtEyeDirection Multiply(LookAtEyeDirection a, float b)
|
||||
{
|
||||
return new LookAtEyeDirection(
|
||||
a.LeftYaw * b,
|
||||
a.LeftPitch * b,
|
||||
a.RightYaw * b,
|
||||
a.RightPitch * b
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,9 @@ namespace UniVRM10
|
||||
/// <summary>
|
||||
/// LeftEyeボーンとRightEyeボーンに回転を適用する
|
||||
/// </summary>
|
||||
public IEnumerable<KeyValuePair<ExpressionKey, float>> Apply(LookAtEyeDirection eyeDirection)
|
||||
public void Apply(LookAtEyeDirection eyeDirection, Dictionary<ExpressionKey, float> actualWeights)
|
||||
{
|
||||
// FIXME
|
||||
var yaw = eyeDirection.LeftYaw;
|
||||
var pitch = eyeDirection.LeftPitch;
|
||||
|
||||
@@ -55,14 +56,21 @@ namespace UniVRM10
|
||||
}
|
||||
|
||||
// Apply
|
||||
SetYawPitchToBones(new LookAtEyeDirection(leftYaw, pitch, rightYaw, pitch));
|
||||
}
|
||||
|
||||
public void Restore()
|
||||
{
|
||||
SetYawPitchToBones(new LookAtEyeDirection(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
private void SetYawPitchToBones(LookAtEyeDirection actualEyeDirection)
|
||||
{
|
||||
if (_leftEye != null && _rightEye != null)
|
||||
{
|
||||
// 目に値を適用する
|
||||
_leftEye.localRotation = Matrix4x4.identity.YawPitchRotation(leftYaw, pitch);
|
||||
_rightEye.localRotation = Matrix4x4.identity.YawPitchRotation(rightYaw, pitch);
|
||||
_leftEye.localRotation = Matrix4x4.identity.YawPitchRotation(actualEyeDirection.LeftYaw, actualEyeDirection.LeftPitch);
|
||||
_rightEye.localRotation = Matrix4x4.identity.YawPitchRotation(actualEyeDirection.RightYaw, actualEyeDirection.RightPitch);
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ namespace UniVRM10
|
||||
_verticalUp = verticalUp;
|
||||
}
|
||||
|
||||
public IEnumerable<KeyValuePair<ExpressionKey, float>> Apply(LookAtEyeDirection eyeDirection)
|
||||
public void Apply(LookAtEyeDirection eyeDirection, Dictionary<ExpressionKey, float> actualWeights)
|
||||
{
|
||||
var yaw = eyeDirection.LeftYaw;
|
||||
var pitch = eyeDirection.LeftPitch;
|
||||
@@ -33,28 +33,32 @@ namespace UniVRM10
|
||||
if (yaw < 0)
|
||||
{
|
||||
// Left
|
||||
yield return new KeyValuePair<ExpressionKey, float>(_lookRightKey, 0);
|
||||
yield return new KeyValuePair<ExpressionKey, float>(_lookLeftKey, Mathf.Clamp(_horizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f));
|
||||
actualWeights[_lookRightKey] = 0;
|
||||
actualWeights[_lookLeftKey] = Mathf.Clamp(_horizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Right
|
||||
yield return new KeyValuePair<ExpressionKey, float>(_lookRightKey, Mathf.Clamp(_horizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f));
|
||||
yield return new KeyValuePair<ExpressionKey, float>(_lookLeftKey, 0);
|
||||
actualWeights[_lookRightKey] = Mathf.Clamp(_horizontalOuter.Map(Mathf.Abs(yaw)), 0, 1.0f);
|
||||
actualWeights[_lookLeftKey] = 0;
|
||||
}
|
||||
|
||||
if (pitch < 0)
|
||||
{
|
||||
// Down
|
||||
yield return new KeyValuePair<ExpressionKey, float>(_lookUpKey, 0);
|
||||
yield return new KeyValuePair<ExpressionKey, float>(_lookDownKey, Mathf.Clamp(_verticalDown.Map(Mathf.Abs(pitch)), 0, 1.0f));
|
||||
actualWeights[_lookUpKey] = 0;
|
||||
actualWeights[_lookDownKey] = Mathf.Clamp(_verticalDown.Map(Mathf.Abs(pitch)), 0, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Up
|
||||
yield return new KeyValuePair<ExpressionKey, float>(_lookUpKey, Mathf.Clamp(_verticalUp.Map(Mathf.Abs(pitch)), 0, 1.0f));
|
||||
yield return new KeyValuePair<ExpressionKey, float>(_lookDownKey, 0);
|
||||
actualWeights[_lookUpKey] = Mathf.Clamp(_verticalUp.Map(Mathf.Abs(pitch)), 0, 1.0f);
|
||||
actualWeights[_lookDownKey] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Restore()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,10 @@ namespace UniVRM10
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
Expression.Dispose();
|
||||
if (Expression != null)
|
||||
{
|
||||
Expression.Restore();
|
||||
}
|
||||
|
||||
if (ModelAsset != null)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ using VrmLib;
|
||||
namespace UniVRM10
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class VRM10ControllerExpression : IDisposable
|
||||
public sealed class VRM10ControllerExpression
|
||||
{
|
||||
public static IExpressionValidatorFactory ExpressionValidatorFactory = new DefaultExpressionValidator.Factory();
|
||||
|
||||
@@ -31,11 +31,6 @@ namespace UniVRM10
|
||||
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)
|
||||
@@ -44,14 +39,31 @@ namespace UniVRM10
|
||||
return;
|
||||
}
|
||||
|
||||
Restore();
|
||||
|
||||
_merger = new ExpressionMerger(ExpressionAvatar.Clips, transform);
|
||||
_keys = ExpressionAvatar.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(ExpressionAvatar);
|
||||
_eyeDirectionProvider = eyeDirectionProvider;
|
||||
_eyeDirectionApplicable = eyeDirectionApplicable;
|
||||
}
|
||||
|
||||
internal void Restore()
|
||||
{
|
||||
_merger?.RestoreMaterialInitialValues();
|
||||
_merger = null;
|
||||
|
||||
_eyeDirectionApplicable?.Restore();
|
||||
_eyeDirectionApplicable = null;
|
||||
}
|
||||
|
||||
public void Process()
|
||||
{
|
||||
@@ -106,7 +118,7 @@ namespace UniVRM10
|
||||
private void Apply()
|
||||
{
|
||||
// 1. Get eye direction from provider.
|
||||
_inputEyeDirection = _eyeDirectionProvider.EyeDirection;
|
||||
_inputEyeDirection = _eyeDirectionProvider?.EyeDirection ?? default;
|
||||
|
||||
// 2. Validate user input, and Output as actual weights.
|
||||
_validator.Validate(_inputWeights, _actualWeights,
|
||||
@@ -114,18 +126,7 @@ namespace UniVRM10
|
||||
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;
|
||||
}
|
||||
}
|
||||
_eyeDirectionApplicable?.Apply(_actualEyeDirection, _actualWeights);
|
||||
|
||||
// 4. Set actual weights to raw blendshapes.
|
||||
_merger.SetValues(_actualWeights);
|
||||
|
||||
@@ -136,7 +136,12 @@ namespace UniVRM10
|
||||
m_head = head;
|
||||
m_leftEye = animator.GetBoneTransform(HumanBodyBones.LeftEye);
|
||||
m_rightEye = animator.GetBoneTransform(HumanBodyBones.RightEye);
|
||||
if (Gaze == null)
|
||||
|
||||
var isRuntimeAsset = true;
|
||||
#if UNITY_EDITOR
|
||||
isRuntimeAsset = Application.isPlaying && !PrefabUtility.IsPartOfAnyPrefab(m_head);
|
||||
#endif
|
||||
if (isRuntimeAsset && LookAtTargetType == LookAtTargetTypes.CalcYawPitchToGaze && Gaze == null)
|
||||
{
|
||||
Gaze = new GameObject().transform;
|
||||
Gaze.name = "__LOOKAT_GAZE__";
|
||||
|
||||
Reference in New Issue
Block a user