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 HumanSkeletonNames = new Dictionary() { {"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"} }; /// /// 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. /// /// Root of your avatar object /// A HumanDescription which can be fed to the AvatarBuilder API 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 skeleton = new List(); Transform[] avatarTransforms = avatarRoot.GetComponentsInChildren(); 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 human = new List(); Transform[] avatarTransforms = avatarRoot.GetComponentsInChildren(); 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(); } } }