Skip to content

Commit

Permalink
Merge pull request #2436 from ousttrue/feature/vrm1_springbone_runtime
Browse files Browse the repository at this point in the history
[vrm-1.0][SpringBone] import 時に springbone 実装をスイッチする IVrm10SpringBoneRuntime インターフェース
  • Loading branch information
ousttrue authored Sep 20, 2024
2 parents c840dcb + 6596242 commit 9e5d11b
Show file tree
Hide file tree
Showing 13 changed files with 757 additions and 44 deletions.
69 changes: 64 additions & 5 deletions Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UniGLTF.Utils;
using UnityEngine;
using UnityEngine.Serialization;

Expand Down Expand Up @@ -66,12 +68,37 @@ public enum UpdateTypes

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 ボーンに対応します。
/// これは、コントロールリグのボーンとは異なります。
Expand All @@ -88,25 +115,57 @@ public UniHumanoid.Humanoid Humanoid
}
}

/// <summary>
/// ランタイム情報
/// </summary>
internal Vrm10Runtime MakeRuntime(bool useControlRig)
{
if (m_springBoneRuntime == null)
{
// deafult に fallback
// TODO: scene に配置した prefab に SpringRuntime をカスタムする手段
m_springBoneRuntime = new Vrm10FastSpringboneRuntime();
}
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 = new Vrm10Runtime(this, m_useControlRig);
m_runtime = MakeRuntime(m_useControlRig);
}
return m_runtime;
}
}

internal void InitializeAtRuntime(bool useControlRig)
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()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UniGLTF.Utils;
Expand All @@ -9,11 +8,15 @@

namespace UniVRM10
{
public class Vrm10RuntimeSpringBone : IDisposable
/// <summary>
/// FastSpringbone(job + singleton) で動作します。
/// FastSpringBoneService に登録します。
/// FastSpringBoneService.LateUpdate[DefaultExecutionOrder(11010)] で動作します。
/// </summary>
public class Vrm10FastSpringboneRuntime : IVrm10SpringBoneRuntime
{
private readonly Vrm10Instance m_instance;
private readonly IReadOnlyDictionary<Transform, TransformState> m_defaultTransformStates;
private readonly FastSpringBones.FastSpringBoneService m_fastSpringBoneService;
private Vrm10Instance m_instance;
private readonly FastSpringBones.FastSpringBoneService m_fastSpringBoneService = FastSpringBones.FastSpringBoneService.Instance;
private FastSpringBoneSpring[] m_springs;
private Quaternion[] m_initialLocalRotations;
private FastSpringBoneBuffer m_fastSpringBoneBuffer;
Expand All @@ -28,26 +31,16 @@ public bool IsSpringBoneEnabled
set => m_fastSpringBoneBuffer.IsSpringBoneEnabled = value;
}

internal Vrm10RuntimeSpringBone(Vrm10Instance instance)
public float DeltaTime => throw new NotImplementedException();

/// <param name="initialTransform">VRMの初期姿勢(T-Pose)状態。instanceがT-Poseから変化していても大丈夫</param>
public void Initialize(Vrm10Instance instance)
{
m_instance = instance;

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

// NOTE: FastSpringBoneService は UnitTest などでは動作しない
if (Application.isPlaying)
{
m_fastSpringBoneService = FastSpringBones.FastSpringBoneService.Instance;
ReconstructSpringBone();
}
}
Expand Down Expand Up @@ -113,7 +106,7 @@ public void ReconstructSpringBone()

private TransformState GetOrAddDefaultTransformState(Transform tf)
{
if (m_defaultTransformStates.TryGetValue(tf, out var defaultTransformState))
if (m_instance.DefaultTransformStates.TryGetValue(tf, out var defaultTransformState))
{
return defaultTransformState;
}
Expand Down Expand Up @@ -152,5 +145,10 @@ public void RestoreInitialTransform()

// TODO:
}

public void Process()
{
// FastSpringBoneService が実行するので何もしない
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
using System;
using System.Linq;
using UniGLTF;
using UniGLTF.Utils;
using UnityEngine;
using UniGLTF.SpringBoneJobs.Blittables;
using UniGLTF.SpringBoneJobs.InputPorts;
using UniGLTF.SpringBoneJobs;

namespace UniVRM10
{
/// <summary>
/// FastSpringbone(job) で動作します。
/// FastSpringBoneService(Singleton)を経由せずに直接実行します。
///
/// シーンに2体以上の vrm-1.0 モデルがある場合は FastSpringBoneService でバッチングする方が効率的です。
/// </summary>
public class Vrm10FastSpringboneRuntimeStandalone : IVrm10SpringBoneRuntime
{
private Vrm10Instance m_instance;
private FastSpringBoneSpring[] m_springs;
private Quaternion[] m_initialLocalRotations;
private FastSpringBoneBuffer m_fastSpringBoneBuffer;
public FastSpringBoneBufferCombiner m_bufferCombiner = new();
private FastSpringBoneScheduler m_fastSpringBoneScheduler;

public Vector3 ExternalForce
{
get => m_fastSpringBoneBuffer.ExternalForce;
set => m_fastSpringBoneBuffer.ExternalForce = value;
}
public bool IsSpringBoneEnabled
{
get => m_fastSpringBoneBuffer.IsSpringBoneEnabled;
set => m_fastSpringBoneBuffer.IsSpringBoneEnabled = value;
}

public float DeltaTime => Time.deltaTime;

public Vrm10FastSpringboneRuntimeStandalone()
{
m_fastSpringBoneScheduler = new(m_bufferCombiner);
}

/// <param name="initialTransform">VRMの初期姿勢(T-Pose)状態。instanceがT-Poseから変化していても大丈夫</param>
public void Initialize(Vrm10Instance instance)
{
m_instance = instance;

// NOTE: FastSpringBoneService は UnitTest などでは動作しない
if (Application.isPlaying)
{
ReconstructSpringBone();
}
}

public void Dispose()
{
m_bufferCombiner.Unregister(m_fastSpringBoneBuffer);
m_fastSpringBoneBuffer.Dispose();

m_fastSpringBoneScheduler.Dispose();
m_bufferCombiner.Dispose();
}

/// <summary>
/// このVRMに紐づくSpringBone関連のバッファを再構築する
/// ランタイム実行時にSpringBoneに対して変更を行いたいときは、このメソッドを明示的に呼ぶ必要がある
/// </summary>
public void ReconstructSpringBone()
{
// release
if (m_fastSpringBoneBuffer != null)
{
m_bufferCombiner.Unregister(m_fastSpringBoneBuffer);
m_fastSpringBoneBuffer.Dispose();
}

// create(Spring情報の再収集。設定変更の反映)
m_springs = m_instance.SpringBone.Springs.Select(spring => new FastSpringBoneSpring
{
center = spring.Center,
colliders = spring.ColliderGroups
.SelectMany(group => group.Colliders)
.Select(collider => new FastSpringBoneCollider
{
Transform = collider.transform,
Collider = new BlittableCollider
{
offset = collider.Offset,
radius = collider.Radius,
tailOrNormal = collider.TailOrNormal,
colliderType = TranslateColliderType(collider.ColliderType)
}
}).ToArray(),
joints = spring.Joints
.Select(joint => new FastSpringBoneJoint
{
Transform = joint.transform,
Joint = new BlittableJointMutable
{
radius = joint.m_jointRadius,
dragForce = joint.m_dragForce,
gravityDir = joint.m_gravityDir,
gravityPower = joint.m_gravityPower,
stiffnessForce = joint.m_stiffnessForce
},
DefaultLocalRotation = GetOrAddDefaultTransformState(joint.transform).LocalRotation,
}).ToArray(),
}).ToArray();

// DOTS buffer 構築
m_fastSpringBoneBuffer = new FastSpringBoneBuffer(m_springs);
m_bufferCombiner.Register(m_fastSpringBoneBuffer);
// reset 用の初期状態の記録
m_initialLocalRotations = m_fastSpringBoneBuffer.Transforms.Select(x => x.localRotation).ToArray();
}

private TransformState GetOrAddDefaultTransformState(Transform tf)
{
if (m_instance.DefaultTransformStates.TryGetValue(tf, out var defaultTransformState))
{
return defaultTransformState;
}

Debug.LogWarning($"{tf.name} does not exist on load.");
return new TransformState(null);
}

private static BlittableColliderType TranslateColliderType(VRM10SpringBoneColliderTypes colliderType)
{
switch (colliderType)
{
case VRM10SpringBoneColliderTypes.Sphere:
return BlittableColliderType.Sphere;
case VRM10SpringBoneColliderTypes.Capsule:
return BlittableColliderType.Capsule;
case VRM10SpringBoneColliderTypes.Plane:
return BlittableColliderType.Plane;
case VRM10SpringBoneColliderTypes.SphereInside:
return BlittableColliderType.SphereInside;
case VRM10SpringBoneColliderTypes.CapsuleInside:
return BlittableColliderType.CapsuleInside;
default:
throw new ArgumentOutOfRangeException();
}
}

public void RestoreInitialTransform()
{
// Spring の joint に対応する transform の回転を初期状態
for (int i = 0; i < m_fastSpringBoneBuffer.Transforms.Length; ++i)
{
var transform = m_fastSpringBoneBuffer.Transforms[i];
transform.localRotation = m_initialLocalRotations[i];
}

// TODO:
}

public void Process()
{
m_fastSpringBoneScheduler.Schedule(DeltaTime).Complete();
}
}
}

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

Loading

0 comments on commit 9e5d11b

Please sign in to comment.