UniVRM/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs

261 lines
8.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UniGLTF.Utils;
using UnityEngine;
using UnityEngine.Serialization;
namespace UniVRM10
{
/// <summary>
/// VRM全体を制御するRoot
///
/// Importer(scripted importer) -> Prefab(editor/asset) -> Instance(scene/MonoBehavior) -> Runtime(play時)
///
/// * DefaultExecutionOrder(11000) means calculate springbone after FinalIK( VRIK )
/// </summary>
[AddComponentMenu("VRM10/VRMInstance")]
[DisallowMultipleComponent]
[DefaultExecutionOrder(11000)]
[RequireComponent(typeof(UniHumanoid.Humanoid))]
public class Vrm10Instance : MonoBehaviour
{
/// <summary>
/// シリアライズ情報
/// </summary>
[SerializeField, Header("VRM1")]
public VRM10Object Vrm;
/// <summary>
/// SpringBone のシリアライズ情報
/// </summary>
/// <returns></returns>
[SerializeField]
public Vrm10InstanceSpringBone SpringBone = new Vrm10InstanceSpringBone();
public enum UpdateTypes
{
None,
Update,
LateUpdate,
}
[SerializeField, Header("Runtime")]
public UpdateTypes UpdateType = UpdateTypes.LateUpdate;
[SerializeField, Header("LookAt")]
public bool DrawLookAtGizmo = true;
/// <summay>
/// The model looks at position of the Transform specified in this field.
/// That behaviour is available only when LookAtTargetType is SpecifiedTransform.
///
/// モデルはここで指定した Transform の位置の方向に目を向けます。
/// LookAtTargetType を SpecifiedTransform に設定したときのみ有効です。
/// </summary>
[SerializeField, FormerlySerializedAs("Gaze")]
public Transform LookAtTarget;
/// <summary>
/// Specify "LookAt" behaviour at runtime.
///
/// 実行時の目の動かし方を指定します。
/// </summary>
[SerializeField]
public VRM10ObjectLookAt.LookAtTargetTypes LookAtTargetType;
private UniHumanoid.Humanoid m_humanoid;
private Vrm10Runtime m_runtime;
// 中継用。InitializeAtRuntime でもらって MakeRuntime で使う
private IVrm10SpringBoneRuntime m_springBoneRuntime;
private IReadOnlyDictionary<Transform, TransformState> m_defaultTransformStates;
/// <summary>
/// ControlRig の生成オプション
/// </summary>
private bool m_useControlRig;
public IReadOnlyDictionary<Transform, TransformState> DefaultTransformStates
{
get
{
if (m_defaultTransformStates == null)
{
if (TryGetComponent<RuntimeGltfInstance>(out var gltfInstance))
{
// ランタイムインポートならここに到達してゼロコストになる
m_defaultTransformStates = gltfInstance.InitialTransformStates;
}
else
{
// エディタでプレハブ配置してる奴ならこっちに到達して収集する
m_defaultTransformStates = GetComponentsInChildren<Transform>()
.ToDictionary(tf => tf, tf => new TransformState(tf));
}
}
return m_defaultTransformStates;
}
}
/// <summary>
/// VRM ファイルに記録された Humanoid ボーンに対応します。
/// これは、コントロールリグのボーンとは異なります。
/// </summary>
public UniHumanoid.Humanoid Humanoid
{
get
{
if (m_humanoid == null)
{
m_humanoid = this.GetComponentOrNull<UniHumanoid.Humanoid>();
}
return m_humanoid;
}
}
internal Vrm10Runtime MakeRuntime(bool useControlRig)
{
if (m_springBoneRuntime == null)
{
// springbone が無い => シーン配置モデルが play されたと見做す
var provider = GetComponent<IVrm10SpringBoneRuntimeProvider>();
if (provider != null)
{
// 明示的カスタマイズ
m_springBoneRuntime = provider.CreateSpringBoneRuntime();
}
if (m_springBoneRuntime == null)
{
// シーン配置 play のデフォルトは singletone ではない方
m_springBoneRuntime = new Vrm10FastSpringboneRuntimeStandalone();
}
m_springBoneRuntime.InitializeAsync(this, new ImmediateCaller());
}
else
{
// importer 内で InitializeAsync が呼び出し済み
}
return new Vrm10Runtime(this, useControlRig, m_springBoneRuntime);
}
public Vrm10Runtime Runtime
{
get
{
if (m_runtime == null)
{
if (this == null) throw new MissingReferenceException("instance was destroyed");
m_runtime = MakeRuntime(m_useControlRig);
}
return m_runtime;
}
}
internal void InitializeAtRuntime(
bool useControlRig,
IVrm10SpringBoneRuntime springBoneRuntime,
IReadOnlyDictionary<Transform, TransformState> defaultTransformStates = null
)
{
m_useControlRig = useControlRig;
m_springBoneRuntime = springBoneRuntime;
if (defaultTransformStates != null)
{
m_defaultTransformStates = defaultTransformStates;
}
else
{
if (TryGetComponent<RuntimeGltfInstance>(out var gltfInstance))
{
// ランタイムインポートならここに到達してゼロコストになる
defaultTransformStates = gltfInstance.InitialTransformStates;
}
else
{
// エディタでプレハブ配置してる奴ならこっちに到達して収集する
defaultTransformStates = GetComponentsInChildren<Transform>()
.ToDictionary(tf => tf, tf => new TransformState(tf));
}
}
}
void Start()
{
if (Vrm == null)
{
Debug.LogError("no VRM10Object");
enabled = false;
return;
}
// cause new Vrm10Runtime.
// init LookAt init rotation.
var _ = Runtime;
}
private void Update()
{
if (UpdateType == UpdateTypes.Update)
{
Runtime.Process();
}
}
private void LateUpdate()
{
if (UpdateType == UpdateTypes.LateUpdate)
{
Runtime.Process();
}
}
private void OnDestroy()
{
if (m_runtime != null)
{
m_runtime.Dispose();
m_runtime = null;
}
}
public bool TryGetBoneTransform(HumanBodyBones bone, out Transform t)
{
if (Humanoid == null)
{
t = null;
return false;
}
t = Humanoid.GetBoneTransform(bone);
if (t == null)
{
return false;
}
return true;
}
private void OnDrawGizmos()
{
foreach (var spring in SpringBone.Springs)
{
spring.DrawGizmos();
}
}
#region Obsolete
[Obsolete]
public Transform Gaze
{
get => LookAtTarget;
set => LookAtTarget = value;
}
#endregion
}
}