Files
AutoMorpherDecompiled/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightTransferUtil.cs

1600 lines
81 KiB
C#

// 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<SkinnedMeshRenderer> bodyMeshes,
IReadOnlyList<Mesh> bodyBakedMeshes,
int referenceBodyIndex = 0,
WeightTransferUtil.Settings settings = null,
Dictionary<Transform, ClothBoneType> boneTypeMap = null,
Dictionary<Transform, Transform> 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<Transform> transformSet1 = (HashSet<Transform>)null;
if (cloth.humanoidMatchedBones != null)
cloth.humanoidMatchedBones.TryGetValue((HumanBodyBones)10, out transformSet1);
HashSet<Transform> transformSet2 = new HashSet<Transform>();
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<Transform>(bones, transform) >= 0)
transformSet2.Add(transform);
}
}
List<Transform> transformList = new List<Transform>();
Dictionary<Transform, int> accIndexByTransform = new Dictionary<Transform, int>();
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<Transform, HumanBodyBones> clothBoneToHuman = new Dictionary<Transform, HumanBodyBones>();
foreach (KeyValuePair<HumanBodyBones, HashSet<Transform>> humanoidMatchedBone in cloth.humanoidMatchedBones)
{
HumanBodyBones key1 = humanoidMatchedBone.Key;
HashSet<Transform> 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<HumanBodyBones, int> humanToTargetBoneIndex = new Dictionary<HumanBodyBones, int>();
Dictionary<Transform, int> dictionary = new Dictionary<Transform, int>();
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<Transform, int> dictionary = new Dictionary<Transform, int>();
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<Transform, HumanBodyBones> clothBoneToHuman,
Dictionary<HumanBodyBones, int> 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<SkinnedMeshRenderer> bodyMeshes,
IReadOnlyList<Mesh> 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<Transform, int> targetBoneIndexByTransform = new Dictionary<Transform, int>();
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<SkinnedMeshRenderer>)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<int>[] 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<Transform> transformSet = (HashSet<Transform>)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<Transform>(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<Transform>(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<Transform, Transform> 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<Transform> transformSet = (HashSet<Transform>)null;
if (accessoryBones != null && accessoryBones.Length != 0)
transformSet = new HashSet<Transform>((IEnumerable<Transform>)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<byte> nativeArray1 = new NativeArray<byte>(length, (Allocator)2, (NativeArrayOptions)1);
List<BoneWeight1> list = new List<BoneWeight1>(length * 4);
NativeArray<BoneWeight1> nativeArray2 = new NativeArray<BoneWeight1>();
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<BoneWeight1>(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<BoneWeight1> list,
int boneIndex,
float weight,
float tiny,
ref int count)
{
if (boneIndex < 0 || (double)weight <= (double)tiny)
return;
List<BoneWeight1> 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<List<int>> equivalentVertices = cloth.equivalentVertices;
int length = finalWeights.Length;
bool[] flagArray = new bool[length];
foreach (List<int> intList1 in equivalentVertices)
{
if (intList1 != null && intList1.Count != 0)
{
List<int> intList2 = (List<int>)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<int>();
intList2.Add(index2);
if (!flagArray[index2])
flag = true;
}
}
if (intList2 != null && flag)
{
Dictionary<int, float> weightMap = new Dictionary<int, float>();
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<KeyValuePair<int, float>> list = weightMap.OrderByDescending<KeyValuePair<int, float>, float>((Func<KeyValuePair<int, float>, float>)(kv => kv.Value)).Take<KeyValuePair<int, float>>(4).ToList<KeyValuePair<int, float>>();
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<int, float> 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<Transform, int> 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<int>[] BuildAdjacencyFromTriangles(int vertexCount, int[] triangles)
{
List<int>[] adj = new List<int>[vertexCount];
for (int index = 0; index < vertexCount; ++index)
adj[index] = new List<int>();
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<int>[] 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<int> intSet = new HashSet<int>();
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<int>[] 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<int> 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<int>[] 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<int> 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<int>[] adjacency,
int startIndex,
float maxDistance,
bool[] visited)
{
Queue<int> intQueue = new Queue<int>();
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<int>[] 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<int>[] 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<int>[] adjacency,
float tiny,
int maxRing)
{
Dictionary<int, float> map = new Dictionary<int, float>();
Queue<int> intQueue = new Queue<int>();
Dictionary<int, int> dictionary = new Dictionary<int, int>();
HashSet<int> intSet = new HashSet<int>();
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<int> 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<int>[] adjacency,
float tiny,
int maxDepth)
{
Queue<int> intQueue = new Queue<int>();
Dictionary<int, int> dictionary = new Dictionary<int, int>();
HashSet<int> intSet = new HashSet<int>();
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<int> 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<int, float> 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<int, float> map, float tiny)
{
if (map == null || map.Count == 0)
return new BoneWeight();
List<KeyValuePair<int, float>> list = map.OrderByDescending<KeyValuePair<int, float>, float>((Func<KeyValuePair<int, float>, float>)(kv => kv.Value)).Take<KeyValuePair<int, float>>(4).ToList<KeyValuePair<int, float>>();
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<int, float> 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;
}
}
}