1600 lines
81 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|