IVrm10SpringBoneRuntime.InitializeAsync

This commit is contained in:
ousttrue 2024-09-24 14:37:31 +09:00
parent 9e5d11be62
commit 0b08507f7a
9 changed files with 166 additions and 156 deletions

View File

@ -122,6 +122,7 @@ namespace UniVRM10
// deafult に fallback
// TODO: scene に配置した prefab に SpringRuntime をカスタムする手段
m_springBoneRuntime = new Vrm10FastSpringboneRuntime();
m_springBoneRuntime.InitializeAsync(this, new ImmediateCaller());
}
return new Vrm10Runtime(this, useControlRig, m_springBoneRuntime);
}

View File

@ -0,0 +1,100 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using UniGLTF;
using UniGLTF.SpringBoneJobs.Blittables;
using UniGLTF.SpringBoneJobs.InputPorts;
using UniGLTF.Utils;
using UnityEngine;
namespace UniVRM10
{
public static class FastSpringBoneBufferFactory
{
/// <summary>
/// このVRMに紐づくSpringBone関連のバッファを構築する。
/// </summary>
/// <param name="awaitCaller"></param>
/// <param name="fastSpringBoneBuffer">TODO: 再利用する</param>
/// <returns></returns>
public static async Task<FastSpringBoneBuffer> ConstructSpringBoneAsync(IAwaitCaller awaitCaller, Vrm10Instance vrm,
FastSpringBoneBuffer fastSpringBoneBuffer = null)
{
// TODO: Dispose せずに再利用する最適化
// new FastSpringBoneBuffer にも構築ロジックがあるので合体して整理する必要あり。
// GC 軽減と await 挟み込み
if (fastSpringBoneBuffer != null)
{
fastSpringBoneBuffer.Dispose();
}
Func<Transform, TransformState> GetOrAddDefaultTransformState = (Transform tf) =>
{
if (vrm.DefaultTransformStates.TryGetValue(tf, out var defaultTransformState))
{
return defaultTransformState;
}
Debug.LogWarning($"{tf.name} does not exist on load.");
return new TransformState(null);
};
// create(Spring情報の再収集。設定変更の反映)
var springs = vrm.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();
await awaitCaller.NextFrame();
fastSpringBoneBuffer = new FastSpringBoneBuffer(springs);
return fastSpringBoneBuffer;
}
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();
}
}
}
}

View File

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

View File

@ -1,10 +1,8 @@
using System;
using System.Linq;
using UniGLTF;
using UniGLTF.Utils;
using UnityEngine;
using UniGLTF.SpringBoneJobs.Blittables;
using UniGLTF.SpringBoneJobs.InputPorts;
using System.Threading.Tasks;
namespace UniVRM10
{
@ -17,9 +15,12 @@ namespace UniVRM10
{
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;
/// <summary>
/// 多重実行防止
/// </summary>
private bool m_building = false;
public Vector3 ExternalForce
{
get => m_fastSpringBoneBuffer.ExternalForce;
@ -33,15 +34,16 @@ namespace UniVRM10
public float DeltaTime => throw new NotImplementedException();
/// <param name="initialTransform">VRMの初期姿勢(T-Pose)状態。instanceがT-Poseから変化していても大丈夫</param>
public void Initialize(Vrm10Instance instance)
public async Task InitializeAsync(Vrm10Instance instance, IAwaitCaller awaitCaller)
{
m_instance = instance;
// NOTE: FastSpringBoneService は UnitTest などでは動作しない
if (Application.isPlaying)
{
ReconstructSpringBone();
m_fastSpringBoneBuffer = await FastSpringBoneBufferFactory.ConstructSpringBoneAsync(awaitCaller, m_instance);
// 登録
m_fastSpringBoneService.BufferCombiner.Register(m_fastSpringBoneBuffer);
}
}
@ -57,93 +59,40 @@ namespace UniVRM10
/// </summary>
public void ReconstructSpringBone()
{
// release
if (m_building)
{
Debug.LogWarning("already building");
return;
}
m_building = true;
// 登録削除
if (m_fastSpringBoneBuffer != null)
{
m_fastSpringBoneService.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();
// new ImmediateCaller() により即時実行して結果を得る。
// スパイクは許容する。
var task = FastSpringBoneBufferFactory.ConstructSpringBoneAsync(new ImmediateCaller(), m_instance, m_fastSpringBoneBuffer);
m_fastSpringBoneBuffer = task.Result;
// DOTS buffer 構築
m_fastSpringBoneBuffer = new FastSpringBoneBuffer(m_springs);
// 登録
m_fastSpringBoneService.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();
}
m_building = false;
}
public void RestoreInitialTransform()
{
// Spring の joint に対応する transform の回転を初期状態
var instance = m_instance.GetComponent<RuntimeGltfInstance>();
for (int i = 0; i < m_fastSpringBoneBuffer.Transforms.Length; ++i)
{
var transform = m_fastSpringBoneBuffer.Transforms[i];
transform.localRotation = m_initialLocalRotations[i];
transform.localRotation = instance.InitialTransformStates[transform].LocalRotation;
}
// TODO:
// TODO: jobs のバッファにも反映する必要あり
}
public void Process()

View File

@ -1,11 +1,11 @@
using System;
using System.Linq;
using UniGLTF;
using UniGLTF.Utils;
using UnityEngine;
using UniGLTF.SpringBoneJobs.Blittables;
using UniGLTF.SpringBoneJobs.InputPorts;
using UniGLTF.SpringBoneJobs;
using System.Threading.Tasks;
namespace UniVRM10
{
@ -18,11 +18,10 @@ namespace UniVRM10
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;
private bool m_building = false;
public Vector3 ExternalForce
{
@ -42,15 +41,15 @@ namespace UniVRM10
m_fastSpringBoneScheduler = new(m_bufferCombiner);
}
/// <param name="initialTransform">VRMの初期姿勢(T-Pose)状態。instanceがT-Poseから変化していても大丈夫</param>
public void Initialize(Vrm10Instance instance)
public async Task InitializeAsync(Vrm10Instance instance, IAwaitCaller awaitCaller)
{
m_instance = instance;
// NOTE: FastSpringBoneService は UnitTest などでは動作しない
if (Application.isPlaying)
{
ReconstructSpringBone();
m_fastSpringBoneBuffer = await FastSpringBoneBufferFactory.ConstructSpringBoneAsync(awaitCaller, m_instance, m_fastSpringBoneBuffer);
m_bufferCombiner.Register(m_fastSpringBoneBuffer);
}
}
@ -69,93 +68,39 @@ namespace UniVRM10
/// </summary>
public void ReconstructSpringBone()
{
// release
if (m_building)
{
Debug.LogWarning("already building");
return;
}
m_building = true;
// 登録解除
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();
// new ImmediateCaller() により即時実行して結果を得る。
// スパイクは許容する。
var task = FastSpringBoneBufferFactory.ConstructSpringBoneAsync(new ImmediateCaller(), m_instance, m_fastSpringBoneBuffer);
m_fastSpringBoneBuffer = task.Result;
// 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 の回転を初期状態
var instance = m_instance.GetComponent<RuntimeGltfInstance>();
for (int i = 0; i < m_fastSpringBoneBuffer.Transforms.Length; ++i)
{
var transform = m_fastSpringBoneBuffer.Transforms[i];
transform.localRotation = m_initialLocalRotations[i];
transform.localRotation = instance.InitialTransformStates[transform].LocalRotation;
}
// TODO:
// TODO: jobs のバッファにも反映する必要あり
}
public void Process()

View File

@ -59,7 +59,6 @@ namespace UniVRM10
LookAt = new Vrm10RuntimeLookAt(instance.Vrm.LookAt, instance.Humanoid, ControlRig);
Expression = new Vrm10RuntimeExpression(instance, LookAt.EyeDirectionApplicable);
SpringBone = springBoneRuntime;
SpringBone.Initialize(instance);
}
public void Dispose()

View File

@ -1,11 +1,13 @@
using System;
using System.Threading.Tasks;
using UniGLTF;
using UnityEngine;
namespace UniVRM10
{
public interface IVrm10SpringBoneRuntime : IDisposable
{
public void Initialize(Vrm10Instance instance);
public Task InitializeAsync(Vrm10Instance instance, IAwaitCaller awaitCaller);
/// <summary>
/// 主に singleton のバッチング更新。

View File

@ -273,6 +273,8 @@ namespace UniVRM10
if (UniGLTF.Extensions.VRMC_springBone.GltfDeserializer.TryGet(Data.GLTF.extensions, out UniGLTF.Extensions.VRMC_springBone.VRMC_springBone springBone))
{
await LoadSpringBoneAsync(awaitCaller, controller, springBone);
// Vrm10Runtime で初期化していたが async にするため、こちらに引越し `v0.127`
await m_springboneRuntime.InitializeAsync(controller, awaitCaller);
}
// constraint
await LoadConstraintAsync(awaitCaller, controller);

View File

@ -6842,6 +6842,7 @@ MonoBehaviour:
m_enableAutoBlink: {fileID: 634488422}
m_enableAutoExpression: {fileID: 1767738855}
m_useAsync: {fileID: 602093299}
m_useSingelton: {fileID: 522109225}
m_target: {fileID: 802105000}
m_motion: {fileID: 4900000, guid: 08df5151e71aed748b13547492fb8b9a, type: 3}
m_texts: