// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts,
// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies.
// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// Eden.AutoMorpher.BoneMatchUtil
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class BoneMatchUtil
{
    public class BoneRootLocalData
    {
        public Transform t;

        public string name;

        public string path;

        public Vector3 rootLocalPos;

        public Quaternion rootLocalRot;

        public HumanBodyBones hBone = HumanBodyBones.LastBone;
    }

    private const float PosTolNear = 0.0001f;

    private const float PosTolMid = 0.0005f;

    private const float PosTolFar = 0.002f;

    private const float RotTolDeg = 30f;

    private const float eps = 1E-06f;

    public List<BoneRootLocalData> ConvertProfileBoneDataToRootLocalData(List<BoneData> boneDataList)
    {
        if (boneDataList == null || boneDataList.Count == 0)
        {
            throw new AutoMorpherException("Profile Bone Data is Missing", "[BoneMatchUtil] ConvertProfileBoneDataToRootLocalData\n - boneDataList is null or empty");
        }
        List<BoneRootLocalData> list = new List<BoneRootLocalData>(boneDataList.Count);
        foreach (BoneData boneData in boneDataList)
        {
            if (boneData != null)
            {
                BoneRootLocalData item = new BoneRootLocalData
                {
                    t = null,
                    name = (boneData.boneName ?? string.Empty),
                    path = (boneData.hierarchyPath ?? string.Empty),
                    rootLocalPos = boneData.rootLocalPosition,
                    rootLocalRot = boneData.rootLocalRotation,
                    hBone = boneData.hBone
                };
                list.Add(item);
            }
        }
        return list;
    }

    public HashSet<Transform> GetMeshBones(List<SkinnedMeshRenderer> meshRenderers)
    {
        HashSet<Transform> hashSet = new HashSet<Transform>();
        if (meshRenderers != null)
        {
            foreach (SkinnedMeshRenderer meshRenderer in meshRenderers)
            {
                if (meshRenderer == null || meshRenderer.bones == null)
                {
                    continue;
                }
                Transform[] bones = meshRenderer.bones;
                foreach (Transform transform in bones)
                {
                    if (transform != null)
                    {
                        hashSet.Add(transform);
                    }
                }
            }
        }
        return hashSet;
    }

    public List<BoneRootLocalData> GetRootLocalBones(Transform rootT, HashSet<Transform> boneList)
    {
        BoneCorrespondenceUtil correspondenceUtil = new BoneCorrespondenceUtil();
        return (from t in boneList.Where((Transform t) => t != null).Distinct()
                select new BoneRootLocalData
                {
                    t = t,
                    name = t.name,
                    path = correspondenceUtil.GetHierarchyPath(rootT, t),
                    rootLocalPos = rootT.InverseTransformPoint(t.position),
                    rootLocalRot = Quaternion.Inverse(rootT.rotation) * t.rotation
                }).ToList();
    }

    public List<BoneRootLocalData> GetBodyRootLocalBones(Transform bodyTransform, Animator bodyAnimator, List<SkinnedMeshRenderer> bodyMeshes)
    {
        if (bodyTransform == null)
        {
            throw new AutoMorpherException("Body Transform is Missing", "[BoneMatchUtil] GetBodyRootLocalBones\n - bodyTransform is null");
        }
        if (bodyAnimator == null)
        {
            throw new AutoMorpherException("Body Animator is Missing", "[BoneMatchUtil] GetBodyRootLocalBones\n - animator is null");
        }
        Dictionary<Transform, HumanBodyBones> humanoidBoneList = this.GetHumanoidBoneList(bodyAnimator);
        if (humanoidBoneList == null || humanoidBoneList.Count == 0)
        {
            throw new AutoMorpherException("Humanoid Bone Map is Missing", "[BoneMatchUtil] GetBodyRootLocalBones\n - Can't find Humanoid Bone List");
        }
        if (bodyMeshes == null || bodyMeshes.Count == 0)
        {
            throw new AutoMorpherException("Body Meshes are Missing", "[BoneMatchUtil] GetBodyRootLocalBones\n - bodyMeshes is null or empty");
        }
        HashSet<Transform> meshBones = this.GetMeshBones(bodyMeshes);
        Dictionary<HumanBodyBones, HashSet<Transform>> dictionary = new MeshClassifier().MeshHumanoidBoneMatcher(bodyAnimator, bodyMeshes);
        HashSet<Transform> hashSet = new HashSet<Transform>();
        HashSet<Transform> hashSet2 = new HashSet<Transform>();
        foreach (KeyValuePair<Transform, HumanBodyBones> item in humanoidBoneList)
        {
            if (item.Value != HumanBodyBones.LastBone && !(item.Key == null))
            {
                if (!dictionary.TryGetValue(item.Value, out var value) || value == null)
                {
                    value = new HashSet<Transform>();
                    dictionary[item.Value] = value;
                }
                value.Add(item.Key);
            }
        }
        foreach (KeyValuePair<HumanBodyBones, HashSet<Transform>> item2 in dictionary)
        {
            HumanBodyBones key = item2.Key;
            if (key == HumanBodyBones.LastBone)
            {
                continue;
            }
            HashSet<Transform> value2 = item2.Value;
            if (value2 == null || value2.Count == 0)
            {
                continue;
            }
            Transform transform = bodyAnimator.GetBoneTransform(key);
            if (transform == null)
            {
                transform = value2.First();
            }
            if (transform == null)
            {
                continue;
            }
            hashSet.Add(transform);
            foreach (Transform item3 in value2)
            {
                if (!(item3 == transform) && !(item3 == null))
                {
                    hashSet2.Add(item3);
                }
            }
        }
        foreach (Transform item4 in meshBones)
        {
            if (!(item4 == null) && !hashSet2.Contains(item4))
            {
                hashSet.Add(item4);
            }
        }
        List<BoneRootLocalData> rootLocalBones = this.GetRootLocalBones(bodyTransform, hashSet);
        foreach (BoneRootLocalData item5 in rootLocalBones)
        {
            if (item5 != null && !(item5.t == null))
            {
                if (humanoidBoneList.TryGetValue(item5.t, out var value3))
                {
                    item5.hBone = value3;
                }
                else
                {
                    item5.hBone = HumanBodyBones.LastBone;
                }
            }
        }
        return rootLocalBones;
    }

    private Dictionary<Transform, HumanBodyBones> GetHumanoidBoneList(Animator bodyAnimator)
    {
        Dictionary<Transform, HumanBodyBones> dictionary = new Dictionary<Transform, HumanBodyBones>();
        if (bodyAnimator == null || bodyAnimator.avatar == null || !bodyAnimator.avatar.isHuman)
        {
            return dictionary;
        }
        foreach (HumanBodyBones value in Enum.GetValues(typeof(HumanBodyBones)))
        {
            if (value != HumanBodyBones.LastBone)
            {
                Transform boneTransform = bodyAnimator.GetBoneTransform(value);
                if (!(boneTransform == null))
                {
                    dictionary.TryAdd(boneTransform, value);
                }
            }
        }
        return dictionary;
    }

    public void MatchClothesToBodyBones(List<BoneRootLocalData> bodyBones, List<BoneRootLocalData> clothesBones, out Dictionary<HumanBodyBones, HashSet<Transform>> clothHumanBones, out Dictionary<Transform, ClothBoneType> clothBoneTypeMap, out Dictionary<Transform, BoneRootLocalData> clothToBodyMatched)
    {
        clothHumanBones = new Dictionary<HumanBodyBones, HashSet<Transform>>();
        clothBoneTypeMap = new Dictionary<Transform, ClothBoneType>();
        clothToBodyMatched = new Dictionary<Transform, BoneRootLocalData>();
        if (bodyBones == null || bodyBones.Count == 0)
        {
            throw new AutoMorpherException("Body Bones are Missing", "[BoneMatchUtil] MatchClothesToBodyBones\n - bodyBones is null or empty");
        }
        if (clothesBones == null || clothesBones.Count == 0)
        {
            throw new AutoMorpherException("Clothes Bones are Missing", "[BoneMatchUtil] MatchClothesToBodyBones\n - clothesBones is null or empty");
        }
        List<(BoneRootLocalData, BoneRootLocalData, float, float, float, float, float)> list = new List<(BoneRootLocalData, BoneRootLocalData, float, float, float, float, float)>();
        BoneCorrespondenceUtil boneCorrespondenceUtil = new BoneCorrespondenceUtil();
        foreach (BoneRootLocalData clothesBone in clothesBones)
        {
            BoneRootLocalData boneRootLocalData = null;
            float num = float.NegativeInfinity;
            float num2 = float.NegativeInfinity;
            float num3 = float.NegativeInfinity;
            float num4 = float.PositiveInfinity;
            float num5 = float.PositiveInfinity;
            foreach (BoneRootLocalData bodyBone in bodyBones)
            {
                float num6 = Vector3.Distance(clothesBone.rootLocalPos, bodyBone.rootLocalPos);
                float num7 = boneCorrespondenceUtil.ComputeDistanceScore(num6, 0.0001f, 0.0005f, 0.002f);
                if (num7 == 0f)
                {
                    continue;
                }
                float num8 = boneCorrespondenceUtil.ComputeNameScore(bodyBone.name, bodyBone.path, clothesBone.name, clothesBone.path);
                if (num8 != 0f)
                {
                    float num9 = Quaternion.Angle(bodyBone.rootLocalRot, clothesBone.rootLocalRot);
                    float num10 = boneCorrespondenceUtil.ComputeRotationScore(num9, 30f) * 1f;
                    if (this.IsBetterCandidate(num7, num, num10, num2, num8, num3, num6, num4, num9, num5))
                    {
                        boneRootLocalData = bodyBone;
                        num = num7;
                        num2 = num10;
                        num3 = num8;
                        num4 = num6;
                        num5 = num9;
                    }
                }
            }
            if (boneRootLocalData != null)
            {
                list.Add((clothesBone, boneRootLocalData, num, num2, num3, num4, num5));
            }
        }
        Dictionary<Transform, BoneRootLocalData> dictionary = new Dictionary<Transform, BoneRootLocalData>();
        foreach (IGrouping<BoneRootLocalData, (BoneRootLocalData, BoneRootLocalData, float, float, float, float, float)> item in from x in list group x by x.Item2)
        {
            if (item.Key == null)
            {
                continue;
            }
            List<(BoneRootLocalData, BoneRootLocalData, float, float, float, float, float)> list2 = item.ToList();
            if (list2.Count == 0)
            {
                continue;
            }
            (BoneRootLocalData, BoneRootLocalData, float, float, float, float, float) tuple = list2[0];
            for (int num11 = 1; num11 < list2.Count; num11++)
            {
                (BoneRootLocalData, BoneRootLocalData, float, float, float, float, float) tuple2 = list2[num11];
                if (this.IsBetterCandidate(tuple2.Item3, tuple.Item3, tuple2.Item4, tuple.Item4, tuple2.Item5, tuple.Item5, tuple2.Item6, tuple.Item6, tuple2.Item7, tuple.Item7))
                {
                    tuple = tuple2;
                }
            }
            if (tuple.Item1 != null && tuple.Item1.t != null && tuple.Item2 != null)
            {
                dictionary[tuple.Item1.t] = tuple.Item2;
            }
        }
        foreach (BoneRootLocalData clothesBone2 in clothesBones)
        {
            if (clothesBone2?.t == null)
            {
                continue;
            }
            if (dictionary.TryGetValue(clothesBone2.t, out var value))
            {
                clothBoneTypeMap[clothesBone2.t] = ClothBoneType.Body;
                clothToBodyMatched[clothesBone2.t] = value;
                HumanBodyBones hBone = value.hBone;
                if (hBone != HumanBodyBones.LastBone)
                {
                    if (!clothHumanBones.TryGetValue(hBone, out var value2))
                    {
                        value2 = new HashSet<Transform>();
                        clothHumanBones[hBone] = value2;
                    }
                    value2.Add(clothesBone2.t);
                }
            }
            else
            {
                clothBoneTypeMap[clothesBone2.t] = ClothBoneType.Accessory;
            }
        }
    }

    private bool IsBetterCandidate(float candDistScore, float bestDistScore, float candRotScore, float bestRotScore, float candNameScore, float bestNameScore, float candD, float bestD, float candAng, float bestAng)
    {
        if (candDistScore > bestDistScore)
        {
            return true;
        }
        if (candDistScore < bestDistScore)
        {
            return false;
        }
        if (candNameScore > bestNameScore)
        {
            return true;
        }
        if (candNameScore < bestNameScore)
        {
            return false;
        }
        if (Mathf.Abs(candRotScore - bestRotScore) > 1E-06f)
        {
            if (candRotScore > bestRotScore)
            {
                return true;
            }
            if (candRotScore < bestRotScore)
            {
                return false;
            }
        }
        if (Mathf.Abs(candD - bestD) > 1E-06f)
        {
            if (candD < bestD)
            {
                return true;
            }
            if (candD > bestD)
            {
                return false;
            }
        }
        if (Mathf.Abs(candAng - bestAng) > 1E-06f)
        {
            if (candAng < bestAng)
            {
                return true;
            }
            return false;
        }
        return false;
    }

    public Dictionary<Transform, Transform> BuildTransformMatchMap(List<BoneRootLocalData> sourceBones, List<BoneRootLocalData> destBones, bool resultReverse = false)
    {
        if (sourceBones == null || sourceBones.Count == 0)
        {
            throw new AutoMorpherException("Source Bones are Missing", "[BoneMatchUtil] BuildTransformMatchMap\n - sourceBones is null or empty");
        }
        if (destBones == null || destBones.Count == 0)
        {
            throw new AutoMorpherException("Destination Bones are Missing", "[BoneMatchUtil] BuildTransformMatchMap\n - destBones is null or empty");
        }
        BoneCorrespondenceUtil boneCorrespondenceUtil = new BoneCorrespondenceUtil();
        Dictionary<Transform, Transform> dictionary = new Dictionary<Transform, Transform>();
        Dictionary<string, BoneRootLocalData> dictionary2 = new Dictionary<string, BoneRootLocalData>();
        Dictionary<string, BoneRootLocalData> dictionary3 = new Dictionary<string, BoneRootLocalData>();
        Dictionary<string, List<BoneRootLocalData>> dictionary4 = new Dictionary<string, List<BoneRootLocalData>>();
        foreach (BoneRootLocalData destBone in destBones)
        {
            if (destBone == null || destBone.t == null)
            {
                continue;
            }
            string text = destBone.path ?? "";
            if (text.Length > 0 && !dictionary2.ContainsKey(text))
            {
                dictionary2.Add(text, destBone);
            }
            if (text.Length > 0)
            {
                string text2 = boneCorrespondenceUtil.NormalizePath(text);
                if (text2.Length > 0 && !dictionary3.ContainsKey(text2))
                {
                    dictionary3.Add(text2, destBone);
                }
            }
            string text3 = destBone.name ?? destBone.t.name;
            if (!string.IsNullOrEmpty(text3))
            {
                if (!dictionary4.TryGetValue(text3, out var value))
                {
                    value = (dictionary4[text3] = new List<BoneRootLocalData>());
                }
                value.Add(destBone);
            }
        }
        HashSet<Transform> hashSet = new HashSet<Transform>();
        foreach (BoneRootLocalData sourceBone in sourceBones)
        {
            if (sourceBone == null || sourceBone.t == null)
            {
                continue;
            }
            Transform transform = null;
            string text4 = sourceBone.path ?? "";
            if (text4.Length > 0 && dictionary2.TryGetValue(text4, out var value2) && value2 != null && value2.t != null && !hashSet.Contains(value2.t))
            {
                transform = value2.t;
            }
            if (transform == null && text4.Length > 0)
            {
                string text5 = boneCorrespondenceUtil.NormalizePath(text4);
                if (text5.Length > 0 && dictionary3.TryGetValue(text5, out var value3) && value3 != null && value3.t != null && !hashSet.Contains(value3.t))
                {
                    transform = value3.t;
                }
            }
            if (transform == null)
            {
                string text6 = sourceBone.name ?? sourceBone.t.name;
                if (!string.IsNullOrEmpty(text6) && dictionary4.TryGetValue(text6, out var value4))
                {
                    float num = float.PositiveInfinity;
                    Transform transform2 = null;
                    for (int i = 0; i < value4.Count; i++)
                    {
                        BoneRootLocalData boneRootLocalData = value4[i];
                        if (boneRootLocalData != null && !(boneRootLocalData.t == null) && !hashSet.Contains(boneRootLocalData.t))
                        {
                            float num2 = Vector3.SqrMagnitude(boneRootLocalData.rootLocalPos - sourceBone.rootLocalPos);
                            if (num2 < num)
                            {
                                num = num2;
                                transform2 = boneRootLocalData.t;
                            }
                        }
                    }
                    transform = transform2;
                }
            }
            if (transform != null)
            {
                if (resultReverse)
                {
                    dictionary[transform] = sourceBone.t;
                }
                else
                {
                    dictionary[sourceBone.t] = transform;
                }
                hashSet.Add(transform);
            }
        }
        return dictionary;
    }

    public void RemapSourceClothMatchToTargetCloth(Transform sourceClothRoot, Transform targetClothRoot, Dictionary<HumanBodyBones, HashSet<Transform>> sourceClothHumanBones, Dictionary<Transform, ClothBoneType> sourceClothBoneTypeMap, out Dictionary<HumanBodyBones, HashSet<Transform>> targetClothHumanBones, out Dictionary<Transform, ClothBoneType> targetClothBoneTypeMap)
    {
        targetClothHumanBones = new Dictionary<HumanBodyBones, HashSet<Transform>>();
        targetClothBoneTypeMap = new Dictionary<Transform, ClothBoneType>();
        if (sourceClothRoot == null || targetClothRoot == null)
        {
            throw new AutoMorpherException("Cloth Root is Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - sourceClothRoot or targetClothRoot is null");
        }
        if (sourceClothHumanBones == null || sourceClothBoneTypeMap == null)
        {
            throw new AutoMorpherException("Source Cloth Match Data is Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - sourceClothHumanBones or sourceClothBoneTypeMap is null");
        }
        HashSet<Transform> hashSet = new HashSet<Transform>();
        foreach (KeyValuePair<Transform, ClothBoneType> item in sourceClothBoneTypeMap)
        {
            if (item.Key != null)
            {
                hashSet.Add(item.Key);
            }
        }
        foreach (KeyValuePair<HumanBodyBones, HashSet<Transform>> sourceClothHumanBone in sourceClothHumanBones)
        {
            HashSet<Transform> value = sourceClothHumanBone.Value;
            if (value == null || value.Count == 0)
            {
                continue;
            }
            foreach (Transform item2 in value)
            {
                if (item2 != null)
                {
                    hashSet.Add(item2);
                }
            }
        }
        if (hashSet.Count == 0)
        {
            throw new AutoMorpherException("Source Cloth Bone Candidates are Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - sourceBoneSet is empty (no bones found in sourceClothBoneTypeMap/sourceClothHumanBones)");
        }
        HashSet<Transform> meshBones = this.GetMeshBones(targetClothRoot.GetComponentsInChildren<SkinnedMeshRenderer>(includeInactive: true).ToList());
        if (meshBones == null || meshBones.Count == 0)
        {
            throw new AutoMorpherException("Target Cloth Bone Candidates are Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - targetBoneSet is null or empty (GetMeshBones returned no bones)");
        }
        List<BoneRootLocalData> rootLocalBones = this.GetRootLocalBones(sourceClothRoot, hashSet);
        List<BoneRootLocalData> rootLocalBones2 = this.GetRootLocalBones(targetClothRoot, meshBones);
        if (rootLocalBones == null || rootLocalBones.Count == 0)
        {
            throw new AutoMorpherException("Source RootLocal Bones are Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - sourceRootLocalBones is null or empty");
        }
        if (rootLocalBones2 == null || rootLocalBones2.Count == 0)
        {
            throw new AutoMorpherException("Target RootLocal Bones are Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - targetRootLocalBones is null or empty");
        }
        Dictionary<Transform, Transform> dictionary = this.BuildTransformMatchMap(rootLocalBones, rootLocalBones2);
        foreach (KeyValuePair<Transform, ClothBoneType> item3 in sourceClothBoneTypeMap)
        {
            Transform key = item3.Key;
            ClothBoneType value2 = item3.Value;
            if (key == null || !dictionary.TryGetValue(key, out var value3) || value3 == null)
            {
                continue;
            }
            if (targetClothBoneTypeMap.TryGetValue(value3, out var value4))
            {
                if (value4 == ClothBoneType.Accessory && value2 == ClothBoneType.Body)
                {
                    targetClothBoneTypeMap[value3] = ClothBoneType.Body;
                }
            }
            else
            {
                targetClothBoneTypeMap.Add(value3, value2);
            }
        }
        foreach (KeyValuePair<HumanBodyBones, HashSet<Transform>> sourceClothHumanBone2 in sourceClothHumanBones)
        {
            HumanBodyBones key2 = sourceClothHumanBone2.Key;
            HashSet<Transform> value5 = sourceClothHumanBone2.Value;
            if (value5 == null || value5.Count == 0)
            {
                continue;
            }
            foreach (Transform item4 in value5)
            {
                if (!(item4 == null) && dictionary.TryGetValue(item4, out var value6) && !(value6 == null))
                {
                    if (!targetClothHumanBones.TryGetValue(key2, out var value7))
                    {
                        value7 = new HashSet<Transform>();
                        targetClothHumanBones[key2] = value7;
                    }
                    value7.Add(value6);
                }
            }
        }
    }

    public void BuildSourceToProxyBoneMap(Animator sourceAvatar, Animator proxyAvatar, out Dictionary<Transform, Transform> sourceToProxy)
    {
        sourceToProxy = new Dictionary<Transform, Transform>();
        if (sourceAvatar == null)
        {
            throw new AutoMorpherException("Source Avatar is Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - sourceAvatar is null");
        }
        if (proxyAvatar == null)
        {
            throw new AutoMorpherException("Proxy Avatar is Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - proxyAvatar is null");
        }
        Transform transform = sourceAvatar.transform;
        Transform transform2 = proxyAvatar.transform;
        HashSet<Transform> meshBones = this.GetMeshBones(transform2.GetComponentsInChildren<SkinnedMeshRenderer>(includeInactive: true).ToList());
        if (meshBones == null || meshBones.Count == 0)
        {
            throw new AutoMorpherException("Proxy Body Bone Candidates are Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - proxyBoneSet is null or empty (GetMeshBones returned no bones)");
        }
        HashSet<Transform> meshBones2 = this.GetMeshBones(transform.GetComponentsInChildren<SkinnedMeshRenderer>(includeInactive: true).ToList());
        if (meshBones2 == null || meshBones2.Count == 0)
        {
            throw new AutoMorpherException("Source Body Bone Candidates are Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - sourceBoneSet is null or empty (GetMeshBones returned no bones)");
        }
        List<BoneRootLocalData> rootLocalBones = this.GetRootLocalBones(transform2, meshBones);
        List<BoneRootLocalData> rootLocalBones2 = this.GetRootLocalBones(transform, meshBones2);
        if (rootLocalBones == null || rootLocalBones.Count == 0)
        {
            throw new AutoMorpherException("Proxy RootLocal Bones are Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - proxyRootLocalBones is null or empty");
        }
        if (rootLocalBones2 == null || rootLocalBones2.Count == 0)
        {
            throw new AutoMorpherException("Source RootLocal Bones are Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - sourceRootLocalBones is null or empty");
        }
        sourceToProxy = this.BuildTransformMatchMap(rootLocalBones, rootLocalBones2, resultReverse: true);
        if (sourceToProxy == null)
        {
            throw new AutoMorpherException("Source To Proxy Map Build Failed", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - BuildTransformMatchMap returned null");
        }
    }
}
