using System; using System.Collections.Generic; using System.Linq; using UniGLTF; using UnityEngine; using UniVRM10.FastSpringBones.Blittables; using UniVRM10.FastSpringBones.System; namespace UniVRM10 { /// /// VRM モデルインスタンスを、状態をもって、元の状態から操作・変更するためのクラス。 /// また、仕様に従ってその操作を行う。 /// /// 操作対象としては以下が挙げられる。 /// - ControlRig /// - Constraint /// - LookAt /// - Expression /// public class Vrm10Runtime : IDisposable { private readonly Vrm10Instance m_target; private readonly IVrm10Constraint[] m_constraints; private readonly Transform m_head; private readonly FastSpringBoneService m_fastSpringBoneService; private readonly IReadOnlyDictionary m_defaultTransformStates; private Vrm10RuntimeControlRig m_controlRig; private FastSpringBoneBuffer m_fastSpringBoneBuffer; private Vrm10RuntimeExpression m_expression; private Vrm10RuntimeLookAt m_lookat; public Vrm10RuntimeExpression Expression => m_expression; public Vrm10RuntimeLookAt LookAt => m_lookat; public Vrm10Runtime(Vrm10Instance target) { m_target = target; if (!target.TryGetBoneTransform(HumanBodyBones.Head, out m_head)) { throw new Exception(); } m_lookat = new Vrm10RuntimeLookAt(target.Vrm.LookAt, target.Humanoid, m_head, target.LookAtTargetType, target.Gaze); m_expression = new Vrm10RuntimeExpression(target, m_lookat, m_lookat.EyeDirectionApplicable); if (m_constraints == null) { m_constraints = target.GetComponentsInChildren(); } if (!Application.isPlaying) { // for UnitTest return; } var instance = target.GetComponent(); if (instance != null) { // ランタイムインポートならここに到達してゼロコストになる m_defaultTransformStates = instance.InitialTransformStates; } else { // エディタでプレハブ配置してる奴ならこっちに到達して収集する m_defaultTransformStates = target.GetComponentsInChildren() .ToDictionary(tf => tf, tf => new TransformState(tf)); } m_fastSpringBoneService = FastSpringBoneService.Instance; m_fastSpringBoneBuffer = CreateFastSpringBoneBuffer(m_target.SpringBone); m_fastSpringBoneService.BufferCombiner.Register(m_fastSpringBoneBuffer); } /// /// このVRMに紐づくSpringBone関連のバッファを再構築する /// ランタイム実行時にSpringBoneに対して変更を行いたいときは、このメソッドを明示的に呼ぶ必要がある /// public void ReconstructSpringBone() { m_fastSpringBoneService.BufferCombiner.Unregister(m_fastSpringBoneBuffer); m_fastSpringBoneBuffer.Dispose(); m_fastSpringBoneBuffer = CreateFastSpringBoneBuffer(m_target.SpringBone); m_fastSpringBoneService.BufferCombiner.Register(m_fastSpringBoneBuffer); } public Vrm10RuntimeControlRig GetOrCreateControlRig() { if (m_controlRig == null) { m_controlRig = new Vrm10RuntimeControlRig(m_target.Humanoid); } return m_controlRig; } private FastSpringBoneBuffer CreateFastSpringBoneBuffer(Vrm10InstanceSpringBone springBone) { return new FastSpringBoneBuffer( 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, tail = collider.Tail, colliderType = TranslateColliderType(collider.ColliderType) } }).ToArray(), joints = spring.Joints .Select(joint => new FastSpringBoneJoint { Transform = joint.transform, Joint = new BlittableJoint { 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()); } private TransformState GetOrAddDefaultTransformState(Transform tf) { if (m_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; default: throw new ArgumentOutOfRangeException(); } } /// /// 毎フレーム関連コンポーネントを解決する /// /// * Contraint /// * Spring /// * LookAt /// * Expression /// /// public void Process() { if (m_controlRig != null) { m_controlRig.Process(); } // // constraint // foreach (var constraint in m_constraints) { constraint.Process(); } // // gaze control // m_target.Runtime.LookAt.Process(m_target.LookAtTargetType, m_target.Gaze); // // expression // m_target.Runtime.Expression.Process(); } public void Dispose() { m_fastSpringBoneService.BufferCombiner.Unregister(m_fastSpringBoneBuffer); m_fastSpringBoneBuffer.Dispose(); } } }