Files
2025-11-30 08:35:03 +02:00

280 lines
10 KiB
C#

using System.Collections.Generic;
using UnityEngine;
namespace UnityGLTF
{
internal class AvatarUtils
{
// A static dictionary containing the mapping from joint/bones names in the model
// to the names Unity uses for them internally.
// In this case they match the naming from the included Mixamo model on the left
// and the Unity equivalent name on the right.
// This does not need to be hard-coded.
public static Dictionary<string, string> HumanSkeletonNames = new Dictionary<string, string>()
{
{"mixamorig:spine1", "Chest"},
{"mixamorig:head", "Head" },
{"mixamorig:hips", "Hips" },
{"mixamorig:lefthandindex3", "Left Index Distal" },
{"mixamorig:lefthandindex2", "Left Index Intermediate" },
{"mixamorig:lefthandindex1", "Left Index Proximal" },
{"mixamorig:leftHandpinky3", "Left Little Distal" },
{"mixamorig:lefthandpinky2", "Left Little Intermediate" },
{"mixamorig:lefthandpinky1", "Left Little Proximal" },
{"mixamorig:lefthandmiddle3", "Left Middle Distal" },
{"mixamorig:leftHandmiddle2", "Left Middle Intermediate" },
{"mixamorig:lefthandmiddle1", "Left Middle Proximal" },
{"mixamorig:lefthandring3", "Left Ring Distal" },
{"mixamorig:lefthandring2", "Left Ring Intermediate" },
{"mixamorig:lefthandring1", "Left Ring Proximal" },
{"mixamorig:lefthandthumb3", "Left Thumb Distal" },
{"mixamorig:lefthandthumb2", "Left Thumb Intermediate" },
{"mixamorig:lefthandthumb1", "Left Thumb Proximal" },
{"mixamorig:leftfoot", "LeftFoot" },
{"mixamorig:lefthand", "LeftHand" },
{"mixamorig:leftforearm", "LeftLowerArm" },
{"mixamorig:leftleg", "LeftLowerLeg" },
{"mixamorig:leftshoulder", "LeftShoulder" },
{"mixamorig:lefttoebase", "LeftToes" },
{"mixamorig:leftarm", "LeftUpperArm" },
{"mixamorig:leftupleg", "LeftUpperLeg" },
{"mixamorig:neck", "Neck" },
{"mixamorig:righthandindex3", "Right Index Distal" },
{"mixamorig:righthandindex2", "Right Index Intermediate" },
{"mixamorig:righthandindex1", "Right Index Proximal" },
{"mixamorig:righthandpinky3", "Right Little Distal" },
{"mixamorig:righthandpinky2", "Right Little Intermediate" },
{"mixamorig:righthandpinky1", "Right Little Proximal" },
{"mixamorig:righthandmiddle3", "Right Middle Distal" },
{"mixamorig:righthandmiddle2", "Right Middle Intermediate" },
{"mixamorig:righthandmiddle1", "Right Middle Proximal" },
{"mixamorig:righthandring3", "Right Ring Distal" },
{"mixamorig:righthandring2", "Right Ring Intermediate" },
{"mixamorig:righthandring1", "Right Ring Proximal" },
{"mixamorig:righthandthumb3", "Right Thumb Distal" },
{"mixamorig:righthandthumb2", "Right Thumb Intermediate" },
{"mixamorig:righthandthumb1", "Right Thumb Proximal" },
{"mixamorig:rightfoot", "RightFoot" },
{"mixamorig:righthand", "RightHand" },
{"mixamorig:rightforearm", "RightLowerArm" },
{"mixamorig:rightleg", "RightLowerLeg" },
{"mixamorig:rightshoulder", "RightShoulder" },
{"mixamorig:righttoebase", "RightToes" },
{"mixamorig:rightarm", "RightUpperArm" },
{"mixamorig:rightupleg", "RightUpperLeg" },
{"mixamorig:spine", "Spine" },
{"mixamorig:spine2", "UpperChest" },
// Other common Avatar formats can also be added here
// { "root", "" },
{ "hips", "Hips" },
{ "spine", "Spine" },
{ "spine1", "Chest" },
{ "chest", "Chest" },
{ "upperchest", "UpperChest" },
{ "neck", "Neck" },
{ "head", "Head" },
{ "lefteye", "LeftEye" },
// { "eyeL_end", "" },
{ "righteye", "RightEye" },
// { "eyeR_end", "" },
{ "leftshoulder", "LeftShoulder" },
{ "leftupperarm", "LeftUpperArm" },
{ "leftarm", "LeftUpperArm" },
{ "leftlowerarm", "LeftLowerArm" },
{ "leftforearm", "LeftLowerArm" },
{ "lefthand", "LeftHand" },
// { "leftThumbMetacarpal", "" },
// { "leftThumbProximal", "" },
// { "leftThumbDistal", "" },
// { "thumbdistalL_end", "" },
// { "leftIndexProximal", "" },
// { "leftIndexIntermediate", "" },
// { "leftIndexDistal", "" },
// { "indexdistalL_end", "" },
// { "leftMiddleProximal", "" },
// { "leftMiddleIntermediate", "" },
// { "leftMiddleDistal", "" },
// { "middledistalL_end", "" },
// { "leftRingProximal", "" },
// { "leftRingIntermediate", "" },
// { "leftRingDistal", "" },
// { "ringdistalL_end", "" },
// { "leftLittleProximal", "" },
// { "leftLittleIntermediate", "" },
// { "leftLittleDistal", "" },
// { "littledistalL_end", "" },
{ "rightshoulder", "RightShoulder" },
{ "rightupperarm", "RightUpperArm" },
{ "rightlowerarm", "RightLowerArm" },
{ "righthand", "RightHand" },
// { "rightThumbMetacarpal", "" },
// { "rightThumbProximal", "" },
// { "rightThumbDistal", "" },
// { "thumbdistalR_end", "" },
// { "rightIndexProximal", "" },
// { "rightIndexIntermediate", "" },
// { "rightIndexDistal", "" },
// { "indexdistalR_end", "" },
// { "rightMiddleProximal", "" },
// { "rightMiddleIntermediate", "" },
// { "rightMiddleDistal", "" },
// { "middledistalR_end", "" },
// { "rightRingProximal", "" },
// { "rightRingIntermediate", "" },
// { "rightRingDistal", "" },
// { "ringdistalR_end", "" },
// { "rightLittleProximal", "" },
// { "rightLittleIntermediate", "" },
// { "rightLittleDistal", "" },
// { "littledistalR_end", "" },
{ "leftupperleg", "LeftUpperLeg" },
{ "leftupleg", "LeftUpperLeg" },
{ "leftlowerleg", "LeftLowerLeg" },
{ "leftleg", "LeftLowerLeg" },
{ "leftfoot", "LeftFoot" },
{ "lefttoes", "LeftToes" },
{ "lefttoebase", "LeftToes" },
// { "toesL_end", "" },
{ "rightupperleg", "RightUpperLeg" },
{ "rightupleg", "RightUpperLeg" },
{ "rightlowerleg", "RightLowerLeg" },
{ "rightleg", "RightLowerLeg" },
{ "rightfoot", "RightFoot" },
{ "righttoes", "RightToes" },
{ "righttoebase", "RightToes" },
// { "toesR_end", "" },
// Meta Avatar
{"hips_jnt", "Hips"},
{"spinelower_jnt", "Spine"},
{"spinemiddle_jnt", "Chest"},
{"chest_jnt", "UpperChest"},
{"neck_jnt", "Neck"},
{"head_jnt", "Head"},
{"jaw_jnt", "Jaw"},
{"lefteye_jnt", "LeftEye"},
{"righteye_jnt", "RightEye"},
{"leftshoulder_jnt", "LeftShoulder"},
{"leftarmupper_jnt", "LeftUpperArm"},
{"leftarmlower_jnt", "LeftLowerArm"},
{"lefthandwrist_jnt", "LeftHand"},
{"rightshoulder_jnt", "RightShoulder"},
{"rightarmupper_jnt", "RightUpperArm"},
{"rightarmlower_jnt", "RightLowerArm"},
{"righthandwrist_jnt", "RightHand"},
{"leftlegupper_jnt", "LeftUpperLeg"},
{"leftleglower_jnt", "LeftLowerLeg"},
{"leftfootball_jnt", "LeftFoot"},
{"leftfoottoe_jnt", "LeftToes"},
{"rightlegupper_jnt", "RightUpperLeg"},
{"rightleglower_jnt", "RightLowerLeg"},
{"rightfootball_jnt", "RightFoot"},
{"rightfoottoe_jnt", "RightToes"}
};
/// <summary>
/// Create a HumanDescription out of an avatar GameObject.
/// The HumanDescription is what is needed to create an Avatar object
/// using the AvatarBuilder API. This function takes care of
/// creating the HumanDescription by going through the avatar's
/// hierarchy, defining its T-Pose in the skeleton, and defining
/// the transform/bone mapping in the HumanBone array.
/// </summary>
/// <param name="avatarRoot">Root of your avatar object</param>
/// <returns>A HumanDescription which can be fed to the AvatarBuilder API</returns>
public static HumanDescription CreateHumanDescription(GameObject avatarRoot)
{
HumanDescription description = new HumanDescription()
{
armStretch = 0.05f,
feetSpacing = 0f,
hasTranslationDoF = false,
legStretch = 0.05f,
lowerArmTwist = 0.5f,
lowerLegTwist = 0.5f,
upperArmTwist = 0.5f,
upperLegTwist = 0.5f,
skeleton = CreateSkeleton(avatarRoot),
human = CreateHuman(avatarRoot),
};
return description;
}
//Create a SkeletonBone array out of an Avatar GameObject
//This assumes that the Avatar as supplied is in a T-Pose
//The local positions of its bones/joints are used to define this T-Pose
private static SkeletonBone[] CreateSkeleton(GameObject avatarRoot)
{
List<SkeletonBone> skeleton = new List<SkeletonBone>();
Transform[] avatarTransforms = avatarRoot.GetComponentsInChildren<Transform>();
foreach (Transform avatarTransform in avatarTransforms)
{
SkeletonBone bone = new SkeletonBone()
{
name = avatarTransform.name,
position = avatarTransform.localPosition,
rotation = avatarTransform.localRotation,
scale = avatarTransform.localScale
};
skeleton.Add(bone);
}
return skeleton.ToArray();
}
//Create a HumanBone array out of an Avatar GameObject
//This is where the various bones/joints get associated with the
//joint names that Unity understands. This is done using the
//static dictionary defined at the top.
private static HumanBone[] CreateHuman(GameObject avatarRoot)
{
List<HumanBone> human = new List<HumanBone>();
Transform[] avatarTransforms = avatarRoot.GetComponentsInChildren<Transform>();
foreach (Transform avatarTransform in avatarTransforms)
{
string humanName = avatarTransform.name.ToLowerInvariant();
if (HumanSkeletonNames.TryGetValue(humanName, out string newHumanName)) {
humanName = newHumanName;
}
else {
// strip away trailing _1, _2, etc.
var split = humanName.Split('_');
var partAfterLastUnderscore = split[split.Length - 1];
// if the last part is a number, remove it
if (int.TryParse(partAfterLastUnderscore, out _))
humanName = string.Join("_", split, 0, split.Length - 1);
if (HumanSkeletonNames.TryGetValue(humanName, out newHumanName))
humanName = newHumanName;
// we can also try prepending "mixamorig:" to the name
if (!HumanSkeletonNames.ContainsValue(humanName))
{
humanName = "mixamorig:" + humanName;
if (HumanSkeletonNames.TryGetValue(humanName, out newHumanName))
humanName = newHumanName;
}
}
if (!HumanSkeletonNames.ContainsValue(humanName))
continue;
HumanBone bone = new HumanBone
{
boneName = avatarTransform.name,
humanName = humanName,
limit = new HumanLimit()
};
bone.limit.useDefaultValues = true;
human.Add(bone);
}
return human.ToArray();
}
}
}