336 lines
12 KiB
C#

#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using System;
using System.Linq;
using System.Collections.Generic;
using UniGLTF.Utils;
namespace UniHumanoid
{
// TODO: BoneLimit.cs に分ける(v0.104以降)
[Serializable]
public struct BoneLimit
{
public HumanBodyBones humanBone;
public string boneName;
public bool useDefaultValues;
public Vector3 min;
public Vector3 max;
public Vector3 center;
public float axisLength;
// HumanTrait.BoneName は HumanBodyBones.ToString とほぼ一対一に対応するが、
// 指のボーンについては " " の有無という微妙な違いがある。
// このスペースは AvatarBuilder.BuildHumanAvatar において必用であり、
// HumanBodyBones.ToString と区別する必要がある。
//
// また、下記についてGCが発生するのでキャッシュします。
// * HumanTrait.BoneName
// * traitName.Replace
// * Enum.Parse
//
private static readonly Dictionary<HumanBodyBones, string> cachedHumanBodyBonesToBoneTraitNameMap =
HumanTrait.BoneName.ToDictionary(
traitName => (HumanBodyBones)Enum.Parse(typeof(HumanBodyBones), traitName.Replace(" ", "")),
traitName => traitName);
// 逆引き
private static readonly Dictionary<string, HumanBodyBones> cachedBoneTraitNameToHumanBodyBonesMap =
HumanTrait.BoneName.ToDictionary(
traitName => traitName,
traitName => (HumanBodyBones)Enum.Parse(typeof(HumanBodyBones), traitName.Replace(" ", "")));
public static BoneLimit From(HumanBone bone)
{
return new BoneLimit
{
humanBone = cachedBoneTraitNameToHumanBodyBonesMap[bone.humanName],
boneName = bone.boneName,
useDefaultValues = bone.limit.useDefaultValues,
min = bone.limit.min,
max = bone.limit.max,
center = bone.limit.center,
axisLength = bone.limit.axisLength,
};
}
public HumanBone ToHumanBone()
{
return new HumanBone
{
boneName = boneName,
humanName = cachedHumanBodyBonesToBoneTraitNameMap[humanBone],
limit = new HumanLimit
{
useDefaultValues = useDefaultValues,
axisLength = axisLength,
center = center,
max = max,
min = min
},
};
}
}
[Serializable]
public class AvatarDescription : ScriptableObject
{
public float armStretch = 0.05f;
public float legStretch = 0.05f;
public float upperArmTwist = 0.5f;
public float lowerArmTwist = 0.5f;
public float upperLegTwist = 0.5f;
public float lowerLegTwist = 0.5f;
public float feetSpacing = 0;
public bool hasTranslationDoF;
public BoneLimit[] human;
public HumanDescription ToHumanDescription(Transform root)
{
var transforms = root.GetComponentsInChildren<Transform>();
var skeletonBones = new SkeletonBone[transforms.Length];
var index = 0;
foreach (var t in transforms)
{
skeletonBones[index] = t.ToSkeletonBone();
index++;
}
var humanBones = new HumanBone[human.Length];
index = 0;
foreach (var bonelimit in human)
{
humanBones[index] = bonelimit.ToHumanBone();
index++;
}
return new HumanDescription
{
skeleton = skeletonBones,
human = humanBones,
armStretch = armStretch,
legStretch = legStretch,
upperArmTwist = upperArmTwist,
lowerArmTwist = lowerArmTwist,
upperLegTwist = upperLegTwist,
lowerLegTwist = lowerLegTwist,
feetSpacing = feetSpacing,
hasTranslationDoF = hasTranslationDoF,
};
}
public Avatar CreateAvatar(Transform root)
{
// force unique name
ForceUniqueName.Process(root);
return AvatarBuilder.BuildHumanAvatar(root.gameObject, ToHumanDescription(root));
}
public Avatar CreateAvatarAndSetup(Transform root)
{
var avatar = CreateAvatar(root);
avatar.name = name;
var animator = root.GetComponent<Animator>();
if (animator != null)
{
var positionMap = root.Traverse().ToDictionary(x => x, x => x.position);
animator.avatar = avatar;
foreach (var x in root.Traverse())
{
x.position = positionMap[x];
}
}
var transfer = root.GetComponent<HumanPoseTransfer>();
if (transfer != null)
{
transfer.Avatar = avatar;
}
return avatar;
}
#if UNITY_EDITOR
public static AvatarDescription CreateFrom(Avatar avatar)
{
var description = default(HumanDescription);
if (!GetHumanDescription(avatar, ref description))
{
return null;
}
return CreateFrom(description);
}
#endif
public static AvatarDescription CreateFrom(HumanDescription description)
{
var avatarDescription = ScriptableObject.CreateInstance<AvatarDescription>();
avatarDescription.name = "AvatarDescription";
avatarDescription.armStretch = description.armStretch;
avatarDescription.legStretch = description.legStretch;
avatarDescription.feetSpacing = description.feetSpacing;
avatarDescription.hasTranslationDoF = description.hasTranslationDoF;
avatarDescription.lowerArmTwist = description.lowerArmTwist;
avatarDescription.lowerLegTwist = description.lowerLegTwist;
avatarDescription.upperArmTwist = description.upperArmTwist;
avatarDescription.upperLegTwist = description.upperLegTwist;
avatarDescription.human = description.human.Select(BoneLimit.From).ToArray();
return avatarDescription;
}
public static AvatarDescription Create(AvatarDescription src = null)
{
var avatarDescription = ScriptableObject.CreateInstance<AvatarDescription>();
avatarDescription.name = "AvatarDescription";
if (src != null)
{
avatarDescription.armStretch = src.armStretch;
avatarDescription.legStretch = src.legStretch;
avatarDescription.feetSpacing = src.feetSpacing;
avatarDescription.upperArmTwist = src.upperArmTwist;
avatarDescription.lowerArmTwist = src.lowerArmTwist;
avatarDescription.upperLegTwist = src.upperLegTwist;
avatarDescription.lowerLegTwist = src.lowerLegTwist;
}
else
{
avatarDescription.armStretch = 0.05f;
avatarDescription.legStretch = 0.05f;
avatarDescription.feetSpacing = 0.0f;
avatarDescription.lowerArmTwist = 0.5f;
avatarDescription.upperArmTwist = 0.5f;
avatarDescription.upperLegTwist = 0.5f;
avatarDescription.lowerLegTwist = 0.5f;
}
return avatarDescription;
}
public static AvatarDescription Create(Transform[] boneTransforms, Skeleton skeleton)
{
return Create(skeleton.Bones.Select(
x => new KeyValuePair<HumanBodyBones, Transform>(x.Key, boneTransforms[x.Value])));
}
public static AvatarDescription Create(IEnumerable<KeyValuePair<HumanBodyBones, Transform>> skeleton)
{
var description = Create();
description.SetHumanBones(skeleton);
return description;
}
public void SetHumanBones(IEnumerable<KeyValuePair<HumanBodyBones, Transform>> skeleton)
{
human = skeleton.Select(x =>
{
return new BoneLimit
{
humanBone = x.Key,
boneName = x.Value.name,
useDefaultValues = true,
};
}).ToArray();
}
#if UNITY_EDITOR
/// <summary>
/// * https://answers.unity.com/questions/612177/how-can-i-access-human-avatar-bone-and-muscle-valu.html
/// </summary>
/// <param name="target"></param>
/// <param name="des"></param>
/// <returns></returns>
public static bool GetHumanDescription(UnityEngine.Object target, ref HumanDescription des)
{
if (target != null)
{
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(target));
if (importer != null)
{
Debug.Log("AssetImporter Type: " + importer.GetType());
ModelImporter modelImporter = importer as ModelImporter;
if (modelImporter != null)
{
des = modelImporter.humanDescription;
Debug.Log("## Cool stuff data by ModelImporter ##");
return true;
}
else
{
Debug.LogWarning("## Please Select Imported Model in Project View not prefab or other things ##");
}
}
}
return false;
}
#endif
public static Avatar CreateAvatarForCopyHierarchy(
Animator src,
GameObject dst,
IDictionary<Transform, Transform> boneMap,
Action<AvatarDescription> modAvatarDesc = null)
{
if (src == null)
{
throw new ArgumentNullException("src");
}
var srcHumanBones = CachedEnum.GetValues<HumanBodyBones>()
.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])
;
var avatarDescription = UniHumanoid.AvatarDescription.Create();
if (modAvatarDesc != null)
{
modAvatarDesc(avatarDescription);
}
avatarDescription.SetHumanBones(map);
var avatar = avatarDescription.CreateAvatar(dst.transform);
avatar.name = "created";
return avatar;
}
public static Avatar RecreateAvatar(Animator src)
{
if (src == null)
{
throw new ArgumentNullException("src");
}
var srcHumanBones = CachedEnum.GetValues<HumanBodyBones>()
.Where(x => x != HumanBodyBones.LastBone)
.Select(x => new { Key = x, Value = src.GetBoneTransform(x) })
.Where(x => x.Value != null)
;
var map =
srcHumanBones
.ToDictionary(x => x.Key, x => x.Value)
;
var avatarDescription = UniHumanoid.AvatarDescription.Create();
avatarDescription.SetHumanBones(map);
var avatar = avatarDescription.CreateAvatar(src.transform);
avatar.name = "created";
return avatar;
}
}
}