diff --git a/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs b/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs index 7bf5c4a5e2..17b9665b93 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using UniGLTF; +using UniGLTF.Utils; using UnityEngine; using UnityEngine.Serialization; @@ -66,12 +68,37 @@ public enum UpdateTypes private UniHumanoid.Humanoid m_humanoid; private Vrm10Runtime m_runtime; + // 中継用。InitializeAtRuntime でもらって MakeRuntime で使う + private IVrm10SpringBoneRuntime m_springBoneRuntime; + private IReadOnlyDictionary m_defaultTransformStates; /// /// ControlRig の生成オプション /// private bool m_useControlRig; + public IReadOnlyDictionary DefaultTransformStates + { + get + { + if (m_defaultTransformStates == null) + { + if (TryGetComponent(out var gltfInstance)) + { + // ランタイムインポートならここに到達してゼロコストになる + m_defaultTransformStates = gltfInstance.InitialTransformStates; + } + else + { + // エディタでプレハブ配置してる奴ならこっちに到達して収集する + m_defaultTransformStates = GetComponentsInChildren() + .ToDictionary(tf => tf, tf => new TransformState(tf)); + } + } + return m_defaultTransformStates; + } + } + /// /// VRM ファイルに記録された Humanoid ボーンに対応します。 /// これは、コントロールリグのボーンとは異なります。 @@ -88,9 +115,17 @@ public UniHumanoid.Humanoid Humanoid } } - /// - /// ランタイム情報 - /// + 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 @@ -98,15 +133,39 @@ public Vrm10Runtime Runtime 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 defaultTransformStates = null + ) { m_useControlRig = useControlRig; + m_springBoneRuntime = springBoneRuntime; + + if (defaultTransformStates != null) + { + m_defaultTransformStates = defaultTransformStates; + } + else + { + if (TryGetComponent(out var gltfInstance)) + { + // ランタイムインポートならここに到達してゼロコストになる + defaultTransformStates = gltfInstance.InitialTransformStates; + } + else + { + // エディタでプレハブ配置してる奴ならこっちに到達して収集する + defaultTransformStates = GetComponentsInChildren() + .ToDictionary(tf => tf, tf => new TransformState(tf)); + } + } } void Start() diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeSpringBone.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs similarity index 82% rename from Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeSpringBone.cs rename to Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs index d14e0e9e74..8fe124cb9a 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeSpringBone.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using UniGLTF; using UniGLTF.Utils; @@ -9,11 +8,15 @@ namespace UniVRM10 { - public class Vrm10RuntimeSpringBone : IDisposable + /// + /// FastSpringbone(job + singleton) で動作します。 + /// FastSpringBoneService に登録します。 + /// FastSpringBoneService.LateUpdate[DefaultExecutionOrder(11010)] で動作します。 + /// + public class Vrm10FastSpringboneRuntime : IVrm10SpringBoneRuntime { - private readonly Vrm10Instance m_instance; - private readonly IReadOnlyDictionary 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; @@ -28,26 +31,16 @@ public bool IsSpringBoneEnabled set => m_fastSpringBoneBuffer.IsSpringBoneEnabled = value; } - internal Vrm10RuntimeSpringBone(Vrm10Instance instance) + public float DeltaTime => throw new NotImplementedException(); + + /// VRMの初期姿勢(T-Pose)状態。instanceがT-Poseから変化していても大丈夫 + public void Initialize(Vrm10Instance instance) { m_instance = instance; - if (instance.TryGetComponent(out var gltfInstance)) - { - // ランタイムインポートならここに到達してゼロコストになる - m_defaultTransformStates = gltfInstance.InitialTransformStates; - } - else - { - // エディタでプレハブ配置してる奴ならこっちに到達して収集する - m_defaultTransformStates = instance.GetComponentsInChildren() - .ToDictionary(tf => tf, tf => new TransformState(tf)); - } - // NOTE: FastSpringBoneService は UnitTest などでは動作しない if (Application.isPlaying) { - m_fastSpringBoneService = FastSpringBones.FastSpringBoneService.Instance; ReconstructSpringBone(); } } @@ -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; } @@ -152,5 +145,10 @@ public void RestoreInitialTransform() // TODO: } + + public void Process() + { + // FastSpringBoneService が実行するので何もしない + } } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeSpringBone.cs.meta b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs.meta similarity index 100% rename from Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeSpringBone.cs.meta rename to Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs.meta diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs new file mode 100644 index 0000000000..780ae12d12 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs @@ -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 +{ + /// + /// FastSpringbone(job) で動作します。 + /// FastSpringBoneService(Singleton)を経由せずに直接実行します。 + /// + /// シーンに2体以上の vrm-1.0 モデルがある場合は FastSpringBoneService でバッチングする方が効率的です。 + /// + 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); + } + + /// VRMの初期姿勢(T-Pose)状態。instanceがT-Poseから変化していても大丈夫 + 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(); + } + + /// + /// このVRMに紐づくSpringBone関連のバッファを再構築する + /// ランタイム実行時にSpringBoneに対して変更を行いたいときは、このメソッドを明示的に呼ぶ必要がある + /// + 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(); + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs.meta b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs.meta new file mode 100644 index 0000000000..1ef24d6c75 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b2db989bb92a9a64b98516b30db3902a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs index bc33b17e0f..513668482a 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs @@ -27,7 +27,7 @@ public class Vrm10Runtime : IDisposable public IVrm10Constraint[] Constraints { get; } public Vrm10RuntimeExpression Expression { get; } public Vrm10RuntimeLookAt LookAt { get; } - public Vrm10RuntimeSpringBone SpringBone { get; } + public IVrm10SpringBoneRuntime SpringBone { get; } public IVrm10Animation VrmAnimation { get; set; } [Obsolete("use Vrm10Runtime.SpringBone.ExternalForce")] @@ -37,7 +37,7 @@ public Vector3 ExternalForce set { SpringBone.ExternalForce = value; } } - public Vrm10Runtime(Vrm10Instance instance, bool useControlRig) + public Vrm10Runtime(Vrm10Instance instance, bool useControlRig, IVrm10SpringBoneRuntime springBoneRuntime) { if (!Application.isPlaying) { @@ -58,7 +58,8 @@ public Vrm10Runtime(Vrm10Instance instance, bool useControlRig) Constraints = instance.GetComponentsInChildren(); LookAt = new Vrm10RuntimeLookAt(instance.Vrm.LookAt, instance.Humanoid, ControlRig); Expression = new Vrm10RuntimeExpression(instance, LookAt.EyeDirectionApplicable); - SpringBone = new Vrm10RuntimeSpringBone(instance); + SpringBone = springBoneRuntime; + SpringBone.Initialize(instance); } public void Dispose() @@ -128,6 +129,9 @@ public void Process() // 5. Apply Expression // LookAt の角度制限などはこちらで処理されます。 Expression.Process(eyeDirection); + + // 6. SpringBone + SpringBone.Process(); } } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs b/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs new file mode 100644 index 0000000000..4ed48cb4f9 --- /dev/null +++ b/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs @@ -0,0 +1,41 @@ +using System; +using UnityEngine; + +namespace UniVRM10 +{ + public interface IVrm10SpringBoneRuntime : IDisposable + { + public void Initialize(Vrm10Instance instance); + + /// + /// 主に singleton のバッチング更新。 + /// + public void ReconstructSpringBone(); + + /// + /// initialTransform 状態に復帰。verlet の速度 も 0 に。 + /// + public void RestoreInitialTransform(); + + /// + /// deltaTime のカスタマイズポイント。通常は Time.dletaTime + /// + public float DeltaTime { get; } + + /// + /// 風などの追加の外力を設定する + /// + public Vector3 ExternalForce { get; set; } + + /// + // SpringBone のランタイムの動作状態を設定する。 + // SpringBone の動きを一時停止したいときは false にする。 + /// + public bool IsSpringBoneEnabled { get; set; } + + /// + /// 毎フレーム Vrm10Runtime.Process から呼ばれる。 + /// + public void Process(); + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs.meta b/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs.meta new file mode 100644 index 0000000000..dfa427709f --- /dev/null +++ b/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 50a24e5a47bb02848abe4d257ffdc7f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/IO/Vrm10.cs b/Assets/VRM10/Runtime/IO/Vrm10.cs index 1f3661ea88..a797df092f 100644 --- a/Assets/VRM10/Runtime/IO/Vrm10.cs +++ b/Assets/VRM10/Runtime/IO/Vrm10.cs @@ -46,7 +46,8 @@ public static async Task LoadPathAsync( IMaterialDescriptorGenerator materialGenerator = null, VrmMetaInformationCallback vrmMetaInformationCallback = null, CancellationToken ct = default, - ImporterContextSettings importerContextSettings = null) + ImporterContextSettings importerContextSettings = null, + IVrm10SpringBoneRuntime springboneRuntime = null) { awaitCaller ??= Application.isPlaying ? new RuntimeOnlyAwaitCaller() @@ -67,7 +68,8 @@ public static async Task LoadPathAsync( materialGenerator, vrmMetaInformationCallback, ct, - importerContextSettings); + importerContextSettings, + springboneRuntime); } /// @@ -174,7 +176,8 @@ private static async Task LoadAsync( IMaterialDescriptorGenerator materialGenerator, VrmMetaInformationCallback vrmMetaInformationCallback, CancellationToken ct, - ImporterContextSettings importerContextSettings = null) + ImporterContextSettings importerContextSettings = null, + IVrm10SpringBoneRuntime springboneRuntime = null) { ct.ThrowIfCancellationRequested(); if (awaitCaller == null) @@ -192,7 +195,8 @@ private static async Task LoadAsync( materialGenerator, vrmMetaInformationCallback, ct, - importerContextSettings); + importerContextSettings, + springboneRuntime); if (instance != null) { if (ct.IsCancellationRequested) @@ -218,7 +222,8 @@ private static async Task LoadAsync( textureDeserializer, materialGenerator, vrmMetaInformationCallback, - ct); + ct, + springboneRuntime); if (migratedInstance != null) { if (ct.IsCancellationRequested) @@ -242,7 +247,8 @@ private static async Task TryLoadingAsVrm10Async( IMaterialDescriptorGenerator materialGenerator, VrmMetaInformationCallback vrmMetaInformationCallback, CancellationToken ct, - ImporterContextSettings importerContextSettings = null) + ImporterContextSettings importerContextSettings = null, + IVrm10SpringBoneRuntime springboneRuntime = null) { ct.ThrowIfCancellationRequested(); if (awaitCaller == null) @@ -269,7 +275,8 @@ private static async Task TryLoadingAsVrm10Async( materialGenerator, vrmMetaInformationCallback, ct, - importerContextSettings); + importerContextSettings, + springboneRuntime); } private static async Task TryMigratingFromVrm0XAsync( @@ -280,7 +287,8 @@ private static async Task TryMigratingFromVrm0XAsync( ITextureDeserializer textureDeserializer, IMaterialDescriptorGenerator materialGenerator, VrmMetaInformationCallback vrmMetaInformationCallback, - CancellationToken ct) + CancellationToken ct, + IVrm10SpringBoneRuntime springboneRuntime) { ct.ThrowIfCancellationRequested(); if (awaitCaller == null) @@ -308,7 +316,8 @@ private static async Task TryMigratingFromVrm0XAsync( textureDeserializer, materialGenerator, vrmMetaInformationCallback, - ct); + ct, + springboneRuntime: springboneRuntime); if (migratedVrm10Instance == null) { throw new Exception(migrationData?.Message ?? "Failed to load migrated."); @@ -327,7 +336,8 @@ private static async Task LoadVrm10DataAsync( IMaterialDescriptorGenerator materialGenerator, VrmMetaInformationCallback vrmMetaInformationCallback, CancellationToken ct, - ImporterContextSettings importerContextSettings = null) + ImporterContextSettings importerContextSettings = null, + IVrm10SpringBoneRuntime springboneRuntime = null) { ct.ThrowIfCancellationRequested(); if (awaitCaller == null) @@ -345,7 +355,8 @@ private static async Task LoadVrm10DataAsync( textureDeserializer: textureDeserializer, materialGenerator: materialGenerator, useControlRig: controlRigGenerationOption != ControlRigGenerationOption.None, - settings: importerContextSettings)) + settings: importerContextSettings, + springboneRuntime: springboneRuntime)) { // 1. Load meta information if callback was available. if (vrmMetaInformationCallback != null) diff --git a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs index 31d49fc5d9..a657b53f67 100644 --- a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs +++ b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs @@ -24,13 +24,16 @@ public class Vrm10Importer : UniGLTF.ImporterContext private VRM10Object m_vrmObject; private List<(ExpressionPreset Preset, VRM10Expression Clip)> m_expressions = new List<(ExpressionPreset, VRM10Expression)>(); + private IVrm10SpringBoneRuntime m_springboneRuntime; + public Vrm10Importer( Vrm10Data vrm, IReadOnlyDictionary externalObjectMap = null, ITextureDeserializer textureDeserializer = null, IMaterialDescriptorGenerator materialGenerator = null, bool useControlRig = false, - ImporterContextSettings settings = null + ImporterContextSettings settings = null, + IVrm10SpringBoneRuntime springboneRuntime = null ) : base(vrm.Data, externalObjectMap, textureDeserializer, settings: settings) { @@ -49,6 +52,8 @@ public Vrm10Importer( { m_externalMap = new Dictionary(); } + + m_springboneRuntime = springboneRuntime ?? new Vrm10FastSpringboneRuntime(); } static void AssignHumanoid(List nodes, UniGLTF.Extensions.VRMC_vrm.HumanBone humanBone, VrmLib.HumanoidBones key) @@ -258,7 +263,7 @@ protected override async Task OnLoadHierarchy(IAwaitCaller awaitCaller, Func(); - controller.InitializeAtRuntime(m_useControlRig); + controller.InitializeAtRuntime(m_useControlRig, m_springboneRuntime); controller.enabled = false; // vrm diff --git a/Assets/VRM10/Tests/ExpressionTests.cs b/Assets/VRM10/Tests/ExpressionTests.cs index f097d503da..c849e17c0d 100644 --- a/Assets/VRM10/Tests/ExpressionTests.cs +++ b/Assets/VRM10/Tests/ExpressionTests.cs @@ -32,7 +32,7 @@ public void DuplicatedMaterialColorBindings() controller.Vrm.Expression.Aa.MaterialColorBindings = src.ToArray(); // ok if no exception - var r = new Vrm10Runtime(controller, useControlRig: false); + var r = controller.MakeRuntime(useControlRig: false); } [Test] @@ -56,7 +56,7 @@ public void DuplicatedMaterialUVBindings() controller.Vrm.Expression.Aa.MaterialUVBindings = src.ToArray(); // ok if no exception - var r = new Vrm10Runtime(controller, useControlRig: false); + var r = controller.MakeRuntime(useControlRig: false); } } } diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10Viewer.unity b/Assets/VRM10_Samples/VRM10Viewer/VRM10Viewer.unity index 8e51e00c93..944621db2d 100644 --- a/Assets/VRM10_Samples/VRM10Viewer/VRM10Viewer.unity +++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10Viewer.unity @@ -124,6 +124,86 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &20563308 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 20563309} + - component: {fileID: 20563311} + - component: {fileID: 20563310} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &20563309 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 20563308} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 522109224} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 9, y: -0.5} + m_SizeDelta: {x: -28, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &20563310 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 20563308} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Singleton +--- !u!222 &20563311 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 20563308} + m_CullTransparentMesh: 0 --- !u!1 &62367394 GameObject: m_ObjectHideFlags: 0 @@ -1122,6 +1202,8 @@ RectTransform: - {fileID: 1767738854} - {fileID: 103723704} - {fileID: 602093298} + - {fileID: 1346594062} + - {fileID: 522109224} m_Father: {fileID: 124675794} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -1506,6 +1588,93 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 491613100} m_CullTransparentMesh: 0 +--- !u!1 &522109223 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 522109224} + - component: {fileID: 522109225} + m_Layer: 5 + m_Name: Singleton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &522109224 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 522109223} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2052921329} + - {fileID: 20563309} + m_Father: {fileID: 339774397} + m_RootOrder: 19 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 162, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &522109225 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 522109223} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 2052921330} + toggleTransition: 1 + graphic: {fileID: 1039332177} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: [] + m_IsOn: 1 --- !u!1 &587234268 GameObject: m_ObjectHideFlags: 0 @@ -3592,6 +3761,82 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1037763548} m_CullTransparentMesh: 0 +--- !u!1 &1039332175 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1039332176} + - component: {fileID: 1039332178} + - component: {fileID: 1039332177} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1039332176 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1039332175} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2052921329} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1039332177 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1039332175} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1039332178 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1039332175} + m_CullTransparentMesh: 0 --- !u!1 &1045380261 GameObject: m_ObjectHideFlags: 0 @@ -5228,6 +5473,86 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1322834809} m_CullTransparentMesh: 0 +--- !u!1 &1346594061 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1346594062} + - component: {fileID: 1346594064} + - component: {fileID: 1346594063} + m_Layer: 5 + m_Name: _SpringBone_ + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1346594062 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1346594061} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 339774397} + m_RootOrder: 18 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 178, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1346594063 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1346594061} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 1 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: SpringBone +--- !u!222 &1346594064 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1346594061} + m_CullTransparentMesh: 0 --- !u!1 &1407428956 GameObject: m_ObjectHideFlags: 0 @@ -7382,6 +7707,83 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2010083453} m_CullTransparentMesh: 0 +--- !u!1 &2052921328 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2052921329} + - component: {fileID: 2052921331} + - component: {fileID: 2052921330} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2052921329 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2052921328} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1039332176} + m_Father: {fileID: 522109224} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 10, y: -10} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2052921330 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2052921328} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2052921331 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2052921328} + m_CullTransparentMesh: 0 --- !u!1 &2055567528 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10ViewerUI.cs b/Assets/VRM10_Samples/VRM10Viewer/VRM10ViewerUI.cs index 91595b59f1..0a84d5e34f 100644 --- a/Assets/VRM10_Samples/VRM10Viewer/VRM10ViewerUI.cs +++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10ViewerUI.cs @@ -44,6 +44,9 @@ public class VRM10ViewerUI : MonoBehaviour [SerializeField] Toggle m_useAsync = default; + [SerializeField] + Toggle m_useSingelton = default; + [SerializeField] GameObject m_target = default; @@ -267,6 +270,7 @@ private void Reset() m_enableAutoBlink = toggles.First(x => x.name == "EnableAutoBlink"); m_enableAutoExpression = toggles.First(x => x.name == "EnableAutoExpression"); m_useAsync = toggles.First(x => x.name == "UseAsync"); + m_useSingelton = toggles.First(x => x.name == "UseSingleton"); #if UNITY_2022_3_OR_NEWER var texts = GameObject.FindObjectsByType(FindObjectsSortMode.InstanceID); @@ -542,7 +546,8 @@ async void LoadModel(string path) showMeshes: false, awaitCaller: m_useAsync.enabled ? new RuntimeOnlyAwaitCaller() : new ImmediateCaller(), vrmMetaInformationCallback: m_texts.UpdateMeta, - ct: cancellationToken); + ct: cancellationToken, + springboneRuntime: m_useSingelton.isOn ? new Vrm10FastSpringboneRuntime() : new Vrm10FastSpringboneRuntimeStandalone()); if (cancellationToken.IsCancellationRequested) { UnityObjectDestroyer.DestroyRuntimeOrEditor(vrm10Instance.gameObject);