Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LookAt の整理 #2138

Merged
merged 5 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 8 additions & 27 deletions Assets/VRM10/Runtime/Components/LookAt/LookAtEyeDirection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,24 @@
{
public readonly struct LookAtEyeDirection
{
/// <summary>
/// Yaw of LeftEye
/// </summary>
public float LeftYaw { get; }

public float Yaw { get; }

/// <summary>
/// Pitch of LeftEye
/// </summary>
public float LeftPitch { get; }

/// <summary>
/// NOTE: 何故か使われていない
/// Yaw of RightEye
/// </summary>
public float RightYaw { get; }

/// <summary>
/// NOTE: 何故か使われていない
/// Pitch of RightEye
/// </summary>
public float RightPitch { get; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

今の段階では左右の目の YawPitch を区別する必要はない。

将来的にも規格レベルで解決するべきハードルが高いので、またいずれ……
参考:vrm-c/vrm-specification#177

public float Pitch { get; }

public LookAtEyeDirection(float leftYaw, float leftPitch, float rightYaw, float rightPitch)
public LookAtEyeDirection(float yaw, float pitch)
{
LeftYaw = leftYaw;
LeftPitch = leftPitch;
RightYaw = rightYaw;
RightPitch = rightPitch;
Yaw = yaw;
Pitch = pitch;
}

public static LookAtEyeDirection Multiply(LookAtEyeDirection a, float b)
{
return new LookAtEyeDirection(
a.LeftYaw * b,
a.LeftPitch * b,
a.RightYaw * b,
a.RightPitch * b
a.Yaw * b,
a.Pitch * b
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public LookAtEyeDirectionApplicableToBone(Transform leftEye, Transform rightEye,
CurveMapper horizontalOuter, CurveMapper horizontalInner, CurveMapper verticalDown, CurveMapper verticalUp)
{
_leftEye = leftEye;
_leftInit= Matrix4x4.Rotate(leftEye.localRotation);
_leftInit = Matrix4x4.Rotate(leftEye.localRotation);
_rightEye = rightEye;
_rightInit = Matrix4x4.Rotate(rightEye.localRotation);
_horizontalOuter = horizontalOuter;
Expand All @@ -35,9 +35,9 @@ public LookAtEyeDirectionApplicableToBone(Transform leftEye, Transform rightEye,
public void Apply(LookAtEyeDirection eyeDirection, Dictionary<ExpressionKey, float> actualWeights)
{
// FIXME
var yaw = eyeDirection.LeftYaw;
var pitch = eyeDirection.LeftPitch;
var yaw = eyeDirection.Yaw;
var pitch = eyeDirection.Pitch;

// horizontal
float leftYaw, rightYaw;
if (yaw < 0)
Expand All @@ -62,20 +62,23 @@ public void Apply(LookAtEyeDirection eyeDirection, Dictionary<ExpressionKey, flo
}

// Apply
SetYawPitchToBones(new LookAtEyeDirection(leftYaw, pitch, rightYaw, pitch));
// 現状、右目左目を個別に動かす機能は無い。
// 特に BlendShape 型に対して実装と、Asset のセットアップが煩雑になるので見送っている。
// 代表して左を採用。
SetYawPitchToBones(new LookAtEyeDirection(leftYaw, pitch));
}

public void Restore()
{
SetYawPitchToBones(new LookAtEyeDirection(0, 0, 0, 0));
SetYawPitchToBones(new LookAtEyeDirection(0, 0));
}

private void SetYawPitchToBones(LookAtEyeDirection actualEyeDirection)
{
if (_leftEye != null && _rightEye != null)
{
_leftEye.localRotation = _leftInit.rotation * Matrix4x4.identity.YawPitchRotation(actualEyeDirection.LeftYaw, actualEyeDirection.LeftPitch);
_rightEye.localRotation = _rightInit.rotation * Matrix4x4.identity.YawPitchRotation(actualEyeDirection.RightYaw, actualEyeDirection.RightPitch);
_leftEye.localRotation = _leftInit.rotation * Matrix4x4.identity.YawPitchRotation(actualEyeDirection.Yaw, actualEyeDirection.Pitch);
_rightEye.localRotation = _rightInit.rotation * Matrix4x4.identity.YawPitchRotation(actualEyeDirection.Yaw, actualEyeDirection.Pitch);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public LookAtEyeDirectionApplicableToExpression(

public void Apply(LookAtEyeDirection eyeDirection, Dictionary<ExpressionKey, float> actualWeights)
{
var yaw = eyeDirection.LeftYaw;
var pitch = eyeDirection.LeftPitch;
var yaw = eyeDirection.Yaw;
var pitch = eyeDirection.Pitch;

if (yaw < 0)
{
Expand Down
15 changes: 15 additions & 0 deletions Assets/VRM10/Runtime/Components/LookAt/LookAtInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using UnityEngine;

namespace UniVRM10
{
/// <summary>
/// LookAt を具体的な値に解決する前の入力値
/// この値を元に LookAtEyeDirection を生成し、
/// LookAtEyeDirection を Bone もしくは MorphTarget に対して適用する。
/// </summary>
public struct LookAtInput
{
public LookAtEyeDirection? YawPitch;
public Vector3? WorldPosition;
}
}
11 changes: 11 additions & 0 deletions Assets/VRM10/Runtime/Components/LookAt/LookAtInput.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 30 additions & 30 deletions Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace UniVRM10
/// </summary>
public class Vrm10Runtime : IDisposable
{
private readonly Vrm10Instance m_target;
private readonly Vrm10Instance m_instance;
private readonly Transform m_head;
private readonly FastSpringBoneService m_fastSpringBoneService;
private readonly IReadOnlyDictionary<Transform, TransformState> m_defaultTransformStates;
Expand Down Expand Up @@ -51,46 +51,46 @@ public Vector3 ExternalForce
}
}

public Vrm10Runtime(Vrm10Instance target, bool useControlRig)
public Vrm10Runtime(Vrm10Instance instance, bool useControlRig)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

target が LookAt target と紛らわしいような気がしたので、名前変更。

{
if (!Application.isPlaying)
{
Debug.LogWarning($"{nameof(Vrm10Runtime)} expects runtime behaviour.");
}

m_target = target;
m_instance = instance;

if (!target.TryGetBoneTransform(HumanBodyBones.Head, out m_head))
if (!instance.TryGetBoneTransform(HumanBodyBones.Head, out m_head))
{
throw new Exception();
}

if (useControlRig)
{
ControlRig = new Vrm10RuntimeControlRig(target.Humanoid, m_target.transform);
ControlRig = new Vrm10RuntimeControlRig(instance.Humanoid, m_instance.transform);
}
Constraints = target.GetComponentsInChildren<IVrm10Constraint>();
LookAt = new Vrm10RuntimeLookAt(target.Vrm.LookAt, target.Humanoid, ControlRig);
Expression = new Vrm10RuntimeExpression(target, LookAt.EyeDirectionApplicable);
Constraints = instance.GetComponentsInChildren<IVrm10Constraint>();
LookAt = new Vrm10RuntimeLookAt(instance.Vrm.LookAt, instance.Humanoid, ControlRig);
Expression = new Vrm10RuntimeExpression(instance, LookAt.EyeDirectionApplicable);

var instance = target.GetComponent<RuntimeGltfInstance>();
if (instance != null)
var gltfInstance = instance.GetComponent<RuntimeGltfInstance>();
if (gltfInstance != null)
{
// ランタイムインポートならここに到達してゼロコストになる
m_defaultTransformStates = instance.InitialTransformStates;
m_defaultTransformStates = gltfInstance.InitialTransformStates;
}
else
{
// エディタでプレハブ配置してる奴ならこっちに到達して収集する
m_defaultTransformStates = target.GetComponentsInChildren<Transform>()
m_defaultTransformStates = instance.GetComponentsInChildren<Transform>()
.ToDictionary(tf => tf, tf => new TransformState(tf));
}

// NOTE: FastSpringBoneService は UnitTest などでは動作しない
if (Application.isPlaying)
{
m_fastSpringBoneService = FastSpringBoneService.Instance;
m_fastSpringBoneBuffer = CreateFastSpringBoneBuffer(m_target.SpringBone);
m_fastSpringBoneBuffer = CreateFastSpringBoneBuffer(m_instance.SpringBone);
m_fastSpringBoneService.BufferCombiner.Register(m_fastSpringBoneBuffer);
}
}
Expand All @@ -111,7 +111,7 @@ public void ReconstructSpringBone()
m_fastSpringBoneService.BufferCombiner.Unregister(m_fastSpringBoneBuffer);

m_fastSpringBoneBuffer.Dispose();
m_fastSpringBoneBuffer = CreateFastSpringBoneBuffer(m_target.SpringBone);
m_fastSpringBoneBuffer = CreateFastSpringBoneBuffer(m_instance.SpringBone);

m_fastSpringBoneService.BufferCombiner.Register(m_fastSpringBoneBuffer);
}
Expand Down Expand Up @@ -203,20 +203,11 @@ public void Process()
Expression.SetWeight(k, v());
}

// TODO: look at target
//
// この Frame の LookAt 値をアップデート
//
// struct LookAtValue
// {
// LookAtTargetTypes;
//
// (float, float) YawPitch; // 一番明確
// Vector3 GazePosition; // 座標系?
// Transform GazeTarget; // 解決順のため遅延させる意図があるか
// }
//
// あとで、LookAt.Process の引き数にわたす
// look at
if (VrmAnimation.LookAt.HasValue)
{
LookAt.LookAtInput = VrmAnimation.LookAt.Value;
}
}

// 2. Control Rig
Expand All @@ -228,11 +219,20 @@ public void Process()
constraint.Process();
}

if (m_instance.LookAtTargetType == VRM10ObjectLookAt.LookAtTargetTypes.SpecifiedTransform
&& m_instance.LookAtTarget != null)
{
// Transform 追跡で視線を生成する。
// 値を上書きします。
LookAt.LookAtInput = new LookAtInput { WorldPosition = m_instance.LookAtTarget.position };
}

// 4. Gaze control
LookAt.Process(m_target.LookAtTargetType, m_target.LookAtTarget);
var eyeDirection = LookAt.Process();

// 5. Apply Expression
Expression.Process(LookAt.EyeDirection);
// LookAt の角度制限などはこちらで処理されます。
Expression.Process(eyeDirection);
}
}
}
46 changes: 24 additions & 22 deletions Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeLookAt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@ public sealed class Vrm10RuntimeLookAt : ILookAtEyeDirectionProvider

internal ILookAtEyeDirectionApplicable EyeDirectionApplicable { get; }

public float Yaw { get; private set; }
public float Pitch { get; private set; }
/// <summary>
/// 入力値。適宜更新可。
/// </summary>
public LookAtInput LookAtInput { get; set; }

/// <summary>
/// 出力値。Process() のみが更新する
/// </summary>
public LookAtEyeDirection EyeDirection { get; private set; }
Comment on lines +16 to 24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vrm10RuntimeLookAt

  • LookAtInput という「LookAt する対象」をユーザ入力として
  • それを Process() で処理し
  • LookAtEyeDirection という「実際に Bone や BlendShape といった ILookAtEyeDirectionApplicable が適用すべき LookAt の状態」を出力する

システムになる。

public float Yaw => EyeDirection.Yaw;
public float Pitch => EyeDirection.Pitch;

/// <summary>
/// Transform that indicates the position center of eyes.
Expand Down Expand Up @@ -52,39 +60,33 @@ internal Vrm10RuntimeLookAt(VRM10ObjectLookAt lookAt, UniHumanoid.Humanoid human
}
}

internal void Process(VRM10ObjectLookAt.LookAtTargetTypes lookAtTargetType, Transform lookAtTarget)
internal LookAtEyeDirection Process()
{
LookAtOriginTransform.localPosition = _lookAtOriginTransformLocalPosition;
LookAtOriginTransform.localRotation = _lookAtOriginTransformLocalRotation;

switch (lookAtTargetType)
if (LookAtInput.YawPitch is LookAtEyeDirection dir)
{
case VRM10ObjectLookAt.LookAtTargetTypes.SpecifiedTransform:
// NOTE: 指定された Transform の位置を向くように Yaw/Pitch を計算して適用する
if (lookAtTarget != null)
{
var value = CalculateYawPitchFromLookAtPosition(lookAtTarget.position);
SetYawPitchManually(value.Yaw, value.Pitch);
}
break;
case VRM10ObjectLookAt.LookAtTargetTypes.YawPitchValue:
// NOTE: 直接 Set された Yaw/Pitch を使って計算する
break;
EyeDirection = dir;
}

EyeDirection = new LookAtEyeDirection(Yaw, Pitch, 0, 0);
else if (LookAtInput.WorldPosition is Vector3 worldPosition)
{
// NOTE: 指定された Transform の位置を向くように Yaw/Pitch を計算して適用する
var (yaw, pitch) = CalculateYawPitchFromLookAtPosition(worldPosition);
EyeDirection = new LookAtEyeDirection(yaw, pitch);
}
return EyeDirection;
}

/// <summary>
/// Yaw/Pitch 値を直接設定します。
/// LookAtTargetTypes が SpecifiedTransform の場合、ここで設定しても値は上書きされます。
/// Vrm10Instance.LookAtTargetTypes が SpecifiedTransform の場合、ここで設定しても値は上書きされます。
/// </summary>
/// <param name="yaw">Headボーンのforwardに対するyaw角(度)</param>
/// <param name="pitch">Headボーンのforwardに対するpitch角(度)</param>
public void SetYawPitchManually(float yaw, float pitch)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SetYawPitchManually() は存続

{
Yaw = yaw;
Pitch = pitch;
LookAtInput = new LookAtInput { YawPitch = new LookAtEyeDirection(yaw, pitch) };
}

public (float Yaw, float Pitch) CalculateYawPitchFromLookAtPosition(Vector3 lookAtWorldPosition)
Expand All @@ -105,7 +107,7 @@ private static Transform InitializeLookAtOriginTransform(Transform rawHead, Tran
return lookAtOrigin;
}

#region Obsolete
#region Obsolete
[Obsolete("Use " + nameof(LookAtOriginTransform))]
public Transform GetLookAtOrigin(Transform head)
{
Expand Down Expand Up @@ -134,6 +136,6 @@ public void SetLookAtYawPitch(float yaw, float pitch)
throw new ArgumentOutOfRangeException(nameof(lookAtTargetType), lookAtTargetType, null);
}
}
#endregion
#endregion
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using UnityEngine;

namespace UniVRM10
{
Expand All @@ -8,5 +9,6 @@ public interface IVrm10Animation : IDisposable
(INormalizedPoseProvider, ITPoseProvider) ControlRig { get; }
IReadOnlyDictionary<ExpressionKey, Func<float>> ExpressionMap { get; }
public void ShowBoxMan(bool enable);
LookAtInput? LookAt { get; }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VrmAnimation から LookAt を供給する。まだ入れ物だけ

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public void Dispose()
readonly Dictionary<ExpressionKey, Action<float>> _ExpressionSetterMap = new();
public IReadOnlyDictionary<ExpressionKey, Action<float>> ExpressionSetterMap => _ExpressionSetterMap;

public LookAtInput? LookAt { get; set; }

void InitializeExpression(ExpressionKey key, Func<float> getter, Action<float> setter)
{
_ExpressionGetterMap.Add(key, getter);
Expand Down
2 changes: 2 additions & 0 deletions Assets/VRM10_Samples/VRM10Viewer/Motions/BvhMotion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public class BvhMotion : IVrm10Animation
IDictionary<ExpressionKey, Func<float>> _ExpressionMap = new Dictionary<ExpressionKey, Func<float>>();
public IReadOnlyDictionary<ExpressionKey, Func<float>> ExpressionMap => (IReadOnlyDictionary<ExpressionKey, Func<float>>)_ExpressionMap;

public LookAtInput? LookAt { get; set; }

public BvhMotion(UniHumanoid.BvhImporterContext context)
{
m_context = context;
Expand Down