// 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 bodyMeshes, IReadOnlyList bodyBakedMeshes, int referenceBodyIndex = 0, Settings settings = null, Dictionary boneTypeMap = null, Dictionary 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 accIndexByTransform; float tiny; if (boneTypeMap != null && cloth.smr != null && cloth.editableMesh != null) { bones = cloth.smr.bones; if (bones != null && bones.Length != 0) { HashSet value = null; if (cloth.humanoidMatchedBones != null) { cloth.humanoidMatchedBones.TryGetValue(HumanBodyBones.Head, out value); } HashSet hashSet = new HashSet(); 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 list = new List(); accIndexByTransform = new Dictionary(); 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 dictionary = new Dictionary(); foreach (KeyValuePair> humanoidMatchedBone in cloth.humanoidMatchedBones) { HumanBodyBones key = humanoidMatchedBone.Key; HashSet value = humanoidMatchedBone.Value; if (value == null) { continue; } foreach (Transform item in value) { if (!(item == null) && !dictionary.ContainsKey(item)) { dictionary.Add(item, key); } } } Dictionary dictionary2 = new Dictionary(); Dictionary dictionary3 = new Dictionary(); 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 dictionary4 = new Dictionary(); 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 clothBoneToHuman, Dictionary 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 bodyMeshes, IReadOnlyList 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 dictionary = new Dictionary(); 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[] 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 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 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 hashSet = null; if (accessoryBones != null && accessoryBones.Length != 0) { hashSet = new HashSet(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 bonesPerVertex = new NativeArray(num, Allocator.Temp); List list = new List(num * 4); NativeArray weights2 = default(NativeArray); 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(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 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> equivalentVertices = cloth.equivalentVertices; int num = finalWeights.Length; bool[] array = new bool[num]; foreach (List item in equivalentVertices) { if (item == null || item.Count == 0) { continue; } List 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(); } list.Add(num2); if (!array[num2]) { flag = true; } } } if (list == null || !flag) { continue; } Dictionary weightMap = new Dictionary(); 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> list2 = weightMap.OrderByDescending((KeyValuePair 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 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[] BuildAdjacencyFromTriangles(int vertexCount, int[] triangles) { List[] array = new List[vertexCount]; for (int i = 0; i < vertexCount; i++) { array[i] = new List(); } 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[] 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 hashSet = new HashSet(); 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[] 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 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[] 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 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[] adjacency, int startIndex, float maxDistance, bool[] visited) { Queue queue = new Queue(); 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[] 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[] 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[] adjacency, float tiny, int maxRing) { Dictionary map = new Dictionary(); Queue queue = new Queue(); Dictionary dictionary = new Dictionary(); HashSet hashSet = new HashSet(); 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 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[] adjacency, float tiny, int maxDepth) { Queue queue = new Queue(); Dictionary dictionary = new Dictionary(); HashSet hashSet = new HashSet(); 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 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 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 map, float tiny) { if (map == null || map.Count == 0) { return default(BoneWeight); } List> list = map.OrderByDescending((KeyValuePair 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; } }