using System; using System.Collections.Generic; using System.Linq; using UniGLTF; using UniGLTF.MeshUtility; using UniGLTF.Utils; 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); } /// /// モデルの正規化を実行する /// /// v0.115 ヒエラルキーのコピーをしなくまりました(仕様変更) /// v0.116 Animator.avatar 代入の副作用回避修正 /// /// v0.114以前: 非破壊 /// - return コピーされて正規化されたヒエラルキー /// v0.115以降: 対象のヒエラルキーが正規化されます。 /// - Transform が変更されます。 /// - Animator.avatar が差し替えられます。 /// - SkinnedMeshRenderer.sharedMesh が差し替えられます。 /// - MeshFilter.sharedMesh が差し替えられます。 /// - return void /// /// 対象モデルのルート /// 強制的にT-Pose化するか public static void Execute(GameObject go, bool forceTPose) { if (forceTPose) { // T-Poseにする 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; } } // Transform の回転とスケールを Mesh に適用します。 // - BlendShape は現状がbakeされます // - 回転とスケールが反映された新しい Mesh が作成されます // - Transform の回転とスケールはクリアされます。world position を維持します var newMeshMap = BoneNormalizer.NormalizeHierarchyFreezeMesh(go); // SkinnedMeshRenderer.sharedMesh と MeshFilter.sharedMesh を新しいMeshで置き換える BoneNormalizer.Replace(go, newMeshMap, true, true); // 回転とスケールが除去された新しいヒエラルキーからAvatarを作る var animator = go.GetComponent(); var newAvatar = UniHumanoid.AvatarDescription.RecreateAvatar(animator); // Animator.avatar を代入したときに副作用でTransformが変更されるのを回避するために削除します。 if (Application.isPlaying) { GameObject.Destroy(animator); } else { GameObject.DestroyImmediate(animator); } animator = go.AddComponent(); animator.avatar = newAvatar; } /// /// 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; } } } } } }