using System; using System.Collections.Generic; using System.Linq; using UniGLTF.MeshUtility; using UniHumanoid; using UnityEngine; namespace VRM { public static class VRMBoneNormalizer { public static void EnforceTPose(GameObject go) { var animator = go.GetComponent(); if (animator == null) { throw new ArgumentException("Animator with avatar is required"); } var avatar = animator.avatar; if (avatar == null) { throw new ArgumentException("avatar is required"); } if (!avatar.isValid) { throw new ArgumentException("invalid avatar"); } if (!avatar.isHuman) { throw new ArgumentException("avatar is not human"); } HumanPoseTransfer.SetTPose(avatar, go.transform); } /// /// モデルの正規化を実行する /// /// 対象モデルのルート /// 強制的にT-Pose化するか /// 正規化済みのモデル public static GameObject Execute(GameObject go, bool forceTPose) { // // T-Poseにする // if (forceTPose) { var hips = go.GetComponent().GetBoneTransform(HumanBodyBones.Hips); var hipsPosition = hips.position; var hipsRotation = hips.rotation; try { EnforceTPose(go); } finally { hips.position = hipsPosition; // restore hipsPosition hips.rotation = hipsRotation; } } // // 正規化されたヒエラルキーを作る // var (normalized, bMap) = BoneNormalizer.Execute(go, (_src, dst, boneMap) => { var src = _src.GetComponent(); var srcHumanBones = Enum.GetValues(typeof(HumanBodyBones)) .Cast() .Where(x => x != HumanBodyBones.LastBone) .Select(x => new { Key = x, Value = src.GetBoneTransform(x) }) .Where(x => x.Value != null) ; var map = srcHumanBones .Where(x => boneMap.ContainsKey(x.Value)) .ToDictionary(x => x.Key, x => boneMap[x.Value]) ; if (dst.GetComponent() == null) { var animator = dst.AddComponent(); } var vrmHuman = go.GetComponent(); var avatarDescription = AvatarDescription.Create(); if (vrmHuman != null && vrmHuman.Description != null) { avatarDescription.armStretch = vrmHuman.Description.armStretch; avatarDescription.legStretch = vrmHuman.Description.legStretch; avatarDescription.upperArmTwist = vrmHuman.Description.upperArmTwist; avatarDescription.lowerArmTwist = vrmHuman.Description.lowerArmTwist; avatarDescription.upperLegTwist = vrmHuman.Description.upperLegTwist; avatarDescription.lowerLegTwist = vrmHuman.Description.lowerLegTwist; avatarDescription.feetSpacing = vrmHuman.Description.feetSpacing; avatarDescription.hasTranslationDoF = vrmHuman.Description.hasTranslationDoF; } avatarDescription.SetHumanBones(map); var avatar = avatarDescription.CreateAvatar(dst.transform); return avatar; }); CopyVRMComponents(go, normalized, bMap); return normalized; } /// /// VRMを構成するコンポーネントをコピーする。 /// /// コピー元 /// コピー先 /// コピー元とコピー先の対応関係 static void CopyVRMComponents(GameObject go, GameObject root, Dictionary map) { { // blendshape var src = go.GetComponent(); if (src != null) { var dst = root.AddComponent(); dst.BlendShapeAvatar = src.BlendShapeAvatar; } } { // springbone var secondary = go.transform.Find("secondary"); if (secondary == null) { secondary = go.transform; } var dstSecondary = root.transform.Find("secondary"); if (dstSecondary == null) { dstSecondary = new GameObject("secondary").transform; dstSecondary.SetParent(root.transform, false); } // VRMSpringBoneColliderGroup foreach (var src in go.transform.GetComponentsInChildren()) { var dst = map[src.transform]; var dstColliderGroup = dst.gameObject.AddComponent(); dstColliderGroup.Colliders = src.Colliders.Select(y => { var offset = dst.worldToLocalMatrix.MultiplyPoint(src.transform.localToWorldMatrix.MultiplyPoint(y.Offset)); var ls = src.transform.UniformedLossyScale(); return new VRMSpringBoneColliderGroup.SphereCollider { Offset = offset, Radius = y.Radius * ls }; }).ToArray(); } // VRMSpringBone foreach (var src in go.transform.GetComponentsInChildren()) { var dst = dstSecondary.gameObject.AddComponent(); dst.m_comment = src.m_comment; dst.m_stiffnessForce = src.m_stiffnessForce; dst.m_gravityPower = src.m_gravityPower; dst.m_gravityDir = src.m_gravityDir; dst.m_dragForce = src.m_dragForce; if (src.m_center != null) { dst.m_center = map[src.m_center]; } dst.RootBones = src.RootBones.Select(x => map[x]).ToList(); dst.m_hitRadius = src.m_hitRadius; if (src.ColliderGroups != null) { dst.ColliderGroups = src.ColliderGroups .Select(x => map[x.transform].GetComponent()).ToArray(); } } } #pragma warning disable 0618 { // meta(obsolete) var src = go.GetComponent(); if (src != null) { src.CopyTo(root); } } #pragma warning restore 0618 { // meta var src = go.GetComponent(); if (src != null) { var dst = root.AddComponent(); dst.Meta = src.Meta; } } { // firstPerson var src = go.GetComponent(); if (src != null) { src.CopyTo(root, map); } } { // look at var src = go.GetComponent(); if (src != null) { var dst = root.AddComponent(); } } { // bone applier var src = go.GetComponent(); if (src != null) { var dst = root.AddComponent(); dst.HorizontalInner.Assign(src.HorizontalInner); dst.HorizontalOuter.Assign(src.HorizontalOuter); dst.VerticalUp.Assign(src.VerticalUp); dst.VerticalDown.Assign(src.VerticalDown); } } { // blendshape applier var src = go.GetComponent(); if (src != null) { var dst = root.AddComponent(); dst.Horizontal.Assign(src.Horizontal); dst.VerticalUp.Assign(src.VerticalUp); dst.VerticalDown.Assign(src.VerticalDown); } } { // humanoid var dst = root.AddComponent(); var src = go.GetComponent(); if (src != null) { dst.Avatar = src.Avatar; dst.Description = src.Description; } else { var animator = go.GetComponent(); if (animator != null) { dst.Avatar = animator.avatar; } } } } } }