// Decompiled with JetBrains decompiler // Type: Eden.AutoMorpher.WeightTransferUtil // Assembly: EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null // MVID: D39968B3-E151-4276-BDB4-E82752BBAFF0 // Assembly location: D:\dev\AutoMorpher\Assets\@Eden_Tools\Eden_AutoMorpher\Script\EdenAutoMorpherScript.dll using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Unity.Collections; using UnityEditor; using UnityEngine; namespace Eden.AutoMorpher { public class WeightTransferUtil { private bool NameContainsAny(string lowerName, string[] patterns) { for (int index = 0; index < patterns.Length; ++index) { if (lowerName.Contains(patterns[index])) return true; } return false; } private bool IsBreastOrButtBone(Transform t) { if (Object.op_Equality((Object)t, (Object)null)) return false; string[] patterns1 = new string[6] { "breast", "boob", "bust", "oppai", "chichi", "mune" }; string[] patterns2 = new string[6] { "butt", "buttock", "buttox", "glute", "glutes", "gluteus" }; string lowerInvariant = ((Object)t).name.ToLowerInvariant(); return this.NameContainsAny(lowerInvariant, patterns1) || this.NameContainsAny(lowerInvariant, patterns2); } public IEnumerator RetargetAndTransfer( ClothInstance cloth, Animator avatarAnimator, IReadOnlyList bodyMeshes, IReadOnlyList bodyBakedMeshes, int referenceBodyIndex = 0, WeightTransferUtil.Settings settings = null, Dictionary boneTypeMap = null, Dictionary bodyToClothesMap = null) { if (cloth == null || Object.op_Equality((Object)cloth.smr, (Object)null) || Object.op_Equality((Object)cloth.editableMesh, (Object)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 (Object.op_Equality((Object)cloth.bakedMesh, (Object)null)) throw new AutoMorpherException("Cloth Baked Mesh is Missing", "[WeightTransferUtil] RetargetAndTransfer\n - cloth.bakedMesh is null"); if (Object.op_Equality((Object)avatarAnimator, (Object)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 (Object.op_Equality((Object)bodyRefSmr, (Object)null) || Object.op_Equality((Object)bodyRefSmr.sharedMesh, (Object)null)) throw new AutoMorpherException("Reference Body Mesh is Invalid", "[WeightTransferUtil] RetargetAndTransfer\n - bodyRefSmr is null or bodyRefSmr.sharedMesh is null\n - referenceBodyIndex : " + referenceBodyIndex.ToString()); if (settings == null) settings = new WeightTransferUtil.Settings(); Transform[] accessoryBones = (Transform[])null; float[,] accessoryWeights = (float[,])null; int bodyBoneCount = 0; BoneWeight[] prevBoneWeights = (BoneWeight[])null; yield return (object)null; if (boneTypeMap != null && Object.op_Inequality((Object)cloth.smr, (Object)null) && Object.op_Inequality((Object)cloth.editableMesh, (Object)null)) { Transform[] bones = cloth.smr.bones; if (bones != null && bones.Length != 0) { HashSet transformSet1 = (HashSet)null; if (cloth.humanoidMatchedBones != null) cloth.humanoidMatchedBones.TryGetValue((HumanBodyBones)10, out transformSet1); HashSet transformSet2 = new HashSet(); for (int index = 0; index < bones.Length; ++index) { Transform key = bones[index]; ClothBoneType clothBoneType; if (!Object.op_Equality((Object)key, (Object)null) && boneTypeMap.TryGetValue(key, out clothBoneType) && clothBoneType == ClothBoneType.Accessory) transformSet2.Add(key); } if (transformSet1 != null) { foreach (Transform transform in transformSet1) { if (!Object.op_Equality((Object)transform, (Object)null) && Array.IndexOf(bones, transform) >= 0) transformSet2.Add(transform); } } List transformList = new List(); Dictionary accIndexByTransform = new Dictionary(); for (int index = 0; index < bones.Length; ++index) { Transform transform = bones[index]; if (!Object.op_Equality((Object)transform, (Object)null) && transformSet2.Contains(transform) && !this.IsBreastOrButtBone(transform) && !accIndexByTransform.ContainsKey(transform)) { accIndexByTransform[transform] = transformList.Count; transformList.Add(transform); } } if (transformList.Count > 0) { accessoryBones = transformList.ToArray(); int vertexCount = cloth.editableMesh.vertexCount; accessoryWeights = new float[vertexCount, accessoryBones.Length]; BoneWeight[] boneWeights = cloth.editableMesh.boneWeights; if (boneWeights != null && boneWeights.Length == vertexCount) { float tiny = 1E-06f; for (int v = 0; v < vertexCount; ++v) { BoneWeight boneWeight = boneWeights[v]; Acc(((BoneWeight)ref boneWeight).boneIndex0, ((BoneWeight)ref boneWeight).weight0, v); Acc(((BoneWeight)ref boneWeight).boneIndex1, ((BoneWeight)ref boneWeight).weight1, v); Acc(((BoneWeight)ref boneWeight).boneIndex2, ((BoneWeight)ref boneWeight).weight2, v); Acc(((BoneWeight)ref boneWeight).boneIndex3, ((BoneWeight)ref boneWeight).weight3, v); } void Acc(int boneIndex, float w, int v) { if ((double)w <= (double)tiny || boneIndex < 0 || boneIndex >= bones.Length) return; Transform bone = bones[boneIndex]; int index; if (Object.op_Equality((Object)bone, (Object)null) || !accIndexByTransform.TryGetValue(bone, out index)) return; accessoryWeights[v, index] += w; } } } } } bodyBoneCount = bodyRefSmr.bones.Length; yield return (object)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); } private void RetargetClothToAvatarSkeleton( ClothInstance cloth, Animator avatarAnimator, SkinnedMeshRenderer bodyRefSmr, Transform[] accessoryBones = null) { SkinnedMeshRenderer smr = cloth.smr; Mesh sharedMesh = cloth.smr.sharedMesh; if (Object.op_Equality((Object)sharedMesh, (Object)null)) throw new AutoMorpherException("Cloth Mesh is Missing", "[WeightTransferUtil] RetargetClothToAvatarSkeleton\n - clothMesh is null"); Transform[] bones1 = smr.bones; Transform[] bones2 = bodyRefSmr.bones; if (bones1 == null || bones1.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 : {(bones1 == null ? -1 : bones1.Length).ToString()}\n - targetBones.Length : {(bones2 == null ? -1 : bones2.Length).ToString()}"); Matrix4x4 localToWorldMatrix = ((Component)smr).transform.localToWorldMatrix; if (accessoryBones == null || accessoryBones.Length == 0) { Dictionary clothBoneToHuman = new Dictionary(); foreach (KeyValuePair> humanoidMatchedBone in cloth.humanoidMatchedBones) { HumanBodyBones key1 = humanoidMatchedBone.Key; HashSet transformSet = humanoidMatchedBone.Value; if (transformSet != null) { foreach (Transform key2 in transformSet) { if (!Object.op_Equality((Object)key2, (Object)null) && !clothBoneToHuman.ContainsKey(key2)) clothBoneToHuman.Add(key2, key1); } } } Dictionary humanToTargetBoneIndex = new Dictionary(); Dictionary dictionary = new Dictionary(); for (int index = 0; index < bones2.Length; ++index) { Transform key = bones2[index]; if (Object.op_Inequality((Object)key, (Object)null) && !dictionary.ContainsKey(key)) dictionary.Add(key, index); } foreach (HumanBodyBones key in Enum.GetValues(typeof(HumanBodyBones))) { if (key != 55) { Transform boneTransform = avatarAnimator.GetBoneTransform(key); int num; if (!Object.op_Equality((Object)boneTransform, (Object)null) && dictionary.TryGetValue(boneTransform, out num)) humanToTargetBoneIndex[key] = num; } } BoneWeight[] boneWeights1 = sharedMesh.boneWeights; if (boneWeights1 == null || boneWeights1.Length == 0) { Debug.LogWarning((object)"[WeightTransferUtil] clothMesh.boneWeights 가 비어 있음. 리타겟할 weight 가 없음."); } else { int vertexCount = sharedMesh.vertexCount; int length = bones2.Length; float[,] numArray = new float[vertexCount, length]; for (int vertIndex = 0; vertIndex < vertexCount; ++vertIndex) { BoneWeight boneWeight = boneWeights1[vertIndex]; this.AccumulateRetargetWeight(numArray, vertIndex, ((BoneWeight)ref boneWeight).boneIndex0, ((BoneWeight)ref boneWeight).weight0, bones1, clothBoneToHuman, humanToTargetBoneIndex); this.AccumulateRetargetWeight(numArray, vertIndex, ((BoneWeight)ref boneWeight).boneIndex1, ((BoneWeight)ref boneWeight).weight1, bones1, clothBoneToHuman, humanToTargetBoneIndex); this.AccumulateRetargetWeight(numArray, vertIndex, ((BoneWeight)ref boneWeight).boneIndex2, ((BoneWeight)ref boneWeight).weight2, bones1, clothBoneToHuman, humanToTargetBoneIndex); this.AccumulateRetargetWeight(numArray, vertIndex, ((BoneWeight)ref boneWeight).boneIndex3, ((BoneWeight)ref boneWeight).weight3, bones1, clothBoneToHuman, humanToTargetBoneIndex); } BoneWeight[] boneWeights2 = this.CollapseWeightsToBoneWeights(numArray, 4, 0.0001f); Matrix4x4[] matrix4x4Array = new Matrix4x4[bones2.Length]; for (int index = 0; index < bones2.Length; ++index) { Transform transform = bones2[index]; matrix4x4Array[index] = !Object.op_Inequality((Object)transform, (Object)null) ? Matrix4x4.identity : Matrix4x4.op_Multiply(transform.worldToLocalMatrix, localToWorldMatrix); } sharedMesh.boneWeights = boneWeights2; sharedMesh.bindposes = matrix4x4Array; smr.rootBone = bodyRefSmr.rootBone; smr.bones = bones2; EditorUtility.SetDirty((Object)sharedMesh); EditorUtility.SetDirty((Object)smr); } } else { 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 dictionary = new Dictionary(); if (bones4 != null) { for (int index = 0; index < bones4.Length; ++index) { Transform key = bones4[index]; if (!Object.op_Equality((Object)key, (Object)null) && !dictionary.ContainsKey(key)) dictionary.Add(key, index); } } int length1 = bones3.Length; int length2 = accessoryBones.Length; Transform[] transformArray = new Transform[length1 + length2]; for (int index = 0; index < length1; ++index) transformArray[index] = bones3[index]; for (int index = 0; index < length2; ++index) transformArray[length1 + index] = accessoryBones[index]; Matrix4x4[] matrix4x4Array = new Matrix4x4[transformArray.Length]; for (int index = 0; index < length1; ++index) { Transform transform = bones3[index]; matrix4x4Array[index] = !Object.op_Inequality((Object)transform, (Object)null) ? Matrix4x4.identity : Matrix4x4.op_Multiply(transform.worldToLocalMatrix, localToWorldMatrix); } for (int index1 = 0; index1 < length2; ++index1) { Transform accessoryBone = accessoryBones[index1]; Matrix4x4 matrix4x4 = Matrix4x4.identity; int index2; if (Object.op_Inequality((Object)accessoryBone, (Object)null) && dictionary.TryGetValue(accessoryBone, out index2)) matrix4x4 = bindposes == null || index2 < 0 || index2 >= bindposes.Length ? Matrix4x4.op_Multiply(accessoryBone.worldToLocalMatrix, localToWorldMatrix) : bindposes[index2]; matrix4x4Array[length1 + index1] = matrix4x4; } sharedMesh.bindposes = matrix4x4Array; sharedMesh.boneWeights = new BoneWeight[sharedMesh.vertexCount]; smr.rootBone = bodyRefSmr.rootBone; smr.bones = transformArray; EditorUtility.SetDirty((Object)sharedMesh); EditorUtility.SetDirty((Object)smr); } } private void AccumulateRetargetWeight( float[,] W, int vertIndex, int clothBoneIndex, float weight, Transform[] clothBones, Dictionary clothBoneToHuman, Dictionary humanToTargetBoneIndex) { if ((double)weight <= 0.0 || clothBoneIndex < 0 || clothBoneIndex >= clothBones.Length) return; Transform clothBone = clothBones[clothBoneIndex]; HumanBodyBones key; int index; if (Object.op_Equality((Object)clothBone, (Object)null) || !clothBoneToHuman.TryGetValue(clothBone, out key) || !humanToTargetBoneIndex.TryGetValue(key, out index)) return; W[vertIndex, index] += weight; } private void TransferFromBodyToCloth( Animator avatarAnimator, ClothInstance cloth, IReadOnlyList bodyMeshes, IReadOnlyList bodyBakedMeshes, WeightTransferUtil.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[] triangles1 = 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).ToString()}\n - targetNormalsWorld.Length : {(clothWorldNormals == null ? -1 : clothWorldNormals.Length).ToString()}"); Transform[] bones1 = cloth.smr.bones; int length1 = bones1.Length; Dictionary targetBoneIndexByTransform = new Dictionary(); for (int index = 0; index < bones1.Length; ++index) { Transform key = bones1[index]; if (Object.op_Inequality((Object)key, (Object)null) && !targetBoneIndexByTransform.ContainsKey(key)) targetBoneIndexByTransform.Add(key, index); } int length2 = 0; int length3 = 0; foreach (SkinnedMeshRenderer bodyMesh in (IEnumerable)bodyMeshes) { if (!Object.op_Equality((Object)bodyMesh, (Object)null) && !Object.op_Equality((Object)bodyMesh.sharedMesh, (Object)null)) { length2 += bodyMesh.sharedMesh.vertexCount; length3 += bodyMesh.sharedMesh.triangles.Length; } } Vector3[] sourceVerts = length2 != 0 && length3 != 0 ? new Vector3[length2] : throw new AutoMorpherException("Valid Body Mesh is Missing", "[WeightTransferUtil] TransferFromBodyToCloth\n - bodyMeshes has no valid mesh (totalVerts == 0 or totalTris == 0)"); Vector3[] sourceNormals = new Vector3[length2]; int[] sourceTris = new int[length3]; float[,] W = new float[length2, length1]; int num1 = 0; int num2 = 0; for (int index1 = 0; index1 < bodyMeshes.Count; ++index1) { SkinnedMeshRenderer bodyMesh = bodyMeshes[index1]; if (!Object.op_Equality((Object)bodyMesh, (Object)null) && !Object.op_Equality((Object)bodyMesh.sharedMesh, (Object)null)) { Mesh sharedMesh = bodyMesh.sharedMesh; Mesh bodyBakedMesh = bodyBakedMeshes[index1]; Vector3[] vertices = bodyBakedMesh.vertices; Vector3[] normals = bodyBakedMesh.normals; int[] triangles2 = bodyBakedMesh.triangles; int vertexCount = bodyBakedMesh.vertexCount; Vector3 lossyScale = ((Component)bodyMesh).transform.lossyScale; Vector3 vector3_1; // ISSUE: explicit constructor call ((Vector3)ref vector3_1).\u002Ector(1f / Mathf.Max(lossyScale.x, 1E-08f), 1f / Mathf.Max(lossyScale.y, 1E-08f), 1f / Mathf.Max(lossyScale.z, 1E-08f)); Matrix4x4 matrix4x4 = Matrix4x4.op_Multiply(((Component)bodyMesh).transform.localToWorldMatrix, Matrix4x4.Scale(vector3_1)); for (int index2 = 0; index2 < vertexCount; ++index2) { sourceVerts[num1 + index2] = ((Matrix4x4)ref matrix4x4).MultiplyPoint3x4(vertices[index2]); Vector3 vector3_2 = ((Matrix4x4)ref matrix4x4).MultiplyVector(normals[index2]); Vector3 normalized = ((Vector3)ref vector3_2).normalized; sourceNormals[num1 + index2] = normalized; } for (int index3 = 0; index3 < triangles2.Length; ++index3) sourceTris[num2 + index3] = triangles2[index3] + num1; BoneWeight[] boneWeights = sharedMesh.boneWeights; Transform[] bones2 = bodyMesh.bones; if (boneWeights != null && boneWeights.Length == vertexCount && bones2 != null) { for (int index4 = 0; index4 < vertexCount; ++index4) { BoneWeight boneWeight = boneWeights[index4]; this.AccumulateBodyWeight(ref W, num1 + index4, ((BoneWeight)ref boneWeight).boneIndex0, ((BoneWeight)ref boneWeight).weight0, bones2, targetBoneIndexByTransform); this.AccumulateBodyWeight(ref W, num1 + index4, ((BoneWeight)ref boneWeight).boneIndex1, ((BoneWeight)ref boneWeight).weight1, bones2, targetBoneIndexByTransform); this.AccumulateBodyWeight(ref W, num1 + index4, ((BoneWeight)ref boneWeight).boneIndex2, ((BoneWeight)ref boneWeight).weight2, bones2, targetBoneIndexByTransform); this.AccumulateBodyWeight(ref W, num1 + index4, ((BoneWeight)ref boneWeight).boneIndex3, ((BoneWeight)ref boneWeight).weight3, bones2, targetBoneIndexByTransform); } } num1 += vertexCount; num2 += triangles2.Length; } } List[] adjacency = cloth.vertexAdjacency; if (adjacency == null || adjacency.Length != worldVertices.Length) adjacency = this.BuildAdjacencyFromTriangles(worldVertices.Length, triangles1); bool[] matchedMask; float[,] matchesClosestSurface = this.FindMatchesClosestSurface(sourceVerts, sourceTris, sourceNormals, worldVertices, clothWorldNormals, W, settings.maxDistance * settings.maxDistance, settings.maxNormalAngleDeg, settings.allowFlippedNormal, out matchedMask); float[,] numArray = this.InpaintApprox(worldVertices, triangles1, matchesClosestSurface, matchedMask, adjacency, 20, 0.7f); if (settings.enableSmoothing && settings.smoothingIterations > 0 && (double)settings.smoothingAlpha > 0.0) numArray = this.SmoothWeightsApprox(worldVertices, numArray, matchedMask, adjacency, settings.smoothingIterations, settings.smoothingAlpha, settings.maxDistance); this.ApplyLegVertexConstraints(avatarAnimator, cloth, numArray, bodyBoneCount, settings.tinyWeight); int vertexCount1 = cloth.editableMesh.vertexCount; int length4 = cloth.smr.bones.Length; if (accessoryBones != null && accessoryBones.Length != 0 && accessoryWeights != null && bodyBoneCount > 0) { HashSet transformSet = (HashSet)null; if (cloth.humanoidMatchedBones != null) cloth.humanoidMatchedBones.TryGetValue((HumanBodyBones)10, out transformSet); Transform transform1 = (Transform)null; if (Object.op_Inequality((Object)avatarAnimator, (Object)null)) transform1 = avatarAnimator.GetBoneTransform((HumanBodyBones)10); if (transformSet != null && Object.op_Inequality((Object)transform1, (Object)null)) { int index5 = Array.IndexOf(cloth.smr.bones, transform1); if (index5 >= 0 && index5 < bodyBoneCount) { foreach (Transform transform2 in transformSet) { if (!Object.op_Equality((Object)transform2, (Object)null)) { int index6 = Array.IndexOf(accessoryBones, transform2); if (index6 >= 0) { for (int index7 = 0; index7 < vertexCount1; ++index7) { float accessoryWeight = accessoryWeights[index7, index6]; if ((double)accessoryWeight > (double)settings.tinyWeight) { numArray[index7, index5] += accessoryWeight; accessoryWeights[index7, index6] = 0.0f; } } } } } } } } BoneWeight[] boneWeightArray; if (accessoryBones != null && accessoryBones.Length != 0 && accessoryWeights != null && bodyBoneCount > 0 && bodyBoneCount + accessoryBones.Length == length4) { boneWeightArray = this.MergeBodyAndAccessoryWeights(numArray, accessoryWeights, bodyBoneCount, settings.tinyWeight, settings.enforceFourBoneLimit); } else { if (settings.enforceFourBoneLimit) this.EnforceMaxBonesPerVertex(numArray, 4, settings.tinyWeight); else this.ZeroSmallWeights(numArray, settings.tinyWeight); this.NormalizeWeightsPerVertex(numArray, settings.tinyWeight); boneWeightArray = this.CollapseWeightsToBoneWeights(numArray, 4, settings.tinyWeight); } Mesh sharedMesh1 = cloth.smr.sharedMesh; if (sharedMesh1.vertexCount != boneWeightArray.Length) throw new AutoMorpherException("Vertex Count and Weight Length Mismatch", $"[WeightTransferUtil] TransferFromBodyToCloth\n - mesh.vertexCount : {sharedMesh1.vertexCount.ToString()}\n - finalWeights.Length : {boneWeightArray.Length.ToString()}"); this.EnsureNonZeroAndNormalizeFinalWeights(cloth, boneWeightArray, adjacency, settings.tinyWeight); this.ApplyEquivalentVertexWeights(cloth, boneWeightArray); this.NormalizeAllBoneWeightsInPlace(boneWeightArray, settings.tinyWeight); sharedMesh1.boneWeights = boneWeightArray; cloth.smr.sharedMesh = sharedMesh1; int zeroWeightVertexCount; if (this.TryApplyBoneWeightsWithValidation(sharedMesh1, boneWeightArray, 1E-10f, out zeroWeightVertexCount)) return; Debug.LogWarning((object)$"[WeightValidation] Zero-weight vertices detected: {zeroWeightVertexCount}"); } public void MoveBodyWeightsToClothesBones( SkinnedMeshRenderer clothesSmr, Mesh clothesMesh, Dictionary bodyToClothesMap, Transform[] accessoryBones) { if (Object.op_Equality((Object)clothesSmr, (Object)null)) throw new AutoMorpherException("Clothes SMR is Missing", "[WeightTransferUtil] MoveBodyWeightsToClothesBones\n - clothesSmr is null"); if (Object.op_Equality((Object)clothesMesh, (Object)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 = Object.op_Inequality((Object)clothesSmr.rootBone, (Object)null) ? clothesSmr.rootBone : ((Component)clothesSmr).transform; HashSet transformSet = (HashSet)null; if (accessoryBones != null && accessoryBones.Length != 0) transformSet = new HashSet((IEnumerable)accessoryBones); Transform[] bones = clothesSmr.bones; Transform[] transformArray = bones != null && bones.Length != 0 ? new Transform[bones.Length] : throw new AutoMorpherException("Clothes Bones are Missing", "[WeightTransferUtil] MoveBodyWeightsToClothesBones\n - clothesSmr.bones is null or empty"); for (int index = 0; index < bones.Length; ++index) { Transform bodyBone = bones[index]; if (Object.op_Equality((Object)bodyBone, (Object)null)) transformArray[index] = clothesFallbackRoot; else if (transformSet != null && transformSet.Contains(bodyBone)) { transformArray[index] = bodyBone; } else { Transform transform = EnsureClothesBoneExists(bodyBone); transformArray[index] = Object.op_Inequality((Object)transform, (Object)null) ? transform : clothesFallbackRoot; } } clothesSmr.bones = transformArray; if (Object.op_Inequality((Object)clothesSmr.rootBone, (Object)null) && (transformSet == null || !transformSet.Contains(clothesSmr.rootBone))) { Transform transform = EnsureClothesBoneExists(clothesSmr.rootBone); if (Object.op_Inequality((Object)transform, (Object)null)) clothesSmr.rootBone = transform; } Transform transform1 = Object.op_Inequality((Object)clothesSmr.rootBone, (Object)null) ? clothesSmr.rootBone : ((Component)clothesSmr).transform; Matrix4x4[] matrix4x4Array = new Matrix4x4[transformArray.Length]; for (int index = 0; index < transformArray.Length; ++index) { Transform transform2 = transformArray[index]; if (Object.op_Equality((Object)transform2, (Object)null)) throw new AutoMorpherException("Remapped Bone is Null", "[WeightTransferUtil] MoveBodyWeightsToClothesBones\n - remappedBones contains null bone after fallback"); matrix4x4Array[index] = Matrix4x4.op_Multiply(transform2.worldToLocalMatrix, transform1.localToWorldMatrix); } clothesMesh.bindposes = matrix4x4Array; Transform EnsureClothesBoneExists(Transform bodyBone) { if (Object.op_Equality((Object)bodyBone, (Object)null)) return (Transform)null; Transform transform1; if (bodyToClothesMap.TryGetValue(bodyBone, out transform1) && Object.op_Inequality((Object)transform1, (Object)null)) return transform1; Transform transform2 = (Transform)null; if (Object.op_Inequality((Object)bodyBone.parent, (Object)null)) transform2 = EnsureClothesBoneExists(bodyBone.parent); if (Object.op_Equality((Object)transform2, (Object)null)) transform2 = clothesFallbackRoot; Transform transform3 = new GameObject(((Object)bodyBone).name).transform; transform3.SetParent(transform2, false); transform3.localPosition = bodyBone.localPosition; transform3.localRotation = bodyBone.localRotation; transform3.localScale = bodyBone.localScale; bodyToClothesMap[bodyBone] = transform3; return transform3; } } private bool TryApplyBoneWeightsWithValidation( Mesh mesh, BoneWeight[] weights, float tiny, out int zeroWeightVertexCount) { bool flag = true; zeroWeightVertexCount = 0; int length = weights.Length; NativeArray nativeArray1 = new NativeArray(length, (Allocator)2, (NativeArrayOptions)1); List list = new List(length * 4); NativeArray nativeArray2 = new NativeArray(); try { for (int index = 0; index < length; ++index) { BoneWeight weight = weights[index]; int count = 0; this.AddIfValid(list, ((BoneWeight)ref weight).boneIndex0, ((BoneWeight)ref weight).weight0, tiny, ref count); this.AddIfValid(list, ((BoneWeight)ref weight).boneIndex1, ((BoneWeight)ref weight).weight1, tiny, ref count); this.AddIfValid(list, ((BoneWeight)ref weight).boneIndex2, ((BoneWeight)ref weight).weight2, tiny, ref count); this.AddIfValid(list, ((BoneWeight)ref weight).boneIndex3, ((BoneWeight)ref weight).weight3, tiny, ref count); if (count == 0) ++zeroWeightVertexCount; nativeArray1[index] = (byte)count; } if (zeroWeightVertexCount > 0) return false; nativeArray2 = new NativeArray(list.Count, (Allocator)2, (NativeArrayOptions)1); for (int index = 0; index < list.Count; ++index) nativeArray2[index] = list[index]; mesh.SetBoneWeights(nativeArray1, nativeArray2); } catch (Exception ex) { Debug.LogWarning((object)("[SetBoneWeights] Failed: " + ex.Message)); flag = false; } finally { if (nativeArray2.IsCreated) nativeArray2.Dispose(); if (nativeArray1.IsCreated) nativeArray1.Dispose(); } return flag; } private void AddIfValid( List list, int boneIndex, float weight, float tiny, ref int count) { if (boneIndex < 0 || (double)weight <= (double)tiny) return; List boneWeight1List = list; BoneWeight1 boneWeight1_1 = new BoneWeight1(); ((BoneWeight1)ref boneWeight1_1).boneIndex = boneIndex; ((BoneWeight1)ref boneWeight1_1).weight = weight; BoneWeight1 boneWeight1_2 = boneWeight1_1; boneWeight1List.Add(boneWeight1_2); ++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 length = finalWeights.Length; bool[] flagArray = new bool[length]; foreach (List intList1 in equivalentVertices) { if (intList1 != null && intList1.Count != 0) { List intList2 = (List)null; bool flag = false; for (int index1 = 0; index1 < intList1.Count; ++index1) { int index2 = intList1[index1]; if (index2 >= 0 && index2 < length) { if (intList2 == null) intList2 = new List(); intList2.Add(index2); if (!flagArray[index2]) flag = true; } } if (intList2 != null && flag) { Dictionary weightMap = new Dictionary(); foreach (int index in intList2) { BoneWeight finalWeight = finalWeights[index]; Accumulate(((BoneWeight)ref finalWeight).boneIndex0, ((BoneWeight)ref finalWeight).weight0); Accumulate(((BoneWeight)ref finalWeight).boneIndex1, ((BoneWeight)ref finalWeight).weight1); Accumulate(((BoneWeight)ref finalWeight).boneIndex2, ((BoneWeight)ref finalWeight).weight2); Accumulate(((BoneWeight)ref finalWeight).boneIndex3, ((BoneWeight)ref finalWeight).weight3); } if (weightMap.Count != 0) { List> list = weightMap.OrderByDescending, float>((Func, float>)(kv => kv.Value)).Take>(4).ToList>(); float num1 = 0.0f; for (int index = 0; index < Math.Min(4, list.Count); ++index) num1 += list[index].Value; if ((double)num1 > 9.99999993922529E-09) { BoneWeight boneWeight = new BoneWeight(); for (int index = 0; index < Math.Min(4, list.Count); ++index) { KeyValuePair keyValuePair = list[index]; int key = keyValuePair.Key; keyValuePair = list[index]; float num2 = keyValuePair.Value / num1; switch (index) { case 0: ((BoneWeight)ref boneWeight).boneIndex0 = key; ((BoneWeight)ref boneWeight).weight0 = num2; break; case 1: ((BoneWeight)ref boneWeight).boneIndex1 = key; ((BoneWeight)ref boneWeight).weight1 = num2; break; case 2: ((BoneWeight)ref boneWeight).boneIndex2 = key; ((BoneWeight)ref boneWeight).weight2 = num2; break; case 3: ((BoneWeight)ref boneWeight).boneIndex3 = key; ((BoneWeight)ref boneWeight).weight3 = num2; break; } } foreach (int index in intList2) { finalWeights[index] = boneWeight; flagArray[index] = true; } } } void Accumulate(int boneIndex, float w) { if ((double)w <= 0.0 || boneIndex < 0) return; float num; if (weightMap.TryGetValue(boneIndex, out num)) weightMap[boneIndex] = num + w; else weightMap[boneIndex] = w; } } } } } private void ApplyLegVertexConstraints( Animator avatarAnimator, ClothInstance cloth, float[,] weights, int bodyBoneCount, float tinyWeight) { if (cloth == null || Object.op_Equality((Object)cloth.smr, (Object)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 length1 = weights.GetLength(0); int length2 = weights.GetLength(1); Transform boneTransform1 = avatarAnimator.GetBoneTransform((HumanBodyBones)1); Transform boneTransform2 = avatarAnimator.GetBoneTransform((HumanBodyBones)2); bool[] flagArray1 = new bool[length2]; bool[] flagArray2 = new bool[length2]; int num = Mathf.Min(bodyBoneCount, bones.Length); for (int index = 0; index < num; ++index) { Transform transform = bones[index]; if (!Object.op_Equality((Object)transform, (Object)null)) { if (transform.IsChildOf(boneTransform1)) flagArray1[index] = true; else if (transform.IsChildOf(boneTransform2)) flagArray2[index] = true; } } for (int index1 = 0; index1 < length1; ++index1) { bool flag1 = isLeftLegVertex != null && index1 < isLeftLegVertex.Length && isLeftLegVertex[index1]; bool flag2 = isRightLegVertex != null && index1 < isRightLegVertex.Length && isRightLegVertex[index1]; if (flag1 || flag2) { for (int index2 = 0; index2 < length2; ++index2) { if ((double)weights[index1, index2] > (double)tinyWeight) { if (flag1) { if (flagArray2[index2]) weights[index1, index2] = 0.0f; } else if (flag2 && flagArray1[index2]) weights[index1, index2] = 0.0f; } } } } } private Vector3[] GetClothWorldNormals(ClothInstance cloth) { if (cloth.worldVertices == null) return (Vector3[])null; int length = cloth.worldVertices.Length; if (Object.op_Equality((Object)cloth.bakedMesh, (Object)null) || cloth.bakedMesh.vertexCount != length) throw new AutoMorpherException("Cloth Baked Mesh is Invalid", $"[WeightTransferUtil] GetClothWorldNormals\n - bakedMesh is null or vertexCount mismatch\n - bakedMesh.vertexCount : {(Object.op_Equality((Object)cloth.bakedMesh, (Object)null) ? -1 : cloth.bakedMesh.vertexCount).ToString()}\n - worldVertices.Length : {length.ToString()}"); Vector3[] clothWorldNormals = new Vector3[length]; Mesh bakedMesh = cloth.bakedMesh; Vector3[] normals = bakedMesh.normals; if (normals == null || normals.Length != length) { bakedMesh.RecalculateNormals(); normals = bakedMesh.normals; } for (int index = 0; index < length; ++index) { Vector3 vector3 = ((Matrix4x4)ref cloth.worldNoScale).MultiplyVector(normals[index]); Vector3 normalized = ((Vector3)ref vector3).normalized; clothWorldNormals[index] = normalized; } return clothWorldNormals; } private void AccumulateBodyWeight( ref float[,] W, int vIndex, int localBoneIndex, float weight, Transform[] bodyBones, Dictionary targetBoneIndexByTransform) { if ((double)weight <= 0.0 || bodyBones == null || localBoneIndex < 0 || localBoneIndex >= bodyBones.Length) return; Transform bodyBone = bodyBones[localBoneIndex]; int index; if (Object.op_Equality((Object)bodyBone, (Object)null) || !targetBoneIndexByTransform.TryGetValue(bodyBone, out index)) return; W[vIndex, index] += weight; } private List[] BuildAdjacencyFromTriangles(int vertexCount, int[] triangles) { List[] adj = new List[vertexCount]; for (int index = 0; index < vertexCount; ++index) adj[index] = new List(); int num = triangles.Length / 3; for (int index = 0; index < num; ++index) { int triangle1 = triangles[index * 3]; int triangle2 = triangles[index * 3 + 1]; int triangle3 = triangles[index * 3 + 2]; this.AddEdge(adj, triangle1, triangle2); this.AddEdge(adj, triangle2, triangle3); this.AddEdge(adj, triangle3, triangle1); } return adj; } private void AddEdge(List[] adj, int a, int b) { if (!adj[a].Contains(b)) adj[a].Add(b); if (adj[b].Contains(a)) return; 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 length1 = targetVerts.Length; int length2 = sourceWeights.GetLength(1); float[,] matchesClosestSurface = new float[length1, length2]; matchedMask = new bool[length1]; int num1 = sourceTris.Length / 3; float num2 = angleThresholdDeg * ((float)Math.PI / 180f); for (int index1 = 0; index1 < length1; ++index1) { Vector3 targetVert = targetVerts[index1]; float num3 = float.PositiveInfinity; int num4 = -1; Vector3 vector3_1 = Vector3.zero; for (int index2 = 0; index2 < num1; ++index2) { int sourceTri1 = sourceTris[index2 * 3]; int sourceTri2 = sourceTris[index2 * 3 + 1]; int sourceTri3 = sourceTris[index2 * 3 + 2]; Vector3 sourceVert1 = sourceVerts[sourceTri1]; Vector3 sourceVert2 = sourceVerts[sourceTri2]; Vector3 sourceVert3 = sourceVerts[sourceTri3]; Vector3 bary; Vector3 vector3_2 = this.ClosestPointOnTriangle(targetVert, sourceVert1, sourceVert2, sourceVert3, out bary); Vector3 vector3_3 = Vector3.op_Subtraction(targetVert, vector3_2); float sqrMagnitude = ((Vector3)ref vector3_3).sqrMagnitude; if ((double)sqrMagnitude < (double)num3) { num3 = sqrMagnitude; num4 = index2; vector3_1 = bary; } } if (num4 < 0) { matchedMask[index1] = false; } else { int num5 = (double)num3 <= (double)distanceThresholdSqr ? 1 : 0; int sourceTri4 = sourceTris[num4 * 3]; int sourceTri5 = sourceTris[num4 * 3 + 1]; int sourceTri6 = sourceTris[num4 * 3 + 2]; Vector3 sourceNormal1 = sourceNormals[sourceTri4]; Vector3 sourceNormal2 = sourceNormals[sourceTri5]; Vector3 sourceNormal3 = sourceNormals[sourceTri6]; double x = (double)vector3_1.x; Vector3 vector3_4 = Vector3.op_Addition(Vector3.op_Addition(Vector3.op_Multiply(sourceNormal1, (float)x), Vector3.op_Multiply(sourceNormal2, vector3_1.y)), Vector3.op_Multiply(sourceNormal3, vector3_1.z)); float num6 = Mathf.Acos(Mathf.Clamp(Vector3.Dot(((Vector3)ref vector3_4).normalized, ((Vector3)ref targetNormals[index1]).normalized), -1f, 1f)); bool flag1 = (double)num6 <= (double)num2; if (flipVertexNormal && 3.1415927410125732 - (double)num6 <= (double)num2) flag1 = true; int num7 = flag1 ? 1 : 0; bool flag2 = (num5 & num7) != 0; matchedMask[index1] = flag2; for (int index3 = 0; index3 < length2; ++index3) { float sourceWeight1 = sourceWeights[sourceTri4, index3]; float sourceWeight2 = sourceWeights[sourceTri5, index3]; float sourceWeight3 = sourceWeights[sourceTri6, index3]; matchesClosestSurface[index1, index3] = (float)((double)sourceWeight1 * (double)vector3_1.x + (double)sourceWeight2 * (double)vector3_1.y + (double)sourceWeight3 * (double)vector3_1.z); } } } return matchesClosestSurface; } private Vector3 ClosestPointOnTriangle( Vector3 p, Vector3 a, Vector3 b, Vector3 c, out Vector3 bary) { Vector3 vector3_1 = Vector3.op_Subtraction(b, a); Vector3 vector3_2 = Vector3.op_Subtraction(c, a); Vector3 vector3_3 = Vector3.op_Subtraction(p, a); float num1 = Vector3.Dot(vector3_1, vector3_3); float num2 = Vector3.Dot(vector3_2, vector3_3); if ((double)num1 <= 0.0 && (double)num2 <= 0.0) { bary = new Vector3(1f, 0.0f, 0.0f); return a; } Vector3 vector3_4 = Vector3.op_Subtraction(p, b); float num3 = Vector3.Dot(vector3_1, vector3_4); float num4 = Vector3.Dot(vector3_2, vector3_4); if ((double)num3 >= 0.0 && (double)num4 <= (double)num3) { bary = new Vector3(0.0f, 1f, 0.0f); return b; } float num5 = (float)((double)num1 * (double)num4 - (double)num3 * (double)num2); if ((double)num5 <= 0.0 && (double)num1 >= 0.0 && (double)num3 <= 0.0) { float num6 = num1 / (num1 - num3); bary = new Vector3(1f - num6, num6, 0.0f); return Vector3.op_Addition(a, Vector3.op_Multiply(vector3_1, num6)); } Vector3 vector3_5 = Vector3.op_Subtraction(p, c); float num7 = Vector3.Dot(vector3_1, vector3_5); float num8 = Vector3.Dot(vector3_2, vector3_5); if ((double)num8 >= 0.0 && (double)num7 <= (double)num8) { bary = new Vector3(0.0f, 0.0f, 1f); return c; } float num9 = (float)((double)num7 * (double)num2 - (double)num1 * (double)num8); if ((double)num9 <= 0.0 && (double)num2 >= 0.0 && (double)num8 <= 0.0) { float num10 = num2 / (num2 - num8); bary = new Vector3(1f - num10, 0.0f, num10); return Vector3.op_Addition(a, Vector3.op_Multiply(vector3_2, num10)); } float num11 = (float)((double)num3 * (double)num8 - (double)num7 * (double)num4); if ((double)num11 <= 0.0 && (double)num4 - (double)num3 >= 0.0 && (double)num7 - (double)num8 >= 0.0) { float num12 = (float)(((double)num4 - (double)num3) / ((double)num4 - (double)num3 + ((double)num7 - (double)num8))); bary = new Vector3(0.0f, 1f - num12, num12); return Vector3.op_Addition(b, Vector3.op_Multiply(Vector3.op_Subtraction(c, b), num12)); } float num13 = (float)(1.0 / ((double)num11 + (double)num9 + (double)num5)); float num14 = num9 * num13; float num15 = num5 * num13; float num16 = 1f - num14 - num15; bary = new Vector3(num16, num14, num15); return Vector3.op_Addition(Vector3.op_Addition(Vector3.op_Multiply(a, num16), Vector3.op_Multiply(b, num14)), Vector3.op_Multiply(c, num15)); } private void ZeroSmallWeights(float[,] weights, float tiny) { int length1 = weights.GetLength(0); int length2 = weights.GetLength(1); for (int index1 = 0; index1 < length1; ++index1) { for (int index2 = 0; index2 < length2; ++index2) { if ((double)weights[index1, index2] <= (double)tiny) weights[index1, index2] = 0.0f; } } } private void EnforceMaxBonesPerVertex(float[,] weights, int maxBonesPerVertex, float tinyWeight) { int length1 = weights.GetLength(0); int length2 = weights.GetLength(1); for (int index1 = 0; index1 < length1; ++index1) { List<(int, float)> valueTupleList = new List<(int, float)>(); for (int index2 = 0; index2 < length2; ++index2) { float weight = weights[index1, index2]; if ((double)weight > (double)tinyWeight) valueTupleList.Add((index2, weight)); } valueTupleList.Sort((Comparison<(int, float)>)((a, b) => b.w.CompareTo(a.w))); HashSet intSet = new HashSet(); int num = Mathf.Min(maxBonesPerVertex, valueTupleList.Count); for (int index3 = 0; index3 < num; ++index3) intSet.Add(valueTupleList[index3].Item1); for (int index4 = 0; index4 < length2; ++index4) { if (!intSet.Contains(index4) || (double)weights[index1, index4] <= (double)tinyWeight) weights[index1, index4] = 0.0f; } } } private void NormalizeWeightsPerVertex(float[,] weights, float tiny) { int length1 = weights.GetLength(0); int length2 = weights.GetLength(1); for (int index1 = 0; index1 < length1; ++index1) { float num1 = 0.0f; for (int index2 = 0; index2 < length2; ++index2) num1 += weights[index1, index2]; if ((double)num1 >= (double)tiny) { float num2 = 1f / num1; for (int index3 = 0; index3 < length2; ++index3) weights[index1, index3] *= num2; } } } private BoneWeight[] CollapseWeightsToBoneWeights( float[,] weights, int maxBonesPerVert, float tiny) { int length1 = weights.GetLength(0); int length2 = weights.GetLength(1); BoneWeight[] boneWeights = new BoneWeight[length1]; for (int index1 = 0; index1 < length1; ++index1) { List<(int, float)> valueTupleList = new List<(int, float)>(); for (int index2 = 0; index2 < length2; ++index2) { float weight = weights[index1, index2]; if ((double)weight > (double)tiny) valueTupleList.Add((index2, weight)); } if (valueTupleList.Count == 0) { boneWeights[index1] = new BoneWeight(); } else { valueTupleList.Sort((Comparison<(int, float)>)((a, b) => b.w.CompareTo(a.w))); int num1 = Mathf.Min(maxBonesPerVert, valueTupleList.Count); float num2 = 0.0f; for (int index3 = 0; index3 < num1; ++index3) num2 += valueTupleList[index3].Item2; if ((double)num2 < (double)tiny) num2 = 1f; float num3 = 1f / num2; BoneWeight boneWeight = new BoneWeight(); for (int index4 = 0; index4 < num1; ++index4) { int num4 = valueTupleList[index4].Item1; float num5 = valueTupleList[index4].Item2 * num3; switch (index4) { case 0: ((BoneWeight)ref boneWeight).boneIndex0 = num4; ((BoneWeight)ref boneWeight).weight0 = num5; break; case 1: ((BoneWeight)ref boneWeight).boneIndex1 = num4; ((BoneWeight)ref boneWeight).weight1 = num5; break; case 2: ((BoneWeight)ref boneWeight).boneIndex2 = num4; ((BoneWeight)ref boneWeight).weight2 = num5; break; case 3: ((BoneWeight)ref boneWeight).boneIndex3 = num4; ((BoneWeight)ref boneWeight).weight3 = num5; break; } } boneWeights[index1] = boneWeight; } } return boneWeights; } private float[,] InpaintApprox( Vector3[] verts, int[] triangles, float[,] weights, bool[] matched, List[] adjacency, int iterations, float alpha) { int length1 = verts.Length; int length2 = weights.GetLength(1); float[,] numArray1 = (float[,])weights.Clone(); float[,] numArray2 = new float[length1, length2]; for (int index1 = 0; index1 < iterations; ++index1) { for (int index2 = 0; index2 < length1; ++index2) { if (matched[index2]) { for (int index3 = 0; index3 < length2; ++index3) numArray2[index2, index3] = numArray1[index2, index3]; } else { List intList = adjacency[index2]; if (intList == null || intList.Count == 0) { for (int index4 = 0; index4 < length2; ++index4) numArray2[index2, index4] = numArray1[index2, index4]; } else { for (int index5 = 0; index5 < length2; ++index5) { float num1 = 0.0f; for (int index6 = 0; index6 < intList.Count; ++index6) { int index7 = intList[index6]; num1 += numArray1[index7, index5]; } float num2 = num1 / (float)intList.Count; numArray2[index2, index5] = Mathf.Lerp(numArray1[index2, index5], num2, alpha); } } } } float[,] numArray3 = numArray2; float[,] numArray4 = numArray1; numArray1 = numArray3; numArray2 = numArray4; } return numArray1; } private float[,] SmoothWeightsApprox( Vector3[] verts, float[,] weights, bool[] matched, List[] adjacency, int numSmoothIterSteps, float smoothAlpha, float distanceThreshold) { int length1 = verts.Length; int length2 = weights.GetLength(1); bool[] visited = new bool[length1]; for (int startIndex = 0; startIndex < length1; ++startIndex) { if (!matched[startIndex]) this.FloodFillWithinDistance(verts, adjacency, startIndex, distanceThreshold, visited); } float[,] numArray1 = (float[,])weights.Clone(); float[,] numArray2 = new float[length1, length2]; for (int index1 = 0; index1 < numSmoothIterSteps; ++index1) { for (int index2 = 0; index2 < length1; ++index2) { if (!visited[index2]) { for (int index3 = 0; index3 < length2; ++index3) numArray2[index2, index3] = numArray1[index2, index3]; } else { List intList = adjacency[index2]; if (intList == null || intList.Count == 0) { for (int index4 = 0; index4 < length2; ++index4) numArray2[index2, index4] = numArray1[index2, index4]; } else { for (int index5 = 0; index5 < length2; ++index5) { float num1 = 0.0f; for (int index6 = 0; index6 < intList.Count; ++index6) { int index7 = intList[index6]; num1 += numArray1[index7, index5]; } float num2 = num1 / (float)intList.Count; numArray2[index2, index5] = Mathf.Lerp(numArray1[index2, index5], num2, smoothAlpha); } } } } float[,] numArray3 = numArray2; float[,] numArray4 = numArray1; numArray1 = numArray3; numArray2 = numArray4; } return numArray1; } private void FloodFillWithinDistance( Vector3[] verts, List[] adjacency, int startIndex, float maxDistance, bool[] visited) { Queue intQueue = new Queue(); intQueue.Enqueue(startIndex); visited[startIndex] = true; while (intQueue.Count > 0) { int index1 = intQueue.Dequeue(); foreach (int index2 in adjacency[index1]) { if (!visited[index2]) { Vector3 vector3 = Vector3.op_Subtraction(verts[startIndex], verts[index2]); if ((double)((Vector3)ref vector3).magnitude < (double)maxDistance) { visited[index2] = true; intQueue.Enqueue(index2); } } } } } private BoneWeight[] MergeBodyAndAccessoryWeights( float[,] bodyWeights, float[,] accessoryWeights, int bodyBoneCount, float tiny, bool enforceFour) { int length1 = bodyWeights.GetLength(0); bodyWeights.GetLength(1); int length2 = accessoryWeights.GetLength(1); BoneWeight[] boneWeightArray = new BoneWeight[length1]; for (int index1 = 0; index1 < length1; ++index1) { List<(int, float)> collection1 = new List<(int, float)>(); float num1 = 0.0f; for (int index2 = 0; index2 < length2; ++index2) { float accessoryWeight = accessoryWeights[index1, index2]; if ((double)accessoryWeight > (double)tiny) { int num2 = bodyBoneCount + index2; collection1.Add((num2, accessoryWeight)); num1 += accessoryWeight; } } List<(int, float)> collection2 = new List<(int, float)>(); float num3 = 0.0f; for (int index3 = 0; index3 < bodyBoneCount; ++index3) { float bodyWeight = bodyWeights[index1, index3]; if ((double)bodyWeight > (double)tiny) { collection2.Add((index3, bodyWeight)); num3 += bodyWeight; } } float num4; if ((double)num3 > (double)tiny && (double)num1 < 1.0 - (double)tiny) { float num5 = Mathf.Max(0.0f, 1f - num1); float num6 = num5 / num3; for (int index4 = 0; index4 < collection2.Count; ++index4) collection2[index4] = (collection2[index4].Item1, collection2[index4].Item2 * num6); num4 = num5; } else { for (int index5 = 0; index5 < collection2.Count; ++index5) collection2[index5] = (collection2[index5].Item1, 0.0f); num4 = 0.0f; } List<(int, float)> valueTupleList = new List<(int, float)>(); valueTupleList.AddRange((IEnumerable<(int, float)>)collection1); valueTupleList.AddRange((IEnumerable<(int, float)>)collection2); valueTupleList.RemoveAll((Predicate<(int, float)>)(t => (double)t.w <= (double)tiny)); if (valueTupleList.Count == 0) { boneWeightArray[index1] = new BoneWeight(); } else { if (enforceFour && valueTupleList.Count > 4) { valueTupleList.Sort((Comparison<(int, float)>)((a, b) => { bool flag1 = a.bone >= bodyBoneCount; bool flag2 = b.bone >= bodyBoneCount; if (flag1 == flag2) return b.w.CompareTo(a.w); return !flag1 ? 1 : -1; })); valueTupleList = valueTupleList.GetRange(0, 4); } float num7 = 0.0f; foreach ((int, float) valueTuple in valueTupleList) num7 += valueTuple.Item2; if ((double)num7 < (double)tiny) num7 = 1f; float num8 = 1f / num7; BoneWeight boneWeight = new BoneWeight(); for (int index6 = 0; index6 < valueTupleList.Count && index6 < 4; ++index6) { int num9 = valueTupleList[index6].Item1; float num10 = valueTupleList[index6].Item2 * num8; switch (index6) { case 0: ((BoneWeight)ref boneWeight).boneIndex0 = num9; ((BoneWeight)ref boneWeight).weight0 = num10; break; case 1: ((BoneWeight)ref boneWeight).boneIndex1 = num9; ((BoneWeight)ref boneWeight).weight1 = num10; break; case 2: ((BoneWeight)ref boneWeight).boneIndex2 = num9; ((BoneWeight)ref boneWeight).weight2 = num10; break; case 3: ((BoneWeight)ref boneWeight).boneIndex3 = num9; ((BoneWeight)ref boneWeight).weight3 = num10; break; } } boneWeightArray[index1] = boneWeight; } } return boneWeightArray; } private void EnsureNonZeroAndNormalizeFinalWeights( ClothInstance cloth, BoneWeight[] weights, List[] adjacency, float tiny) { if (cloth == null || weights == null || weights.Length == 0) return; this.PreNormalizeBoneWeightsInPlace(weights, tiny); this.FillZeroWeightsFromNeighbors(weights, adjacency, tiny); this.NormalizeAllBoneWeightsInPlace(weights, tiny); } private void PreNormalizeBoneWeightsInPlace(BoneWeight[] weights, float tiny) { for (int index = 0; index < weights.Length; ++index) { BoneWeight weight = weights[index]; float num1 = this.Sum(in weights[index]); if ((double)num1 >= (double)tiny) { float num2 = 1f / num1; ref BoneWeight local1 = ref weight; ((BoneWeight)ref local1).weight0 = ((BoneWeight)ref local1).weight0 * num2; ref BoneWeight local2 = ref weight; ((BoneWeight)ref local2).weight1 = ((BoneWeight)ref local2).weight1 * num2; ref BoneWeight local3 = ref weight; ((BoneWeight)ref local3).weight2 = ((BoneWeight)ref local3).weight2 * num2; ref BoneWeight local4 = ref weight; ((BoneWeight)ref local4).weight3 = ((BoneWeight)ref local4).weight3 * num2; weights[index] = weight; } } } private void FillZeroWeightsFromNeighbors( BoneWeight[] weights, List[] adjacency, float tiny) { int length = weights.Length; for (int v = 0; v < length; ++v) { if ((double)this.Sum(in weights[v]) < (double)tiny) { BoneWeight bw = this.AverageFromNeighbors(v, weights, adjacency, tiny, 1); if ((double)this.Sum(in bw) >= (double)tiny) weights[v] = bw; } } for (int start = 0; start < length; ++start) { if ((double)this.Sum(in weights[start]) < (double)tiny) { int nonZeroVertexBfs = this.FindNearestNonZeroVertexBfs(start, weights, adjacency, tiny, 8); if (nonZeroVertexBfs >= 0) { weights[start] = weights[nonZeroVertexBfs]; } else { BoneWeight boneWeight = new BoneWeight(); ((BoneWeight)ref boneWeight).boneIndex0 = 0; ((BoneWeight)ref boneWeight).weight0 = 1f; weights[start] = boneWeight; } } } } private BoneWeight AverageFromNeighbors( int v, BoneWeight[] weights, List[] adjacency, float tiny, int maxRing) { Dictionary map = new Dictionary(); Queue intQueue = new Queue(); Dictionary dictionary = new Dictionary(); HashSet intSet = new HashSet(); intQueue.Enqueue(v); dictionary[v] = 0; intSet.Add(v); while (intQueue.Count > 0) { int key1 = intQueue.Dequeue(); int num = dictionary[key1]; if (num < maxRing) { List intList = adjacency[key1]; if (intList != null) { foreach (int key2 in intList) { if (key2 >= 0 && key2 < weights.Length && intSet.Add(key2)) { dictionary[key2] = num + 1; intQueue.Enqueue(key2); BoneWeight bw = weights[key2]; if ((double)this.Sum(in bw) >= (double)tiny) { this.Acc(map, ((BoneWeight)ref bw).boneIndex0, ((BoneWeight)ref bw).weight0, tiny); this.Acc(map, ((BoneWeight)ref bw).boneIndex1, ((BoneWeight)ref bw).weight1, tiny); this.Acc(map, ((BoneWeight)ref bw).boneIndex2, ((BoneWeight)ref bw).weight2, tiny); this.Acc(map, ((BoneWeight)ref bw).boneIndex3, ((BoneWeight)ref bw).weight3, tiny); } } } } } } return this.BuildTop4Normalized(map, tiny); } private int FindNearestNonZeroVertexBfs( int start, BoneWeight[] weights, List[] adjacency, float tiny, int maxDepth) { Queue intQueue = new Queue(); Dictionary dictionary = new Dictionary(); HashSet intSet = new HashSet(); intQueue.Enqueue(start); dictionary[start] = 0; intSet.Add(start); while (intQueue.Count > 0) { int key1 = intQueue.Dequeue(); int num = dictionary[key1]; if (num > 0 && (double)this.Sum(in weights[key1]) >= (double)tiny) return key1; if (num < maxDepth) { List intList = adjacency[key1]; if (intList != null) { foreach (int key2 in intList) { if (key2 >= 0 && key2 < weights.Length && intSet.Add(key2)) { dictionary[key2] = num + 1; intQueue.Enqueue(key2); } } } } } return -1; } private void NormalizeAllBoneWeightsInPlace(BoneWeight[] weights, float tiny) { for (int index = 0; index < weights.Length; ++index) { float num1 = this.Sum(in weights[index]); if ((double)num1 >= (double)tiny) { float num2 = 1f / num1; BoneWeight weight = weights[index]; ref BoneWeight local1 = ref weight; ((BoneWeight)ref local1).weight0 = ((BoneWeight)ref local1).weight0 * num2; ref BoneWeight local2 = ref weight; ((BoneWeight)ref local2).weight1 = ((BoneWeight)ref local2).weight1 * num2; ref BoneWeight local3 = ref weight; ((BoneWeight)ref local3).weight2 = ((BoneWeight)ref local3).weight2 * num2; ref BoneWeight local4 = ref weight; ((BoneWeight)ref local4).weight3 = ((BoneWeight)ref local4).weight3 * num2; weights[index] = weight; } } } private float Sum(in BoneWeight bw) { BoneWeight boneWeight = bw; double weight0 = (double)((BoneWeight)ref boneWeight).weight0; boneWeight = bw; double weight1 = (double)((BoneWeight)ref boneWeight).weight1; double num1 = weight0 + weight1; boneWeight = bw; double weight2 = (double)((BoneWeight)ref boneWeight).weight2; double num2 = num1 + weight2; boneWeight = bw; double weight3 = (double)((BoneWeight)ref boneWeight).weight3; return (float)(num2 + weight3); } private void Acc(Dictionary map, int boneIndex, float w, float tiny) { if (boneIndex < 0 || (double)w <= (double)tiny) return; float num; if (map.TryGetValue(boneIndex, out num)) map[boneIndex] = num + w; else map[boneIndex] = w; } private BoneWeight BuildTop4Normalized(Dictionary map, float tiny) { if (map == null || map.Count == 0) return new BoneWeight(); List> list = map.OrderByDescending, float>((Func, float>)(kv => kv.Value)).Take>(4).ToList>(); float num1 = 0.0f; for (int index = 0; index < list.Count; ++index) num1 += list[index].Value; if ((double)num1 < (double)tiny) return new BoneWeight(); float num2 = 1f / num1; BoneWeight boneWeight = new BoneWeight(); for (int index = 0; index < list.Count; ++index) { KeyValuePair keyValuePair = list[index]; int key = keyValuePair.Key; keyValuePair = list[index]; float num3 = keyValuePair.Value * num2; switch (index) { case 0: ((BoneWeight)ref boneWeight).boneIndex0 = key; ((BoneWeight)ref boneWeight).weight0 = num3; break; case 1: ((BoneWeight)ref boneWeight).boneIndex1 = key; ((BoneWeight)ref boneWeight).weight1 = num3; break; case 2: ((BoneWeight)ref boneWeight).boneIndex2 = key; ((BoneWeight)ref boneWeight).weight2 = num3; break; case 3: ((BoneWeight)ref boneWeight).boneIndex3 = key; ((BoneWeight)ref boneWeight).weight3 = num3; break; } } return boneWeight; } [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(0.0f, 1f)] public float smoothingAlpha = 0.25f; [Header("본 수 제한 & 임계값")] public bool enforceFourBoneLimit = true; public float tinyWeight = 0.0001f; public bool weightInClothes; } } }