Add VRM FastSpringBone support

This commit is contained in:
notargs
2021-09-29 16:14:41 +09:00
parent da81c27b81
commit 0025182b91
7 changed files with 230 additions and 1 deletions

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VRM
{
/// <summary>
/// FastSpringBoneに関連して、特定のGameObjectと紐付いたIDisposableの破棄を担当するクラス
/// </summary>
public sealed class FastSpringBoneDisposer : MonoBehaviour
{
private readonly List<IDisposable> _disposables = new List<IDisposable>();
public void Add(IDisposable disposable)
{
_disposables.Add(disposable);
}
private void OnDestroy()
{
foreach (var disposable in _disposables)
{
disposable.Dispose();
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0b96d696de06439e97ac33ed2d890c88
timeCreated: 1632893490

View File

@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using VRM.FastSpringBones.Blittables;
using VRM.FastSpringBones.Components;
using VRMShaders;
using Object = UnityEngine.Object;
namespace VRM
{
/// <summary>
/// 指定されたGameObject内にあるSpringBoneをFastSpringBoneに差し替えるユーティリティ
/// </summary>
public static class FastSpringBoneReplacer
{
public static async Task ReplaceAsync(GameObject gameObject, IAwaitCaller awaitCaller = null, CancellationToken token = default)
{
var service = FastSpringBoneService.Instance;
var springBones = gameObject.GetComponentsInChildren<VRMSpringBone>();
var disposer = gameObject.AddComponent<FastSpringBoneDisposer>();
// VRMSpringBoneで動いた後の状態がFastSpringBoneの初期状態にならないようにするためawait UniTask.Yield()する前にVRMSpringBoneをdisableにしておく
foreach (var springBone in springBones)
{
springBone.enabled = false;
};
if (awaitCaller != null)
{
await awaitCaller.NextFrame();
token.ThrowIfCancellationRequested();
}
var vrmColliderGroups = gameObject.GetComponentsInChildren<VRMSpringBoneColliderGroup>();
var colliderGroupDictionary = new Dictionary<VRMSpringBoneColliderGroup, FastSpringBoneColliderGroup>();
// Colliderを差し替える
foreach (var vrmColliderGroup in vrmColliderGroups)
{
if (awaitCaller != null)
{
await awaitCaller.NextFrame();
token.ThrowIfCancellationRequested();
}
var fastSpringBoneCollider = vrmColliderGroup.gameObject.AddComponent<FastSpringBoneColliderGroup>();
fastSpringBoneCollider.Initialize(
service.TransformRegistry,
vrmColliderGroup.Colliders
.Select(data => new BlittableCollider(data.Offset, data.Radius))
.ToArray()
);
colliderGroupDictionary[vrmColliderGroup] = fastSpringBoneCollider;
}
var springRootBones =
(
from springBone in springBones
from rootBone in springBone.RootBones
select (springBone, rootBone)
).ToList();
for (var i = 0; i < springRootBones.Count; i++)
{
var current = springRootBones[i];
// 他のRootBoneのどれかが、自分の親もしくは同じTransformなら自分自身を削除する
if (springRootBones
.Where(other => other != current)
.Any(other => current.rootBone.IsChildOf(other.rootBone)))
{
springRootBones.RemoveAt(i);
--i;
}
}
if (awaitCaller != null)
{
await awaitCaller.NextFrame();
token.ThrowIfCancellationRequested();
}
token.ThrowIfCancellationRequested();
foreach (var (vrmSpringBone, rootBoneTransform) in springRootBones)
{
// FastSpringRootBoneに差し替える
var fastSpringRootBone =
new FastSpringRootBone(
service.TransformRegistry,
rootBoneTransform,
service.RootBoneRegistry,
service.ColliderGroupRegistry);
disposer.Add(fastSpringRootBone);
var colliderGroups =
vrmSpringBone.ColliderGroups != null
? vrmSpringBone.ColliderGroups.Select(group => colliderGroupDictionary[@group]).ToArray()
: Array.Empty<FastSpringBoneColliderGroup>();
fastSpringRootBone.Initialize(
vrmSpringBone.m_gravityPower,
vrmSpringBone.m_gravityDir,
vrmSpringBone.m_dragForce,
vrmSpringBone.m_stiffnessForce,
colliderGroups,
vrmSpringBone.m_hitRadius,
vrmSpringBone.m_center
);
Object.Destroy(vrmSpringBone);
if (awaitCaller != null)
{
await awaitCaller.NextFrame();
token.ThrowIfCancellationRequested();
}
token.ThrowIfCancellationRequested();
}
// Colliderを削除
foreach (var vrmSpringBoneColliderGroup in vrmColliderGroups)
{
Object.Destroy(vrmSpringBoneColliderGroup);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a344a05a3634bac419be8a218d8b85d6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
using UnityEngine;
using VRM.FastSpringBones.Components;
using VRM.FastSpringBones.Registries;
namespace VRM
{
/// <summary>
/// Scene 上に単一で存在する、FastSpringBone の ServiceLocator 兼 EntryPoint
/// </summary>
public class FastSpringBoneService : MonoBehaviour
{
public RootBoneRegistry RootBoneRegistry { get; private set; }
public TransformRegistry TransformRegistry { get; private set; }
public ColliderGroupRegistry ColliderGroupRegistry { get; private set; }
public FastSpringBoneScheduler FastSpringBoneScheduler { get; private set; }
private static FastSpringBoneService _instance;
public static FastSpringBoneService Instance
{
get
{
if (!_instance)
{
var gameObject = new GameObject("FastSpringBone Service");
DontDestroyOnLoad(gameObject);
_instance = gameObject.AddComponent<FastSpringBoneService>();
}
return _instance;
}
}
/// <summary>
/// 専有しているインスタンスを破棄する
/// </summary>
public static void Free()
{
Destroy(_instance.gameObject);
_instance = null;
}
private void Awake()
{
RootBoneRegistry = new RootBoneRegistry();
TransformRegistry = new TransformRegistry();
ColliderGroupRegistry = new ColliderGroupRegistry();
FastSpringBoneScheduler = gameObject.AddComponent<FastSpringBoneScheduler>();
FastSpringBoneScheduler.Initialize(
RootBoneRegistry,
TransformRegistry,
ColliderGroupRegistry);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7c3edad580b54158b37169025a1e207d
timeCreated: 1632894654

View File

@@ -5,7 +5,8 @@
"GUID:8d76e605759c3f64a957d63ef96ada7c",
"GUID:da3e51d19d51a544fa14d43fee843098",
"GUID:301b251fd9834274c9228e0532f444f7",
"GUID:a9bc101fb0471f94a8f99fd242fdd934"
"GUID:a9bc101fb0471f94a8f99fd242fdd934",
"GUID:ac229b552c3025545b074203f857547c"
],
"includePlatforms": [],
"excludePlatforms": [],