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
{
///
/// 指定されたGameObject内にあるSpringBoneをFastSpringBoneに差し替えるユーティリティ
///
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();
var disposer = gameObject.AddComponent();
// 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();
var colliderGroupDictionary = new Dictionary();
// Colliderを差し替える
foreach (var vrmColliderGroup in vrmColliderGroups)
{
if (awaitCaller != null)
{
await awaitCaller.NextFrame();
token.ThrowIfCancellationRequested();
}
var fastSpringBoneCollider = vrmColliderGroup.gameObject.AddComponent();
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();
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);
}
}
}
}