mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-06 13:15:45 -05:00
Merge pull request #713 from Santarh/expressionImpl2
Refactoring API VRM1.0 Expression & LookAt
This commit is contained in:
commit
0058e261f8
|
|
@ -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<ExpressionKey, float>(x.Key, x.Value)));
|
||||
m_target.Expression.SetWeights(m_expressionKeyWeights);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<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;
|
||||
}
|
||||
|
||||
public sealed class Factory : IExpressionValidatorFactory
|
||||
{
|
||||
public IExpressionValidator Create(VRM10ExpressionAvatar expressionAvatar)
|
||||
{
|
||||
return new DefaultExpressionValidator(expressionAvatar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 50fc6f6edac946548edb751de64c1cb1
|
||||
timeCreated: 1611916405
|
||||
|
|
@ -9,7 +9,7 @@ namespace UniVRM10
|
|||
/// <summary>
|
||||
/// ブレンドシェイプを蓄えてまとめて適用するクラス
|
||||
/// </summary>
|
||||
class ExpressionMerger
|
||||
internal sealed class ExpressionMerger
|
||||
{
|
||||
/// <summary>
|
||||
/// Key から Expression を得る
|
||||
|
|
@ -35,15 +35,6 @@ namespace UniVRM10
|
|||
m_materialValueBindingMerger = new MaterialValueBindingMerger(m_clipMap, root);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 蓄積した値を適用する
|
||||
/// </summary>
|
||||
public void Apply()
|
||||
{
|
||||
m_morphTargetBindingMerger.Apply();
|
||||
m_materialValueBindingMerger.Apply();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// まとめて反映する。1フレームに1回呼び出されることを想定
|
||||
/// </summary>
|
||||
|
|
@ -54,14 +45,12 @@ namespace UniVRM10
|
|||
{
|
||||
AccumulateValue(kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
m_morphTargetBindingMerger.Apply();
|
||||
m_materialValueBindingMerger.Apply();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 即時に反映しない。後にApplyによって反映する
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
public void AccumulateValue(ExpressionKey key, float value)
|
||||
private void AccumulateValue(ExpressionKey key, float value)
|
||||
{
|
||||
m_valueMap[key] = value;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
/// <summary>
|
||||
/// Validate Expression constraints (ex. overrideBlink)
|
||||
/// </summary>
|
||||
public interface IExpressionValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Validate input weights with Expression constraints.
|
||||
/// </summary>
|
||||
void Validate(IReadOnlyDictionary<ExpressionKey, float> inputWeights, IDictionary<ExpressionKey, float> actualWeights,
|
||||
LookAtEyeDirection inputEyeDirection, out LookAtEyeDirection actualEyeDirection,
|
||||
out float blinkOverrideRate, out float lookAtOverrideRate, out float mouthOverrideRate);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1de32ec5101c48298bb78d6835b5d4e2
|
||||
timeCreated: 1611916321
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace UniVRM10
|
||||
{
|
||||
public interface IExpressionValidatorFactory
|
||||
{
|
||||
IExpressionValidator Create(VRM10ExpressionAvatar expressionAvatar);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5a46e9573ca04d098b71780a849cd30e
|
||||
timeCreated: 1611916369
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
/// <summary>
|
||||
/// Receive LooAtEyeDirection, and Apply to bone transforms.
|
||||
/// </summary>
|
||||
internal interface ILookAtEyeDirectionApplicable
|
||||
{
|
||||
IEnumerable<KeyValuePair<ExpressionKey, float>> Apply(LookAtEyeDirection eyeDirection);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 099d76e017bc4b229ed2f852990b18ea
|
||||
timeCreated: 1612166872
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace UniVRM10
|
||||
{
|
||||
internal interface ILookAtEyeDirectionProvider
|
||||
{
|
||||
LookAtEyeDirection EyeDirection { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f4729047efb44254af696cb9eb01be01
|
||||
timeCreated: 1612173634
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3a10e9a01fd446dc98513b556999ba67
|
||||
timeCreated: 1612163300
|
||||
|
|
@ -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
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace UniVRM10
|
|||
///
|
||||
/// A.Value * A.Weight + B.Value * B.Weight ...
|
||||
///
|
||||
class MorphTargetBindingMerger
|
||||
internal sealed class MorphTargetBindingMerger
|
||||
{
|
||||
class DictionaryKeyMorphTargetBindingComparer : IEqualityComparer<MorphTargetBinding>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using System.Collections.Generic;
|
|||
namespace UniVRM10
|
||||
{
|
||||
[Serializable]
|
||||
public class PreviewMeshItem
|
||||
public sealed class PreviewMeshItem
|
||||
{
|
||||
public string Path
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace UniVRM10
|
|||
/// <summary>
|
||||
/// プレビュー向けのシーンを管理する
|
||||
/// </summary>
|
||||
public class PreviewSceneManager : MonoBehaviour
|
||||
public sealed class PreviewSceneManager : MonoBehaviour
|
||||
{
|
||||
public GameObject Prefab;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|||
///
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class PreviewMaterialItem
|
||||
public sealed class PreviewMaterialItem
|
||||
{
|
||||
public readonly Material Material;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using UnityEngine;
|
|||
namespace UniVRM10
|
||||
{
|
||||
[CreateAssetMenu(menuName = "VRM10/Expression")]
|
||||
public class VRM10Expression : ScriptableObject
|
||||
public sealed class VRM10Expression : ScriptableObject
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ using UnityEditor;
|
|||
namespace UniVRM10
|
||||
{
|
||||
[CreateAssetMenu(menuName = "VRM10/ExpressionAvatar")]
|
||||
public class VRM10ExpressionAvatar : ScriptableObject
|
||||
public sealed class VRM10ExpressionAvatar : ScriptableObject
|
||||
{
|
||||
[SerializeField]
|
||||
public List<VRM10Expression> Clips = new List<VRM10Expression>();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LeftEyeボーンとRightEyeボーンに回転を適用する
|
||||
/// </summary>
|
||||
public IEnumerable<KeyValuePair<ExpressionKey, float>> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0c973408bdfe4936ac9847d0a55855a2
|
||||
timeCreated: 1612167497
|
||||
|
|
@ -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<KeyValuePair<ExpressionKey, float>> Apply(LookAtEyeDirection eyeDirection)
|
||||
{
|
||||
var yaw = eyeDirection.LeftYaw;
|
||||
var pitch = eyeDirection.LeftPitch;
|
||||
|
||||
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));
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1d8a407ebe89449e9b03e1ed8a759f60
|
||||
timeCreated: 1612171380
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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:
|
||||
|
|
@ -96,17 +96,14 @@ namespace UniVRM10
|
|||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
public void Setup()
|
||||
{
|
||||
Expression.OnStart(transform);
|
||||
|
||||
// get lookat origin
|
||||
var animator = GetComponent<Animator>();
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -118,7 +115,7 @@ namespace UniVRM10
|
|||
/// * Expression
|
||||
///
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ExpressionKey> _keys = new List<ExpressionKey>();
|
||||
private Dictionary<ExpressionKey, float> _inputWeights = new Dictionary<ExpressionKey, float>();
|
||||
private Dictionary<ExpressionKey, float> _actualWeights = new Dictionary<ExpressionKey, float>();
|
||||
private ExpressionMerger _merger;
|
||||
private IExpressionValidator _validator;
|
||||
private LookAtEyeDirection _inputEyeDirection;
|
||||
private LookAtEyeDirection _actualEyeDirection;
|
||||
private ILookAtEyeDirectionProvider _eyeDirectionProvider;
|
||||
private ILookAtEyeDirectionApplicable _eyeDirectionApplicable;
|
||||
|
||||
public IReadOnlyList<ExpressionKey> ExpressionKeys => _keys;
|
||||
public IReadOnlyDictionary<ExpressionKey, float> 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<ExpressionKey, float> 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<KeyValuePair<ExpressionKey, float>> 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()
|
||||
/// <summary>
|
||||
/// 入力 Weight を基に、Validation を行い実際にモデルに適用される Weights を計算し、Merger を介して適用する。
|
||||
/// この際、LookAt の情報を pull してそれも適用する。
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
/// <summay>
|
||||
|
|
@ -107,7 +115,7 @@ namespace UniVRM10
|
|||
/// LookAtTargetType に応じた yaw, pitch を得る
|
||||
/// </summary>
|
||||
/// <returns>Headボーンのforwardに対するyaw角(度), pitch角(度)</returns>
|
||||
public (float, float) GetLookAtYawPitch(Transform head)
|
||||
private (float, float) GetLookAtYawPitch(Transform head)
|
||||
{
|
||||
switch (LookAtTargetType)
|
||||
{
|
||||
|
|
@ -123,108 +131,37 @@ namespace UniVRM10
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LeftEyeボーンとRightEyeボーンに回転を適用する
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Expression による LookAt を処理する(関連する Expression の Weight を変更する)
|
||||
/// </summary>
|
||||
/// <param name="yaw"></param>
|
||||
/// <param name="pitch"></param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user