2025-04-25 21:14:54 +09:00

262 lines
9.6 KiB
C#

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<Animator>();
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);
}
/// <summary>
/// モデルの正規化を実行する
///
/// v0.115 ヒエラルキーのコピーをしなくまりました(仕様変更)
/// v0.116 Animator.avatar 代入の副作用回避修正
///
/// v0.114以前: 非破壊
/// - return コピーされて正規化されたヒエラルキー
/// v0.115以降: 対象のヒエラルキーが正規化されます。
/// - Transform が変更されます。
/// - Animator.avatar が差し替えられます。
/// - SkinnedMeshRenderer.sharedMesh が差し替えられます。
/// - MeshFilter.sharedMesh が差し替えられます。
/// - return void
/// </summary>
/// <param name="go">対象モデルのルート</param>
/// <param name="forceTPose">強制的にT-Pose化するか</param>
public static void Execute(GameObject go, bool forceTPose)
{
if (forceTPose)
{
// T-Poseにする
var hips = go.GetComponent<Animator>().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<Animator>();
var newAvatar = UniHumanoid.AvatarDescription.RecreateAvatar(animator);
// Animator.avatar を代入したときに副作用でTransformが変更されるのを回避するために削除します。
if (Application.isPlaying)
{
GameObject.Destroy(animator);
}
else
{
GameObject.DestroyImmediate(animator);
}
animator = go.AddComponent<Animator>();
animator.avatar = newAvatar;
}
/// <summary>
/// VRMを構成するコンポーネントをコピーする。
/// </summary>
/// <param name="go">コピー元</param>
/// <param name="root">コピー先</param>
/// <param name="map">コピー元とコピー先の対応関係</param>
static void CopyVRMComponents(GameObject go, GameObject root,
Dictionary<Transform, Transform> map)
{
{
// blendshape
var src = go.GetComponent<VRMBlendShapeProxy>();
if (src != null)
{
var dst = root.AddComponent<VRMBlendShapeProxy>();
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<VRMSpringBoneColliderGroup>())
{
var dst = map[src.transform];
var dstColliderGroup = dst.gameObject.AddComponent<VRMSpringBoneColliderGroup>();
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<VRMSpringBone>())
{
var dst = dstSecondary.gameObject.AddComponent<VRMSpringBone>();
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<VRMSpringBoneColliderGroup>()).ToArray();
}
}
}
#pragma warning disable 0618
{
// meta(obsolete)
var src = go.GetComponent<VRMMetaInformation>();
if (src != null)
{
src.CopyTo(root);
}
}
#pragma warning restore 0618
{
// meta
var src = go.GetComponent<VRMMeta>();
if (src != null)
{
var dst = root.AddComponent<VRMMeta>();
dst.Meta = src.Meta;
}
}
{
// firstPerson
var src = go.GetComponent<VRMFirstPerson>();
if (src != null)
{
src.CopyTo(root, map);
}
}
{
// look at
var src = go.GetComponent<VRMLookAtHead>();
if (src != null)
{
var dst = root.AddComponent<VRMLookAtHead>();
}
}
{
// bone applier
var src = go.GetComponent<VRMLookAtBoneApplyer>();
if (src != null)
{
var dst = root.AddComponent<VRMLookAtBoneApplyer>();
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<VRMLookAtBlendShapeApplyer>();
if (src != null)
{
var dst = root.AddComponent<VRMLookAtBlendShapeApplyer>();
dst.Horizontal.Assign(src.Horizontal);
dst.VerticalUp.Assign(src.VerticalUp);
dst.VerticalDown.Assign(src.VerticalDown);
}
}
{
// humanoid
var dst = root.AddComponent<VRMHumanoidDescription>();
var src = go.GetComponent<VRMHumanoidDescription>();
if (src != null)
{
dst.Avatar = src.Avatar;
dst.Description = src.Description;
}
else
{
var animator = go.GetComponent<Animator>();
if (animator != null)
{
dst.Avatar = animator.avatar;
}
}
}
}
}
}