// 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.WeightTransferUtil
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections;
using UnityEditor;
using UnityEngine;

public class WeightTransferUtil
{
    [Serializable]
    public class Settings
    {
        [Header("최근접 표면 매칭")]
        public float maxDistance = 0.1f;

        public float maxNormalAngleDeg = 35f;

        public bool allowFlippedNormal = true;

        [Header("추가 스무딩")]
        public bool enableSmoothing = true;

        public int smoothingIterations = 4;

        [Range(0f, 1f)]
        public float smoothingAlpha = 0.25f;

        [Header("본 수 제한 & 임계값")]
        public bool enforceFourBoneLimit = true;

        public float tinyWeight = 0.0001f;

        public bool weightInClothes;
    }

    private bool NameContainsAny(string lowerName, string[] patterns)
    {
        for (int i = 0; i < patterns.Length; i++)
        {
            if (lowerName.Contains(patterns[i]))
            {
                return true;
            }
        }
        return false;
    }

    private bool IsBreastOrButtBone(Transform t)
    {
        if (t == null)
        {
            return false;
        }
        string[] patterns = new string[6] { "breast", "boob", "bust", "oppai", "chichi", "mune" };
        string[] patterns2 = new string[6] { "butt", "buttock", "buttox", "glute", "glutes", "gluteus" };
        string lowerName = t.name.ToLowerInvariant();
        if (this.NameContainsAny(lowerName, patterns))
        {
            return true;
        }
        if (this.NameContainsAny(lowerName, patterns2))
        {
            return true;
        }
        return false;
    }

    public IEnumerator RetargetAndTransfer(ClothInstance cloth, Animator avatarAnimator, IReadOnlyList<SkinnedMeshRenderer> bodyMeshes, IReadOnlyList<Mesh> bodyBakedMeshes, int referenceBodyIndex = 0, Settings settings = null, Dictionary<Transform, ClothBoneType> boneTypeMap = null, Dictionary<Transform, Transform> bodyToClothesMap = null)
    {
        if (cloth == null || cloth.smr == null || cloth.editableMesh == null)
        {
            throw new AutoMorpherException("Cloth Instance is Invalid", "[WeightTransferUtil] RetargetAndTransfer\n - cloth is null or cloth.smr is null or cloth.editableMesh is null");
        }
        if (cloth.worldVertices == null || cloth.worldVertices.Length == 0)
        {
            throw new AutoMorpherException("Cloth World Vertices are Missing", "[WeightTransferUtil] RetargetAndTransfer\n - cloth.worldVertices is null or empty");
        }
        if (cloth.bakedMesh == null)
        {
            throw new AutoMorpherException("Cloth Baked Mesh is Missing", "[WeightTransferUtil] RetargetAndTransfer\n - cloth.bakedMesh is null");
        }
        if (avatarAnimator == null || !avatarAnimator.isHuman)
        {
            throw new AutoMorpherException("Avatar Animator is Invalid", "[WeightTransferUtil] RetargetAndTransfer\n - avatarAnimator is null or not a Humanoid");
        }
        if (bodyMeshes == null || bodyMeshes.Count == 0)
        {
            throw new AutoMorpherException("Body Meshes are Missing", "[WeightTransferUtil] RetargetAndTransfer\n - bodyMeshes is null or empty");
        }
        if (referenceBodyIndex < 0 || referenceBodyIndex >= bodyMeshes.Count)
        {
            referenceBodyIndex = 0;
        }
        SkinnedMeshRenderer bodyRefSmr = bodyMeshes[referenceBodyIndex];
        if (bodyRefSmr == null || bodyRefSmr.sharedMesh == null)
        {
            throw new AutoMorpherException("Reference Body Mesh is Invalid", "[WeightTransferUtil] RetargetAndTransfer\n - bodyRefSmr is null or bodyRefSmr.sharedMesh is null\n - referenceBodyIndex : " + referenceBodyIndex);
        }
        if (settings == null)
        {
            settings = new Settings();
        }
        Transform[] accessoryBones = null;
        float[,] accessoryWeights = null;
        BoneWeight[] prevBoneWeights = null;
        yield return null;
        Transform[] bones;
        Dictionary<Transform, int> accIndexByTransform;
        float tiny;
        if (boneTypeMap != null && cloth.smr != null && cloth.editableMesh != null)
        {
            bones = cloth.smr.bones;
            if (bones != null && bones.Length != 0)
            {
                HashSet<Transform> value = null;
                if (cloth.humanoidMatchedBones != null)
                {
                    cloth.humanoidMatchedBones.TryGetValue(HumanBodyBones.Head, out value);
                }
                HashSet<Transform> hashSet = new HashSet<Transform>();
                foreach (Transform transform in bones)
                {
                    if (!(transform == null) && boneTypeMap.TryGetValue(transform, out var value2) && value2 == ClothBoneType.Accessory)
                    {
                        hashSet.Add(transform);
                    }
                }
                if (value != null)
                {
                    foreach (Transform item in value)
                    {
                        if (!(item == null) && Array.IndexOf(bones, item) >= 0)
                        {
                            hashSet.Add(item);
                        }
                    }
                }
                List<Transform> list = new List<Transform>();
                accIndexByTransform = new Dictionary<Transform, int>();
                foreach (Transform transform2 in bones)
                {
                    if (!(transform2 == null) && hashSet.Contains(transform2) && !this.IsBreastOrButtBone(transform2) && !accIndexByTransform.ContainsKey(transform2))
                    {
                        accIndexByTransform[transform2] = list.Count;
                        list.Add(transform2);
                    }
                }
                if (list.Count > 0)
                {
                    accessoryBones = list.ToArray();
                    int vertexCount = cloth.editableMesh.vertexCount;
                    accessoryWeights = new float[vertexCount, accessoryBones.Length];
                    BoneWeight[] boneWeights = cloth.editableMesh.boneWeights;
                    if (boneWeights != null && boneWeights.Length == vertexCount)
                    {
                        tiny = 1E-06f;
                        for (int k = 0; k < vertexCount; k++)
                        {
                            BoneWeight boneWeight = boneWeights[k];
                            Acc(boneWeight.boneIndex0, boneWeight.weight0, k);
                            Acc(boneWeight.boneIndex1, boneWeight.weight1, k);
                            Acc(boneWeight.boneIndex2, boneWeight.weight2, k);
                            Acc(boneWeight.boneIndex3, boneWeight.weight3, k);
                        }
                    }
                }
            }
        }
        int bodyBoneCount = bodyRefSmr.bones.Length;
        yield return null;
        this.RetargetClothToAvatarSkeleton(cloth, avatarAnimator, bodyRefSmr, accessoryBones);
        this.TransferFromBodyToCloth(avatarAnimator, cloth, bodyMeshes, bodyBakedMeshes, settings, accessoryBones, accessoryWeights, bodyBoneCount, prevBoneWeights);
        if (settings.weightInClothes)
        {
            this.MoveBodyWeightsToClothesBones(cloth.smr, cloth.smr.sharedMesh, bodyToClothesMap, accessoryBones);
        }
        void Acc(int boneIndex, float w, int v)
        {
            if (!(w <= tiny) && boneIndex >= 0 && boneIndex < bones.Length)
            {
                Transform transform3 = bones[boneIndex];
                if (!(transform3 == null) && accIndexByTransform.TryGetValue(transform3, out var value3))
                {
                    accessoryWeights[v, value3] += w;
                }
            }
        }
    }

    private void RetargetClothToAvatarSkeleton(ClothInstance cloth, Animator avatarAnimator, SkinnedMeshRenderer bodyRefSmr, Transform[] accessoryBones = null)
    {
        SkinnedMeshRenderer smr = cloth.smr;
        Mesh sharedMesh = cloth.smr.sharedMesh;
        if (sharedMesh == null)
        {
            throw new AutoMorpherException("Cloth Mesh is Missing", "[WeightTransferUtil] RetargetClothToAvatarSkeleton\n - clothMesh is null");
        }
        Transform[] bones = smr.bones;
        Transform[] bones2 = bodyRefSmr.bones;
        if (bones == null || bones.Length == 0 || bones2 == null || bones2.Length == 0)
        {
            throw new AutoMorpherException("Cloth or Target Bones are Missing", "[WeightTransferUtil] RetargetClothToAvatarSkeleton\n - clothBones is null/empty or targetBones is null/empty\n - clothBones.Length : " + ((bones == null) ? (-1) : bones.Length) + "\n - targetBones.Length : " + ((bones2 == null) ? (-1) : bones2.Length));
        }
        Matrix4x4 localToWorldMatrix = smr.transform.localToWorldMatrix;
        if (accessoryBones == null || accessoryBones.Length == 0)
        {
            Dictionary<Transform, HumanBodyBones> dictionary = new Dictionary<Transform, HumanBodyBones>();
            foreach (KeyValuePair<HumanBodyBones, HashSet<Transform>> humanoidMatchedBone in cloth.humanoidMatchedBones)
            {
                HumanBodyBones key = humanoidMatchedBone.Key;
                HashSet<Transform> value = humanoidMatchedBone.Value;
                if (value == null)
                {
                    continue;
                }
                foreach (Transform item in value)
                {
                    if (!(item == null) && !dictionary.ContainsKey(item))
                    {
                        dictionary.Add(item, key);
                    }
                }
            }
            Dictionary<HumanBodyBones, int> dictionary2 = new Dictionary<HumanBodyBones, int>();
            Dictionary<Transform, int> dictionary3 = new Dictionary<Transform, int>();
            for (int i = 0; i < bones2.Length; i++)
            {
                Transform transform = bones2[i];
                if (transform != null && !dictionary3.ContainsKey(transform))
                {
                    dictionary3.Add(transform, i);
                }
            }
            foreach (HumanBodyBones value4 in Enum.GetValues(typeof(HumanBodyBones)))
            {
                if (value4 != HumanBodyBones.LastBone)
                {
                    Transform boneTransform = avatarAnimator.GetBoneTransform(value4);
                    if (!(boneTransform == null) && dictionary3.TryGetValue(boneTransform, out var value2))
                    {
                        dictionary2[value4] = value2;
                    }
                }
            }
            BoneWeight[] boneWeights = sharedMesh.boneWeights;
            if (boneWeights == null || boneWeights.Length == 0)
            {
                Debug.LogWarning("[WeightTransferUtil] clothMesh.boneWeights 가 비어 있음. 리타겟할 weight 가 없음.");
                return;
            }
            int vertexCount = sharedMesh.vertexCount;
            int num = bones2.Length;
            float[,] array = new float[vertexCount, num];
            for (int j = 0; j < vertexCount; j++)
            {
                BoneWeight boneWeight = boneWeights[j];
                this.AccumulateRetargetWeight(array, j, boneWeight.boneIndex0, boneWeight.weight0, bones, dictionary, dictionary2);
                this.AccumulateRetargetWeight(array, j, boneWeight.boneIndex1, boneWeight.weight1, bones, dictionary, dictionary2);
                this.AccumulateRetargetWeight(array, j, boneWeight.boneIndex2, boneWeight.weight2, bones, dictionary, dictionary2);
                this.AccumulateRetargetWeight(array, j, boneWeight.boneIndex3, boneWeight.weight3, bones, dictionary, dictionary2);
            }
            BoneWeight[] boneWeights2 = this.CollapseWeightsToBoneWeights(array, 4, 0.0001f);
            Matrix4x4[] array2 = new Matrix4x4[bones2.Length];
            for (int k = 0; k < bones2.Length; k++)
            {
                Transform transform2 = bones2[k];
                if (transform2 != null)
                {
                    array2[k] = transform2.worldToLocalMatrix * localToWorldMatrix;
                }
                else
                {
                    array2[k] = Matrix4x4.identity;
                }
            }
            sharedMesh.boneWeights = boneWeights2;
            sharedMesh.bindposes = array2;
            smr.rootBone = bodyRefSmr.rootBone;
            smr.bones = bones2;
            EditorUtility.SetDirty(sharedMesh);
            EditorUtility.SetDirty(smr);
            return;
        }
        Transform[] bones3 = bodyRefSmr.bones;
        if (bones3 == null || bones3.Length == 0)
        {
            throw new AutoMorpherException("Target Body Bones are Missing", "[WeightTransferUtil] RetargetClothToAvatarSkeleton\n - bodyRefSmr.bones is null or empty");
        }
        Transform[] bones4 = smr.bones;
        Matrix4x4[] bindposes = sharedMesh.bindposes;
        Dictionary<Transform, int> dictionary4 = new Dictionary<Transform, int>();
        if (bones4 != null)
        {
            for (int l = 0; l < bones4.Length; l++)
            {
                Transform transform3 = bones4[l];
                if (!(transform3 == null) && !dictionary4.ContainsKey(transform3))
                {
                    dictionary4.Add(transform3, l);
                }
            }
        }
        int num2 = bones3.Length;
        int num3 = accessoryBones.Length;
        Transform[] array3 = new Transform[num2 + num3];
        for (int m = 0; m < num2; m++)
        {
            array3[m] = bones3[m];
        }
        for (int n = 0; n < num3; n++)
        {
            array3[num2 + n] = accessoryBones[n];
        }
        Matrix4x4[] array4 = new Matrix4x4[array3.Length];
        for (int num4 = 0; num4 < num2; num4++)
        {
            Transform transform4 = bones3[num4];
            if (transform4 != null)
            {
                array4[num4] = transform4.worldToLocalMatrix * localToWorldMatrix;
            }
            else
            {
                array4[num4] = Matrix4x4.identity;
            }
        }
        for (int num5 = 0; num5 < num3; num5++)
        {
            Transform transform5 = accessoryBones[num5];
            Matrix4x4 matrix4x = Matrix4x4.identity;
            if (transform5 != null && dictionary4.TryGetValue(transform5, out var value3))
            {
                matrix4x = ((bindposes == null || value3 < 0 || value3 >= bindposes.Length) ? (transform5.worldToLocalMatrix * localToWorldMatrix) : bindposes[value3]);
            }
            array4[num2 + num5] = matrix4x;
        }
        sharedMesh.bindposes = array4;
        sharedMesh.boneWeights = new BoneWeight[sharedMesh.vertexCount];
        smr.rootBone = bodyRefSmr.rootBone;
        smr.bones = array3;
        EditorUtility.SetDirty(sharedMesh);
        EditorUtility.SetDirty(smr);
    }

    private void AccumulateRetargetWeight(float[,] W, int vertIndex, int clothBoneIndex, float weight, Transform[] clothBones, Dictionary<Transform, HumanBodyBones> clothBoneToHuman, Dictionary<HumanBodyBones, int> humanToTargetBoneIndex)
    {
        if (!(weight <= 0f) && clothBoneIndex >= 0 && clothBoneIndex < clothBones.Length)
        {
            Transform transform = clothBones[clothBoneIndex];
            if (!(transform == null) && clothBoneToHuman.TryGetValue(transform, out var value) && humanToTargetBoneIndex.TryGetValue(value, out var value2))
            {
                W[vertIndex, value2] += weight;
            }
        }
    }

    private void TransferFromBodyToCloth(Animator avatarAnimator, ClothInstance cloth, IReadOnlyList<SkinnedMeshRenderer> bodyMeshes, IReadOnlyList<Mesh> bodyBakedMeshes, Settings settings, Transform[] accessoryBones, float[,] accessoryWeights, int bodyBoneCount, BoneWeight[] prevBoneWeights = null)
    {
        Vector3[] worldVertices = cloth.worldVertices;
        if (worldVertices == null || worldVertices.Length == 0)
        {
            throw new AutoMorpherException("Cloth World Vertices are Missing", "[WeightTransferUtil] TransferFromBodyToCloth\n - cloth.worldVertices is null or empty");
        }
        int[] triangles = cloth.editableMesh.triangles;
        Vector3[] clothWorldNormals = this.GetClothWorldNormals(cloth);
        if (clothWorldNormals == null || clothWorldNormals.Length != worldVertices.Length)
        {
            throw new AutoMorpherException("Cloth World Normals Calculation Failed", "[WeightTransferUtil] TransferFromBodyToCloth\n - targetNormalsWorld is null or length mismatch\n - targetVertsWorld.Length : " + ((worldVertices == null) ? (-1) : worldVertices.Length) + "\n - targetNormalsWorld.Length : " + ((clothWorldNormals == null) ? (-1) : clothWorldNormals.Length));
        }
        Transform[] bones = cloth.smr.bones;
        int num = bones.Length;
        Dictionary<Transform, int> dictionary = new Dictionary<Transform, int>();
        for (int i = 0; i < bones.Length; i++)
        {
            Transform transform = bones[i];
            if (transform != null && !dictionary.ContainsKey(transform))
            {
                dictionary.Add(transform, i);
            }
        }
        int num2 = 0;
        int num3 = 0;
        foreach (SkinnedMeshRenderer bodyMesh in bodyMeshes)
        {
            if (!(bodyMesh == null) && !(bodyMesh.sharedMesh == null))
            {
                num2 += bodyMesh.sharedMesh.vertexCount;
                num3 += bodyMesh.sharedMesh.triangles.Length;
            }
        }
        if (num2 == 0 || num3 == 0)
        {
            throw new AutoMorpherException("Valid Body Mesh is Missing", "[WeightTransferUtil] TransferFromBodyToCloth\n - bodyMeshes has no valid mesh (totalVerts == 0 or totalTris == 0)");
        }
        Vector3[] array = new Vector3[num2];
        Vector3[] array2 = new Vector3[num2];
        int[] array3 = new int[num3];
        float[,] W = new float[num2, num];
        int num4 = 0;
        int num5 = 0;
        for (int j = 0; j < bodyMeshes.Count; j++)
        {
            SkinnedMeshRenderer skinnedMeshRenderer = bodyMeshes[j];
            if (skinnedMeshRenderer == null || skinnedMeshRenderer.sharedMesh == null)
            {
                continue;
            }
            Mesh sharedMesh = skinnedMeshRenderer.sharedMesh;
            Mesh mesh = bodyBakedMeshes[j];
            Vector3[] vertices = mesh.vertices;
            Vector3[] normals = mesh.normals;
            int[] triangles2 = mesh.triangles;
            int vertexCount = mesh.vertexCount;
            Vector3 lossyScale = skinnedMeshRenderer.transform.lossyScale;
            Vector3 vector = new Vector3(1f / Mathf.Max(lossyScale.x, 1E-08f), 1f / Mathf.Max(lossyScale.y, 1E-08f), 1f / Mathf.Max(lossyScale.z, 1E-08f));
            Matrix4x4 matrix4x = skinnedMeshRenderer.transform.localToWorldMatrix * Matrix4x4.Scale(vector);
            for (int k = 0; k < vertexCount; k++)
            {
                array[num4 + k] = matrix4x.MultiplyPoint3x4(vertices[k]);
                Vector3 normalized = matrix4x.MultiplyVector(normals[k]).normalized;
                array2[num4 + k] = normalized;
            }
            for (int l = 0; l < triangles2.Length; l++)
            {
                array3[num5 + l] = triangles2[l] + num4;
            }
            BoneWeight[] boneWeights = sharedMesh.boneWeights;
            Transform[] bones2 = skinnedMeshRenderer.bones;
            if (boneWeights != null && boneWeights.Length == vertexCount && bones2 != null)
            {
                for (int m = 0; m < vertexCount; m++)
                {
                    BoneWeight boneWeight = boneWeights[m];
                    this.AccumulateBodyWeight(ref W, num4 + m, boneWeight.boneIndex0, boneWeight.weight0, bones2, dictionary);
                    this.AccumulateBodyWeight(ref W, num4 + m, boneWeight.boneIndex1, boneWeight.weight1, bones2, dictionary);
                    this.AccumulateBodyWeight(ref W, num4 + m, boneWeight.boneIndex2, boneWeight.weight2, bones2, dictionary);
                    this.AccumulateBodyWeight(ref W, num4 + m, boneWeight.boneIndex3, boneWeight.weight3, bones2, dictionary);
                }
            }
            num4 += vertexCount;
            num5 += triangles2.Length;
        }
        List<int>[] array4 = cloth.vertexAdjacency;
        if (array4 == null || array4.Length != worldVertices.Length)
        {
            array4 = this.BuildAdjacencyFromTriangles(worldVertices.Length, triangles);
        }
        float[,] weights = this.FindMatchesClosestSurface(array, array3, array2, worldVertices, clothWorldNormals, W, settings.maxDistance * settings.maxDistance, settings.maxNormalAngleDeg, settings.allowFlippedNormal, out var matchedMask);
        weights = this.InpaintApprox(worldVertices, triangles, weights, matchedMask, array4, 20, 0.7f);
        if (settings.enableSmoothing && settings.smoothingIterations > 0 && settings.smoothingAlpha > 0f)
        {
            weights = this.SmoothWeightsApprox(worldVertices, weights, matchedMask, array4, settings.smoothingIterations, settings.smoothingAlpha, settings.maxDistance);
        }
        this.ApplyLegVertexConstraints(avatarAnimator, cloth, weights, bodyBoneCount, settings.tinyWeight);
        int vertexCount2 = cloth.editableMesh.vertexCount;
        num = cloth.smr.bones.Length;
        if (accessoryBones != null && accessoryBones.Length != 0 && accessoryWeights != null && bodyBoneCount > 0)
        {
            HashSet<Transform> value = null;
            if (cloth.humanoidMatchedBones != null)
            {
                cloth.humanoidMatchedBones.TryGetValue(HumanBodyBones.Head, out value);
            }
            Transform transform2 = null;
            if (avatarAnimator != null)
            {
                transform2 = avatarAnimator.GetBoneTransform(HumanBodyBones.Head);
            }
            if (value != null && transform2 != null)
            {
                int num6 = Array.IndexOf(cloth.smr.bones, transform2);
                if (num6 >= 0 && num6 < bodyBoneCount)
                {
                    foreach (Transform item in value)
                    {
                        if (item == null)
                        {
                            continue;
                        }
                        int num7 = Array.IndexOf(accessoryBones, item);
                        if (num7 < 0)
                        {
                            continue;
                        }
                        for (int n = 0; n < vertexCount2; n++)
                        {
                            float num8 = accessoryWeights[n, num7];
                            if (!(num8 <= settings.tinyWeight))
                            {
                                weights[n, num6] += num8;
                                accessoryWeights[n, num7] = 0f;
                            }
                        }
                    }
                }
            }
        }
        BoneWeight[] array5;
        if (accessoryBones != null && accessoryBones.Length != 0 && accessoryWeights != null && bodyBoneCount > 0 && bodyBoneCount + accessoryBones.Length == num)
        {
            array5 = this.MergeBodyAndAccessoryWeights(weights, accessoryWeights, bodyBoneCount, settings.tinyWeight, settings.enforceFourBoneLimit);
        }
        else
        {
            if (settings.enforceFourBoneLimit)
            {
                this.EnforceMaxBonesPerVertex(weights, 4, settings.tinyWeight);
            }
            else
            {
                this.ZeroSmallWeights(weights, settings.tinyWeight);
            }
            this.NormalizeWeightsPerVertex(weights, settings.tinyWeight);
            array5 = this.CollapseWeightsToBoneWeights(weights, 4, settings.tinyWeight);
        }
        Mesh sharedMesh2 = cloth.smr.sharedMesh;
        if (sharedMesh2.vertexCount != array5.Length)
        {
            throw new AutoMorpherException("Vertex Count and Weight Length Mismatch", "[WeightTransferUtil] TransferFromBodyToCloth\n - mesh.vertexCount : " + sharedMesh2.vertexCount + "\n - finalWeights.Length : " + array5.Length);
        }
        this.EnsureNonZeroAndNormalizeFinalWeights(cloth, array5, array4, settings.tinyWeight);
        this.ApplyEquivalentVertexWeights(cloth, array5);
        this.NormalizeAllBoneWeightsInPlace(array5, settings.tinyWeight);
        sharedMesh2.boneWeights = array5;
        cloth.smr.sharedMesh = sharedMesh2;
        if (!this.TryApplyBoneWeightsWithValidation(sharedMesh2, array5, 1E-10f, out var zeroWeightVertexCount))
        {
            Debug.LogWarning($"[WeightValidation] Zero-weight vertices detected: {zeroWeightVertexCount}");
        }
    }

    public void MoveBodyWeightsToClothesBones(SkinnedMeshRenderer clothesSmr, Mesh clothesMesh, Dictionary<Transform, Transform> bodyToClothesMap, Transform[] accessoryBones)
    {
        if (clothesSmr == null)
        {
            throw new AutoMorpherException("Clothes SMR is Missing", "[WeightTransferUtil] MoveBodyWeightsToClothesBones\n - clothesSmr is null");
        }
        if (clothesMesh == null)
        {
            throw new AutoMorpherException("Clothes Mesh is Missing", "[WeightTransferUtil] MoveBodyWeightsToClothesBones\n - clothesMesh is null");
        }
        if (bodyToClothesMap == null)
        {
            throw new AutoMorpherException("Body To Clothes Map is Missing", "[WeightTransferUtil] MoveBodyWeightsToClothesBones\n - bodyToClothesMap is null");
        }
        Transform clothesFallbackRoot = ((clothesSmr.rootBone != null) ? clothesSmr.rootBone : clothesSmr.transform);
        HashSet<Transform> hashSet = null;
        if (accessoryBones != null && accessoryBones.Length != 0)
        {
            hashSet = new HashSet<Transform>(accessoryBones);
        }
        Transform[] bones = clothesSmr.bones;
        if (bones == null || bones.Length == 0)
        {
            throw new AutoMorpherException("Clothes Bones are Missing", "[WeightTransferUtil] MoveBodyWeightsToClothesBones\n - clothesSmr.bones is null or empty");
        }
        Transform[] array = new Transform[bones.Length];
        for (int i = 0; i < bones.Length; i++)
        {
            Transform transform = bones[i];
            if (transform == null)
            {
                array[i] = clothesFallbackRoot;
                continue;
            }
            if (hashSet != null && hashSet.Contains(transform))
            {
                array[i] = transform;
                continue;
            }
            Transform transform2 = EnsureClothesBoneExists(transform);
            array[i] = ((transform2 != null) ? transform2 : clothesFallbackRoot);
        }
        clothesSmr.bones = array;
        if (clothesSmr.rootBone != null && (hashSet == null || !hashSet.Contains(clothesSmr.rootBone)))
        {
            Transform transform3 = EnsureClothesBoneExists(clothesSmr.rootBone);
            if (transform3 != null)
            {
                clothesSmr.rootBone = transform3;
            }
        }
        Transform transform4 = ((clothesSmr.rootBone != null) ? clothesSmr.rootBone : clothesSmr.transform);
        Matrix4x4[] array2 = new Matrix4x4[array.Length];
        for (int j = 0; j < array.Length; j++)
        {
            Transform transform5 = array[j];
            if (transform5 == null)
            {
                throw new AutoMorpherException("Remapped Bone is Null", "[WeightTransferUtil] MoveBodyWeightsToClothesBones\n - remappedBones contains null bone after fallback");
            }
            array2[j] = transform5.worldToLocalMatrix * transform4.localToWorldMatrix;
        }
        clothesMesh.bindposes = array2;
        Transform EnsureClothesBoneExists(Transform bodyBone)
        {
            if (bodyBone == null)
            {
                return null;
            }
            if (bodyToClothesMap.TryGetValue(bodyBone, out var value) && value != null)
            {
                return value;
            }
            Transform transform6 = null;
            if (bodyBone.parent != null)
            {
                transform6 = EnsureClothesBoneExists(bodyBone.parent);
            }
            if (transform6 == null)
            {
                transform6 = clothesFallbackRoot;
            }
            Transform transform7 = new GameObject(bodyBone.name).transform;
            transform7.SetParent(transform6, worldPositionStays: false);
            transform7.localPosition = bodyBone.localPosition;
            transform7.localRotation = bodyBone.localRotation;
            transform7.localScale = bodyBone.localScale;
            bodyToClothesMap[bodyBone] = transform7;
            return transform7;
        }
    }

    private bool TryApplyBoneWeightsWithValidation(Mesh mesh, BoneWeight[] weights, float tiny, out int zeroWeightVertexCount)
    {
        bool result = true;
        zeroWeightVertexCount = 0;
        int num = weights.Length;
        NativeArray<byte> bonesPerVertex = new NativeArray<byte>(num, Allocator.Temp);
        List<BoneWeight1> list = new List<BoneWeight1>(num * 4);
        NativeArray<BoneWeight1> weights2 = default(NativeArray<BoneWeight1>);
        try
        {
            for (int i = 0; i < num; i++)
            {
                BoneWeight boneWeight = weights[i];
                int count = 0;
                this.AddIfValid(list, boneWeight.boneIndex0, boneWeight.weight0, tiny, ref count);
                this.AddIfValid(list, boneWeight.boneIndex1, boneWeight.weight1, tiny, ref count);
                this.AddIfValid(list, boneWeight.boneIndex2, boneWeight.weight2, tiny, ref count);
                this.AddIfValid(list, boneWeight.boneIndex3, boneWeight.weight3, tiny, ref count);
                if (count == 0)
                {
                    zeroWeightVertexCount++;
                }
                bonesPerVertex[i] = (byte)count;
            }
            if (zeroWeightVertexCount > 0)
            {
                return false;
            }
            weights2 = new NativeArray<BoneWeight1>(list.Count, Allocator.Temp);
            for (int j = 0; j < list.Count; j++)
            {
                weights2[j] = list[j];
            }
            mesh.SetBoneWeights(bonesPerVertex, weights2);
        }
        catch (Exception ex)
        {
            Debug.LogWarning("[SetBoneWeights] Failed: " + ex.Message);
            result = false;
        }
        finally
        {
            if (weights2.IsCreated)
            {
                weights2.Dispose();
            }
            if (bonesPerVertex.IsCreated)
            {
                bonesPerVertex.Dispose();
            }
        }
        return result;
    }

    private void AddIfValid(List<BoneWeight1> list, int boneIndex, float weight, float tiny, ref int count)
    {
        if (boneIndex >= 0 && !(weight <= tiny))
        {
            list.Add(new BoneWeight1
            {
                boneIndex = boneIndex,
                weight = weight
            });
            count++;
        }
    }

    private void ApplyEquivalentVertexWeights(ClothInstance cloth, BoneWeight[] finalWeights)
    {
        if (cloth == null || cloth.equivalentVertices == null || cloth.equivalentVertices.Count == 0 || finalWeights == null || finalWeights.Length == 0)
        {
            return;
        }
        List<List<int>> equivalentVertices = cloth.equivalentVertices;
        int num = finalWeights.Length;
        bool[] array = new bool[num];
        foreach (List<int> item in equivalentVertices)
        {
            if (item == null || item.Count == 0)
            {
                continue;
            }
            List<int> list = null;
            bool flag = false;
            for (int i = 0; i < item.Count; i++)
            {
                int num2 = item[i];
                if (num2 >= 0 && num2 < num)
                {
                    if (list == null)
                    {
                        list = new List<int>();
                    }
                    list.Add(num2);
                    if (!array[num2])
                    {
                        flag = true;
                    }
                }
            }
            if (list == null || !flag)
            {
                continue;
            }
            Dictionary<int, float> weightMap = new Dictionary<int, float>();
            foreach (int item2 in list)
            {
                BoneWeight boneWeight = finalWeights[item2];
                Accumulate(boneWeight.boneIndex0, boneWeight.weight0);
                Accumulate(boneWeight.boneIndex1, boneWeight.weight1);
                Accumulate(boneWeight.boneIndex2, boneWeight.weight2);
                Accumulate(boneWeight.boneIndex3, boneWeight.weight3);
            }
            if (weightMap.Count == 0)
            {
                continue;
            }
            List<KeyValuePair<int, float>> list2 = weightMap.OrderByDescending((KeyValuePair<int, float> kv) => kv.Value).Take(4).ToList();
            float num3 = 0f;
            for (int num4 = 0; num4 < Math.Min(4, list2.Count); num4++)
            {
                num3 += list2[num4].Value;
            }
            if (num3 <= 1E-08f)
            {
                continue;
            }
            BoneWeight boneWeight2 = default(BoneWeight);
            for (int num5 = 0; num5 < Math.Min(4, list2.Count); num5++)
            {
                int key = list2[num5].Key;
                float num6 = list2[num5].Value / num3;
                switch (num5)
                {
                    case 0:
                        boneWeight2.boneIndex0 = key;
                        boneWeight2.weight0 = num6;
                        break;
                    case 1:
                        boneWeight2.boneIndex1 = key;
                        boneWeight2.weight1 = num6;
                        break;
                    case 2:
                        boneWeight2.boneIndex2 = key;
                        boneWeight2.weight2 = num6;
                        break;
                    case 3:
                        boneWeight2.boneIndex3 = key;
                        boneWeight2.weight3 = num6;
                        break;
                }
            }
            foreach (int item3 in list)
            {
                finalWeights[item3] = boneWeight2;
                array[item3] = true;
            }
            void Accumulate(int boneIndex, float w)
            {
                if (!(w <= 0f) && boneIndex >= 0)
                {
                    if (weightMap.TryGetValue(boneIndex, out var value))
                    {
                        weightMap[boneIndex] = value + w;
                    }
                    else
                    {
                        weightMap[boneIndex] = w;
                    }
                }
            }
        }
    }

    private void ApplyLegVertexConstraints(Animator avatarAnimator, ClothInstance cloth, float[,] weights, int bodyBoneCount, float tinyWeight)
    {
        if (cloth == null || cloth.smr == null)
        {
            return;
        }
        bool[] isLeftLegVertex = cloth.isLeftLegVertex;
        bool[] isRightLegVertex = cloth.isRightLegVertex;
        if (isLeftLegVertex == null && isRightLegVertex == null)
        {
            return;
        }
        Transform[] bones = cloth.smr.bones;
        if (bones == null)
        {
            return;
        }
        int length = weights.GetLength(0);
        int length2 = weights.GetLength(1);
        Transform boneTransform = avatarAnimator.GetBoneTransform(HumanBodyBones.LeftUpperLeg);
        Transform boneTransform2 = avatarAnimator.GetBoneTransform(HumanBodyBones.RightUpperLeg);
        bool[] array = new bool[length2];
        bool[] array2 = new bool[length2];
        int num = Mathf.Min(bodyBoneCount, bones.Length);
        for (int i = 0; i < num; i++)
        {
            Transform transform = bones[i];
            if (!(transform == null))
            {
                if (transform.IsChildOf(boneTransform))
                {
                    array[i] = true;
                }
                else if (transform.IsChildOf(boneTransform2))
                {
                    array2[i] = true;
                }
            }
        }
        for (int j = 0; j < length; j++)
        {
            bool flag = isLeftLegVertex != null && j < isLeftLegVertex.Length && isLeftLegVertex[j];
            bool flag2 = isRightLegVertex != null && j < isRightLegVertex.Length && isRightLegVertex[j];
            if (!flag && !flag2)
            {
                continue;
            }
            for (int k = 0; k < length2; k++)
            {
                if (weights[j, k] <= tinyWeight)
                {
                    continue;
                }
                if (flag)
                {
                    if (array2[k])
                    {
                        weights[j, k] = 0f;
                    }
                }
                else if (flag2 && array[k])
                {
                    weights[j, k] = 0f;
                }
            }
        }
    }

    private Vector3[] GetClothWorldNormals(ClothInstance cloth)
    {
        if (cloth.worldVertices == null)
        {
            return null;
        }
        int num = cloth.worldVertices.Length;
        if (cloth.bakedMesh == null || cloth.bakedMesh.vertexCount != num)
        {
            throw new AutoMorpherException("Cloth Baked Mesh is Invalid", "[WeightTransferUtil] GetClothWorldNormals\n - bakedMesh is null or vertexCount mismatch\n - bakedMesh.vertexCount : " + ((cloth.bakedMesh == null) ? (-1) : cloth.bakedMesh.vertexCount) + "\n - worldVertices.Length : " + num);
        }
        Vector3[] array = new Vector3[num];
        Mesh bakedMesh = cloth.bakedMesh;
        Vector3[] normals = bakedMesh.normals;
        if (normals == null || normals.Length != num)
        {
            bakedMesh.RecalculateNormals();
            normals = bakedMesh.normals;
        }
        for (int i = 0; i < num; i++)
        {
            Vector3 normalized = cloth.worldNoScale.MultiplyVector(normals[i]).normalized;
            array[i] = normalized;
        }
        return array;
    }

    private void AccumulateBodyWeight(ref float[,] W, int vIndex, int localBoneIndex, float weight, Transform[] bodyBones, Dictionary<Transform, int> targetBoneIndexByTransform)
    {
        if (!(weight <= 0f) && bodyBones != null && localBoneIndex >= 0 && localBoneIndex < bodyBones.Length)
        {
            Transform transform = bodyBones[localBoneIndex];
            if (!(transform == null) && targetBoneIndexByTransform.TryGetValue(transform, out var value))
            {
                W[vIndex, value] += weight;
            }
        }
    }

    private List<int>[] BuildAdjacencyFromTriangles(int vertexCount, int[] triangles)
    {
        List<int>[] array = new List<int>[vertexCount];
        for (int i = 0; i < vertexCount; i++)
        {
            array[i] = new List<int>();
        }
        int num = triangles.Length / 3;
        for (int j = 0; j < num; j++)
        {
            int num2 = triangles[j * 3];
            int num3 = triangles[j * 3 + 1];
            int num4 = triangles[j * 3 + 2];
            this.AddEdge(array, num2, num3);
            this.AddEdge(array, num3, num4);
            this.AddEdge(array, num4, num2);
        }
        return array;
    }

    private void AddEdge(List<int>[] adj, int a, int b)
    {
        if (!adj[a].Contains(b))
        {
            adj[a].Add(b);
        }
        if (!adj[b].Contains(a))
        {
            adj[b].Add(a);
        }
    }

    private float[,] FindMatchesClosestSurface(Vector3[] sourceVerts, int[] sourceTris, Vector3[] sourceNormals, Vector3[] targetVerts, Vector3[] targetNormals, float[,] sourceWeights, float distanceThresholdSqr, float angleThresholdDeg, bool flipVertexNormal, out bool[] matchedMask)
    {
        int num = targetVerts.Length;
        int length = sourceWeights.GetLength(1);
        float[,] array = new float[num, length];
        matchedMask = new bool[num];
        int num2 = sourceTris.Length / 3;
        float num3 = angleThresholdDeg * (MathF.PI / 180f);
        for (int i = 0; i < num; i++)
        {
            Vector3 vector = targetVerts[i];
            float num4 = float.PositiveInfinity;
            int num5 = -1;
            Vector3 vector2 = Vector3.zero;
            for (int j = 0; j < num2; j++)
            {
                int num6 = sourceTris[j * 3];
                int num7 = sourceTris[j * 3 + 1];
                int num8 = sourceTris[j * 3 + 2];
                Vector3 a = sourceVerts[num6];
                Vector3 b = sourceVerts[num7];
                Vector3 c = sourceVerts[num8];
                Vector3 bary;
                Vector3 vector3 = this.ClosestPointOnTriangle(vector, a, b, c, out bary);
                float sqrMagnitude = (vector - vector3).sqrMagnitude;
                if (sqrMagnitude < num4)
                {
                    num4 = sqrMagnitude;
                    num5 = j;
                    vector2 = bary;
                }
            }
            if (num5 < 0)
            {
                matchedMask[i] = false;
                continue;
            }
            bool num9 = num4 <= distanceThresholdSqr;
            int num10 = sourceTris[num5 * 3];
            int num11 = sourceTris[num5 * 3 + 1];
            int num12 = sourceTris[num5 * 3 + 2];
            Vector3 vector4 = sourceNormals[num10];
            Vector3 vector5 = sourceNormals[num11];
            Vector3 vector6 = sourceNormals[num12];
            Vector3 normalized = (vector4 * vector2.x + vector5 * vector2.y + vector6 * vector2.z).normalized;
            Vector3 normalized2 = targetNormals[i].normalized;
            float num13 = Mathf.Acos(Mathf.Clamp(Vector3.Dot(normalized, normalized2), -1f, 1f));
            bool flag = num13 <= num3;
            if (flipVertexNormal && MathF.PI - num13 <= num3)
            {
                flag = true;
            }
            bool flag2 = num9 && flag;
            matchedMask[i] = flag2;
            for (int k = 0; k < length; k++)
            {
                float num14 = sourceWeights[num10, k];
                float num15 = sourceWeights[num11, k];
                float num16 = sourceWeights[num12, k];
                array[i, k] = num14 * vector2.x + num15 * vector2.y + num16 * vector2.z;
            }
        }
        return array;
    }

    private Vector3 ClosestPointOnTriangle(Vector3 p, Vector3 a, Vector3 b, Vector3 c, out Vector3 bary)
    {
        Vector3 vector = b - a;
        Vector3 vector2 = c - a;
        Vector3 rhs = p - a;
        float num = Vector3.Dot(vector, rhs);
        float num2 = Vector3.Dot(vector2, rhs);
        if (num <= 0f && num2 <= 0f)
        {
            bary = new Vector3(1f, 0f, 0f);
            return a;
        }
        Vector3 rhs2 = p - b;
        float num3 = Vector3.Dot(vector, rhs2);
        float num4 = Vector3.Dot(vector2, rhs2);
        if (num3 >= 0f && num4 <= num3)
        {
            bary = new Vector3(0f, 1f, 0f);
            return b;
        }
        float num5 = num * num4 - num3 * num2;
        if (num5 <= 0f && num >= 0f && num3 <= 0f)
        {
            float num6 = num / (num - num3);
            bary = new Vector3(1f - num6, num6, 0f);
            return a + vector * num6;
        }
        Vector3 rhs3 = p - c;
        float num7 = Vector3.Dot(vector, rhs3);
        float num8 = Vector3.Dot(vector2, rhs3);
        if (num8 >= 0f && num7 <= num8)
        {
            bary = new Vector3(0f, 0f, 1f);
            return c;
        }
        float num9 = num7 * num2 - num * num8;
        if (num9 <= 0f && num2 >= 0f && num8 <= 0f)
        {
            float num10 = num2 / (num2 - num8);
            bary = new Vector3(1f - num10, 0f, num10);
            return a + vector2 * num10;
        }
        float num11 = num3 * num8 - num7 * num4;
        if (num11 <= 0f && num4 - num3 >= 0f && num7 - num8 >= 0f)
        {
            float num12 = (num4 - num3) / (num4 - num3 + (num7 - num8));
            bary = new Vector3(0f, 1f - num12, num12);
            return b + (c - b) * num12;
        }
        float num13 = 1f / (num11 + num9 + num5);
        float num14 = num9 * num13;
        float num15 = num5 * num13;
        float num16 = 1f - num14 - num15;
        bary = new Vector3(num16, num14, num15);
        return a * num16 + b * num14 + c * num15;
    }

    private void ZeroSmallWeights(float[,] weights, float tiny)
    {
        int length = weights.GetLength(0);
        int length2 = weights.GetLength(1);
        for (int i = 0; i < length; i++)
        {
            for (int j = 0; j < length2; j++)
            {
                if (weights[i, j] <= tiny)
                {
                    weights[i, j] = 0f;
                }
            }
        }
    }

    private void EnforceMaxBonesPerVertex(float[,] weights, int maxBonesPerVertex, float tinyWeight)
    {
        int length = weights.GetLength(0);
        int length2 = weights.GetLength(1);
        for (int i = 0; i < length; i++)
        {
            List<(int, float)> list = new List<(int, float)>();
            for (int j = 0; j < length2; j++)
            {
                float num = weights[i, j];
                if (num > tinyWeight)
                {
                    list.Add((j, num));
                }
            }
            list.Sort(((int bone, float w) a, (int bone, float w) b) => b.w.CompareTo(a.w));
            HashSet<int> hashSet = new HashSet<int>();
            int num2 = Mathf.Min(maxBonesPerVertex, list.Count);
            for (int num3 = 0; num3 < num2; num3++)
            {
                hashSet.Add(list[num3].Item1);
            }
            for (int num4 = 0; num4 < length2; num4++)
            {
                if (!hashSet.Contains(num4) || weights[i, num4] <= tinyWeight)
                {
                    weights[i, num4] = 0f;
                }
            }
        }
    }

    private void NormalizeWeightsPerVertex(float[,] weights, float tiny)
    {
        int length = weights.GetLength(0);
        int length2 = weights.GetLength(1);
        for (int i = 0; i < length; i++)
        {
            float num = 0f;
            for (int j = 0; j < length2; j++)
            {
                num += weights[i, j];
            }
            if (!(num < tiny))
            {
                float num2 = 1f / num;
                for (int k = 0; k < length2; k++)
                {
                    weights[i, k] *= num2;
                }
            }
        }
    }

    private BoneWeight[] CollapseWeightsToBoneWeights(float[,] weights, int maxBonesPerVert, float tiny)
    {
        int length = weights.GetLength(0);
        int length2 = weights.GetLength(1);
        BoneWeight[] array = new BoneWeight[length];
        for (int i = 0; i < length; i++)
        {
            List<(int, float)> list = new List<(int, float)>();
            for (int j = 0; j < length2; j++)
            {
                float num = weights[i, j];
                if (num > tiny)
                {
                    list.Add((j, num));
                }
            }
            if (list.Count == 0)
            {
                array[i] = default(BoneWeight);
                continue;
            }
            list.Sort(((int idx, float w) a, (int idx, float w) b) => b.w.CompareTo(a.w));
            int num2 = Mathf.Min(maxBonesPerVert, list.Count);
            float num3 = 0f;
            for (int num4 = 0; num4 < num2; num4++)
            {
                num3 += list[num4].Item2;
            }
            if (num3 < tiny)
            {
                num3 = 1f;
            }
            float num5 = 1f / num3;
            BoneWeight boneWeight = default(BoneWeight);
            for (int num6 = 0; num6 < num2; num6++)
            {
                int item = list[num6].Item1;
                float num7 = list[num6].Item2 * num5;
                switch (num6)
                {
                    case 0:
                        boneWeight.boneIndex0 = item;
                        boneWeight.weight0 = num7;
                        break;
                    case 1:
                        boneWeight.boneIndex1 = item;
                        boneWeight.weight1 = num7;
                        break;
                    case 2:
                        boneWeight.boneIndex2 = item;
                        boneWeight.weight2 = num7;
                        break;
                    case 3:
                        boneWeight.boneIndex3 = item;
                        boneWeight.weight3 = num7;
                        break;
                }
            }
            array[i] = boneWeight;
        }
        return array;
    }

    private float[,] InpaintApprox(Vector3[] verts, int[] triangles, float[,] weights, bool[] matched, List<int>[] adjacency, int iterations, float alpha)
    {
        int num = verts.Length;
        int length = weights.GetLength(1);
        float[,] array = (float[,])weights.Clone();
        float[,] array2 = new float[num, length];
        for (int i = 0; i < iterations; i++)
        {
            for (int j = 0; j < num; j++)
            {
                if (matched[j])
                {
                    for (int k = 0; k < length; k++)
                    {
                        array2[j, k] = array[j, k];
                    }
                    continue;
                }
                List<int> list = adjacency[j];
                if (list == null || list.Count == 0)
                {
                    for (int l = 0; l < length; l++)
                    {
                        array2[j, l] = array[j, l];
                    }
                    continue;
                }
                for (int m = 0; m < length; m++)
                {
                    float num2 = 0f;
                    for (int n = 0; n < list.Count; n++)
                    {
                        int num3 = list[n];
                        num2 += array[num3, m];
                    }
                    float b = num2 / (float)list.Count;
                    array2[j, m] = Mathf.Lerp(array[j, m], b, alpha);
                }
            }
            float[,] array3 = array2;
            float[,] array4 = array;
            array = array3;
            array2 = array4;
        }
        return array;
    }

    private float[,] SmoothWeightsApprox(Vector3[] verts, float[,] weights, bool[] matched, List<int>[] adjacency, int numSmoothIterSteps, float smoothAlpha, float distanceThreshold)
    {
        int num = verts.Length;
        int length = weights.GetLength(1);
        bool[] array = new bool[num];
        for (int i = 0; i < num; i++)
        {
            if (!matched[i])
            {
                this.FloodFillWithinDistance(verts, adjacency, i, distanceThreshold, array);
            }
        }
        float[,] array2 = (float[,])weights.Clone();
        float[,] array3 = new float[num, length];
        for (int j = 0; j < numSmoothIterSteps; j++)
        {
            for (int k = 0; k < num; k++)
            {
                if (!array[k])
                {
                    for (int l = 0; l < length; l++)
                    {
                        array3[k, l] = array2[k, l];
                    }
                    continue;
                }
                List<int> list = adjacency[k];
                if (list == null || list.Count == 0)
                {
                    for (int m = 0; m < length; m++)
                    {
                        array3[k, m] = array2[k, m];
                    }
                    continue;
                }
                for (int n = 0; n < length; n++)
                {
                    float num2 = 0f;
                    for (int num3 = 0; num3 < list.Count; num3++)
                    {
                        int num4 = list[num3];
                        num2 += array2[num4, n];
                    }
                    float b = num2 / (float)list.Count;
                    array3[k, n] = Mathf.Lerp(array2[k, n], b, smoothAlpha);
                }
            }
            float[,] array4 = array3;
            float[,] array5 = array2;
            array2 = array4;
            array3 = array5;
        }
        return array2;
    }

    private void FloodFillWithinDistance(Vector3[] verts, List<int>[] adjacency, int startIndex, float maxDistance, bool[] visited)
    {
        Queue<int> queue = new Queue<int>();
        queue.Enqueue(startIndex);
        visited[startIndex] = true;
        while (queue.Count > 0)
        {
            int num = queue.Dequeue();
            foreach (int item in adjacency[num])
            {
                if (!visited[item] && (verts[startIndex] - verts[item]).magnitude < maxDistance)
                {
                    visited[item] = true;
                    queue.Enqueue(item);
                }
            }
        }
    }

    private BoneWeight[] MergeBodyAndAccessoryWeights(float[,] bodyWeights, float[,] accessoryWeights, int bodyBoneCount, float tiny, bool enforceFour)
    {
        int length = bodyWeights.GetLength(0);
        bodyWeights.GetLength(1);
        int length2 = accessoryWeights.GetLength(1);
        BoneWeight[] array = new BoneWeight[length];
        for (int i = 0; i < length; i++)
        {
            List<(int, float)> list = new List<(int, float)>();
            float num = 0f;
            for (int j = 0; j < length2; j++)
            {
                float num2 = accessoryWeights[i, j];
                if (num2 > tiny)
                {
                    int item = bodyBoneCount + j;
                    list.Add((item, num2));
                    num += num2;
                }
            }
            List<(int, float)> list2 = new List<(int, float)>();
            float num3 = 0f;
            for (int k = 0; k < bodyBoneCount; k++)
            {
                float num4 = bodyWeights[i, k];
                if (num4 > tiny)
                {
                    list2.Add((k, num4));
                    num3 += num4;
                }
            }
            if (num3 > tiny && num < 1f - tiny)
            {
                float num5 = Mathf.Max(0f, 1f - num);
                float num6 = num5 / num3;
                for (int l = 0; l < list2.Count; l++)
                {
                    list2[l] = (list2[l].Item1, list2[l].Item2 * num6);
                }
                num3 = num5;
            }
            else
            {
                for (int m = 0; m < list2.Count; m++)
                {
                    list2[m] = (list2[m].Item1, 0f);
                }
                num3 = 0f;
            }
            List<(int, float)> list3 = new List<(int, float)>();
            list3.AddRange(list);
            list3.AddRange(list2);
            list3.RemoveAll(((int bone, float w) t) => t.w <= tiny);
            if (list3.Count == 0)
            {
                array[i] = default(BoneWeight);
                continue;
            }
            if (enforceFour && list3.Count > 4)
            {
                list3.Sort(delegate ((int bone, float w) a, (int bone, float w) b)
                {
                    bool flag = a.bone >= bodyBoneCount;
                    bool flag2 = b.bone >= bodyBoneCount;
                    return (flag != flag2) ? ((!flag) ? 1 : (-1)) : b.w.CompareTo(a.w);
                });
                list3 = list3.GetRange(0, 4);
            }
            float num7 = 0f;
            foreach (var item3 in list3)
            {
                num7 += item3.Item2;
            }
            if (num7 < tiny)
            {
                num7 = 1f;
            }
            float num8 = 1f / num7;
            BoneWeight boneWeight = default(BoneWeight);
            for (int num9 = 0; num9 < list3.Count && num9 < 4; num9++)
            {
                int item2 = list3[num9].Item1;
                float num10 = list3[num9].Item2 * num8;
                switch (num9)
                {
                    case 0:
                        boneWeight.boneIndex0 = item2;
                        boneWeight.weight0 = num10;
                        break;
                    case 1:
                        boneWeight.boneIndex1 = item2;
                        boneWeight.weight1 = num10;
                        break;
                    case 2:
                        boneWeight.boneIndex2 = item2;
                        boneWeight.weight2 = num10;
                        break;
                    case 3:
                        boneWeight.boneIndex3 = item2;
                        boneWeight.weight3 = num10;
                        break;
                }
            }
            array[i] = boneWeight;
        }
        return array;
    }

    private void EnsureNonZeroAndNormalizeFinalWeights(ClothInstance cloth, BoneWeight[] weights, List<int>[] adjacency, float tiny)
    {
        if (cloth != null && weights != null && weights.Length != 0)
        {
            this.PreNormalizeBoneWeightsInPlace(weights, tiny);
            this.FillZeroWeightsFromNeighbors(weights, adjacency, tiny);
            this.NormalizeAllBoneWeightsInPlace(weights, tiny);
        }
    }

    private void PreNormalizeBoneWeightsInPlace(BoneWeight[] weights, float tiny)
    {
        for (int i = 0; i < weights.Length; i++)
        {
            BoneWeight boneWeight = weights[i];
            float num = this.Sum(in weights[i]);
            if (!(num < tiny))
            {
                float num2 = 1f / num;
                boneWeight.weight0 *= num2;
                boneWeight.weight1 *= num2;
                boneWeight.weight2 *= num2;
                boneWeight.weight3 *= num2;
                weights[i] = boneWeight;
            }
        }
    }

    private void FillZeroWeightsFromNeighbors(BoneWeight[] weights, List<int>[] adjacency, float tiny)
    {
        int num = weights.Length;
        for (int i = 0; i < num; i++)
        {
            if (!(this.Sum(in weights[i]) >= tiny))
            {
                BoneWeight bw = this.AverageFromNeighbors(i, weights, adjacency, tiny, 1);
                if (this.Sum(in bw) >= tiny)
                {
                    weights[i] = bw;
                }
            }
        }
        for (int j = 0; j < num; j++)
        {
            if (!(this.Sum(in weights[j]) >= tiny))
            {
                int num2 = this.FindNearestNonZeroVertexBfs(j, weights, adjacency, tiny, 8);
                if (num2 >= 0)
                {
                    weights[j] = weights[num2];
                    continue;
                }
                weights[j] = new BoneWeight
                {
                    boneIndex0 = 0,
                    weight0 = 1f
                };
            }
        }
    }

    private BoneWeight AverageFromNeighbors(int v, BoneWeight[] weights, List<int>[] adjacency, float tiny, int maxRing)
    {
        Dictionary<int, float> map = new Dictionary<int, float>();
        Queue<int> queue = new Queue<int>();
        Dictionary<int, int> dictionary = new Dictionary<int, int>();
        HashSet<int> hashSet = new HashSet<int>();
        queue.Enqueue(v);
        dictionary[v] = 0;
        hashSet.Add(v);
        while (queue.Count > 0)
        {
            int num = queue.Dequeue();
            int num2 = dictionary[num];
            if (num2 >= maxRing)
            {
                continue;
            }
            List<int> list = adjacency[num];
            if (list == null)
            {
                continue;
            }
            foreach (int item in list)
            {
                if (item >= 0 && item < weights.Length && hashSet.Add(item))
                {
                    dictionary[item] = num2 + 1;
                    queue.Enqueue(item);
                    BoneWeight bw = weights[item];
                    if (!(this.Sum(in bw) < tiny))
                    {
                        this.Acc(map, bw.boneIndex0, bw.weight0, tiny);
                        this.Acc(map, bw.boneIndex1, bw.weight1, tiny);
                        this.Acc(map, bw.boneIndex2, bw.weight2, tiny);
                        this.Acc(map, bw.boneIndex3, bw.weight3, tiny);
                    }
                }
            }
        }
        return this.BuildTop4Normalized(map, tiny);
    }

    private int FindNearestNonZeroVertexBfs(int start, BoneWeight[] weights, List<int>[] adjacency, float tiny, int maxDepth)
    {
        Queue<int> queue = new Queue<int>();
        Dictionary<int, int> dictionary = new Dictionary<int, int>();
        HashSet<int> hashSet = new HashSet<int>();
        queue.Enqueue(start);
        dictionary[start] = 0;
        hashSet.Add(start);
        while (queue.Count > 0)
        {
            int num = queue.Dequeue();
            int num2 = dictionary[num];
            if (num2 > 0 && this.Sum(in weights[num]) >= tiny)
            {
                return num;
            }
            if (num2 >= maxDepth)
            {
                continue;
            }
            List<int> list = adjacency[num];
            if (list == null)
            {
                continue;
            }
            foreach (int item in list)
            {
                if (item >= 0 && item < weights.Length && hashSet.Add(item))
                {
                    dictionary[item] = num2 + 1;
                    queue.Enqueue(item);
                }
            }
        }
        return -1;
    }

    private void NormalizeAllBoneWeightsInPlace(BoneWeight[] weights, float tiny)
    {
        for (int i = 0; i < weights.Length; i++)
        {
            float num = this.Sum(in weights[i]);
            if (!(num < tiny))
            {
                float num2 = 1f / num;
                BoneWeight boneWeight = weights[i];
                boneWeight.weight0 *= num2;
                boneWeight.weight1 *= num2;
                boneWeight.weight2 *= num2;
                boneWeight.weight3 *= num2;
                weights[i] = boneWeight;
            }
        }
    }

    private float Sum(in BoneWeight bw)
    {
        return bw.weight0 + bw.weight1 + bw.weight2 + bw.weight3;
    }

    private void Acc(Dictionary<int, float> map, int boneIndex, float w, float tiny)
    {
        if (boneIndex >= 0 && !(w <= tiny))
        {
            if (map.TryGetValue(boneIndex, out var value))
            {
                map[boneIndex] = value + w;
            }
            else
            {
                map[boneIndex] = w;
            }
        }
    }

    private BoneWeight BuildTop4Normalized(Dictionary<int, float> map, float tiny)
    {
        if (map == null || map.Count == 0)
        {
            return default(BoneWeight);
        }
        List<KeyValuePair<int, float>> list = map.OrderByDescending((KeyValuePair<int, float> kv) => kv.Value).Take(4).ToList();
        float num = 0f;
        for (int num2 = 0; num2 < list.Count; num2++)
        {
            num += list[num2].Value;
        }
        if (num < tiny)
        {
            return default(BoneWeight);
        }
        float num3 = 1f / num;
        BoneWeight result = default(BoneWeight);
        for (int num4 = 0; num4 < list.Count; num4++)
        {
            int key = list[num4].Key;
            float num5 = list[num4].Value * num3;
            switch (num4)
            {
                case 0:
                    result.boneIndex0 = key;
                    result.weight0 = num5;
                    break;
                case 1:
                    result.boneIndex1 = key;
                    result.weight1 = num5;
                    break;
                case 2:
                    result.boneIndex2 = key;
                    result.weight2 = num5;
                    break;
                case 3:
                    result.boneIndex3 = key;
                    result.weight3 = num5;
                    break;
            }
        }
        return result;
    }
}
