From 34507ca208964ec034ed6b346563e1c406ca3397 Mon Sep 17 00:00:00 2001 From: tymmkang Date: Sun, 1 Feb 2026 19:26:39 +0900 Subject: [PATCH] =?UTF-8?q?EdenAutoMorpherScript=20=EB=94=94=EC=BB=B4?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=86=8C=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../@Eden_Tools/Eden_AutoMorpher/Script.meta | 8 + .../Eden_AutoMorpher/Script/AutoMorpherDev.cs | 58 ++ .../Script/AutoMorpherDev.cs.meta | 2 + .../Script/AutoMorpherException.cs | 16 + .../Script/AutoMorpherException.cs.meta | 2 + .../Eden_AutoMorpher/Script/BakedBodyMesh.cs | 30 + .../Script/BakedBodyMesh.cs.meta | 2 + .../Script/BodyPoseMatchSetupUtil.cs | 196 +++++ .../Script/BodyPoseMatchSetupUtil.cs.meta | 2 + .../Script/BodyPoseMatchUtil.cs | 115 +++ .../Script/BodyPoseMatchUtil.cs.meta | 2 + .../Script/BodyPoseMatch_Arm.cs | 556 +++++++++++++ .../Script/BodyPoseMatch_Arm.cs.meta | 2 + .../Script/BodyPoseMatch_CommonUtil.cs | 248 ++++++ .../Script/BodyPoseMatch_CommonUtil.cs.meta | 2 + .../Script/BodyPoseMatch_Leg.cs | 627 ++++++++++++++ .../Script/BodyPoseMatch_Leg.cs.meta | 2 + .../Script/BodyPoseMatch_Torso.cs | 145 ++++ .../Script/BodyPoseMatch_Torso.cs.meta | 2 + .../Script/BodyPoseToClothApplier.cs | 251 ++++++ .../Script/BodyPoseToClothApplier.cs.meta | 2 + .../Script/BoneAlignmentUtil.cs | 169 ++++ .../Script/BoneAlignmentUtil.cs.meta | 2 + .../Script/BoneCorrespondenceUtil.cs | 195 +++++ .../Script/BoneCorrespondenceUtil.cs.meta | 2 + .../Eden_AutoMorpher/Script/BoneMatchUtil.cs | 613 ++++++++++++++ .../Script/BoneMatchUtil.cs.meta | 2 + .../Eden_AutoMorpher/Script/BvhNode.cs | 20 + .../Eden_AutoMorpher/Script/BvhNode.cs.meta | 2 + .../Eden_AutoMorpher/Script/BvhTriangle.cs | 18 + .../Script/BvhTriangle.cs.meta | 2 + .../Script/BvhTriangleMesh.cs | 767 ++++++++++++++++++ .../Script/BvhTriangleMesh.cs.meta | 2 + .../Eden_AutoMorpher/Script/ClothBoneType.cs | 9 + .../Script/ClothBoneType.cs.meta | 2 + .../Script/ClothHumanoidMaskUtil.cs | 302 +++++++ .../Script/ClothHumanoidMaskUtil.cs.meta | 2 + .../Eden_AutoMorpher/Script/ClothInstance.cs | 539 ++++++++++++ .../Script/ClothInstance.cs.meta | 2 + .../Script/ClothInstanceTotal.cs | 307 +++++++ .../Script/ClothInstanceTotal.cs.meta | 2 + .../Script/EdenAutoMorpher.cs | 597 ++++++++++++++ .../Script/EdenAutoMorpher.cs.meta | 2 + .../Script/EdenAutoMorpherConfig.cs | 56 ++ .../Script/EdenAutoMorpherConfig.cs.meta | 2 + .../Script/EdenAutoMorpherManager.cs | 396 +++++++++ .../Script/EdenAutoMorpherManager.cs.meta | 2 + .../Script/EdenAutoMorpher_SetUpUtil.cs | 676 +++++++++++++++ .../Script/EdenAutoMorpher_SetUpUtil.cs.meta | 2 + .../Eden_AutoMorpher/Script/MeshClassifier.cs | 277 +++++++ .../Script/MeshClassifier.cs.meta | 2 + .../Eden_AutoMorpher/Script/MeshMatcher.cs | 246 ++++++ .../Script/MeshMatcher.cs.meta | 2 + .../Eden_AutoMorpher/Script/MorpherMode.cs | 11 + .../Script/MorpherMode.cs.meta | 2 + .../Eden_AutoMorpher/Script/MorpherState.cs | 12 + .../Script/MorpherState.cs.meta | 2 + .../Eden_AutoMorpher/Script/PcaUtil.cs | 162 ++++ .../Eden_AutoMorpher/Script/PcaUtil.cs.meta | 2 + .../Eden_AutoMorpher/Script/ProcessInfo.cs | 12 + .../Script/ProcessInfo.cs.meta | 2 + .../Eden_AutoMorpher/Script/ProfileLoader.cs | 298 +++++++ .../Script/ProfileLoader.cs.meta | 2 + .../Script/ProfilePoseMatchUtil.cs | 80 ++ .../Script/ProfilePoseMatchUtil.cs.meta | 2 + .../Eden_AutoMorpher/Script/RegionStats.cs | 16 + .../Script/RegionStats.cs.meta | 2 + .../Eden_AutoMorpher/Script/SkinningUtil.cs | 274 +++++++ .../Script/SkinningUtil.cs.meta | 2 + .../Eden_AutoMorpher/Script/TempBoneMarker.cs | 15 + .../Script/TempBoneMarker.cs.meta | 2 + .../Eden_AutoMorpher/Script/TransformInfo.cs | 72 ++ .../Script/TransformInfo.cs.meta | 2 + .../Eden_AutoMorpher/Script/TriangleUtil.cs | 57 ++ .../Script/TriangleUtil.cs.meta | 2 + .../Script/VertexFittingUtil.cs | 675 +++++++++++++++ .../Script/VertexFittingUtil.cs.meta | 2 + .../Script/VertexMoverUtil.cs | 173 ++++ .../Script/VertexMoverUtil.cs.meta | 2 + .../Eden_AutoMorpher/Script/WeightKernel.cs | 9 + .../Script/WeightKernel.cs.meta | 2 + .../Script/WeightTransferUtil.cs | 6 + .../Script/WeightTransferUtil.cs.meta | 2 + .../Script/WorldVertexUtil.cs | 68 ++ .../Script/WorldVertexUtil.cs.meta | 2 + 85 files changed, 9461 insertions(+) create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherDev.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherDev.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherException.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherException.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BakedBodyMesh.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BakedBodyMesh.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchSetupUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchSetupUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Arm.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Arm.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_CommonUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_CommonUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Leg.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Leg.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Torso.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Torso.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseToClothApplier.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseToClothApplier.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneAlignmentUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneAlignmentUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneCorrespondenceUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneCorrespondenceUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneMatchUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneMatchUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhNode.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhNode.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangle.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangle.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangleMesh.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangleMesh.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothBoneType.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothBoneType.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothHumanoidMaskUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothHumanoidMaskUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstance.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstance.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstanceTotal.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstanceTotal.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherConfig.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherConfig.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherManager.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherManager.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher_SetUpUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher_SetUpUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshClassifier.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshClassifier.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshMatcher.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshMatcher.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherMode.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherMode.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherState.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherState.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/PcaUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/PcaUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProcessInfo.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProcessInfo.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfileLoader.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfileLoader.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfilePoseMatchUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfilePoseMatchUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/RegionStats.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/RegionStats.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/SkinningUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/SkinningUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/TempBoneMarker.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/TempBoneMarker.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/TransformInfo.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/TransformInfo.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/TriangleUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/TriangleUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexFittingUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexFittingUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexMoverUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexMoverUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightKernel.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightKernel.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightTransferUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightTransferUtil.cs.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/WorldVertexUtil.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/Script/WorldVertexUtil.cs.meta diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script.meta new file mode 100644 index 0000000..76a9cf0 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 43568339d28ae25468f26127090298f4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherDev.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherDev.cs new file mode 100644 index 0000000..638a629 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherDev.cs @@ -0,0 +1,58 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.AutoMorpherDev +using System; +using System.Diagnostics; +using Eden.AutoMorpher; +using UnityEngine; + +public static class AutoMorpherDev +{ + public readonly struct ScopeTimer : IDisposable + { + private readonly bool _enabled; + + private readonly string _label; + + private readonly Stopwatch _sw; + + public ScopeTimer(bool enabled, string label) + { + _enabled = enabled; + _label = label; + if (enabled) + { + _sw = Stopwatch.StartNew(); + } + else + { + _sw = null; + } + } + + public void Dispose() + { + if (_enabled && _sw != null) + { + _sw.Stop(); + UnityEngine.Debug.Log($"[DevTimer] {_label}: {_sw.ElapsedMilliseconds} ms"); + } + } + } + + public static bool isDeveloperMode; + + public static void Log(string message) + { + if (isDeveloperMode) + { + UnityEngine.Debug.Log(message); + } + } + + public static ScopeTimer Profile(string label) + { + return new ScopeTimer(isDeveloperMode, label); + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherDev.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherDev.cs.meta new file mode 100644 index 0000000..ae935a5 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherDev.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 73372fe46c4d1bd4e8f8bb36159b8bb7 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherException.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherException.cs new file mode 100644 index 0000000..1421d22 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherException.cs @@ -0,0 +1,16 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.AutoMorpherException +using System; + +public class AutoMorpherException : Exception +{ + public string title; + + public AutoMorpherException(string title, string message) + : base(message) + { + this.title = title; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherException.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherException.cs.meta new file mode 100644 index 0000000..9f20574 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/AutoMorpherException.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 514ced1d3e497a246952dc49c763f668 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BakedBodyMesh.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BakedBodyMesh.cs new file mode 100644 index 0000000..ffa42cc --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BakedBodyMesh.cs @@ -0,0 +1,30 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BakedBodyMesh +using UnityEngine; + +public class BakedBodyMesh +{ + public SkinnedMeshRenderer smr; + + public Mesh bakedMesh; + + public BakedBodyMesh(SkinnedMeshRenderer _smr) + { + smr = _smr; + bakedMesh = new Mesh(); + smr.BakeMesh(bakedMesh); + } + + public void ReBakeMesh() + { + smr.BakeMesh(bakedMesh); + } + + ~BakedBodyMesh() + { + smr = null; + bakedMesh = null; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BakedBodyMesh.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BakedBodyMesh.cs.meta new file mode 100644 index 0000000..a33611a --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BakedBodyMesh.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 988a170527bb2d74da01c54552bc6b7d \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchSetupUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchSetupUtil.cs new file mode 100644 index 0000000..750a3a5 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchSetupUtil.cs @@ -0,0 +1,196 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BodyPoseMatchSetupUtil +using System.Collections.Generic; +using System.Linq; +using Eden.AutoMorpher; +using Eden.AutoMorpher.profile; +using UnityEngine; + +public class BodyPoseMatchSetupUtil +{ + public void AdjustAvatarScaleByNeck(Transform avatarRoot, Dictionary> humanBoneMap, float targetHeight) + { + if (avatarRoot == null) + { + Debug.LogWarning("[AvatarBodyMatchUtil] NormalizeAvatarScaleByNeck: avatar == null"); + return; + } + Transform boneFromBoneMap = new BodyPoseMatch_CommonUtil().GetBoneFromBoneMap(humanBoneMap, HumanBodyBones.Neck); + if (boneFromBoneMap == null) + { + Debug.LogWarning("[AvatarBodyMatchUtil] " + avatarRoot.name + " 에서 Neck 본을 찾지 못했습니다. 스케일 정규화를 건너뜁니다."); + return; + } + Transform transform = avatarRoot.transform; + float num = boneFromBoneMap.position.y - transform.position.y; + if (Mathf.Approximately(num, 0f)) + { + Debug.LogWarning($"[AvatarBodyMatchUtil] {avatarRoot.name} Neck Y가 0에 가까워 스케일 계산을 건너뜁니다. (neckY = {num})"); + return; + } + float num2 = targetHeight / num; + Vector3 localScale = transform.localScale; + localScale *= num2; + transform.localScale = localScale; + } + + public GameObject CreateBodyProxy(Animator sourceAvatar, IReadOnlyList sourceBodyMeshes, out List proxyBodyMeshes, out Dictionary sourceToProxy) + { + proxyBodyMeshes = null; + if (sourceAvatar == null) + { + Debug.LogError("[AvatarBodyMatchUtil] CreateSourceBodyProxy: sourceAvatar == null"); + sourceToProxy = new Dictionary(); + return null; + } + GameObject clone = Object.Instantiate(sourceAvatar.gameObject); + clone.name = sourceAvatar.name + "_BodyProxy"; + HashSet remainTransforms = new HashSet(); + remainTransforms.Add(clone.transform); + Animator component = clone.GetComponent(); + if (component != null) + { + component.enabled = false; + remainTransforms.Add(component.transform); + } + if (component != null && component.avatar != null && component.avatar.isHuman) + { + for (int i = 0; i < 55; i++) + { + HumanBodyBones humanBoneId = (HumanBodyBones)i; + Transform boneTransform = component.GetBoneTransform(humanBoneId); + if (boneTransform != null) + { + remainTransforms.Add(boneTransform); + } + } + } + HashSet hashSet = new HashSet(); + if (sourceBodyMeshes != null) + { + foreach (SkinnedMeshRenderer sourceBodyMesh in sourceBodyMeshes) + { + if (!(sourceBodyMesh == null) && !(sourceBodyMesh.sharedMesh == null)) + { + hashSet.Add(sourceBodyMesh.sharedMesh); + } + } + } + SkinnedMeshRenderer[] componentsInChildren = clone.GetComponentsInChildren(includeInactive: true); + List list = new List(); + SkinnedMeshRenderer[] array = componentsInChildren; + foreach (SkinnedMeshRenderer skinnedMeshRenderer in array) + { + if (!(skinnedMeshRenderer == null)) + { + Mesh sharedMesh = skinnedMeshRenderer.sharedMesh; + if (!(sharedMesh == null) && hashSet.Contains(sharedMesh)) + { + list.Add(skinnedMeshRenderer); + } + } + } + if (list.Count == 0) + { + Debug.LogWarning("[AvatarBodyMatchUtil] CreateSourceBodyProxy: clone에서 동일 sharedMesh를 가진 BodyMesh를 찾지 못했습니다."); + } + if (list.Count > 0) + { + MeshClassifier meshClassifier = new MeshClassifier(); + foreach (SkinnedMeshRenderer item in list) + { + if (item == null) + { + continue; + } + remainTransforms.Add(item.transform); + HashSet activeBones = meshClassifier.GetActiveBones(item); + if (activeBones == null) + { + Debug.LogWarning("[AvatarBodyMatchUtil] CreateSourceBodyProxy: clone smr '" + item.name + "' has null bones array (mesh='" + item.sharedMesh?.name + "')"); + continue; + } + foreach (Transform item2 in activeBones) + { + if (!(item2 == null)) + { + remainTransforms.Add(item2); + } + } + } + } + foreach (Transform item3 in remainTransforms.ToList()) + { + AddWithParents(item3); + } + Transform[] componentsInChildren2 = clone.GetComponentsInChildren(includeInactive: true); + for (int num = componentsInChildren2.Length - 1; num >= 0; num--) + { + Transform transform = componentsInChildren2[num]; + if (!(transform == null) && !(transform == clone.transform) && !remainTransforms.Contains(transform)) + { + Object.DestroyImmediate(transform.gameObject); + } + } + proxyBodyMeshes = list; + new BoneMatchUtil().BuildSourceToProxyBoneMap(sourceAvatar, component, out sourceToProxy); + return clone; + void AddWithParents(Transform t) + { + while (t != null && t != clone.transform) + { + remainTransforms.Add(t); + t = t.parent; + } + } + } + + public Vector3 GetComprehensiveScale(Transform rootT, Dictionary> clothHumanoidBoneMap, ProfileData profileData) + { + if (rootT == null) + { + throw new AutoMorpherException("Root Transform is Missing", "[BodyPoseMatch_CommonUtil] GetComprehensiveScale\n - rootT is null"); + } + if (profileData.bones == null || profileData.bones.Count == 0) + { + throw new AutoMorpherException("Profile Bones are Missing", "[BodyPoseMatch_CommonUtil] GetComprehensiveScale\n - profileData.bones is null or empty"); + } + Transform transform = null; + if (clothHumanoidBoneMap.TryGetValue(HumanBodyBones.Hips, out var value) && value != null && value.Count > 0) + { + foreach (Transform item in value) + { + if (item != null) + { + transform = item; + break; + } + } + } + if (transform == null) + { + throw new AutoMorpherException("Hip Transform is Missing", "[BodyPoseMatch_CommonUtil] GetComprehensiveScale\n - failed to get [hip] transform from clothHumanoidBoneMap"); + } + BoneData boneData = null; + for (int i = 0; i < profileData.bones.Count; i++) + { + BoneData boneData2 = profileData.bones[i]; + if (boneData2 != null && boneData2.hBone == HumanBodyBones.Hips) + { + boneData = boneData2; + break; + } + } + if (boneData == null) + { + throw new AutoMorpherException("Hip Bone Data is Missing in Profile", "[BodyPoseMatch_CommonUtil] GetComprehensiveScale\n - profileData bones does not contain Hips"); + } + Vector3 rootLocalScale = boneData.rootLocalScale; + Vector3 lossyScale = rootT.lossyScale; + Vector3 lossyScale2 = transform.lossyScale; + Vector3 vector = new Vector3(lossyScale2.x / lossyScale.x, lossyScale2.y / lossyScale.y, lossyScale2.z / lossyScale.z); + return new Vector3(vector.x / rootLocalScale.x, vector.y / rootLocalScale.y, vector.z / rootLocalScale.z); + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchSetupUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchSetupUtil.cs.meta new file mode 100644 index 0000000..5c3be17 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchSetupUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c9574059c6a92f24bbab8bf1fdc02d9a \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchUtil.cs new file mode 100644 index 0000000..8c2bf3f --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchUtil.cs @@ -0,0 +1,115 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BodyPoseMatchUtil +using System.Collections.Generic; +using Eden.AutoMorpher; +using UnityEngine; + +public class BodyPoseMatchUtil +{ + private WorldVertexUtil _worldVertexUtil; + + private MeshClassifier meshClassifier; + + private BodyPoseMatch_CommonUtil poseMatchCommonUtil; + + private bool doDebug; + + public BodyPoseMatchUtil() + { + _worldVertexUtil = new WorldVertexUtil(); + meshClassifier = new MeshClassifier(); + poseMatchCommonUtil = new BodyPoseMatch_CommonUtil(); + } + + public GameObject AutoAdjustBodyPose(GameObject sourceAvatar, IReadOnlyList sourceBodyMeshes, GameObject targetAvatar, IReadOnlyList targetBodyMeshes, out Dictionary sourceToProxy, float neckTargetHeight = 1.5f, bool onlyScaling = false) + { + Animator component = sourceAvatar.GetComponent(); + Animator component2 = targetAvatar.GetComponent(); + if (component == null || component2 == null) + { + Debug.LogError("[AvatarBodyMatchUtil] sourceAvatar 또는 targetAvatar가 null입니다."); + sourceToProxy = new Dictionary(); + return null; + } + Dictionary> humanBoneMap = meshClassifier.MeshHumanoidBoneMatcher(component, sourceBodyMeshes); + Dictionary> dictionary = meshClassifier.MeshHumanoidBoneMatcher(component2, targetBodyMeshes); + BodyPoseMatchSetupUtil bodyPoseMatchSetupUtil = new BodyPoseMatchSetupUtil(); + bodyPoseMatchSetupUtil.AdjustAvatarScaleByNeck(sourceAvatar.transform, humanBoneMap, neckTargetHeight); + bodyPoseMatchSetupUtil.AdjustAvatarScaleByNeck(targetAvatar.transform, dictionary, neckTargetHeight); + List proxyBodyMeshes; + GameObject gameObject = bodyPoseMatchSetupUtil.CreateBodyProxy(component, sourceBodyMeshes, out proxyBodyMeshes, out sourceToProxy); + Animator component3 = gameObject.GetComponent(); + if (onlyScaling) + { + gameObject.transform.SetParent(targetAvatar.transform); + gameObject.transform.localPosition = Vector3.zero; + return gameObject; + } + Dictionary> dictionary2 = meshClassifier.MeshHumanoidBoneMatcher(component3, proxyBodyMeshes); + if (dictionary2 == null || dictionary2.Count == 0) + { + throw new AutoMorpherException("Proxy Bone Map is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters\n - proxyBoneMap is null"); + } + gameObject.transform.SetParent(targetAvatar.transform); + gameObject.transform.localPosition = Vector3.zero; + List list = new List(); + foreach (SkinnedMeshRenderer item in proxyBodyMeshes) + { + list.Add(new BakedBodyMesh(item)); + } + List list2 = new List(); + foreach (SkinnedMeshRenderer targetBodyMesh in targetBodyMeshes) + { + list2.Add(new BakedBodyMesh(targetBodyMesh)); + } + BodyPoseMatch_Torso bodyPoseMatch_Torso = new BodyPoseMatch_Torso(); + bodyPoseMatch_Torso.AlignTorsoByNeck(gameObject, list, dictionary2, targetAvatar, list2, dictionary); + if (doDebug) + { + bodyPoseMatch_Torso.DrawTorsoPcaDebug(gameObject, list, dictionary2, Color.yellow, Color.cyan, 1f, 20f); + bodyPoseMatch_Torso.DrawTorsoPcaDebug(targetAvatar, list2, dictionary, Color.red, Color.green, 1f, 20f); + } + foreach (BakedBodyMesh item2 in list) + { + item2.ReBakeMesh(); + } + BodyPoseMatch_Arm bodyPoseMatch_Arm = new BodyPoseMatch_Arm(); + bodyPoseMatch_Arm.AlignUpperArmByArmPcaCenters(list, dictionary2, list2, dictionary); + if (doDebug) + { + bodyPoseMatch_Arm.DrawArmPcaDebug(gameObject, list, dictionary2, isLeft: true, Color.yellow, Color.cyan, 1f, 20f); + bodyPoseMatch_Arm.DrawArmPcaDebug(targetAvatar, list2, dictionary, isLeft: true, Color.red, Color.green, 1f, 20f); + } + foreach (BakedBodyMesh item3 in list) + { + item3.ReBakeMesh(); + } + bodyPoseMatch_Arm.ScalingBothArmsLength(list, dictionary2, list2, dictionary); + foreach (BakedBodyMesh item4 in list) + { + item4.ReBakeMesh(); + } + BodyPoseMatch_Leg bodyPoseMatch_Leg = new BodyPoseMatch_Leg(); + bodyPoseMatch_Leg.AlignBothUpperLegs(gameObject, list, dictionary2, targetAvatar, list2, dictionary); + foreach (BakedBodyMesh item5 in list) + { + item5.ReBakeMesh(); + } + bodyPoseMatch_Leg.ScalingBothLegsAndFoots(gameObject, list, dictionary2, targetAvatar, list2, dictionary); + if (doDebug) + { + foreach (BakedBodyMesh item6 in list) + { + item6.ReBakeMesh(); + } + bodyPoseMatch_Leg.DrawLegPcaDebug(gameObject, list, dictionary2, isLeft: true, Color.yellow, Color.cyan, 1f, 5f); + bodyPoseMatch_Leg.DrawLegPcaDebug(gameObject, list, dictionary2, isLeft: false, Color.yellow, Color.cyan, 1f, 5f); + bodyPoseMatch_Leg.DrawLegPcaDebug(targetAvatar, list2, dictionary, isLeft: true, Color.magenta, Color.green, 1f, 5f); + bodyPoseMatch_Leg.DrawLegPcaDebug(targetAvatar, list2, dictionary, isLeft: false, Color.magenta, Color.green, 1f, 5f); + bodyPoseMatch_Arm.DrawForearmExtremeDebugPair(gameObject.gameObject, proxyBodyMeshes, targetAvatar, targetBodyMeshes, isLeft: true, 1f, 5f); + } + return gameObject; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchUtil.cs.meta new file mode 100644 index 0000000..1402df2 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatchUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 40b77c723c5c82c40a80c64f4a9d7589 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Arm.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Arm.cs new file mode 100644 index 0000000..da6b18f --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Arm.cs @@ -0,0 +1,556 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BodyPoseMatch_Arm +using System.Collections.Generic; +using Eden.AutoMorpher; +using Eden.AutoMorpher.profile; +using UnityEngine; + +public class BodyPoseMatch_Arm +{ + private readonly BodyPoseMatch_CommonUtil poseMatchCommonUtil; + + public BodyPoseMatch_Arm() + { + poseMatchCommonUtil = new BodyPoseMatch_CommonUtil(); + } + + public void AlignUpperArmByArmCenters(IReadOnlyCollection sourceLeftUpperArmSet, IReadOnlyCollection sourceRightUpperArmSet, Vector3 sourceShoulderCenterWorld, Vector3 targetShoulderCenterWorld, Transform axisReferenceTransform, bool lockRightAxis = true) + { + if (sourceLeftUpperArmSet == null || sourceLeftUpperArmSet.Count == 0) + { + throw new AutoMorpherException("Source Left UpperArm Set is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmCenters\n - sourceLeftUpperArmSet is null or empty"); + } + if (sourceRightUpperArmSet == null || sourceRightUpperArmSet.Count == 0) + { + throw new AutoMorpherException("Source Right UpperArm Set is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmCenters\n - sourceRightUpperArmSet is null or empty"); + } + if (axisReferenceTransform == null) + { + throw new AutoMorpherException("Axis Reference Transform is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmCenters\n - axisReferenceTransform is null"); + } + Vector3 vector = targetShoulderCenterWorld - sourceShoulderCenterWorld; + if (lockRightAxis) + { + Vector3 right = axisReferenceTransform.right; + float num = Vector3.Dot(vector, right); + vector -= right * num; + } + foreach (Transform item in sourceLeftUpperArmSet) + { + if (!(item == null)) + { + item.position += vector; + } + } + foreach (Transform item2 in sourceRightUpperArmSet) + { + if (!(item2 == null)) + { + item2.position += vector; + } + } + } + + public void AlignUpperArmByArmPcaCenters(IReadOnlyList proxyBakedBodyMeshes, Dictionary> proxyBoneMap, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap) + { + if (proxyBoneMap == null) + { + throw new AutoMorpherException("Proxy Bone Map is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters\n - proxyBoneMap is null"); + } + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(proxyBoneMap, HumanBodyBones.Hips); + if (boneFromBoneMap == null) + { + throw new AutoMorpherException("Proxy Hips is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters\n - proxy hips transform is null"); + } + RegionStats regionStats = ComputeArmRegionStats(proxyBakedBodyMeshes, proxyBoneMap, isLeft: true); + RegionStats regionStats2 = ComputeArmRegionStats(proxyBakedBodyMeshes, proxyBoneMap, isLeft: false); + bool flag = regionStats.length > 0.0001f; + bool flag2 = regionStats2.length > 0.0001f; + if (!flag && !flag2) + { + Debug.LogWarning("[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters: proxy arm PCA failed. Skip."); + return; + } + Vector3 sourceShoulderCenterWorld = CalculateShoulderCenter(flag, flag2, regionStats.center, regionStats2.center); + RegionStats regionStats3 = ComputeArmRegionStats(targetBakedBodyMeshes, targetBoneMap, isLeft: true); + RegionStats regionStats4 = ComputeArmRegionStats(targetBakedBodyMeshes, targetBoneMap, isLeft: false); + bool flag3 = regionStats3.length > 0.0001f; + bool flag4 = regionStats4.length > 0.0001f; + if (!flag3 && !flag4) + { + Debug.LogWarning("[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters: target arm PCA failed. Skip."); + return; + } + Vector3 targetShoulderCenterWorld = CalculateShoulderCenter(flag3, flag4, regionStats3.center, regionStats4.center); + if (!proxyBoneMap.TryGetValue(HumanBodyBones.LeftUpperArm, out var value) || value == null || value.Count == 0) + { + throw new AutoMorpherException("Proxy Left UpperArm Set is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters\n - proxyBoneMap has no LeftUpperArm set"); + } + if (!proxyBoneMap.TryGetValue(HumanBodyBones.RightUpperArm, out var value2) || value2 == null || value2.Count == 0) + { + throw new AutoMorpherException("Proxy Right UpperArm Set is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters\n - proxyBoneMap has no RightUpperArm set"); + } + AlignUpperArmByArmCenters(value, value2, sourceShoulderCenterWorld, targetShoulderCenterWorld, boneFromBoneMap); + } + + public void AlignUpperArmByArmPcaCenters(IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, Dictionary> clothBoneMap, Transform clothesTransform, ProfileData profileData, Vector3 comprehensiveScale) + { + if (clothBoneMap == null) + { + throw new AutoMorpherException("Cloth Bone Map is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters\n - clothBoneMap is null"); + } + if (profileData == null) + { + throw new AutoMorpherException("Profile Data is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters\n - profileData is null"); + } + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(clothBoneMap, HumanBodyBones.Hips); + if (boneFromBoneMap == null) + { + throw new AutoMorpherException("Cloth Hips is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters\n - cloth hips transform is null"); + } + bool flag = profileData.LeftUpperArmSpatialData != null && profileData.LeftUpperArmSpatialData.pcaData != null && profileData.LeftUpperArmSpatialData.pcaData.pcaLength > 0.0001f; + bool flag2 = profileData.RightUpperArmSpatialData != null && profileData.RightUpperArmSpatialData.pcaData != null && profileData.RightUpperArmSpatialData.pcaData.pcaLength > 0.0001f; + if (!flag && !flag2) + { + Debug.LogWarning("[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters: profile arm data invalid. Skip."); + return; + } + Vector3 leftCenter = Vector3.zero; + if (flag) + { + leftCenter = poseMatchCommonUtil.GetProfilePcaCenterWorld(clothBoneMap, profileData.LeftUpperArmSpatialData, comprehensiveScale); + } + Vector3 rightCenter = Vector3.zero; + if (flag2) + { + rightCenter = poseMatchCommonUtil.GetProfilePcaCenterWorld(clothBoneMap, profileData.RightUpperArmSpatialData, comprehensiveScale); + } + Vector3 sourceShoulderCenterWorld = CalculateShoulderCenter(flag, flag2, leftCenter, rightCenter); + RegionStats regionStats = ComputeArmRegionStats(targetBakedBodyMeshes, targetBoneMap, isLeft: true); + RegionStats regionStats2 = ComputeArmRegionStats(targetBakedBodyMeshes, targetBoneMap, isLeft: false); + bool flag3 = regionStats.length > 0.0001f; + bool flag4 = regionStats2.length > 0.0001f; + if (!flag3 && !flag4) + { + Debug.LogWarning("[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters: target arm PCA failed. Skip."); + return; + } + Vector3 targetShoulderCenterWorld = CalculateShoulderCenter(flag3, flag4, regionStats.center, regionStats2.center); + if (!clothBoneMap.TryGetValue(HumanBodyBones.LeftUpperArm, out var value) || value == null || value.Count == 0) + { + throw new AutoMorpherException("Cloth Left UpperArm Set is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters\n - clothBoneMap has no LeftUpperArm set"); + } + if (!clothBoneMap.TryGetValue(HumanBodyBones.RightUpperArm, out var value2) || value2 == null || value2.Count == 0) + { + throw new AutoMorpherException("Cloth Right UpperArm Set is Missing", "[BodyPoseMatch_Arm] AlignUpperArmByArmPcaCenters\n - clothBoneMap has no RightUpperArm set"); + } + AlignUpperArmByArmCenters(value, value2, sourceShoulderCenterWorld, targetShoulderCenterWorld, boneFromBoneMap); + } + + private Vector3 CalculateShoulderCenter(bool leftValid, bool rightValid, Vector3 leftCenter, Vector3 rightCenter) + { + if (leftValid && rightValid) + { + return 0.5f * (leftCenter + rightCenter); + } + if (leftValid) + { + return leftCenter; + } + return rightCenter; + } + + public RegionStats ComputeArmRegionStats(IReadOnlyList avatarBakedBodyMeshes, Dictionary> avatarBoneMap, bool isLeft) + { + HumanBodyBones[] targetHumanBones = ((!isLeft) ? new HumanBodyBones[3] + { + HumanBodyBones.RightUpperArm, + HumanBodyBones.RightLowerArm, + HumanBodyBones.RightHand + } : new HumanBodyBones[3] + { + HumanBodyBones.LeftUpperArm, + HumanBodyBones.LeftLowerArm, + HumanBodyBones.LeftHand + }); + List list = poseMatchCommonUtil.CollectHumanoidVerticesWorld(targetHumanBones, avatarBakedBodyMeshes, avatarBoneMap); + if (list == null || list.Count == 0) + { + Debug.LogWarning($"[BodyPoseMatch_Arm] ComputeArmRegionStats: arm vertices 0. isLeft={isLeft}"); + return default(RegionStats); + } + RegionStats result = new PcaUtil().ComputeRegionStats(list); + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(avatarBoneMap, isLeft ? HumanBodyBones.LeftUpperArm : HumanBodyBones.RightUpperArm); + Transform boneFromBoneMap2 = poseMatchCommonUtil.GetBoneFromBoneMap(avatarBoneMap, isLeft ? HumanBodyBones.LeftLowerArm : HumanBodyBones.RightLowerArm); + Transform boneFromBoneMap3 = poseMatchCommonUtil.GetBoneFromBoneMap(avatarBoneMap, isLeft ? HumanBodyBones.LeftHand : HumanBodyBones.RightHand); + Vector3 principalAxis = Vector3.zero; + if (boneFromBoneMap != null && boneFromBoneMap3 != null) + { + principalAxis = boneFromBoneMap3.position - boneFromBoneMap.position; + } + else if (boneFromBoneMap != null && boneFromBoneMap2 != null) + { + principalAxis = boneFromBoneMap2.position - boneFromBoneMap.position; + } + else if (boneFromBoneMap2 != null && boneFromBoneMap3 != null) + { + principalAxis = boneFromBoneMap3.position - boneFromBoneMap2.position; + } + if (principalAxis.sqrMagnitude > 1E-08f) + { + principalAxis.Normalize(); + result.principalAxis = principalAxis; + } + return result; + } + + private void ScalingUpperArmLength(Dictionary> sourceBoneMap, Dictionary> targetBoneMap, bool isLeft, bool autoDetectAxis, int forceAxisIndex) + { + HumanBodyBones humanBodyBones = (isLeft ? HumanBodyBones.LeftUpperArm : HumanBodyBones.RightUpperArm); + HumanBodyBones bone = (isLeft ? HumanBodyBones.LeftLowerArm : HumanBodyBones.RightLowerArm); + HumanBodyBones bone2 = (isLeft ? HumanBodyBones.LeftHand : HumanBodyBones.RightHand); + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(sourceBoneMap, humanBodyBones); + Transform boneFromBoneMap2 = poseMatchCommonUtil.GetBoneFromBoneMap(sourceBoneMap, bone); + Transform boneFromBoneMap3 = poseMatchCommonUtil.GetBoneFromBoneMap(sourceBoneMap, bone2); + Transform boneFromBoneMap4 = poseMatchCommonUtil.GetBoneFromBoneMap(targetBoneMap, humanBodyBones); + Transform boneFromBoneMap5 = poseMatchCommonUtil.GetBoneFromBoneMap(targetBoneMap, bone); + Transform boneFromBoneMap6 = poseMatchCommonUtil.GetBoneFromBoneMap(targetBoneMap, bone2); + HashSet value; + if (boneFromBoneMap == null || boneFromBoneMap2 == null || boneFromBoneMap3 == null || boneFromBoneMap4 == null || boneFromBoneMap5 == null || boneFromBoneMap6 == null) + { + Debug.LogWarning("[BodyPoseMatch_Arm] ScalingSingArmLenght: some arm bones are null. Skip."); + } + else if (sourceBoneMap == null || !sourceBoneMap.TryGetValue(humanBodyBones, out value) || value == null || value.Count == 0) + { + Debug.LogWarning("[BodyPoseMatch_Arm] ScalingSingArmLenght: sourceUpperSet missing. Skip."); + } + else + { + poseMatchCommonUtil.BoneLengthAdjust(boneFromBoneMap, boneFromBoneMap2, boneFromBoneMap4, boneFromBoneMap5, value, autoDetectAxis, forceAxisIndex); + } + } + + private void ScalingLowerArmLength(Dictionary> sourceBoneMap, Dictionary> targetBoneMap, bool isLeft, bool autoDetectAxis, int forceAxisIndex, float sourceLowerarmExtremeX, float targetLowerarmExtremeX) + { + HumanBodyBones humanBodyBones = (isLeft ? HumanBodyBones.LeftLowerArm : HumanBodyBones.RightLowerArm); + HumanBodyBones bone = (isLeft ? HumanBodyBones.LeftHand : HumanBodyBones.RightHand); + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(sourceBoneMap, humanBodyBones); + Transform boneFromBoneMap2 = poseMatchCommonUtil.GetBoneFromBoneMap(sourceBoneMap, bone); + Transform boneFromBoneMap3 = poseMatchCommonUtil.GetBoneFromBoneMap(targetBoneMap, humanBodyBones); + if (boneFromBoneMap == null || boneFromBoneMap2 == null || boneFromBoneMap3 == null) + { + Debug.LogWarning("[BodyPoseMatch_Arm] ScalingSingArmLenght: some arm bones are null. Skip."); + return; + } + float x = boneFromBoneMap.position.x; + float x2 = boneFromBoneMap3.position.x; + float num = Mathf.Abs(sourceLowerarmExtremeX - x); + float num2 = Mathf.Abs(targetLowerarmExtremeX - x2); + if (num < 0.0001f || num2 < 0.0001f) + { + Debug.LogWarning($"[BodyPoseMatch_Arm] ScalingSingArmLenght: span too small. source={num}, target={num2}"); + return; + } + float num3 = num2 / num; + int num4 = ResolveArmLengthAxisIndex(boneFromBoneMap, boneFromBoneMap2, autoDetectAxis, forceAxisIndex); + Vector3 localScale = boneFromBoneMap.localScale; + switch (num4) + { + case 0: + localScale.x *= num3; + break; + case 1: + localScale.y *= num3; + break; + case 2: + localScale.z *= num3; + break; + } + foreach (Transform item in sourceBoneMap[humanBodyBones]) + { + if (!(item == null)) + { + item.localScale = localScale; + } + } + } + + public void ScalingBothArmsLength(IReadOnlyList proxyBakedBodyMeshes, Dictionary> proxyBoneMap, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, bool autoDetectAxis = true, int forceAxisIndex = 1) + { + if (proxyBoneMap == null || targetBoneMap == null) + { + throw new AutoMorpherException("Cloth Bone Map is Missing", "[BodyPoseMatch_Arm] ScalingSingArmLenght\n - BoneMap is null"); + } + ScalingUpperArmLength(proxyBoneMap, targetBoneMap, isLeft: true, autoDetectAxis, forceAxisIndex); + ScalingUpperArmLength(proxyBoneMap, targetBoneMap, isLeft: false, autoDetectAxis, forceAxisIndex); + if (proxyBakedBodyMeshes != null) + { + foreach (BakedBodyMesh proxyBakedBodyMesh in proxyBakedBodyMeshes) + { + proxyBakedBodyMesh.ReBakeMesh(); + } + } + ScalingLowerArmLength_BodyMatch(proxyBakedBodyMeshes, proxyBoneMap, targetBakedBodyMeshes, targetBoneMap, autoDetectAxis, forceAxisIndex); + } + + private void ScalingLowerArmLength_BodyMatch(IReadOnlyList proxyBakedBodyMeshes, Dictionary> proxyBoneMap, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, bool autoDetectAxis, int forceAxisIndex) + { + if (TryGetForearmExtremeX(proxyBakedBodyMeshes, proxyBoneMap, HumanBodyBones.LeftHand, isLeft: true, out var extremeX) && TryGetForearmExtremeX(targetBakedBodyMeshes, targetBoneMap, HumanBodyBones.LeftHand, isLeft: true, out var extremeX2)) + { + ScalingLowerArmLength(proxyBoneMap, targetBoneMap, isLeft: true, autoDetectAxis, forceAxisIndex, extremeX, extremeX2); + } + else + { + Debug.LogWarning("[BodyPoseMatch_Arm] ScalingLowerArmLength_BodyMatch: left extreme calc failed. Skip left lower."); + } + if (TryGetForearmExtremeX(proxyBakedBodyMeshes, proxyBoneMap, HumanBodyBones.RightHand, isLeft: false, out var extremeX3) && TryGetForearmExtremeX(targetBakedBodyMeshes, targetBoneMap, HumanBodyBones.RightHand, isLeft: false, out var extremeX4)) + { + ScalingLowerArmLength(proxyBoneMap, targetBoneMap, isLeft: false, autoDetectAxis, forceAxisIndex, extremeX3, extremeX4); + } + else + { + Debug.LogWarning("[BodyPoseMatch_Arm] ScalingLowerArmLength_BodyMatch: right extreme calc failed. Skip right lower."); + } + } + + public void ScalingBothArmsLength(IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, Dictionary> clothBoneMap, ProfileData profileData, Vector3 comprehensiveScale, bool autoDetectAxis = true, int forceAxisIndex = 1) + { + if (clothBoneMap == null || targetBoneMap == null) + { + throw new AutoMorpherException("Cloth Bone Map is Missing", "[BodyPoseMatch_Arm] ScalingSingArmLenght\n - BoneMap is null"); + } + if (profileData == null) + { + throw new AutoMorpherException("Profile Data is Missing", "[BodyPoseMatch_Arm] ScalingSingArmLenght\n - profileData is null"); + } + ScalingUpperArmLength(clothBoneMap, targetBoneMap, isLeft: true, autoDetectAxis, forceAxisIndex); + ScalingUpperArmLength(clothBoneMap, targetBoneMap, isLeft: false, autoDetectAxis, forceAxisIndex); + ScalingLowerArmLength_ProfileMatch(targetBakedBodyMeshes, targetBoneMap, clothBoneMap, profileData, comprehensiveScale, autoDetectAxis, forceAxisIndex); + } + + private void ScalingLowerArmLength_ProfileMatch(IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, Dictionary> clothBoneMap, ProfileData profileData, Vector3 comprehensiveScale, bool autoDetectAxis, int forceAxisIndex) + { + float extremeX; + if (profileData.LeftLowerArm_HandSpatialData == null || profileData.LeftLowerArm_HandSpatialData.volumeData == null) + { + Debug.LogWarning("[BodyPoseMatch_Arm] ScalingLowerArmLength_ProfileMatch: left spatial data missing. Skip left lower."); + } + else if (TryGetForearmExtremeX(targetBakedBodyMeshes, targetBoneMap, HumanBodyBones.LeftHand, isLeft: true, out extremeX)) + { + float profileForearmExtremeX = GetProfileForearmExtremeX(clothBoneMap, profileData.LeftLowerArm_HandSpatialData, comprehensiveScale, isLeft: true); + ScalingLowerArmLength(clothBoneMap, targetBoneMap, isLeft: true, autoDetectAxis, forceAxisIndex, profileForearmExtremeX, extremeX); + } + else + { + Debug.LogWarning("[BodyPoseMatch_Arm] ScalingLowerArmLength_ProfileMatch: left target extreme calc failed. Skip left lower."); + } + float extremeX2; + if (profileData.RightLowerArm_HandSpatialData == null || profileData.RightLowerArm_HandSpatialData.volumeData == null) + { + Debug.LogWarning("[BodyPoseMatch_Arm] ScalingLowerArmLength_ProfileMatch: right spatial data missing. Skip right lower."); + } + else if (TryGetForearmExtremeX(targetBakedBodyMeshes, targetBoneMap, HumanBodyBones.RightHand, isLeft: false, out extremeX2)) + { + float profileForearmExtremeX2 = GetProfileForearmExtremeX(clothBoneMap, profileData.RightLowerArm_HandSpatialData, comprehensiveScale, isLeft: false); + ScalingLowerArmLength(clothBoneMap, targetBoneMap, isLeft: false, autoDetectAxis, forceAxisIndex, profileForearmExtremeX2, extremeX2); + } + else + { + Debug.LogWarning("[BodyPoseMatch_Arm] ScalingLowerArmLength_ProfileMatch: right target extreme calc failed. Skip right lower."); + } + } + + private bool TryGetForearmExtremeX(IReadOnlyList bakedBodyMeshes, Dictionary> boneMap, HumanBodyBones targetBone, bool isLeft, out float extremeX, float weightThreshold = 0.15f, int sampleStep = 1) + { + extremeX = (isLeft ? float.PositiveInfinity : float.NegativeInfinity); + if (bakedBodyMeshes == null || boneMap == null) + { + return false; + } + if (!boneMap.TryGetValue(targetBone, out var value) || value == null || value.Count == 0) + { + return false; + } + List list = poseMatchCommonUtil.CollectWeightedVerticesWorld(bakedBodyMeshes, value, weightThreshold, sampleStep); + if (list == null || list.Count == 0) + { + return false; + } + for (int i = 0; i < list.Count; i++) + { + float x = list[i].x; + if (isLeft) + { + if (x < extremeX) + { + extremeX = x; + } + } + else if (x > extremeX) + { + extremeX = x; + } + } + return true; + } + + private float GetProfileForearmExtremeX(Dictionary> clothBoneMap, BoneSpatialData spatialData, Vector3 comprehensiveScale, bool isLeft) + { + if (spatialData == null || spatialData.volumeData == null) + { + throw new AutoMorpherException("Profile Volume Data is Missing", "[BodyPoseMatch_Arm] GetProfileForearmExtremeX\n - spatialData or spatialData.volumeData is null"); + } + if (poseMatchCommonUtil.GetBoneFromBoneMap(clothBoneMap, spatialData.refBone) == null) + { + throw new AutoMorpherException("Profile Ref Bone Transform is Missing", "[BodyPoseMatch_Arm] GetProfileForearmExtremeX\n - refBone transform is null"); + } + BodyPoseMatch_CommonUtil bodyPoseMatch_CommonUtil = new BodyPoseMatch_CommonUtil(); + float x = bodyPoseMatch_CommonUtil.GetProfileVolumeMinWorld(clothBoneMap, spatialData, 1, comprehensiveScale).x; + float x2 = bodyPoseMatch_CommonUtil.GetProfileVolumeMaxWorld(clothBoneMap, spatialData, 1, comprehensiveScale).x; + if (!isLeft) + { + return x2; + } + return x; + } + + private int ResolveArmLengthAxisIndex(Transform lowerArm, Transform hand, bool autoDetectAxis, int forceAxisIndex) + { + if (!autoDetectAxis) + { + return Mathf.Clamp(forceAxisIndex, 0, 2); + } + Vector3 direction = hand.position - lowerArm.position; + Vector3 vector = lowerArm.InverseTransformDirection(direction); + float num = Mathf.Abs(vector.x); + float num2 = Mathf.Abs(vector.y); + float num3 = Mathf.Abs(vector.z); + if (num2 >= num && num2 >= num3) + { + return 1; + } + if (num >= num3) + { + return 0; + } + return 2; + } + + public void DrawArmPcaDebug(GameObject avatarObject, IReadOnlyList avatarBakedBodyMeshes, Dictionary> avatarBoneMap, bool isLeft, Color centerColor, Color axisColor, float axisScale = 1f, float duration = 0.1f) + { + if (avatarBakedBodyMeshes != null && avatarBoneMap != null) + { + RegionStats regionStats = ComputeArmRegionStats(avatarBakedBodyMeshes, avatarBoneMap, isLeft); + if (!(regionStats.length < 0.0001f)) + { + Vector3 center = regionStats.center; + Vector3 vector = ((regionStats.principalAxis.sqrMagnitude > 1E-08f) ? regionStats.principalAxis.normalized : Vector3.up); + float num = 0.02f * axisScale; + Debug.DrawLine(center + Vector3.up * num, center - Vector3.up * num, centerColor, duration); + Debug.DrawLine(center + Vector3.right * num, center - Vector3.right * num, centerColor, duration); + Debug.DrawLine(center + Vector3.forward * num, center - Vector3.forward * num, centerColor, duration); + float num2 = regionStats.length * 0.5f * axisScale; + Debug.DrawLine(center - vector * num2, center + vector * num2, axisColor, duration); + } + } + } + + public void DrawForearmExtremeDebugPair(GameObject proxyObject, IReadOnlyList proxyBodyMeshes, GameObject targetObject, IReadOnlyList targetBodyMeshes, bool isLeft, float size = 0.03f, float duration = 3f) + { + DrawForearmExtremeDebugSingle(proxyObject, proxyBodyMeshes, isLeft, Color.blue, Color.cyan, size, duration); + DrawForearmExtremeDebugSingle(targetObject, targetBodyMeshes, isLeft, Color.red, Color.magenta, size, duration); + } + + public void DrawForearmExtremeDebugSingle(GameObject avatarObject, IReadOnlyList bodyMeshes, bool isLeft, Color lineColor, Color pointColor, float size = 0.03f, float duration = 3f) + { + if (avatarObject == null || bodyMeshes == null || bodyMeshes.Count == 0) + { + return; + } + Animator component = avatarObject.GetComponent(); + if (!(component == null)) + { + HumanBodyBones humanBoneId = (isLeft ? HumanBodyBones.LeftLowerArm : HumanBodyBones.RightLowerArm); + Transform boneTransform = component.GetBoneTransform(humanBoneId); + if (boneTransform == null) + { + Debug.LogWarning($"[BodyPoseMatch_Arm] DrawForearmExtremeDebugSingle: elbow bone missing. isLeft={isLeft}"); + return; + } + if (!TryGetForearmExtremePoint(component, bodyMeshes, isLeft, out var extremePos)) + { + Debug.LogWarning($"[BodyPoseMatch_Arm] DrawForearmExtremeDebugSingle: extreme vertex not found. isLeft={isLeft}"); + return; + } + Debug.DrawLine(boneTransform.position, extremePos, lineColor, duration); + Debug.DrawLine(extremePos + Vector3.up * size, extremePos - Vector3.up * size, pointColor, duration); + Debug.DrawLine(extremePos + Vector3.right * size, extremePos - Vector3.right * size, pointColor, duration); + Debug.DrawLine(extremePos + Vector3.forward * size, extremePos - Vector3.forward * size, pointColor, duration); + } + } + + private bool TryGetForearmExtremePoint(Animator avatar, IReadOnlyList bodyMeshes, bool isLeft, out Vector3 extremePos) + { + extremePos = default(Vector3); + if (avatar == null || bodyMeshes == null || bodyMeshes.Count == 0) + { + return false; + } + HumanBodyBones humanBoneId = (isLeft ? HumanBodyBones.LeftHand : HumanBodyBones.RightHand); + Transform boneTransform = avatar.GetBoneTransform(humanBoneId); + if (boneTransform == null) + { + return false; + } + HashSet targetBoneSet = new HashSet { boneTransform }; + List list = new List(bodyMeshes.Count); + for (int i = 0; i < bodyMeshes.Count; i++) + { + SkinnedMeshRenderer skinnedMeshRenderer = bodyMeshes[i]; + if (!(skinnedMeshRenderer == null)) + { + BakedBodyMesh bakedBodyMesh = new BakedBodyMesh(skinnedMeshRenderer); + bakedBodyMesh.ReBakeMesh(); + list.Add(bakedBodyMesh); + } + } + if (list.Count == 0) + { + return false; + } + List list2 = poseMatchCommonUtil.CollectWeightedVerticesWorld(list, targetBoneSet, 0.1f); + if (list2 == null || list2.Count == 0) + { + return false; + } + bool flag = false; + float num = (isLeft ? float.PositiveInfinity : float.NegativeInfinity); + for (int j = 0; j < list2.Count; j++) + { + Vector3 vector = list2[j]; + float x = vector.x; + if (!flag) + { + flag = true; + num = x; + extremePos = vector; + } + else if (isLeft) + { + if (x < num) + { + num = x; + extremePos = vector; + } + } + else if (x > num) + { + num = x; + extremePos = vector; + } + } + return flag; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Arm.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Arm.cs.meta new file mode 100644 index 0000000..aec19bb --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Arm.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 60d86795fca316b438216c5648161dce \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_CommonUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_CommonUtil.cs new file mode 100644 index 0000000..4aa7170 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_CommonUtil.cs @@ -0,0 +1,248 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BodyPoseMatch_CommonUtil +using System.Collections.Generic; +using System.Linq; +using Eden.AutoMorpher; +using Eden.AutoMorpher.profile; +using UnityEngine; + +public class BodyPoseMatch_CommonUtil +{ + public Transform GetBoneFromBoneMap(Dictionary> boneMap, HumanBodyBones bone) + { + if (boneMap == null || !boneMap.TryGetValue(bone, out var value) || value == null) + { + return null; + } + return value.FirstOrDefault(); + } + + public Vector3 GetProfilePcaCenterWorld(Dictionary> clothBoneMap, BoneSpatialData boneSpatialData, Vector3 comprehensiveScale, string errorContext = "") + { + if (clothBoneMap == null) + { + throw new AutoMorpherException("Cloth Bone Map is Missing", "[BodyPoseMatch_CommonUtil] GetProfilePcaCenterWorld\n - clothBoneMap is null (context=" + errorContext + ")"); + } + if (boneSpatialData == null || boneSpatialData.pcaData == null) + { + throw new AutoMorpherException("Profile Bone Spatial Data is Missing", "[BodyPoseMatch_CommonUtil] GetProfilePcaCenterWorld\n - boneSpatialData or boneSpatialData.pcaData is null (context=" + errorContext + ")"); + } + Transform boneFromBoneMap = GetBoneFromBoneMap(clothBoneMap, boneSpatialData.refBone); + if (boneFromBoneMap == null) + { + throw new AutoMorpherException("Profile Ref Bone Transform is Missing", "[BodyPoseMatch_CommonUtil] GetProfilePcaCenterWorld" + $"\n - refBone transform is null (refBone={boneSpatialData.refBone}, context={errorContext})"); + } + if (GetBoneFromBoneMap(clothBoneMap, HumanBodyBones.Hips) == null) + { + throw new AutoMorpherException("Hip Transform is Missing", "[BodyPoseMatch_CommonUtil] GetProfilePcaCenterWorld\n - hip transform is null (context=" + errorContext + ")"); + } + Vector3 pcaCenter = boneSpatialData.pcaData.pcaCenter; + Vector3 position = new Vector3(pcaCenter.x / comprehensiveScale.x, pcaCenter.y / comprehensiveScale.y, pcaCenter.z / comprehensiveScale.z); + return boneFromBoneMap.TransformPoint(position); + } + + public Vector3 GetProfileVolumeMinWorld(Dictionary> clothBoneMap, BoneSpatialData spatialData, int axis, Vector3 comprehensiveScale) + { + return GetProfileVolumeWorld(clothBoneMap, spatialData, axis, comprehensiveScale, isMin: true); + } + + public Vector3 GetProfileVolumeMaxWorld(Dictionary> clothBoneMap, BoneSpatialData spatialData, int axis, Vector3 comprehensiveScale) + { + return GetProfileVolumeWorld(clothBoneMap, spatialData, axis, comprehensiveScale, isMin: false); + } + + public Vector3 GetProfileVolumeWorld(Dictionary> clothBoneMap, BoneSpatialData spatialData, int axisIndex, Vector3 comprehensiveScale, bool isMin) + { + if (clothBoneMap == null) + { + throw new AutoMorpherException("Cloth Bone Map is Missing", "[BodyPoseMatch_CommonUtil] GetProfileVolumeWorld\n - clothBoneMap is null"); + } + if (spatialData == null || spatialData.volumeData == null) + { + throw new AutoMorpherException("Profile Volume Data is Missing", "[BodyPoseMatch_CommonUtil] GetProfileVolumeWorld\n - spatialData or spatialData.volumeData is null"); + } + Transform boneFromBoneMap = GetBoneFromBoneMap(clothBoneMap, spatialData.refBone); + if (boneFromBoneMap == null) + { + throw new AutoMorpherException("Profile Ref Bone Transform is Missing", "[BodyPoseMatch_CommonUtil] GetProfileVolumeWorld" + $"\n - refBone transform is null (refBone={spatialData.refBone})"); + } + Vector3 vector = axisIndex switch + { + 0 => isMin ? spatialData.volumeData.fMinLocalPos : spatialData.volumeData.fMaxLocalPos, + 1 => isMin ? spatialData.volumeData.rMinLocalPos : spatialData.volumeData.rMaxLocalPos, + 2 => isMin ? spatialData.volumeData.uMinLocalPos : spatialData.volumeData.uMaxLocalPos, + _ => throw new AutoMorpherException("Unsupported Volume Axis Index", "[BodyPoseMatch_CommonUtil] GetProfileVolumeWorld" + $"\n - axisIndex must be 0(Forward), 1(Right), 2(Up) (axisIndex={axisIndex})"), + }; + Vector3 position = new Vector3(vector.x / comprehensiveScale.x, vector.y / comprehensiveScale.y, vector.z / comprehensiveScale.z); + return boneFromBoneMap.TransformPoint(position); + } + + public List CollectWeightedVerticesWorld(IReadOnlyList bakedBodyMeshes, HashSet targetBoneSet, float weightThreshold = 0.15f, int sampleStep = 1) + { + List list = new List(); + if (bakedBodyMeshes == null || targetBoneSet == null || targetBoneSet.Count == 0) + { + return list; + } + foreach (BakedBodyMesh bakedBodyMesh in bakedBodyMeshes) + { + if (bakedBodyMesh == null || bakedBodyMesh.smr == null) + { + continue; + } + Mesh sharedMesh = bakedBodyMesh.smr.sharedMesh; + if (sharedMesh == null) + { + continue; + } + BoneWeight[] boneWeights = sharedMesh.boneWeights; + Transform[] bones = bakedBodyMesh.smr.bones; + if (boneWeights == null || boneWeights.Length == 0 || bones == null || bones.Length == 0) + { + continue; + } + Vector3[] worldVerticesWithBakedMesh = new WorldVertexUtil().GetWorldVerticesWithBakedMesh(bakedBodyMesh.smr, bakedBodyMesh.bakedMesh); + if (worldVerticesWithBakedMesh == null) + { + continue; + } + int num = Mathf.Min(worldVerticesWithBakedMesh.Length, boneWeights.Length); + for (int i = 0; i < num; i += sampleStep) + { + BoneWeight boneWeight = boneWeights[i]; + float wSum = 0f; + Acc(boneWeight.boneIndex0, boneWeight.weight0); + Acc(boneWeight.boneIndex1, boneWeight.weight1); + Acc(boneWeight.boneIndex2, boneWeight.weight2); + Acc(boneWeight.boneIndex3, boneWeight.weight3); + if (!(wSum < weightThreshold)) + { + list.Add(worldVerticesWithBakedMesh[i]); + } + void Acc(int index, float w) + { + if (index >= 0 && index < bones.Length && !(w <= 0f)) + { + Transform transform = bones[index]; + if (transform != null && targetBoneSet.Contains(transform)) + { + wSum += w; + } + } + } + } + } + return list; + } + + public List CollectHumanoidVerticesWorld(IReadOnlyList targetHumanBones, IReadOnlyList bakedBodyMeshes, Dictionary> boneMap, float weightThreshold = 0.15f, int sampleStep = 1) + { + List result = new List(); + if (bakedBodyMeshes == null || boneMap == null || boneMap.Count == 0 || targetHumanBones == null) + { + throw new AutoMorpherException("Invalid Arguments for CollectHumanoidVerticesWorld", "[BodyPoseMatchCommonUtil] CollectHumanoidVerticesWorld\n - bakedBodyMeshes is null OR boneMap is null/empty OR targetHumanBones is null"); + } + HashSet hashSet = new HashSet(); + foreach (HumanBodyBones targetHumanBone in targetHumanBones) + { + if (!boneMap.TryGetValue(targetHumanBone, out var value) || value == null) + { + continue; + } + foreach (Transform item in value) + { + if (item != null) + { + hashSet.Add(item); + } + } + } + if (hashSet.Count == 0) + { + Debug.Log("[AvatarBodyMatchUtil] CollectBodyPartVerticesWorld: targetBoneSet is empty."); + return result; + } + return CollectWeightedVerticesWorld(bakedBodyMeshes, hashSet, weightThreshold, sampleStep); + } + + public void BoneLengthAdjust(Transform proxyParent, Transform proxyChild, Transform targetParent, Transform targetChild, HashSet proxyParentSet, bool autoDetectAxis = true, int forceAxisIndex = 1) + { + if (proxyParent == null || proxyChild == null || targetParent == null || targetChild == null) + { + Debug.LogWarning("[AvatarBodyMatchUtil] MatchBoneSegmentLength: bone null"); + return; + } + float num = Vector3.Distance(proxyParent.position, proxyChild.position); + float num2 = Vector3.Distance(targetParent.position, targetChild.position); + if (num < 1E-06f || num2 < 1E-06f) + { + Debug.LogWarning($"[AvatarBodyMatchUtil] MatchBoneSegmentLength: too small length. proxy={num}, target={num2}"); + return; + } + float num3 = num2 / num; + int num7; + if (autoDetectAxis) + { + Vector3 direction = proxyChild.position - proxyParent.position; + Vector3 vector = proxyParent.InverseTransformDirection(direction); + float num4 = Mathf.Abs(vector.x); + float num5 = Mathf.Abs(vector.y); + float num6 = Mathf.Abs(vector.z); + num7 = ((num5 >= num4 && num5 >= num6) ? 1 : ((!(num4 >= num6)) ? 2 : 0)); + } + else + { + num7 = Mathf.Clamp(forceAxisIndex, 0, 2); + } + Vector3 localScale = proxyParent.localScale; + switch (num7) + { + case 0: + localScale.x *= num3; + break; + case 1: + localScale.y *= num3; + break; + case 2: + localScale.z *= num3; + break; + } + foreach (Transform item in proxyParentSet) + { + item.localScale = localScale; + } + } + + public Transform CreateTempMarker(string markerObjectName, Transform parentTransform, IReadOnlyList childTransforms, Vector3 markerWorldPosition, Quaternion markerWorldRotation, Vector3 markerLocalScale, out TempBoneMarker tempBoneMarker) + { + if (parentTransform == null) + { + throw new AutoMorpherException("Marker Parent Transform is Missing", "[BodyPoseMatch_CommonUtil] CreateTempMarker\n - parentTransform is null"); + } + if (childTransforms == null || childTransforms.Count == 0) + { + throw new AutoMorpherException("Child Transforms are Missing", "[BodyPoseMatch_CommonUtil] CreateTempMarker\n - childTransforms is null or empty"); + } + Transform transform = new GameObject(string.IsNullOrEmpty(markerObjectName) ? "scaleSupportBone" : markerObjectName).transform; + transform.SetParent(parentTransform, worldPositionStays: true); + transform.position = markerWorldPosition; + transform.rotation = markerWorldRotation; + transform.localScale = markerLocalScale; + tempBoneMarker = transform.gameObject.AddComponent(); + tempBoneMarker.originalParent = parentTransform; + tempBoneMarker.additionalInfo = ""; + tempBoneMarker.wrappedChildNames = new List(); + for (int i = 0; i < childTransforms.Count; i++) + { + Transform transform2 = childTransforms[i]; + if (!(transform2 == null)) + { + tempBoneMarker.wrappedChildNames.Add(transform2.name); + transform2.SetParent(transform, worldPositionStays: true); + } + } + return transform; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_CommonUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_CommonUtil.cs.meta new file mode 100644 index 0000000..05cb4d3 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_CommonUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 365154814e57582478896172f94bfcca \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Leg.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Leg.cs new file mode 100644 index 0000000..5a3fa2c --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Leg.cs @@ -0,0 +1,627 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BodyPoseMatch_Leg +using System.Collections.Generic; +using Eden.AutoMorpher; +using Eden.AutoMorpher.profile; +using UnityEngine; + +public class BodyPoseMatch_Leg +{ + private readonly BodyPoseMatch_CommonUtil poseMatchCommonUtil; + + public BodyPoseMatch_Leg() + { + poseMatchCommonUtil = new BodyPoseMatch_CommonUtil(); + } + + public void AlignBothUpperLegs(GameObject proxyObject, IReadOnlyList proxyBakedBodyMeshes, Dictionary> proxyBoneMap, GameObject targetObject, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap) + { + if (proxyObject == null || targetObject == null) + { + throw new AutoMorpherException("Proxy or Target Object is Missing", "[BodyPoseMatch_Leg] AlignBothUpperLegs\n - proxyObject or targetObject is null"); + } + AlignUpperLeg_BodyMatch(proxyObject, proxyBakedBodyMeshes, proxyBoneMap, targetObject, targetBakedBodyMeshes, targetBoneMap, isLeft: true); + AlignUpperLeg_BodyMatch(proxyObject, proxyBakedBodyMeshes, proxyBoneMap, targetObject, targetBakedBodyMeshes, targetBoneMap, isLeft: false); + } + + public void AlignBothUpperLegs(Transform targetTransform, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, Transform clothesTransform, Dictionary> clothBoneMap, ProfileData profileData, Vector3 comprehensiveScale) + { + AlignUpperLeg_Profile(targetTransform, targetBakedBodyMeshes, targetBoneMap, clothesTransform, clothBoneMap, profileData, comprehensiveScale, isLeft: true); + AlignUpperLeg_Profile(targetTransform, targetBakedBodyMeshes, targetBoneMap, clothesTransform, clothBoneMap, profileData, comprehensiveScale, isLeft: false); + } + + public void AlignUpperLeg_BodyMatch(GameObject proxyObject, IReadOnlyList proxyBakedBodyMeshes, Dictionary> proxyBoneMap, GameObject targetObject, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, bool isLeft) + { + if (proxyObject == null || targetObject == null) + { + throw new AutoMorpherException("Proxy or Target Object is Missing", "[BodyPoseMatch_Leg] AlignUpperLeg_BodyMatch\n - proxyObject or targetObject is null"); + } + RegionStats regionStats = ComputeLegRegionStats(proxyObject.transform, proxyBakedBodyMeshes, proxyBoneMap, isLeft); + RegionStats regionStats2 = ComputeLegRegionStats(targetObject.transform, targetBakedBodyMeshes, targetBoneMap, isLeft); + if (regionStats.length < 0.0001f || regionStats2.length < 0.0001f) + { + Debug.LogWarning("[BodyPoseMatch_Leg] AlignUpperLeg_BodyMatch: leg PCA 통계가 비정상입니다."); + return; + } + HumanBodyBones key = (isLeft ? HumanBodyBones.LeftUpperLeg : HumanBodyBones.RightUpperLeg); + if (proxyBoneMap == null || !proxyBoneMap.TryGetValue(key, out var value) || value == null || value.Count == 0) + { + Debug.LogWarning("[BodyPoseMatch_Leg] AlignUpperLeg_BodyMatch: proxy UpperLeg 본을 찾지 못했습니다."); + } + else + { + AlignUpperLegCore(value, regionStats.center, regionStats2.center); + } + } + + public void AlignUpperLeg_Profile(Transform targetTransform, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, Transform clothesTransform, Dictionary> clothBoneMap, ProfileData profileData, Vector3 comprehensiveScale, bool isLeft) + { + if (targetTransform == null) + { + throw new AutoMorpherException("Target Object is Missing", "[BodyPoseMatch_Leg] AlignUpperLeg_Profile\n - targetObject is null"); + } + HumanBodyBones key = (isLeft ? HumanBodyBones.LeftUpperLeg : HumanBodyBones.RightUpperLeg); + BoneSpatialData boneSpatialData = (isLeft ? profileData.LeftUpperLegSpatialData : profileData.RightUpperLegSpatialData); + if (boneSpatialData == null || boneSpatialData.pcaData == null) + { + Debug.LogWarning("[BodyPoseMatch_Leg] AlignUpperLeg: " + (isLeft ? "Left" : "Right") + " spatial data missing."); + return; + } + Vector3 profilePcaCenterWorld = poseMatchCommonUtil.GetProfilePcaCenterWorld(clothBoneMap, boneSpatialData, comprehensiveScale); + RegionStats regionStats = ComputeLegRegionStats(targetTransform, targetBakedBodyMeshes, targetBoneMap, isLeft); + HashSet value; + if (regionStats.length < 0.0001f) + { + Debug.LogWarning("[BodyPoseMatch_Leg] AlignUpperLeg: Target leg PCA 통계가 비정상입니다."); + } + else if (clothBoneMap == null || !clothBoneMap.TryGetValue(key, out value) || value == null || value.Count == 0) + { + Debug.LogWarning("[BodyPoseMatch_Leg] AlignUpperLeg: clothes UpperLeg 본을 찾지 못했습니다."); + } + else + { + AlignUpperLegCore(value, profilePcaCenterWorld, regionStats.center); + } + } + + private void AlignUpperLegCore(IReadOnlyCollection upperLegSet, Vector3 sourceLegCenterWorld, Vector3 targetLegCenterWorld) + { + if (upperLegSet == null || upperLegSet.Count == 0) + { + return; + } + Vector3 vector = targetLegCenterWorld - sourceLegCenterWorld; + vector.y = 0f; + foreach (Transform item in upperLegSet) + { + if (!(item == null)) + { + item.position += vector; + } + } + } + + public RegionStats ComputeLegRegionStats(Transform avatarTransform, IReadOnlyList avatarBakedBodyMeshes, Dictionary> avatarBoneMap, bool isLeft) + { + HumanBodyBones[] targetHumanBones = ((!isLeft) ? new HumanBodyBones[2] + { + HumanBodyBones.RightUpperLeg, + HumanBodyBones.RightLowerLeg + } : new HumanBodyBones[2] + { + HumanBodyBones.LeftUpperLeg, + HumanBodyBones.LeftLowerLeg + }); + List list = poseMatchCommonUtil.CollectHumanoidVerticesWorld(targetHumanBones, avatarBakedBodyMeshes, avatarBoneMap); + if (list == null || list.Count == 0) + { + Debug.LogWarning($"[BodyPoseMatch_Leg] ComputeLegRegionStats: leg vertices 0개. avatar={avatarTransform?.name}, isLeft={isLeft}"); + return default(RegionStats); + } + RegionStats result = new PcaUtil().ComputeRegionStats(list); + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(avatarBoneMap, isLeft ? HumanBodyBones.LeftUpperLeg : HumanBodyBones.RightUpperLeg); + Transform boneFromBoneMap2 = poseMatchCommonUtil.GetBoneFromBoneMap(avatarBoneMap, isLeft ? HumanBodyBones.LeftLowerLeg : HumanBodyBones.RightLowerLeg); + if (boneFromBoneMap != null && boneFromBoneMap2 != null) + { + Vector3 normalized = (boneFromBoneMap2.position - boneFromBoneMap.position).normalized; + Vector3 vector = result.principalAxis.normalized; + if (Vector3.Dot(vector, normalized) < 0f) + { + vector = -vector; + } + result.principalAxis = vector; + } + return result; + } + + public void ScalingBothLegsAndFoots(GameObject proxyObject, IReadOnlyList proxyBakedBodyMeshes, Dictionary> proxyBoneMap, GameObject targetObject, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, bool autoDetectAxis = true, int forceAxisIndex = 1) + { + ScalingLeg(proxyBoneMap, targetBoneMap, isLeft: true, autoDetectAxis, forceAxisIndex); + ScalingLeg(proxyBoneMap, targetBoneMap, isLeft: false, autoDetectAxis, forceAxisIndex); + if (proxyBakedBodyMeshes != null) + { + foreach (BakedBodyMesh proxyBakedBodyMesh in proxyBakedBodyMeshes) + { + proxyBakedBodyMesh?.ReBakeMesh(); + } + } + ScalingFoot(proxyObject, proxyBakedBodyMeshes, proxyBoneMap, targetObject, targetBakedBodyMeshes, targetBoneMap, isLeft: true); + ScalingFoot(proxyObject, proxyBakedBodyMeshes, proxyBoneMap, targetObject, targetBakedBodyMeshes, targetBoneMap, isLeft: false); + } + + public void ScalingBothLegsAndFoots(Transform targetTransform, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, Transform clothTransform, Dictionary> clothBoneMap, ProfileData profileData, Vector3 comprehensiveScale, bool autoDetectAxis = true, int forceAxisIndex = 1) + { + ScalingLeg(clothBoneMap, targetBoneMap, isLeft: true, autoDetectAxis, forceAxisIndex); + ScalingLeg(clothBoneMap, targetBoneMap, isLeft: false, autoDetectAxis, forceAxisIndex); + ScalingFoot(profileData, comprehensiveScale, clothBoneMap, targetTransform, targetBakedBodyMeshes, targetBoneMap, isLeft: true); + ScalingFoot(profileData, comprehensiveScale, clothBoneMap, targetTransform, targetBakedBodyMeshes, targetBoneMap, isLeft: false); + } + + public void ScalingLeg(Dictionary> proxyBoneMap, Dictionary> targetBoneMap, bool isLeft, bool autoDetectAxis = true, int forceAxisIndex = 1) + { + HumanBodyBones humanBodyBones = (isLeft ? HumanBodyBones.LeftUpperLeg : HumanBodyBones.RightUpperLeg); + HumanBodyBones bone = (isLeft ? HumanBodyBones.LeftLowerLeg : HumanBodyBones.RightLowerLeg); + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(proxyBoneMap, humanBodyBones); + Transform boneFromBoneMap2 = poseMatchCommonUtil.GetBoneFromBoneMap(proxyBoneMap, bone); + Transform boneFromBoneMap3 = poseMatchCommonUtil.GetBoneFromBoneMap(targetBoneMap, humanBodyBones); + Transform boneFromBoneMap4 = poseMatchCommonUtil.GetBoneFromBoneMap(targetBoneMap, bone); + if (boneFromBoneMap == null || boneFromBoneMap2 == null || boneFromBoneMap3 == null || boneFromBoneMap4 == null) + { + Debug.LogWarning("[AvatarBodyMatchUtil] ScalingLeg: some leg bones are null"); + return; + } + if (proxyBoneMap == null || !proxyBoneMap.TryGetValue(humanBodyBones, out var value) || value == null || value.Count == 0) + { + Debug.LogWarning("[AvatarBodyMatchUtil] ScalingLeg: proxy upperLeg bone set missing"); + return; + } + poseMatchCommonUtil.BoneLengthAdjust(boneFromBoneMap, boneFromBoneMap2, boneFromBoneMap3, boneFromBoneMap4, value, autoDetectAxis, forceAxisIndex); + HumanBodyBones humanBodyBones2 = (isLeft ? HumanBodyBones.LeftFoot : HumanBodyBones.RightFoot); + Transform boneFromBoneMap5 = poseMatchCommonUtil.GetBoneFromBoneMap(proxyBoneMap, humanBodyBones2); + Transform boneFromBoneMap6 = poseMatchCommonUtil.GetBoneFromBoneMap(targetBoneMap, humanBodyBones2); + if (boneFromBoneMap5 == null || boneFromBoneMap6 == null) + { + Debug.LogWarning("[AvatarBodyMatchUtil] ScalingFoot: proxyFoot or targetFoot is null"); + return; + } + float y = boneFromBoneMap5.position.y; + float num = boneFromBoneMap6.position.y - y; + if (!(Mathf.Abs(num) > 1E-06f)) + { + return; + } + Vector3 vector = new Vector3(0f, num, 0f); + if (proxyBoneMap == null || !proxyBoneMap.TryGetValue(humanBodyBones2, out var value2) || value2 == null || value2.Count <= 0) + { + return; + } + foreach (Transform item in value2) + { + if (!(item == null)) + { + item.position += vector; + } + } + } + + private void ScalingFootCore(Transform proxyHip, Transform targetFoot, bool isLeft, HashSet footBoneSet, float proxyToeF, float proxyHeelF, float proxySideMin, float proxySideMax, float proxySoleY, float targetToeF, float targetHeelF, float targetSideMin, float targetSideMax, float targetSoleY) + { + if (proxyHip == null || targetFoot == null) + { + Debug.LogWarning("[BodyPoseMatch_Leg] ScalingFootCore: proxyHip or targetFoot is null"); + return; + } + if (footBoneSet == null || footBoneSet.Count == 0) + { + Debug.LogWarning("[BodyPoseMatch_Leg] ScalingFootCore: footBoneSet is null or empty"); + return; + } + Dictionary> dictionary = new Dictionary>(); + foreach (Transform item in footBoneSet) + { + if (!(item == null)) + { + Transform parent = item.parent; + if (!dictionary.TryGetValue(parent, out var value)) + { + value = new List(); + dictionary.Add(parent, value); + } + value.Add(item); + } + } + foreach (KeyValuePair> item2 in dictionary) + { + Transform key = item2.Key; + List value2 = item2.Value; + if (key == null || value2 == null || value2.Count == 0) + { + continue; + } + Transform transform = value2[0]; + if (!(transform == null)) + { + TempBoneMarker tempBoneMarker; + Transform transform2 = poseMatchCommonUtil.CreateTempMarker("scaleSupportBone", key, value2, transform.position, Quaternion.identity, transform.localScale, out tempBoneMarker); + if (!(transform2 == null)) + { + ScaleAndAlignAlongAxis(proxyHip, proxyHip.forward, proxyHip.up, transform2, proxyToeF, proxyHeelF, targetFoot, targetToeF, targetHeelF); + ScaleAndAlignAlongAxis(proxyHip, proxyHip.right, proxyHip.up, transform2, isLeft ? proxySideMin : proxySideMax, isLeft ? proxySideMax : proxySideMin, targetFoot, isLeft ? targetSideMin : targetSideMax, isLeft ? targetSideMax : targetSideMin); + ScaleAndAlignAlongAxis(proxyHip, proxyHip.up, proxyHip.forward, transform2, 0f, proxySoleY, targetFoot, 0f, targetSoleY); + } + } + } + } + + public void ScalingFoot(GameObject proxyObject, IReadOnlyList proxyBakedBodyMeshes, Dictionary> proxyBoneMap, GameObject targetObject, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, bool isLeft) + { + if (proxyObject == null || targetObject == null) + { + throw new AutoMorpherException("Proxy or Target Object is Missing", "[BodyPoseMatch_Leg] ScalingFoot\n - proxyObject or targetObject is null"); + } + HumanBodyBones humanBodyBones = (isLeft ? HumanBodyBones.LeftFoot : HumanBodyBones.RightFoot); + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(proxyBoneMap, humanBodyBones); + Transform boneFromBoneMap2 = poseMatchCommonUtil.GetBoneFromBoneMap(targetBoneMap, humanBodyBones); + if (boneFromBoneMap == null || boneFromBoneMap2 == null) + { + Debug.LogWarning("[AvatarBodyMatchUtil] ScalingFoot: proxyFoot or targetFoot is null"); + return; + } + if (!TryGetFootSpatialData(proxyObject.transform, proxyBakedBodyMeshes, proxyBoneMap, isLeft, out var _, out var footMinUp, out var heelF, out var toeF, out var sideMin, out var sideMax) || !TryGetFootSpatialData(targetObject.transform, targetBakedBodyMeshes, targetBoneMap, isLeft, out var _, out var footMinUp2, out var heelF2, out var toeF2, out var sideMin2, out var sideMax2)) + { + Debug.LogWarning("[AvatarBodyMatchUtil] ScalingFoot: foot spatial data 계산 실패"); + return; + } + Transform boneFromBoneMap3 = poseMatchCommonUtil.GetBoneFromBoneMap(proxyBoneMap, HumanBodyBones.Hips); + HashSet value; + if (boneFromBoneMap3 == null) + { + Debug.LogWarning("[BodyPoseMatch_Leg] ScalingFoot: proxyHip is null"); + } + else if (proxyBoneMap == null || !proxyBoneMap.TryGetValue(humanBodyBones, out value) || value == null || value.Count == 0) + { + Debug.LogWarning("[BodyPoseMatch_Leg] ScalingFoot: footBoneSet is missing"); + } + else + { + ScalingFootCore(boneFromBoneMap3, boneFromBoneMap2, isLeft, value, toeF, heelF, sideMin, sideMax, footMinUp, toeF2, heelF2, sideMin2, sideMax2, footMinUp2); + } + } + + public void ScalingFoot(ProfileData profileData, Vector3 comprehensiveScale, Dictionary> clothBoneMap, Transform targetTransform, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, bool isLeft) + { + if (targetTransform == null) + { + throw new AutoMorpherException("Target Transform is Missing", "[BodyPoseMatch_Leg] ScalingFoot\n - targetTransform is null"); + } + HumanBodyBones humanBodyBones = (isLeft ? HumanBodyBones.LeftFoot : HumanBodyBones.RightFoot); + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(clothBoneMap, humanBodyBones); + Transform boneFromBoneMap2 = poseMatchCommonUtil.GetBoneFromBoneMap(targetBoneMap, humanBodyBones); + if (boneFromBoneMap == null || boneFromBoneMap2 == null) + { + Debug.LogWarning("[AvatarBodyMatchUtil] ScalingFoot: proxyFoot or targetFoot is null"); + return; + } + if (!TryGetFootSpatialData(targetTransform.transform, targetBakedBodyMeshes, targetBoneMap, isLeft, out var _, out var footMinUp, out var heelF, out var toeF, out var sideMin, out var sideMax)) + { + Debug.LogWarning("[AvatarBodyMatchUtil] ScalingFoot: foot spatial data 계산 실패"); + return; + } + Transform boneFromBoneMap3 = poseMatchCommonUtil.GetBoneFromBoneMap(clothBoneMap, HumanBodyBones.Hips); + BoneSpatialData boneSpatialData = (isLeft ? profileData.LeftFootSpatialData : profileData.RightFootSpatialData); + if (boneSpatialData == null) + { + Debug.LogWarning("[AvatarBodyMatchUtil] AlignNeckCenterByPcaWithHipZ: Profile FootSpatialData Data is Not exist."); + return; + } + Transform boneFromBoneMap4 = poseMatchCommonUtil.GetBoneFromBoneMap(clothBoneMap, boneSpatialData.refBone); + if (boneFromBoneMap4 == null) + { + Debug.LogWarning("[BodyPoseMatch_Leg] ScalingFoot: clothRefBone is null"); + return; + } + BodyPoseMatch_CommonUtil bodyPoseMatch_CommonUtil = new BodyPoseMatch_CommonUtil(); + float proxyToeF = Vector3.Distance(boneFromBoneMap4.position, bodyPoseMatch_CommonUtil.GetProfileVolumeMaxWorld(clothBoneMap, boneSpatialData, 0, comprehensiveScale)); + float proxyHeelF = 0f - Vector3.Distance(boneFromBoneMap4.position, bodyPoseMatch_CommonUtil.GetProfileVolumeMinWorld(clothBoneMap, boneSpatialData, 0, comprehensiveScale)); + float proxySideMax = Vector3.Distance(boneFromBoneMap4.position, bodyPoseMatch_CommonUtil.GetProfileVolumeMaxWorld(clothBoneMap, boneSpatialData, 1, comprehensiveScale)); + float proxySideMin = 0f - Vector3.Distance(boneFromBoneMap4.position, bodyPoseMatch_CommonUtil.GetProfileVolumeMinWorld(clothBoneMap, boneSpatialData, 1, comprehensiveScale)); + float proxySoleY = 0f - Vector3.Distance(boneFromBoneMap4.position, bodyPoseMatch_CommonUtil.GetProfileVolumeMinWorld(clothBoneMap, boneSpatialData, 2, comprehensiveScale)); + if (clothBoneMap == null || !clothBoneMap.TryGetValue(humanBodyBones, out var value) || value == null || value.Count == 0) + { + Debug.LogWarning("[BodyPoseMatch_Leg] ScalingFoot: cloth footBoneSet is missing"); + } + else + { + ScalingFootCore(boneFromBoneMap3, boneFromBoneMap2, isLeft, value, proxyToeF, proxyHeelF, proxySideMin, proxySideMax, proxySoleY, toeF, heelF, sideMin, sideMax, footMinUp); + } + } + + private void ScaleAndAlignAlongAxis(Transform baseTransform, Vector3 targetWorldAxis, Vector3 targetWorldUpAxis, Transform proxyFoot, float proxyMaxF, float proxyMinF, Transform targetFoot, float targetMaxF, float targetMinF) + { + float num = proxyMaxF - proxyMinF; + float num2 = targetMaxF - targetMinF; + if (Mathf.Abs(num) < 0.0001f || Mathf.Abs(num2) < 0.0001f) + { + Debug.LogWarning("[AdjustFootForwardScale] foot span too small."); + return; + } + float num3 = num2 / num; + if (!float.IsFinite(num3) || num3 <= 0f) + { + Debug.LogWarning($"[ScaleAndAlignAlongAxis] invalid scaleF: {num3}"); + return; + } + Vector3 position = proxyFoot.position + targetWorldAxis * proxyMinF; + Vector3 position2 = targetFoot.position + targetWorldAxis * targetMinF; + GameObject gameObject = new GameObject("proxyAnchor"); + gameObject.transform.SetParent(proxyFoot); + gameObject.transform.position = position; + GameObject gameObject2 = new GameObject("targetAnchor"); + gameObject2.transform.SetParent(targetFoot); + gameObject2.transform.position = position2; + Transform parent = proxyFoot.parent; + Vector3 position3 = proxyFoot.position; + Transform transform = new GameObject("ScaleParnet").transform; + transform.SetParent(parent); + transform.position = position3; + transform.rotation = Quaternion.LookRotation(targetWorldAxis, targetWorldUpAxis); + proxyFoot.SetParent(transform, worldPositionStays: true); + proxyFoot.localPosition = Vector3.zero; + Vector3 localScale = transform.localScale; + localScale.z *= num3; + transform.localScale = localScale; + proxyFoot.SetParent(parent, worldPositionStays: true); + proxyFoot.position = position3; + float num4 = Vector3.Dot(gameObject2.transform.position - gameObject.transform.position, targetWorldAxis); + Vector3 vector = targetWorldAxis * num4; + proxyFoot.position += vector; + Object.DestroyImmediate(transform.gameObject); + Object.DestroyImmediate(gameObject); + Object.DestroyImmediate(gameObject2); + } + + private bool TryGetFootSpatialData(Transform avatarTransform, IReadOnlyList avatarBakedBodyMeshes, Dictionary> avatarBoneMap, bool isLeft, out float footMaxUp, out float footMinUp, out float heelF, out float toeF, out float sideMin, out float sideMax, float weightThreshold = 0.15f, int sampleStep = 1) + { + footMaxUp = float.NegativeInfinity; + footMinUp = float.PositiveInfinity; + heelF = float.PositiveInfinity; + toeF = float.NegativeInfinity; + sideMin = float.PositiveInfinity; + sideMax = float.NegativeInfinity; + if (avatarTransform == null || avatarBakedBodyMeshes == null || avatarBoneMap == null || avatarBoneMap.Count == 0) + { + return false; + } + HumanBodyBones key = (isLeft ? HumanBodyBones.LeftFoot : HumanBodyBones.RightFoot); + if (!avatarBoneMap.TryGetValue(key, out var value) || value == null || value.Count == 0) + { + Debug.LogWarning("[BodyPoseMatch_Leg] TryGetFootSpatialData\n - foot bone set is missing"); + return false; + } + Vector3 up = Vector3.up; + Vector3 forward = Vector3.forward; + Vector3 right = Vector3.right; + HashSet hashSet = new HashSet(); + Transform transform = null; + foreach (Transform item in value) + { + if (item == null) + { + continue; + } + if (transform == null) + { + transform = item; + } + Stack stack = new Stack(); + stack.Push(item); + while (stack.Count > 0) + { + Transform transform2 = stack.Pop(); + if (!(transform2 == null) && hashSet.Add(transform2)) + { + for (int i = 0; i < transform2.childCount; i++) + { + stack.Push(transform2.GetChild(i)); + } + } + } + } + if (transform == null || hashSet.Count == 0) + { + return false; + } + List list = poseMatchCommonUtil.CollectWeightedVerticesWorld(avatarBakedBodyMeshes, hashSet, weightThreshold, sampleStep); + if (list == null || list.Count == 0) + { + return false; + } + for (int j = 0; j < list.Count; j++) + { + Vector3 lhs = list[j] - transform.position; + float num = Vector3.Dot(lhs, up); + if (num < footMinUp) + { + footMinUp = num; + } + if (num > footMaxUp) + { + footMaxUp = num; + } + float num2 = Vector3.Dot(lhs, forward); + if (num2 < heelF) + { + heelF = num2; + } + if (num2 > toeF) + { + toeF = num2; + } + float num3 = Vector3.Dot(lhs, right); + if (num3 < sideMin) + { + sideMin = num3; + } + if (num3 > sideMax) + { + sideMax = num3; + } + } + return true; + } + + public void DrawLegPcaDebug(GameObject avatarObject, IReadOnlyList avatarBakedBodyMeshes, Dictionary> avatarBoneMap, bool isLeft, Color centerColor, Color axisColor, float axisScale = 1f, float duration = 1f) + { + if (!(avatarObject == null)) + { + RegionStats regionStats = ComputeLegRegionStats(avatarObject.transform, avatarBakedBodyMeshes, avatarBoneMap, isLeft); + if (regionStats.length < 0.0001f) + { + Debug.LogWarning($"[AvatarBodyMatchUtil] DrawLegPcaDebug: PCA length too small (avatar={avatarObject.name}, isLeft={isLeft})"); + return; + } + Vector3 center = regionStats.center; + Vector3 normalized = regionStats.principalAxis.normalized; + float num = 0.03f * axisScale; + Debug.DrawLine(center + Vector3.up * num, center - Vector3.up * num, centerColor, duration); + Debug.DrawLine(center + Vector3.right * num, center - Vector3.right * num, centerColor, duration); + Debug.DrawLine(center + Vector3.forward * num, center - Vector3.forward * num, centerColor, duration); + float num2 = regionStats.length * 0.5f * axisScale; + Vector3 start = center - normalized * num2; + Vector3 end = center + normalized * num2; + Debug.DrawLine(start, end, axisColor, duration); + Debug.Log($"[DrawLegPcaDebug] avatar={avatarObject.name}, isLeft={isLeft}, " + $"center={center}, axis={normalized}, len={regionStats.length}, avgRadius={regionStats.avgRadius}"); + } + } + + public void DrawFootSpatialDebug(GameObject avatarObject, IReadOnlyList avatarBakedBodyMeshes, Dictionary> avatarBoneMap, bool isLeft, Color centerColor, Color axisColor, Color boxColor, float axisScale = 0.1f, float duration = 1f, float weightThreshold = 0.15f, int sampleStep = 1) + { + if (avatarObject == null) + { + return; + } + if (!TryGetFootSpatialData(avatarObject.transform, avatarBakedBodyMeshes, avatarBoneMap, isLeft, out var footMaxUp, out var footMinUp, out var heelF, out var toeF, out var sideMin, out var sideMax, weightThreshold, sampleStep)) + { + Debug.LogWarning("[BodyPoseMatch_Leg] DrawFootSpatialDebug" + $"\n - Failed to compute Foot Spatial Data (avatar={avatarObject.name}, isLeft={isLeft})"); + } + else + { + if (avatarBoneMap == null || avatarBoneMap.Count == 0) + { + return; + } + HumanBodyBones humanBodyBones = (isLeft ? HumanBodyBones.LeftFoot : HumanBodyBones.RightFoot); + if (!avatarBoneMap.TryGetValue(humanBodyBones, out var value) || value == null || value.Count == 0) + { + return; + } + Transform transform = null; + foreach (Transform item in value) + { + if (item != null) + { + transform = item; + break; + } + } + if (transform == null) + { + return; + } + Vector3 position = transform.position; + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(avatarBoneMap, HumanBodyBones.Hips); + Vector3 vector = ((boneFromBoneMap != null) ? boneFromBoneMap.forward : avatarObject.transform.forward); + vector.y = 0f; + if (vector.sqrMagnitude < 1E-06f) + { + vector = Vector3.forward; + } + vector.Normalize(); + Vector3 vector2 = Vector3.Cross(Vector3.up, vector); + if (vector2.sqrMagnitude < 1E-06f) + { + vector2 = Vector3.right; + } + vector2.Normalize(); + Vector3 vector3 = ((boneFromBoneMap != null) ? boneFromBoneMap.up : avatarObject.transform.up); + if (vector3.sqrMagnitude < 1E-06f) + { + vector3 = Vector3.up; + } + vector3.Normalize(); + float num = Mathf.Max(0.0001f, axisScale * 0.5f); + DrawCross(position, vector3, vector2, centerColor, num, duration); + DrawCross(position, vector3, vector, centerColor, num, duration); + float len = Mathf.Max(0.0001f, axisScale * 2f); + DrawAxisLine(position, vector3, len, axisColor, duration); + DrawAxisLine(position, vector, len, axisColor, duration); + DrawAxisLine(position, vector2, len, axisColor, duration); + Vector3[] array = new Vector3[8]; + int num2 = 0; + for (int i = 0; i < 2; i++) + { + float num3 = ((i == 0) ? footMinUp : footMaxUp); + for (int j = 0; j < 2; j++) + { + float num4 = ((j == 0) ? heelF : toeF); + for (int k = 0; k < 2; k++) + { + float num5 = ((k == 0) ? sideMin : sideMax); + array[num2++] = position + vector3 * num3 + vector * num4 + vector2 * num5; + } + } + } + DrawBoxEdges(array, boxColor, duration); + float size = Mathf.Max(5E-05f, num * 0.6f); + for (int l = 0; l < array.Length; l++) + { + DrawCross(array[l], vector, vector2, boxColor, size, duration); + } + Debug.Log($"[DrawFootSpatialDebug] avatar={avatarObject.name}, isLeft={isLeft}\n" + $" - footBone={humanBodyBones}, footRoot={transform.name}\n" + $" - up(min,max)=({footMinUp:F4},{footMaxUp:F4})\n" + $" - forward(heel,toe)=({heelF:F4},{toeF:F4})\n" + $" - side(min,max)=({sideMin:F4},{sideMax:F4})\n" + $" - forwardDir={vector}, rightDir={vector2}, upDir={vector3}"); + } + static void DrawAxisLine(Vector3 start, Vector3 dir, float num6, Color color, float d) + { + if (!(dir.sqrMagnitude < 1E-10f)) + { + dir.Normalize(); + Debug.DrawLine(start, start + dir * num6, color, d); + } + } + static void DrawBoxEdges(Vector3[] c, Color color, float d) + { + DrawSegment(c[0], c[1], color, d); + DrawSegment(c[1], c[3], color, d); + DrawSegment(c[3], c[2], color, d); + DrawSegment(c[2], c[0], color, d); + DrawSegment(c[4], c[5], color, d); + DrawSegment(c[5], c[7], color, d); + DrawSegment(c[7], c[6], color, d); + DrawSegment(c[6], c[4], color, d); + DrawSegment(c[0], c[4], color, d); + DrawSegment(c[1], c[5], color, d); + DrawSegment(c[2], c[6], color, d); + DrawSegment(c[3], c[7], color, d); + } + static void DrawCross(Vector3 pos, Vector3 axisA, Vector3 axisB, Color color, float num6, float d) + { + if (axisA.sqrMagnitude < 1E-10f) + { + axisA = Vector3.up; + } + if (axisB.sqrMagnitude < 1E-10f) + { + axisB = Vector3.right; + } + axisA.Normalize(); + axisB.Normalize(); + Debug.DrawLine(pos - axisA * num6, pos + axisA * num6, color, d); + Debug.DrawLine(pos - axisB * num6, pos + axisB * num6, color, d); + } + static void DrawSegment(Vector3 p0, Vector3 p1, Color color, float d) + { + Debug.DrawLine(p0, p1, color, d); + } + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Leg.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Leg.cs.meta new file mode 100644 index 0000000..84407d7 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Leg.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d5c24488eded1b844bc1790158497120 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Torso.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Torso.cs new file mode 100644 index 0000000..a0e58b9 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Torso.cs @@ -0,0 +1,145 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BodyPoseMatch_Torso +using System.Collections.Generic; +using System.Linq; +using Eden.AutoMorpher; +using Eden.AutoMorpher.profile; +using UnityEngine; + +public class BodyPoseMatch_Torso +{ + private readonly BodyPoseMatch_CommonUtil poseMatchCommonUtil; + + public BodyPoseMatch_Torso() + { + poseMatchCommonUtil = new BodyPoseMatch_CommonUtil(); + } + + public void AlignTorsoByNeck(IReadOnlyCollection sourceHipSet, Vector3 sourceNeckCenterWorld, Vector3 targetNeckCenterWorld) + { + if (sourceHipSet == null || sourceHipSet.Count == 0) + { + throw new AutoMorpherException("Source Hip Set is Missing", "[BodyPoseMatch_Torso] AlignTorsoByNeck\n - sourceHipSet is null or empty"); + } + Transform transform = sourceHipSet.FirstOrDefault((Transform t) => t != null); + if (transform == null) + { + throw new AutoMorpherException("Source Hip Transform is Missing", "[BodyPoseMatch_Torso] AlignTorsoByNeck\n - sourceHipSet has no valid Transform"); + } + Vector3 lhs = targetNeckCenterWorld - sourceNeckCenterWorld; + Vector3 forward = transform.forward; + float num = Vector3.Dot(lhs, forward); + Vector3 vector = forward * num; + foreach (Transform item in sourceHipSet) + { + if (!(item == null)) + { + item.position += vector; + } + } + } + + public void AlignTorsoByNeck(GameObject proxyObject, IReadOnlyList proxyBakedBodyMeshes, Dictionary> proxyBoneMap, GameObject targetObject, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap) + { + if (proxyObject == null || targetObject == null) + { + throw new AutoMorpherException("Proxy/Target Object is Missing", "[BodyPoseMatch_Torso] AlignTorsoByNeck\n - proxyObject or targetObject is null"); + } + RegionStats regionStats = ComputeNeckRegionStats(proxyObject.transform, proxyBakedBodyMeshes, proxyBoneMap); + RegionStats regionStats2 = ComputeNeckRegionStats(targetObject.transform, targetBakedBodyMeshes, targetBoneMap); + if (regionStats.length < 0.0001f || regionStats2.length < 0.0001f) + { + Debug.LogWarning("[BodyPoseMatch_Torso] AlignTorsoByNeck: Neck PCA 통계가 비정상입니다."); + return; + } + if (proxyBoneMap == null || !proxyBoneMap.TryGetValue(HumanBodyBones.Hips, out var value) || value == null || value.Count == 0) + { + throw new AutoMorpherException("Proxy Hip Set is Missing", "[BodyPoseMatch_Torso] AlignTorsoByNeck\n - proxyBoneMap has no Hips bone set"); + } + AlignTorsoByNeck(value, regionStats.center, regionStats2.center); + } + + public void AlignTorsoByNeck(Transform targetTransform, IReadOnlyList targetBakedBodyMeshes, Dictionary> targetBoneMap, Dictionary> clothBoneMap, Transform clothesTransform, ProfileData profileData, Vector3 comprehensiveScale) + { + if (targetTransform == null) + { + throw new AutoMorpherException("Target Transform is Missing", "[BodyPoseMatch_Torso] AlignTorsoByNeck\n - targetTransform is null"); + } + if (profileData == null || profileData.NeckSpatialData == null) + { + throw new AutoMorpherException("Profile Neck Data is Missing", "[BodyPoseMatch_Torso] AlignTorsoByNeck\n - profileData or profileData.NeckSpatialData is null"); + } + RegionStats regionStats = ComputeNeckRegionStats(targetTransform, targetBakedBodyMeshes, targetBoneMap); + if (regionStats.length < 0.0001f) + { + Debug.LogWarning("[BodyPoseMatch_Torso] AlignTorsoByNeck: Neck PCA 통계가 비정상입니다."); + return; + } + if (clothBoneMap == null || !clothBoneMap.TryGetValue(HumanBodyBones.Hips, out var value) || value == null || value.Count == 0) + { + throw new AutoMorpherException("Cloth Hip Set is Missing", "[BodyPoseMatch_Torso] AlignTorsoByNeck\n - clothBoneMap has no Hips bone set"); + } + Vector3 profilePcaCenterWorld = poseMatchCommonUtil.GetProfilePcaCenterWorld(clothBoneMap, profileData.NeckSpatialData, comprehensiveScale); + AlignTorsoByNeck(value, profilePcaCenterWorld, regionStats.center); + } + + public RegionStats ComputeNeckRegionStats(Transform avatarTransform, IReadOnlyList avatarBakedBodyMeshes, Dictionary> avatarBoneMap) + { + HumanBodyBones[] targetHumanBones = new HumanBodyBones[1] { HumanBodyBones.Neck }; + List list = poseMatchCommonUtil.CollectHumanoidVerticesWorld(targetHumanBones, avatarBakedBodyMeshes, avatarBoneMap); + if (list == null || list.Count == 0) + { + Debug.LogWarning("[AvatarBodyMatchUtil] ComputeNeckRegionStats: neck vertices 0개. avatar=" + avatarTransform?.name); + return default(RegionStats); + } + RegionStats result = new PcaUtil().ComputeRegionStats(list); + Vector3 vector = avatarTransform.up; + Transform boneFromBoneMap = poseMatchCommonUtil.GetBoneFromBoneMap(avatarBoneMap, HumanBodyBones.Head); + Transform boneFromBoneMap2 = poseMatchCommonUtil.GetBoneFromBoneMap(avatarBoneMap, HumanBodyBones.UpperChest); + Transform boneFromBoneMap3 = poseMatchCommonUtil.GetBoneFromBoneMap(avatarBoneMap, HumanBodyBones.Chest); + Transform boneFromBoneMap4 = poseMatchCommonUtil.GetBoneFromBoneMap(avatarBoneMap, HumanBodyBones.Hips); + Transform transform = ((boneFromBoneMap2 != null) ? boneFromBoneMap2 : boneFromBoneMap3); + if (boneFromBoneMap != null && transform != null) + { + vector = boneFromBoneMap.position - transform.position; + } + else if (boneFromBoneMap != null && boneFromBoneMap4 != null) + { + vector = boneFromBoneMap.position - boneFromBoneMap4.position; + } + if (vector.sqrMagnitude > 1E-08f) + { + vector.Normalize(); + } + if (Vector3.Dot(vector, avatarTransform.transform.up) < 0f) + { + vector = -vector; + } + result.principalAxis = vector; + return result; + } + + public void DrawTorsoPcaDebug(GameObject avatarObject, IReadOnlyList avatarBakedBodyMeshes, Dictionary> avatarBoneMap, Color centerColor, Color axisColor, float axisScale = 1f, float duration = 0.1f) + { + if (!(avatarObject == null)) + { + RegionStats regionStats = ComputeNeckRegionStats(avatarObject.transform, avatarBakedBodyMeshes, avatarBoneMap); + if (regionStats.length < 0.0001f) + { + Debug.LogWarning($"[AvatarBodyMatchUtil] DrawNeckPcaDebug: neck.length가 너무 작음. avatar={avatarObject.name}, length={regionStats.length}"); + return; + } + Vector3 center = regionStats.center; + Vector3 normalized = regionStats.principalAxis.normalized; + float num = 0.02f * axisScale; + Debug.DrawLine(center + Vector3.up * num, center - Vector3.up * num, centerColor, duration); + Debug.DrawLine(center + Vector3.right * num, center - Vector3.right * num, centerColor, duration); + float num2 = regionStats.length * 0.5f * axisScale; + Vector3 end = center + normalized * num2; + Debug.DrawLine(center, end, axisColor, duration); + Debug.Log($"[AvatarBodyMatchUtil] DrawNeckPcaDebug: avatar={avatarObject.name}, center={center}, axis={normalized}, halfLen={num2}"); + } + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Torso.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Torso.cs.meta new file mode 100644 index 0000000..fefb753 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseMatch_Torso.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 13807354cce38d84eb41a5a8d38d99ae \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseToClothApplier.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseToClothApplier.cs new file mode 100644 index 0000000..b42a680 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseToClothApplier.cs @@ -0,0 +1,251 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BodyPoseToClothApplier +using System.Collections.Generic; +using System.Linq; +using Eden.AutoMorpher; +using UnityEngine; + +public class BodyPoseToClothApplier +{ + private class BoneMapping + { + public Transform proxyBone; + + public Transform clothBone; + + public int depth; + + public HumanBodyBones? humanBone; + } + + public void ApplyBodyPoseToClothes(Transform bodyProxyRoot, Transform clothesRoot, Dictionary clothToBodyMatched, Dictionary sourceToProxy, bool copyPosition = true, bool copyRotation = false, bool copyScale = true) + { + if (bodyProxyRoot == null) + { + throw new AutoMorpherException("Body Proxy Root is Missing", "[BodyToClothPoseApplier] ApplyDeformedProxyPoseToCloth\n - bodyProxyRoot is null"); + } + if (clothesRoot == null) + { + throw new AutoMorpherException("Clothes Root is Missing", "[BodyToClothPoseApplier] ApplyDeformedProxyPoseToCloth\n - clothesRoot is null"); + } + if (clothToBodyMatched == null || clothToBodyMatched.Count == 0) + { + throw new AutoMorpherException("Cloth To Body Matched Map is Missing", "[BodyToClothPoseApplier] ApplyDeformedProxyPoseToCloth\n - clothToBodyMatched is null or empty"); + } + if (sourceToProxy == null) + { + throw new AutoMorpherException("Source To Proxy Map is Missing", "[BodyToClothPoseApplier] ApplyDeformedProxyPoseToCloth\n - sourceToProxy is null"); + } + clothesRoot.localScale = bodyProxyRoot.localScale; + Animator component = bodyProxyRoot.GetComponent(); + if (component == null) + { + throw new AutoMorpherException("Body Proxy Animator is Missing", "[BodyToClothPoseApplier] ApplyDeformedProxyPoseToCloth\n - bodyProxyRoot has no Animator"); + } + Transform transform = component.transform; + Dictionary dictionary = BuildDepthMap(transform); + Dictionary dictionary2 = new Dictionary(); + List list = new List(); + HashSet hashSet = new HashSet(); + foreach (KeyValuePair item in clothToBodyMatched) + { + Transform key = item.Key; + BoneMatchUtil.BoneRootLocalData value = item.Value; + if (key == null || value == null) + { + continue; + } + Transform t = value.t; + if (t == null || !sourceToProxy.TryGetValue(t, out var value2) || value2 == null) + { + continue; + } + HumanBodyBones hBone = value.hBone; + if (hashSet.Add(key)) + { + int depth = 999; + if (dictionary != null && dictionary.TryGetValue(value2, out var value3)) + { + depth = value3; + } + list.Add(new BoneMapping + { + proxyBone = value2, + clothBone = key, + depth = depth, + humanBone = ((hBone != HumanBodyBones.LastBone) ? new HumanBodyBones?(hBone) : ((HumanBodyBones?)null)) + }); + } + if (!dictionary2.ContainsKey(value2)) + { + dictionary2.Add(value2, key); + } + } + MirrorBoneMarkers(transform, dictionary2, dictionary, list); + foreach (BoneMapping item2 in (from m in list + where m != null && m.proxyBone != null && m.clothBone != null + orderby m.depth + select m).ToList()) + { + if (!(item2.proxyBone == null) && !(item2.clothBone == null)) + { + if (copyPosition) + { + item2.clothBone.position = item2.proxyBone.position; + } + if (copyRotation) + { + item2.clothBone.rotation = item2.proxyBone.rotation; + } + if (copyScale) + { + item2.clothBone.localScale = item2.proxyBone.localScale; + } + } + } + } + + private Dictionary BuildDepthMap(Transform root) + { + Dictionary dictionary = new Dictionary(); + if (root == null) + { + return dictionary; + } + Transform[] componentsInChildren = root.GetComponentsInChildren(includeInactive: true); + foreach (Transform transform in componentsInChildren) + { + if (!(transform == null)) + { + dictionary[transform] = GetDepthFromRoot(transform, root); + } + } + return dictionary; + } + + private int GetDepthFromRoot(Transform t, Transform root) + { + int num = 0; + Transform transform = t; + while (transform != null && transform != root) + { + num++; + transform = transform.parent; + } + return num; + } + + private void MirrorBoneMarkers(Transform proxyArmature, Dictionary proxyToCloth, Dictionary proxyDepthMap, List mappings) + { + if (proxyArmature == null || proxyToCloth == null || mappings == null) + { + return; + } + TempBoneMarker[] componentsInChildren = proxyArmature.GetComponentsInChildren(includeInactive: true); + if (componentsInChildren == null || componentsInChildren.Length == 0) + { + return; + } + HashSet hashSet = new HashSet(); + for (int i = 0; i < mappings.Count; i++) + { + BoneMapping boneMapping = mappings[i]; + if (boneMapping != null && boneMapping.proxyBone != null) + { + hashSet.Add(boneMapping.proxyBone); + } + } + foreach (TempBoneMarker item in (from bm in componentsInChildren + where bm != null && bm.transform != null + orderby (proxyDepthMap != null && proxyDepthMap.TryGetValue(bm.transform, out var value3)) ? value3 : int.MaxValue + select bm).ToList()) + { + Transform transform = item.transform; + if (transform == null) + { + continue; + } + Transform originalParent = item.originalParent; + if (originalParent == null || !proxyToCloth.TryGetValue(originalParent, out var value) || value == null) + { + continue; + } + List list = CollectDirectChilds(value); + Transform transform2 = FindMarkerInChild(value); + TempBoneMarker tempBoneMarker = null; + if (transform2 == null) + { + transform2 = new GameObject(transform.name).transform; + transform2.SetParent(value, worldPositionStays: true); + tempBoneMarker = transform2.gameObject.AddComponent(); + } + else + { + tempBoneMarker = transform2.GetComponent(); + } + transform2.position = transform.position; + transform2.rotation = transform.rotation; + transform2.localScale = transform.localScale; + tempBoneMarker.originalParent = value; + tempBoneMarker.additionalInfo = item.additionalInfo; + tempBoneMarker.wrappedChildNames = item.wrappedChildNames; + for (int num = 0; num < list.Count; num++) + { + Transform transform3 = list[num]; + if (!(transform3 == null) && !(transform3 == transform2)) + { + transform3.SetParent(transform2, worldPositionStays: true); + } + } + if (!proxyToCloth.ContainsKey(transform)) + { + proxyToCloth.Add(transform, transform2); + } + if (hashSet.Add(transform)) + { + int depth = 999; + if (proxyDepthMap != null && proxyDepthMap.TryGetValue(transform, out var value2)) + { + depth = value2; + } + mappings.Add(new BoneMapping + { + proxyBone = transform, + clothBone = transform2, + depth = depth, + humanBone = null + }); + } + } + } + + private Transform FindMarkerInChild(Transform parent) + { + if (parent == null) + { + return null; + } + for (int i = 0; i < parent.childCount; i++) + { + Transform child = parent.GetChild(i); + if (!(child == null) && child.GetComponent() != null) + { + return child; + } + } + return null; + } + + private List CollectDirectChilds(Transform parentTransform) + { + int childCount = parentTransform.childCount; + List list = new List(childCount); + for (int i = 0; i < childCount; i++) + { + list.Add(parentTransform.GetChild(i)); + } + return list; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseToClothApplier.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseToClothApplier.cs.meta new file mode 100644 index 0000000..4701ff2 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BodyPoseToClothApplier.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f39701ad78a777b48a7b931538b63608 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneAlignmentUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneAlignmentUtil.cs new file mode 100644 index 0000000..e468f54 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneAlignmentUtil.cs @@ -0,0 +1,169 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BoneAlignmentUtil +using System.Collections.Generic; +using System.Linq; +using Eden.AutoMorpher; +using UnityEngine; + +public class BoneAlignmentUtil +{ + public void AlignClothBonesToAvatar(ClothInstance cloth, Animator targetAvatarAnimator) + { + if (cloth == null || targetAvatarAnimator == null) + { + Debug.LogWarning("[BoneAlignmentUtil] cloth 또는 targetAvatarAnimator 가 null."); + return; + } + foreach (KeyValuePair> humanoidMatchedBone in cloth.humanoidMatchedBones) + { + HumanBodyBones key = humanoidMatchedBone.Key; + foreach (Transform item in humanoidMatchedBone.Value) + { + if (!(item == null)) + { + Transform boneTransform = targetAvatarAnimator.GetBoneTransform(key); + if (!(boneTransform == null)) + { + item.position = boneTransform.position; + item.localScale = Vector3.one; + item.name = boneTransform.name; + } + } + } + } + } + + public void ReparentAccessoryBonesToAvatar(EdenAutoMorpherConfig config) + { + if (config.clothBoneTypeMap == null || config.targetAvatarObject == null) + { + return; + } + Animator component = config.targetAvatarObject.GetComponent(); + if (component == null || !component.isHuman) + { + return; + } + Dictionary dictionary = new Dictionary(); + if (config.clothesHumanoidMatchedBones != null) + { + foreach (KeyValuePair> clothesHumanoidMatchedBone in config.clothesHumanoidMatchedBones) + { + HumanBodyBones key = clothesHumanoidMatchedBone.Key; + foreach (Transform item2 in clothesHumanoidMatchedBone.Value) + { + Transform boneTransform = component.GetBoneTransform(key); + if (item2 != null && boneTransform != null) + { + dictionary[item2] = boneTransform; + } + } + } + } + foreach (KeyValuePair item3 in config.clothBoneTypeMap) + { + if (item3.Value != ClothBoneType.Accessory) + { + continue; + } + Transform key2 = item3.Key; + if (key2 == null || !key2.IsChildOf(config.targetClothesObject.transform)) + { + continue; + } + Transform parent = key2.parent; + Transform transform = null; + while (parent != null) + { + if (config.clothBoneTypeMap.TryGetValue(parent, out var value) && value == ClothBoneType.Body) + { + transform = parent; + break; + } + parent = parent.parent; + } + if (!(transform == null) && dictionary.TryGetValue(transform, out var value2)) + { + key2.SetParent(value2, worldPositionStays: true); + } + } + foreach (KeyValuePair item4 in dictionary) + { + Transform key3 = item4.Key; + Transform value3 = item4.Value; + if (key3 == null || value3 == null) + { + continue; + } + List list = new List(); + foreach (Transform item5 in key3) + { + list.Add(item5); + } + foreach (Transform item6 in list) + { + if (!(item6 == null) && !config.clothBoneTypeMap.ContainsKey(item6)) + { + item6.SetParent(value3, worldPositionStays: true); + } + } + } + } + + public void CleanupTempBones(Transform root) + { + if (root == null) + { + return; + } + TempBoneMarker[] componentsInChildren = root.GetComponentsInChildren(includeInactive: true); + if (componentsInChildren == null || componentsInChildren.Length == 0) + { + return; + } + TempBoneMarker[] array = (from m in componentsInChildren + where m != null + orderby GetDepth(m.transform) descending + select m).ToArray(); + foreach (TempBoneMarker tempBoneMarker in array) + { + if (tempBoneMarker == null) + { + continue; + } + Transform transform = tempBoneMarker.transform; + Transform transform2 = tempBoneMarker.originalParent; + if (transform2 == null) + { + transform2 = transform.parent; + } + if (transform2 != null) + { + while (transform.childCount > 0) + { + transform.GetChild(0).SetParent(transform2, worldPositionStays: true); + } + } + if (!Application.isPlaying) + { + Object.DestroyImmediate(transform.gameObject); + } + else + { + Object.Destroy(transform.gameObject); + } + } + static int GetDepth(Transform t) + { + int num2 = 0; + while (t != null) + { + num2++; + t = t.parent; + } + return num2; + } + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneAlignmentUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneAlignmentUtil.cs.meta new file mode 100644 index 0000000..020b32d --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneAlignmentUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d079bedd1d590d743a87a7526aac81d0 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneCorrespondenceUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneCorrespondenceUtil.cs new file mode 100644 index 0000000..43f83ef --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneCorrespondenceUtil.cs @@ -0,0 +1,195 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BoneCorrespondenceUtil +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +public class BoneCorrespondenceUtil +{ + private char[] nameDelims = new char[8] { ' ', '-', '_', ':', '.', '|', '\\', '/' }; + + public float ComputeDistanceScore(float d, float near, float mid, float far) + { + if (d < near) + { + return 120f; + } + if (d < mid) + { + return 90f; + } + if (d < far) + { + return 50f; + } + return 0f; + } + + public float ComputeRotationScore(float ang, float RotTolDeg) + { + return Mathf.Clamp01(1f - ang / RotTolDeg); + } + + public float ComputeNameScore(string bodyName, string bodyPath, string clothesName, string clothesPath) + { + string text = StripDigits(NormalizeName(bodyName)); + string text2 = StripDigits(NormalizeName(clothesName)); + string text3 = StripDigits(NormalizePath(bodyPath)); + string text4 = StripDigits(NormalizePath(clothesPath)); + if (!string.IsNullOrEmpty(text3) && text3 == text4) + { + return 80f; + } + if (!string.IsNullOrEmpty(text) && text == text2) + { + return 70f; + } + if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text2) && (text.Contains(text2) || text2.Contains(text))) + { + return 40f; + } + return SubsequenceCoverage(bodyName, clothesName) * 10f; + } + + private float SubsequenceCoverage(string bodyRaw, string clothesRaw) + { + string text = StripDigits(NormalizeName(bodyRaw)); + string text2 = StripDigits(NormalizeName(clothesRaw)); + if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2)) + { + return 0f; + } + return (float)SubNameMatchBest(text, text2) / (float)text.Length; + } + + private int SubNameMatchBest(string a, string b, int minMatchCount = 3) + { + if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) + { + return 0; + } + if (a.Length < minMatchCount || b.Length < minMatchCount) + { + return 0; + } + int num = 0; + for (int i = 0; i <= a.Length - minMatchCount; i++) + { + int num2 = SubNameMatch(a, i, b, minMatchCount); + if (num2 > num) + { + num = num2; + } + } + return num; + } + + private int SubNameMatch(string a, int startA, string b, int minMatchCount = 3) + { + int length = a.Length; + int length2 = b.Length; + int num = 0; + int num2 = 0; + int num3 = 0; + int num4 = 0; + for (int i = startA; i < length; i++) + { + for (int j = num3; j < length2; j++) + { + num3 = j + 1; + if (a[i] == b[j]) + { + num4 = i + 1; + num2++; + if (num2 > num) + { + num = num2; + } + break; + } + if (num2 > num) + { + num = num2; + } + num2 = 0; + } + if (num3 >= length2) + { + break; + } + } + if (num2 > num) + { + num = num2; + } + if (num < minMatchCount) + { + return 0; + } + return num4 - startA; + } + + public string GetHierarchyPath(Transform root, Transform t) + { + if (t == null) + { + return ""; + } + Stack stack = new Stack(); + Transform transform = t; + while (transform != null && transform != root) + { + stack.Push(transform.name); + transform = transform.parent; + } + return string.Join("/", stack); + } + + public string NormalizeName(string s) + { + if (string.IsNullOrEmpty(s)) + { + return ""; + } + s = s.ToLowerInvariant(); + s = s.Replace("left", "l").Replace("right", "r"); + char[] array = nameDelims; + foreach (char c in array) + { + s = s.Replace(c.ToString(), ""); + } + s = StripDigits(s); + return s; + } + + public string NormalizePath(string p) + { + if (string.IsNullOrEmpty(p)) + { + return ""; + } + return string.Join("/", (from x in p.Split('/') + where !string.IsNullOrEmpty(x) + select x).Select(NormalizeName)); + } + + public string StripDigits(string s) + { + if (string.IsNullOrEmpty(s)) + { + return ""; + } + StringBuilder stringBuilder = new StringBuilder(s.Length); + foreach (char c in s) + { + if (!char.IsDigit(c)) + { + stringBuilder.Append(c); + } + } + return stringBuilder.ToString(); + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneCorrespondenceUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneCorrespondenceUtil.cs.meta new file mode 100644 index 0000000..b7d0a82 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneCorrespondenceUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1a08d4f82c8d94f4cadc7d8de2833278 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneMatchUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneMatchUtil.cs new file mode 100644 index 0000000..031dd6e --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneMatchUtil.cs @@ -0,0 +1,613 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BoneMatchUtil +using System; +using System.Collections.Generic; +using System.Linq; +using Eden.AutoMorpher; +using Eden.AutoMorpher.profile; +using UnityEngine; + +public class BoneMatchUtil +{ + public class BoneRootLocalData + { + public Transform t; + + public string name; + + public string path; + + public Vector3 rootLocalPos; + + public Quaternion rootLocalRot; + + public HumanBodyBones hBone = HumanBodyBones.LastBone; + } + + private const float PosTolNear = 0.0001f; + + private const float PosTolMid = 0.0005f; + + private const float PosTolFar = 0.002f; + + private const float RotTolDeg = 30f; + + private const float eps = 1E-06f; + + public List ConvertProfileBoneDataToRootLocalData(List boneDataList) + { + if (boneDataList == null || boneDataList.Count == 0) + { + throw new AutoMorpherException("Profile Bone Data is Missing", "[BoneMatchUtil] ConvertProfileBoneDataToRootLocalData\n - boneDataList is null or empty"); + } + List list = new List(boneDataList.Count); + foreach (BoneData boneData in boneDataList) + { + if (boneData != null) + { + BoneRootLocalData item = new BoneRootLocalData + { + t = null, + name = (boneData.boneName ?? string.Empty), + path = (boneData.hierarchyPath ?? string.Empty), + rootLocalPos = boneData.rootLocalPosition, + rootLocalRot = boneData.rootLocalRotation, + hBone = boneData.hBone + }; + list.Add(item); + } + } + return list; + } + + public HashSet GetMeshBones(List meshRenderers) + { + HashSet hashSet = new HashSet(); + if (meshRenderers != null) + { + foreach (SkinnedMeshRenderer meshRenderer in meshRenderers) + { + if (meshRenderer == null || meshRenderer.bones == null) + { + continue; + } + Transform[] bones = meshRenderer.bones; + foreach (Transform transform in bones) + { + if (transform != null) + { + hashSet.Add(transform); + } + } + } + } + return hashSet; + } + + public List GetRootLocalBones(Transform rootT, HashSet boneList) + { + BoneCorrespondenceUtil correspondenceUtil = new BoneCorrespondenceUtil(); + return (from t in boneList.Where((Transform t) => t != null).Distinct() + select new BoneRootLocalData + { + t = t, + name = t.name, + path = correspondenceUtil.GetHierarchyPath(rootT, t), + rootLocalPos = rootT.InverseTransformPoint(t.position), + rootLocalRot = Quaternion.Inverse(rootT.rotation) * t.rotation + }).ToList(); + } + + public List GetBodyRootLocalBones(Transform bodyTransform, Animator bodyAnimator, List bodyMeshes) + { + if (bodyTransform == null) + { + throw new AutoMorpherException("Body Transform is Missing", "[BoneMatchUtil] GetBodyRootLocalBones\n - bodyTransform is null"); + } + if (bodyAnimator == null) + { + throw new AutoMorpherException("Body Animator is Missing", "[BoneMatchUtil] GetBodyRootLocalBones\n - animator is null"); + } + Dictionary humanoidBoneList = GetHumanoidBoneList(bodyAnimator); + if (humanoidBoneList == null || humanoidBoneList.Count == 0) + { + throw new AutoMorpherException("Humanoid Bone Map is Missing", "[BoneMatchUtil] GetBodyRootLocalBones\n - Can't find Humanoid Bone List"); + } + if (bodyMeshes == null || bodyMeshes.Count == 0) + { + throw new AutoMorpherException("Body Meshes are Missing", "[BoneMatchUtil] GetBodyRootLocalBones\n - bodyMeshes is null or empty"); + } + HashSet meshBones = GetMeshBones(bodyMeshes); + Dictionary> dictionary = new MeshClassifier().MeshHumanoidBoneMatcher(bodyAnimator, bodyMeshes); + HashSet hashSet = new HashSet(); + HashSet hashSet2 = new HashSet(); + foreach (KeyValuePair item in humanoidBoneList) + { + if (item.Value != HumanBodyBones.LastBone && !(item.Key == null)) + { + if (!dictionary.TryGetValue(item.Value, out var value) || value == null) + { + value = new HashSet(); + dictionary[item.Value] = value; + } + value.Add(item.Key); + } + } + foreach (KeyValuePair> item2 in dictionary) + { + HumanBodyBones key = item2.Key; + if (key == HumanBodyBones.LastBone) + { + continue; + } + HashSet value2 = item2.Value; + if (value2 == null || value2.Count == 0) + { + continue; + } + Transform transform = bodyAnimator.GetBoneTransform(key); + if (transform == null) + { + transform = value2.First(); + } + if (transform == null) + { + continue; + } + hashSet.Add(transform); + foreach (Transform item3 in value2) + { + if (!(item3 == transform) && !(item3 == null)) + { + hashSet2.Add(item3); + } + } + } + foreach (Transform item4 in meshBones) + { + if (!(item4 == null) && !hashSet2.Contains(item4)) + { + hashSet.Add(item4); + } + } + List rootLocalBones = GetRootLocalBones(bodyTransform, hashSet); + foreach (BoneRootLocalData item5 in rootLocalBones) + { + if (item5 != null && !(item5.t == null)) + { + if (humanoidBoneList.TryGetValue(item5.t, out var value3)) + { + item5.hBone = value3; + } + else + { + item5.hBone = HumanBodyBones.LastBone; + } + } + } + return rootLocalBones; + } + + private Dictionary GetHumanoidBoneList(Animator bodyAnimator) + { + Dictionary dictionary = new Dictionary(); + if (bodyAnimator == null || bodyAnimator.avatar == null || !bodyAnimator.avatar.isHuman) + { + return dictionary; + } + foreach (HumanBodyBones value in Enum.GetValues(typeof(HumanBodyBones))) + { + if (value != HumanBodyBones.LastBone) + { + Transform boneTransform = bodyAnimator.GetBoneTransform(value); + if (!(boneTransform == null)) + { + dictionary.TryAdd(boneTransform, value); + } + } + } + return dictionary; + } + + public void MatchClothesToBodyBones(List bodyBones, List clothesBones, out Dictionary> clothHumanBones, out Dictionary clothBoneTypeMap, out Dictionary clothToBodyMatched) + { + clothHumanBones = new Dictionary>(); + clothBoneTypeMap = new Dictionary(); + clothToBodyMatched = new Dictionary(); + if (bodyBones == null || bodyBones.Count == 0) + { + throw new AutoMorpherException("Body Bones are Missing", "[BoneMatchUtil] MatchClothesToBodyBones\n - bodyBones is null or empty"); + } + if (clothesBones == null || clothesBones.Count == 0) + { + throw new AutoMorpherException("Clothes Bones are Missing", "[BoneMatchUtil] MatchClothesToBodyBones\n - clothesBones is null or empty"); + } + List<(BoneRootLocalData, BoneRootLocalData, float, float, float, float, float)> list = new List<(BoneRootLocalData, BoneRootLocalData, float, float, float, float, float)>(); + BoneCorrespondenceUtil boneCorrespondenceUtil = new BoneCorrespondenceUtil(); + foreach (BoneRootLocalData clothesBone in clothesBones) + { + BoneRootLocalData boneRootLocalData = null; + float num = float.NegativeInfinity; + float num2 = float.NegativeInfinity; + float num3 = float.NegativeInfinity; + float num4 = float.PositiveInfinity; + float num5 = float.PositiveInfinity; + foreach (BoneRootLocalData bodyBone in bodyBones) + { + float num6 = Vector3.Distance(clothesBone.rootLocalPos, bodyBone.rootLocalPos); + float num7 = boneCorrespondenceUtil.ComputeDistanceScore(num6, 0.0001f, 0.0005f, 0.002f); + if (num7 == 0f) + { + continue; + } + float num8 = boneCorrespondenceUtil.ComputeNameScore(bodyBone.name, bodyBone.path, clothesBone.name, clothesBone.path); + if (num8 != 0f) + { + float num9 = Quaternion.Angle(bodyBone.rootLocalRot, clothesBone.rootLocalRot); + float num10 = boneCorrespondenceUtil.ComputeRotationScore(num9, 30f) * 1f; + if (IsBetterCandidate(num7, num, num10, num2, num8, num3, num6, num4, num9, num5)) + { + boneRootLocalData = bodyBone; + num = num7; + num2 = num10; + num3 = num8; + num4 = num6; + num5 = num9; + } + } + } + if (boneRootLocalData != null) + { + list.Add((clothesBone, boneRootLocalData, num, num2, num3, num4, num5)); + } + } + Dictionary dictionary = new Dictionary(); + foreach (IGrouping item in from x in list + group x by x.body) + { + if (item.Key == null) + { + continue; + } + List<(BoneRootLocalData, BoneRootLocalData, float, float, float, float, float)> list2 = item.ToList(); + if (list2.Count == 0) + { + continue; + } + (BoneRootLocalData, BoneRootLocalData, float, float, float, float, float) tuple = list2[0]; + for (int num11 = 1; num11 < list2.Count; num11++) + { + (BoneRootLocalData, BoneRootLocalData, float, float, float, float, float) tuple2 = list2[num11]; + if (IsBetterCandidate(tuple2.Item3, tuple.Item3, tuple2.Item4, tuple.Item4, tuple2.Item5, tuple.Item5, tuple2.Item6, tuple.Item6, tuple2.Item7, tuple.Item7)) + { + tuple = tuple2; + } + } + if (tuple.Item1 != null && tuple.Item1.t != null && tuple.Item2 != null) + { + dictionary[tuple.Item1.t] = tuple.Item2; + } + } + foreach (BoneRootLocalData clothesBone2 in clothesBones) + { + if (clothesBone2?.t == null) + { + continue; + } + if (dictionary.TryGetValue(clothesBone2.t, out var value)) + { + clothBoneTypeMap[clothesBone2.t] = ClothBoneType.Body; + clothToBodyMatched[clothesBone2.t] = value; + HumanBodyBones hBone = value.hBone; + if (hBone != HumanBodyBones.LastBone) + { + if (!clothHumanBones.TryGetValue(hBone, out var value2)) + { + value2 = new HashSet(); + clothHumanBones[hBone] = value2; + } + value2.Add(clothesBone2.t); + } + } + else + { + clothBoneTypeMap[clothesBone2.t] = ClothBoneType.Accessory; + } + } + } + + private bool IsBetterCandidate(float candDistScore, float bestDistScore, float candRotScore, float bestRotScore, float candNameScore, float bestNameScore, float candD, float bestD, float candAng, float bestAng) + { + if (candDistScore > bestDistScore) + { + return true; + } + if (candDistScore < bestDistScore) + { + return false; + } + if (candNameScore > bestNameScore) + { + return true; + } + if (candNameScore < bestNameScore) + { + return false; + } + if (Mathf.Abs(candRotScore - bestRotScore) > 1E-06f) + { + if (candRotScore > bestRotScore) + { + return true; + } + if (candRotScore < bestRotScore) + { + return false; + } + } + if (Mathf.Abs(candD - bestD) > 1E-06f) + { + if (candD < bestD) + { + return true; + } + if (candD > bestD) + { + return false; + } + } + if (Mathf.Abs(candAng - bestAng) > 1E-06f) + { + if (candAng < bestAng) + { + return true; + } + return false; + } + return false; + } + + public Dictionary BuildTransformMatchMap(List sourceBones, List destBones, bool resultReverse = false) + { + if (sourceBones == null || sourceBones.Count == 0) + { + throw new AutoMorpherException("Source Bones are Missing", "[BoneMatchUtil] BuildTransformMatchMap\n - sourceBones is null or empty"); + } + if (destBones == null || destBones.Count == 0) + { + throw new AutoMorpherException("Destination Bones are Missing", "[BoneMatchUtil] BuildTransformMatchMap\n - destBones is null or empty"); + } + BoneCorrespondenceUtil boneCorrespondenceUtil = new BoneCorrespondenceUtil(); + Dictionary dictionary = new Dictionary(); + Dictionary dictionary2 = new Dictionary(); + Dictionary dictionary3 = new Dictionary(); + Dictionary> dictionary4 = new Dictionary>(); + foreach (BoneRootLocalData destBone in destBones) + { + if (destBone == null || destBone.t == null) + { + continue; + } + string text = destBone.path ?? ""; + if (text.Length > 0 && !dictionary2.ContainsKey(text)) + { + dictionary2.Add(text, destBone); + } + if (text.Length > 0) + { + string text2 = boneCorrespondenceUtil.NormalizePath(text); + if (text2.Length > 0 && !dictionary3.ContainsKey(text2)) + { + dictionary3.Add(text2, destBone); + } + } + string text3 = destBone.name ?? destBone.t.name; + if (!string.IsNullOrEmpty(text3)) + { + if (!dictionary4.TryGetValue(text3, out var value)) + { + value = (dictionary4[text3] = new List()); + } + value.Add(destBone); + } + } + HashSet hashSet = new HashSet(); + foreach (BoneRootLocalData sourceBone in sourceBones) + { + if (sourceBone == null || sourceBone.t == null) + { + continue; + } + Transform transform = null; + string text4 = sourceBone.path ?? ""; + if (text4.Length > 0 && dictionary2.TryGetValue(text4, out var value2) && value2 != null && value2.t != null && !hashSet.Contains(value2.t)) + { + transform = value2.t; + } + if (transform == null && text4.Length > 0) + { + string text5 = boneCorrespondenceUtil.NormalizePath(text4); + if (text5.Length > 0 && dictionary3.TryGetValue(text5, out var value3) && value3 != null && value3.t != null && !hashSet.Contains(value3.t)) + { + transform = value3.t; + } + } + if (transform == null) + { + string text6 = sourceBone.name ?? sourceBone.t.name; + if (!string.IsNullOrEmpty(text6) && dictionary4.TryGetValue(text6, out var value4)) + { + float num = float.PositiveInfinity; + Transform transform2 = null; + for (int i = 0; i < value4.Count; i++) + { + BoneRootLocalData boneRootLocalData = value4[i]; + if (boneRootLocalData != null && !(boneRootLocalData.t == null) && !hashSet.Contains(boneRootLocalData.t)) + { + float num2 = Vector3.SqrMagnitude(boneRootLocalData.rootLocalPos - sourceBone.rootLocalPos); + if (num2 < num) + { + num = num2; + transform2 = boneRootLocalData.t; + } + } + } + transform = transform2; + } + } + if (transform != null) + { + if (resultReverse) + { + dictionary[transform] = sourceBone.t; + } + else + { + dictionary[sourceBone.t] = transform; + } + hashSet.Add(transform); + } + } + return dictionary; + } + + public void RemapSourceClothMatchToTargetCloth(Transform sourceClothRoot, Transform targetClothRoot, Dictionary> sourceClothHumanBones, Dictionary sourceClothBoneTypeMap, out Dictionary> targetClothHumanBones, out Dictionary targetClothBoneTypeMap) + { + targetClothHumanBones = new Dictionary>(); + targetClothBoneTypeMap = new Dictionary(); + if (sourceClothRoot == null || targetClothRoot == null) + { + throw new AutoMorpherException("Cloth Root is Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - sourceClothRoot or targetClothRoot is null"); + } + if (sourceClothHumanBones == null || sourceClothBoneTypeMap == null) + { + throw new AutoMorpherException("Source Cloth Match Data is Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - sourceClothHumanBones or sourceClothBoneTypeMap is null"); + } + HashSet hashSet = new HashSet(); + foreach (KeyValuePair item in sourceClothBoneTypeMap) + { + if (item.Key != null) + { + hashSet.Add(item.Key); + } + } + foreach (KeyValuePair> sourceClothHumanBone in sourceClothHumanBones) + { + HashSet value = sourceClothHumanBone.Value; + if (value == null || value.Count == 0) + { + continue; + } + foreach (Transform item2 in value) + { + if (item2 != null) + { + hashSet.Add(item2); + } + } + } + if (hashSet.Count == 0) + { + throw new AutoMorpherException("Source Cloth Bone Candidates are Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - sourceBoneSet is empty (no bones found in sourceClothBoneTypeMap/sourceClothHumanBones)"); + } + HashSet meshBones = GetMeshBones(targetClothRoot.GetComponentsInChildren(includeInactive: true).ToList()); + if (meshBones == null || meshBones.Count == 0) + { + throw new AutoMorpherException("Target Cloth Bone Candidates are Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - targetBoneSet is null or empty (GetMeshBones returned no bones)"); + } + List rootLocalBones = GetRootLocalBones(sourceClothRoot, hashSet); + List rootLocalBones2 = GetRootLocalBones(targetClothRoot, meshBones); + if (rootLocalBones == null || rootLocalBones.Count == 0) + { + throw new AutoMorpherException("Source RootLocal Bones are Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - sourceRootLocalBones is null or empty"); + } + if (rootLocalBones2 == null || rootLocalBones2.Count == 0) + { + throw new AutoMorpherException("Target RootLocal Bones are Missing", "[BoneMatchUtil] RemapClothMatchResultToTargetCloth\n - targetRootLocalBones is null or empty"); + } + Dictionary dictionary = BuildTransformMatchMap(rootLocalBones, rootLocalBones2); + foreach (KeyValuePair item3 in sourceClothBoneTypeMap) + { + Transform key = item3.Key; + ClothBoneType value2 = item3.Value; + if (key == null || !dictionary.TryGetValue(key, out var value3) || value3 == null) + { + continue; + } + if (targetClothBoneTypeMap.TryGetValue(value3, out var value4)) + { + if (value4 == ClothBoneType.Accessory && value2 == ClothBoneType.Body) + { + targetClothBoneTypeMap[value3] = ClothBoneType.Body; + } + } + else + { + targetClothBoneTypeMap.Add(value3, value2); + } + } + foreach (KeyValuePair> sourceClothHumanBone2 in sourceClothHumanBones) + { + HumanBodyBones key2 = sourceClothHumanBone2.Key; + HashSet value5 = sourceClothHumanBone2.Value; + if (value5 == null || value5.Count == 0) + { + continue; + } + foreach (Transform item4 in value5) + { + if (!(item4 == null) && dictionary.TryGetValue(item4, out var value6) && !(value6 == null)) + { + if (!targetClothHumanBones.TryGetValue(key2, out var value7)) + { + value7 = new HashSet(); + targetClothHumanBones[key2] = value7; + } + value7.Add(value6); + } + } + } + } + + public void BuildSourceToProxyBoneMap(Animator sourceAvatar, Animator proxyAvatar, out Dictionary sourceToProxy) + { + sourceToProxy = new Dictionary(); + if (sourceAvatar == null) + { + throw new AutoMorpherException("Source Avatar is Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - sourceAvatar is null"); + } + if (proxyAvatar == null) + { + throw new AutoMorpherException("Proxy Avatar is Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - proxyAvatar is null"); + } + Transform transform = sourceAvatar.transform; + Transform transform2 = proxyAvatar.transform; + HashSet meshBones = GetMeshBones(transform2.GetComponentsInChildren(includeInactive: true).ToList()); + if (meshBones == null || meshBones.Count == 0) + { + throw new AutoMorpherException("Proxy Body Bone Candidates are Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - proxyBoneSet is null or empty (GetMeshBones returned no bones)"); + } + HashSet meshBones2 = GetMeshBones(transform.GetComponentsInChildren(includeInactive: true).ToList()); + if (meshBones2 == null || meshBones2.Count == 0) + { + throw new AutoMorpherException("Source Body Bone Candidates are Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - sourceBoneSet is null or empty (GetMeshBones returned no bones)"); + } + List rootLocalBones = GetRootLocalBones(transform2, meshBones); + List rootLocalBones2 = GetRootLocalBones(transform, meshBones2); + if (rootLocalBones == null || rootLocalBones.Count == 0) + { + throw new AutoMorpherException("Proxy RootLocal Bones are Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - proxyRootLocalBones is null or empty"); + } + if (rootLocalBones2 == null || rootLocalBones2.Count == 0) + { + throw new AutoMorpherException("Source RootLocal Bones are Missing", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - sourceRootLocalBones is null or empty"); + } + sourceToProxy = BuildTransformMatchMap(rootLocalBones, rootLocalBones2, resultReverse: true); + if (sourceToProxy == null) + { + throw new AutoMorpherException("Source To Proxy Map Build Failed", "[AvatarBodyMatchUtil] BuildSourceToProxyBoneMap\n - BuildTransformMatchMap returned null"); + } + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneMatchUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneMatchUtil.cs.meta new file mode 100644 index 0000000..f26a086 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BoneMatchUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1b3f5d2f72b363c4a89a5c46103505c5 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhNode.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhNode.cs new file mode 100644 index 0000000..8b3d6d9 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhNode.cs @@ -0,0 +1,20 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BvhNode +using UnityEngine; + +public struct BvhNode +{ + public Bounds bounds; + + public int leftChild; + + public int rightChild; + + public int start; + + public int count; + + public bool isLeaf; +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhNode.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhNode.cs.meta new file mode 100644 index 0000000..32440d0 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhNode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8cff8387b82491d47a5ad0ba7d35a706 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangle.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangle.cs new file mode 100644 index 0000000..51f0663 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangle.cs @@ -0,0 +1,18 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BvhTriangle +using UnityEngine; + +public struct BvhTriangle +{ + public Vector3 a; + + public Vector3 b; + + public Vector3 c; + + public Vector3 normal; + + public HumanBodyBones mainHumanBone; +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangle.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangle.cs.meta new file mode 100644 index 0000000..22cee41 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangle.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 32009a1b0b1882e4f87cdff91a494b17 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangleMesh.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangleMesh.cs new file mode 100644 index 0000000..18307c8 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangleMesh.cs @@ -0,0 +1,767 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.BvhTriangleMesh +using System.Collections.Generic; +using Eden.AutoMorpher; +using UnityEngine; + +public class BvhTriangleMesh +{ + public struct ClosestHit + { + public int triangleIndex; + + public Vector3 closestPoint; + + public Vector3 normal; + + public float sqrDistance; + + public HumanBodyBones mainHumanBone; + } + + private TriangleUtil triangleUtil; + + private HumanBodyBones[] humanBones = new HumanBodyBones[34] + { + HumanBodyBones.Hips, + HumanBodyBones.Spine, + HumanBodyBones.Chest, + HumanBodyBones.UpperChest, + HumanBodyBones.Neck, + HumanBodyBones.LeftShoulder, + HumanBodyBones.LeftUpperArm, + HumanBodyBones.LeftLowerArm, + HumanBodyBones.LeftHand, + HumanBodyBones.LeftThumbProximal, + HumanBodyBones.LeftIndexProximal, + HumanBodyBones.LeftMiddleProximal, + HumanBodyBones.LeftRingProximal, + HumanBodyBones.LeftLittleProximal, + HumanBodyBones.RightShoulder, + HumanBodyBones.RightUpperArm, + HumanBodyBones.RightLowerArm, + HumanBodyBones.RightHand, + HumanBodyBones.RightThumbProximal, + HumanBodyBones.RightIndexProximal, + HumanBodyBones.RightMiddleProximal, + HumanBodyBones.RightRingProximal, + HumanBodyBones.RightLittleProximal, + HumanBodyBones.LeftUpperLeg, + HumanBodyBones.LeftLowerLeg, + HumanBodyBones.LeftFoot, + HumanBodyBones.LeftToes, + HumanBodyBones.RightUpperLeg, + HumanBodyBones.RightLowerLeg, + HumanBodyBones.RightFoot, + HumanBodyBones.RightToes, + HumanBodyBones.Head, + HumanBodyBones.LeftEye, + HumanBodyBones.RightEye + }; + + public BvhTriangle[] triangles; + + public BvhNode[] nodes; + + public int[] triIndices; + + private const int LeafMaxTriangles = 4; + + public BvhTriangleMesh() + { + triangleUtil = new TriangleUtil(); + } + + private HumanBodyBones[] BuildVertexMainHumanBone(SkinnedMeshRenderer smr, Animator animator, HumanBodyBones[] bodyBones) + { + Mesh sharedMesh = smr.sharedMesh; + BoneWeight[] boneWeights = sharedMesh.boneWeights; + int[] boneToBodyIndex = BuildBoneToBodyIndexMap(smr, animator, bodyBones); + HumanBodyBones[] array = new HumanBodyBones[sharedMesh.vertexCount]; + for (int i = 0; i < sharedMesh.vertexCount; i++) + { + BoneWeight boneWeight = boneWeights[i]; + int bestBodyIdx = -1; + float bestWeight = 0f; + Try(boneWeight.boneIndex0, boneWeight.weight0); + Try(boneWeight.boneIndex1, boneWeight.weight1); + Try(boneWeight.boneIndex2, boneWeight.weight2); + Try(boneWeight.boneIndex3, boneWeight.weight3); + array[i] = ((bestBodyIdx >= 0) ? bodyBones[bestBodyIdx] : HumanBodyBones.LastBone); + void Try(int boneIdx, float w) + { + if (!(w <= 0f) && boneIdx >= 0 && boneIdx < boneToBodyIndex.Length) + { + int num = boneToBodyIndex[boneIdx]; + if (num >= 0 && w > bestWeight) + { + bestWeight = w; + bestBodyIdx = num; + } + } + } + } + return array; + } + + public BvhTriangleMesh BuildFromSkinnedMeshes(IReadOnlyList renderers, Animator animator) + { + if (renderers == null || renderers.Count == 0) + { + return null; + } + BvhTriangleMesh bvhTriangleMesh = new BvhTriangleMesh(); + int num = 0; + foreach (SkinnedMeshRenderer renderer in renderers) + { + if (!(renderer == null) && !(renderer.sharedMesh == null)) + { + num += renderer.sharedMesh.triangles.Length / 3; + } + } + if (num == 0) + { + return null; + } + bvhTriangleMesh.triangles = new BvhTriangle[num]; + int num2 = 0; + Mesh mesh = new Mesh(); + foreach (SkinnedMeshRenderer renderer2 in renderers) + { + if (renderer2 == null || renderer2.sharedMesh == null) + { + continue; + } + mesh.Clear(); + renderer2.BakeMesh(mesh); + Vector3[] vertices = mesh.vertices; + int[] array = renderer2.sharedMesh.triangles; + BoneWeight[] boneWeights = renderer2.sharedMesh.boneWeights; + int[] boneToBodyIndex = BuildBoneToBodyIndexMap(renderer2, animator, humanBones); + int num3 = array.Length / 3; + Transform transform = renderer2.transform; + Vector3 lossyScale = transform.lossyScale; + Vector3 vector = new Vector3(1f / Mathf.Max(lossyScale.x, 1E-08f), 1f / Mathf.Max(lossyScale.y, 1E-08f), 1f / Mathf.Max(lossyScale.z, 1E-08f)); + Matrix4x4 matrix4x = transform.localToWorldMatrix * Matrix4x4.Scale(vector); + for (int i = 0; i < num3; i++) + { + int num4 = array[i * 3]; + int num5 = array[i * 3 + 1]; + int num6 = array[i * 3 + 2]; + Vector3 vector2 = matrix4x.MultiplyPoint3x4(vertices[num4]); + Vector3 vector3 = matrix4x.MultiplyPoint3x4(vertices[num5]); + Vector3 vector4 = matrix4x.MultiplyPoint3x4(vertices[num6]); + Vector3 normal = Vector3.Cross(vector3 - vector2, vector4 - vector2); + float magnitude = normal.magnitude; + if (magnitude > 1E-08f) + { + normal /= magnitude; + } + else + { + normal = Vector3.up; + } + int num7 = ComputeTriangleMainHumanBoneIndex(num4, num5, num6, boneWeights, boneToBodyIndex, humanBones.Length); + HumanBodyBones mainHumanBone = ((num7 >= 0) ? humanBones[num7] : HumanBodyBones.LastBone); + bvhTriangleMesh.triangles[num2++] = new BvhTriangle + { + a = vector2, + b = vector3, + c = vector4, + normal = normal, + mainHumanBone = mainHumanBone + }; + mesh.Clear(); + } + } + int num8 = num; + int[] array2 = new int[num8]; + for (int j = 0; j < num8; j++) + { + array2[j] = j; + } + bvhTriangleMesh.triIndices = array2; + List list = new List(); + BuildRecursive(bvhTriangleMesh.triangles, array2, 0, num8, list); + bvhTriangleMesh.nodes = list.ToArray(); + return bvhTriangleMesh; + } + + private int[] BuildBoneToBodyIndexMap(SkinnedMeshRenderer smr, Animator animator, HumanBodyBones[] bodyBones) + { + Transform[] bones = smr.bones; + int[] array = new int[bones.Length]; + for (int i = 0; i < array.Length; i++) + { + array[i] = -1; + } + if (animator == null || bodyBones == null || bones == null) + { + return array; + } + Dictionary dictionary = new Dictionary(); + for (int j = 0; j < bones.Length; j++) + { + if (!(bones[j] == null) && !dictionary.ContainsKey(bones[j])) + { + dictionary.Add(bones[j], j); + } + } + Dictionary> dictionary2 = new MeshClassifier().MeshHumanoidBoneMatcher(animator, new SkinnedMeshRenderer[1] { smr }); + for (int k = 0; k < bodyBones.Length; k++) + { + HumanBodyBones key = bodyBones[k]; + if (!dictionary2.TryGetValue(key, out var value) || value == null) + { + continue; + } + foreach (Transform item in value) + { + if (!(item == null) && dictionary.TryGetValue(item, out var value2)) + { + array[value2] = k; + } + } + } + for (int l = 0; l < bones.Length; l++) + { + if (array[l] != -1) + { + continue; + } + Transform transform = bones[l]; + if (transform == null) + { + continue; + } + Transform parent = transform.parent; + if (!(parent == null) && dictionary.TryGetValue(parent, out var value3)) + { + int num = array[value3]; + if (num != -1) + { + array[l] = num; + } + } + } + return array; + } + + private int ComputeTriangleMainHumanBoneIndex(int vi0, int vi1, int vi2, BoneWeight[] weights, int[] boneToBodyIndex, int bodyBonesCount) + { + if (weights == null || weights.Length == 0 || boneToBodyIndex == null || boneToBodyIndex.Length == 0) + { + return -1; + } + float[] scores = new float[bodyBonesCount]; + Accumulate(vi0); + Accumulate(vi1); + Accumulate(vi2); + int result = -1; + float num = 0f; + for (int i = 0; i < scores.Length; i++) + { + if (scores[i] > num) + { + num = scores[i]; + result = i; + } + } + return result; + void Accumulate(int v) + { + if (v >= 0 && v < weights.Length) + { + BoneWeight boneWeight = weights[v]; + Add(boneWeight.boneIndex0, boneWeight.weight0); + Add(boneWeight.boneIndex1, boneWeight.weight1); + Add(boneWeight.boneIndex2, boneWeight.weight2); + Add(boneWeight.boneIndex3, boneWeight.weight3); + } + } + void Add(int boneIdx, float w) + { + if (!(w <= 0f) && boneIdx >= 0 && boneIdx < boneToBodyIndex.Length) + { + int num2 = boneToBodyIndex[boneIdx]; + if (num2 >= 0) + { + scores[num2] += w; + } + } + } + } + + public BvhTriangleMesh BuildFromSkinnedMeshes(IList renderers) + { + if (renderers == null || renderers.Count == 0) + { + return null; + } + BvhTriangleMesh bvhTriangleMesh = new BvhTriangleMesh(); + int num = 0; + foreach (SkinnedMeshRenderer renderer in renderers) + { + if (!(renderer == null) && !(renderer.sharedMesh == null)) + { + num += renderer.sharedMesh.triangles.Length / 3; + } + } + if (num == 0) + { + return null; + } + bvhTriangleMesh.triangles = new BvhTriangle[num]; + int num2 = 0; + foreach (SkinnedMeshRenderer renderer2 in renderers) + { + if (renderer2 == null || renderer2.sharedMesh == null) + { + continue; + } + Mesh sharedMesh = renderer2.sharedMesh; + Vector3[] vertices = sharedMesh.vertices; + int[] array = sharedMesh.triangles; + int num3 = array.Length / 3; + for (int i = 0; i < num3; i++) + { + int num4 = array[i * 3]; + int num5 = array[i * 3 + 1]; + int num6 = array[i * 3 + 2]; + Vector3 vector = renderer2.transform.TransformPoint(vertices[num4]); + Vector3 vector2 = renderer2.transform.TransformPoint(vertices[num5]); + Vector3 vector3 = renderer2.transform.TransformPoint(vertices[num6]); + Vector3 normal = Vector3.Cross(vector2 - vector, vector3 - vector); + float magnitude = normal.magnitude; + if (magnitude > 1E-08f) + { + normal /= magnitude; + } + else + { + normal = Vector3.up; + } + bvhTriangleMesh.triangles[num2++] = new BvhTriangle + { + a = vector, + b = vector2, + c = vector3, + normal = normal + }; + } + } + int num7 = num; + int[] array2 = new int[num7]; + for (int j = 0; j < num7; j++) + { + array2[j] = j; + } + bvhTriangleMesh.triIndices = array2; + List list = new List(); + BuildRecursive(bvhTriangleMesh.triangles, array2, 0, num7, list); + bvhTriangleMesh.nodes = list.ToArray(); + return bvhTriangleMesh; + } + + public BvhTriangleMesh BuildFromMesh(Mesh mesh, Transform transform) + { + BvhTriangleMesh bvhTriangleMesh = new BvhTriangleMesh(); + Vector3[] vertices = mesh.vertices; + int[] array = mesh.triangles; + int num = array.Length / 3; + bvhTriangleMesh.triangles = new BvhTriangle[num]; + for (int i = 0; i < num; i++) + { + int num2 = array[i * 3]; + int num3 = array[i * 3 + 1]; + int num4 = array[i * 3 + 2]; + Vector3 vector = transform.TransformPoint(vertices[num2]); + Vector3 vector2 = transform.TransformPoint(vertices[num3]); + Vector3 vector3 = transform.TransformPoint(vertices[num4]); + Vector3 normal = Vector3.Cross(vector2 - vector, vector3 - vector); + float magnitude = normal.magnitude; + if (magnitude > 1E-08f) + { + normal /= magnitude; + } + else + { + normal = Vector3.up; + } + bvhTriangleMesh.triangles[i] = new BvhTriangle + { + a = vector, + b = vector2, + c = vector3, + normal = normal + }; + } + int[] array2 = new int[num]; + for (int j = 0; j < num; j++) + { + array2[j] = j; + } + bvhTriangleMesh.triIndices = array2; + List list = new List(); + BuildRecursive(bvhTriangleMesh.triangles, array2, 0, num, list); + bvhTriangleMesh.nodes = list.ToArray(); + return bvhTriangleMesh; + } + + private int BuildRecursive(BvhTriangle[] tris, int[] triIndices, int start, int count, List outNodes) + { + int count2 = outNodes.Count; + BvhNode bvhNode = default(BvhNode); + Bounds bounds = default(Bounds); + bool flag = true; + for (int i = start; i < start + count; i++) + { + BvhTriangle bvhTriangle = tris[triIndices[i]]; + if (flag) + { + bounds = new Bounds(bvhTriangle.a, Vector3.zero); + bounds.Encapsulate(bvhTriangle.b); + bounds.Encapsulate(bvhTriangle.c); + flag = false; + } + else + { + bounds.Encapsulate(bvhTriangle.a); + bounds.Encapsulate(bvhTriangle.b); + bounds.Encapsulate(bvhTriangle.c); + } + } + bvhNode.bounds = bounds; + if (count <= 4) + { + bvhNode.isLeaf = true; + bvhNode.start = start; + bvhNode.count = count; + bvhNode.leftChild = -1; + bvhNode.rightChild = -1; + outNodes.Add(bvhNode); + return count2; + } + Vector3 size = bounds.size; + int num = 0; + if (size.y > size.x && size.y > size.z) + { + num = 1; + } + else if (size.z > size.x && size.z > size.y) + { + num = 2; + } + float num2 = 0f; + for (int j = start; j < start + count; j++) + { + BvhTriangle bvhTriangle2 = tris[triIndices[j]]; + num2 += ((bvhTriangle2.a + bvhTriangle2.b + bvhTriangle2.c) / 3f)[num]; + } + num2 /= (float)count; + int num3 = Partition(tris, triIndices, start, count, num, num2); + if (num3 == start || num3 == start + count) + { + num3 = start + count / 2; + } + bvhNode.isLeaf = false; + bvhNode.start = -1; + bvhNode.count = 0; + outNodes.Add(bvhNode); + int leftChild = BuildRecursive(tris, triIndices, start, num3 - start, outNodes); + int rightChild = BuildRecursive(tris, triIndices, num3, start + count - num3, outNodes); + bvhNode.leftChild = leftChild; + bvhNode.rightChild = rightChild; + outNodes[count2] = bvhNode; + return count2; + } + + private int Partition(BvhTriangle[] tris, int[] triIndices, int start, int count, int axis, float splitPos) + { + int num = start; + int num2 = start + count - 1; + while (num <= num2) + { + BvhTriangle bvhTriangle = tris[triIndices[num]]; + BvhTriangle bvhTriangle2 = tris[triIndices[num2]]; + float num3 = (bvhTriangle.a[axis] + bvhTriangle.b[axis] + bvhTriangle.c[axis]) / 3f; + _ = (bvhTriangle2.a[axis] + bvhTriangle2.b[axis] + bvhTriangle2.c[axis]) / 3f; + if (num3 < splitPos) + { + num++; + continue; + } + int num4 = triIndices[num]; + triIndices[num] = triIndices[num2]; + triIndices[num2] = num4; + num2--; + } + return num; + } + + public ClosestHit QueryClosest(Vector3 point) + { + ClosestHit best = new ClosestHit + { + triangleIndex = -1, + sqrDistance = float.MaxValue + }; + if (nodes == null || nodes.Length == 0) + { + return best; + } + QueryClosestRecursive(0, point, ref best); + return best; + } + + public ClosestHit QueryClosest(Vector3 point, HashSet allowedBones) + { + ClosestHit best = new ClosestHit + { + triangleIndex = -1, + sqrDistance = float.MaxValue + }; + if (nodes == null || nodes.Length == 0) + { + return best; + } + if (allowedBones == null || allowedBones.Count == 0) + { + QueryClosestRecursive(0, point, ref best); + } + else + { + QueryClosestRecursiveFiltered(0, point, ref best, allowedBones); + } + return best; + } + + private void QueryClosestRecursiveFiltered(int nodeIndex, Vector3 p, ref ClosestHit best, HashSet allowedBones) + { + BvhNode bvhNode = nodes[nodeIndex]; + if (DistanceSqPointAABB(p, bvhNode.bounds) > best.sqrDistance) + { + return; + } + if (bvhNode.isLeaf) + { + int num = bvhNode.start + bvhNode.count; + for (int i = bvhNode.start; i < num; i++) + { + int num2 = triIndices[i]; + BvhTriangle bvhTriangle = triangles[num2]; + if (allowedBones.Contains(bvhTriangle.mainHumanBone)) + { + Vector3 vector = triangleUtil.ClosestPointOnTriangle(p, bvhTriangle.a, bvhTriangle.b, bvhTriangle.c); + float sqrMagnitude = (p - vector).sqrMagnitude; + if (sqrMagnitude < best.sqrDistance) + { + best.sqrDistance = sqrMagnitude; + best.triangleIndex = num2; + best.closestPoint = vector; + best.normal = bvhTriangle.normal; + best.mainHumanBone = bvhTriangle.mainHumanBone; + } + } + } + } + else + { + int leftChild = bvhNode.leftChild; + int rightChild = bvhNode.rightChild; + float num3 = DistanceSqPointAABB(p, nodes[leftChild].bounds); + float num4 = DistanceSqPointAABB(p, nodes[rightChild].bounds); + if (num3 < num4) + { + QueryClosestRecursiveFiltered(leftChild, p, ref best, allowedBones); + QueryClosestRecursiveFiltered(rightChild, p, ref best, allowedBones); + } + else + { + QueryClosestRecursiveFiltered(rightChild, p, ref best, allowedBones); + QueryClosestRecursiveFiltered(leftChild, p, ref best, allowedBones); + } + } + } + + private void QueryClosestRecursive(int nodeIndex, Vector3 p, ref ClosestHit best) + { + BvhNode bvhNode = nodes[nodeIndex]; + if (DistanceSqPointAABB(p, bvhNode.bounds) > best.sqrDistance) + { + return; + } + if (bvhNode.isLeaf) + { + int num = bvhNode.start + bvhNode.count; + for (int i = bvhNode.start; i < num; i++) + { + int num2 = triIndices[i]; + BvhTriangle bvhTriangle = triangles[num2]; + Vector3 vector = triangleUtil.ClosestPointOnTriangle(p, bvhTriangle.a, bvhTriangle.b, bvhTriangle.c); + float sqrMagnitude = (p - vector).sqrMagnitude; + if (sqrMagnitude < best.sqrDistance) + { + best.sqrDistance = sqrMagnitude; + best.triangleIndex = num2; + best.closestPoint = vector; + best.normal = bvhTriangle.normal; + best.mainHumanBone = bvhTriangle.mainHumanBone; + } + } + } + else + { + int leftChild = bvhNode.leftChild; + int rightChild = bvhNode.rightChild; + float num3 = DistanceSqPointAABB(p, nodes[leftChild].bounds); + float num4 = DistanceSqPointAABB(p, nodes[rightChild].bounds); + if (num3 < num4) + { + QueryClosestRecursive(leftChild, p, ref best); + QueryClosestRecursive(rightChild, p, ref best); + } + else + { + QueryClosestRecursive(rightChild, p, ref best); + QueryClosestRecursive(leftChild, p, ref best); + } + } + } + + private float DistanceSqPointAABB(Vector3 p, Bounds b) + { + float num = Mathf.Max(b.min.x - p.x, 0f, p.x - b.max.x); + float num2 = Mathf.Max(b.min.y - p.y, 0f, p.y - b.max.y); + float num3 = Mathf.Max(b.min.z - p.z, 0f, p.z - b.max.z); + return num * num + num2 * num2 + num3 * num3; + } + + public int QueryClosestN(Vector3 point, int maxCount, float maxDistance, List results) + { + results.Clear(); + if (nodes == null || nodes.Length == 0 || maxCount <= 0) + { + return 0; + } + float num = maxDistance * maxDistance; + float currentMaxSq = num; + QueryClosestNRecursive(0, point, maxCount, num, results, ref currentMaxSq); + return results.Count; + } + + private void QueryClosestNRecursive(int nodeIndex, Vector3 p, int maxCount, float maxDistanceSq, List bestHits, ref float currentMaxSq) + { + BvhNode bvhNode = nodes[nodeIndex]; + if (DistanceSqPointAABB(p, bvhNode.bounds) > currentMaxSq) + { + return; + } + if (bvhNode.isLeaf) + { + int num = bvhNode.start + bvhNode.count; + for (int i = bvhNode.start; i < num; i++) + { + int num2 = triIndices[i]; + BvhTriangle bvhTriangle = triangles[num2]; + Vector3 vector = triangleUtil.ClosestPointOnTriangle(p, bvhTriangle.a, bvhTriangle.b, bvhTriangle.c); + float sqrMagnitude = (p - vector).sqrMagnitude; + if (sqrMagnitude > maxDistanceSq) + { + continue; + } + if (bestHits.Count < maxCount) + { + bestHits.Add(new ClosestHit + { + triangleIndex = num2, + closestPoint = vector, + normal = bvhTriangle.normal, + sqrDistance = sqrMagnitude, + mainHumanBone = bvhTriangle.mainHumanBone + }); + if (bestHits.Count == maxCount) + { + currentMaxSq = GetMaxSqrDistance(bestHits, maxDistanceSq); + } + } + else + { + if (sqrMagnitude >= currentMaxSq) + { + continue; + } + int index = 0; + float sqrDistance = bestHits[0].sqrDistance; + for (int j = 1; j < bestHits.Count; j++) + { + if (bestHits[j].sqrDistance > sqrDistance) + { + sqrDistance = bestHits[j].sqrDistance; + index = j; + } + } + bestHits[index] = new ClosestHit + { + triangleIndex = num2, + closestPoint = vector, + normal = bvhTriangle.normal, + sqrDistance = sqrMagnitude + }; + currentMaxSq = GetMaxSqrDistance(bestHits, maxDistanceSq); + } + } + } + else + { + int leftChild = bvhNode.leftChild; + int rightChild = bvhNode.rightChild; + float num3 = DistanceSqPointAABB(p, nodes[leftChild].bounds); + float num4 = DistanceSqPointAABB(p, nodes[rightChild].bounds); + if (num3 < num4) + { + QueryClosestNRecursive(leftChild, p, maxCount, maxDistanceSq, bestHits, ref currentMaxSq); + QueryClosestNRecursive(rightChild, p, maxCount, maxDistanceSq, bestHits, ref currentMaxSq); + } + else + { + QueryClosestNRecursive(rightChild, p, maxCount, maxDistanceSq, bestHits, ref currentMaxSq); + QueryClosestNRecursive(leftChild, p, maxCount, maxDistanceSq, bestHits, ref currentMaxSq); + } + } + } + + private float GetMaxSqrDistance(List bestHits, float maxDistanceSq) + { + if (bestHits.Count == 0) + { + return maxDistanceSq; + } + float sqrDistance = bestHits[0].sqrDistance; + for (int i = 1; i < bestHits.Count; i++) + { + if (bestHits[i].sqrDistance > sqrDistance) + { + sqrDistance = bestHits[i].sqrDistance; + } + } + return Mathf.Min(sqrDistance, maxDistanceSq); + } + + public Vector3 ComputeMoveVectorToSurface(Vector3 p, float targetGap = 0f) + { + ClosestHit closestHit = QueryClosest(p); + if (closestHit.triangleIndex < 0) + { + return Vector3.zero; + } + Vector3 result = closestHit.closestPoint - p; + if (targetGap > 0f) + { + result += closestHit.normal.normalized * targetGap; + } + return result; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangleMesh.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangleMesh.cs.meta new file mode 100644 index 0000000..a4b6954 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/BvhTriangleMesh.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 26359c45f906f7346836cb8290c0ae62 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothBoneType.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothBoneType.cs new file mode 100644 index 0000000..fa26ca7 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothBoneType.cs @@ -0,0 +1,9 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.ClothBoneType +public enum ClothBoneType +{ + Body, + Accessory +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothBoneType.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothBoneType.cs.meta new file mode 100644 index 0000000..09f3370 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothBoneType.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5f2e2dc7719ac914497c5f6f71c28f66 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothHumanoidMaskUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothHumanoidMaskUtil.cs new file mode 100644 index 0000000..803c2ce --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothHumanoidMaskUtil.cs @@ -0,0 +1,302 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.ClothHumanoidMaskUtil +using System.Collections.Generic; +using Eden.AutoMorpher; +using UnityEngine; + +public class ClothHumanoidMaskUtil +{ + public void BuildExcludedVertexMaskForHandsAndHead(ClothInstance clothInstance, bool excludeHandRoot = false, bool excludeThumbRoot = false) + { + if (clothInstance == null || clothInstance.smr == null) + { + Debug.LogWarning("[ClothHumanoidMaskUtil] clothInstance / smr 가 null"); + return; + } + SkinnedMeshRenderer smr = clothInstance.smr; + Mesh mesh = clothInstance.editableMesh ?? smr.sharedMesh; + if (mesh == null) + { + Debug.LogWarning("[ClothHumanoidMaskUtil] mesh 가 null"); + return; + } + BoneWeight[] boneWeights = mesh.boneWeights; + Transform[] bones = smr.bones; + if (boneWeights == null || boneWeights.Length == 0 || bones == null || bones.Length == 0) + { + Debug.LogWarning("[ClothHumanoidMaskUtil] boneWeights 또는 bones 비어있음"); + return; + } + if (clothInstance.humanoidMatchedBones == null || clothInstance.humanoidMatchedBones.Count == 0) + { + Debug.LogWarning("[ClothHumanoidMaskUtil] humanoidMatchedBones 가 비어있음 (BodyToClothPoseApplier에서 매칭 안 되었을 가능성)"); + return; + } + int num = mesh.vertexCount; + if (boneWeights.Length != num) + { + num = Mathf.Min(num, boneWeights.Length); + } + HashSet excludedBones = new HashSet(); + Dictionary> humanoidMatchedBones = clothInstance.humanoidMatchedBones; + if (humanoidMatchedBones.TryGetValue(HumanBodyBones.Head, out var value) && value != null) + { + foreach (Transform item in value) + { + if (!(item == null)) + { + AddHierarchy(item, includeRoot: true); + } + } + } + if (humanoidMatchedBones.TryGetValue(HumanBodyBones.LeftHand, out var value2) && value2 != null) + { + foreach (Transform item2 in value2) + { + if (!(item2 == null)) + { + AddHierarchy(item2, excludeHandRoot); + } + } + } + if (humanoidMatchedBones.TryGetValue(HumanBodyBones.RightHand, out var value3) && value3 != null) + { + foreach (Transform item3 in value3) + { + if (!(item3 == null)) + { + AddHierarchy(item3, excludeHandRoot); + } + } + } + if (humanoidMatchedBones.TryGetValue(HumanBodyBones.LeftThumbProximal, out var value4) && value4 != null) + { + foreach (Transform item4 in value4) + { + if (!(item4 == null)) + { + if (excludeThumbRoot) + { + excludedBones.Add(item4); + } + else + { + excludedBones.Remove(item4); + } + } + } + } + if (humanoidMatchedBones.TryGetValue(HumanBodyBones.RightThumbProximal, out var value5) && value5 != null) + { + foreach (Transform item5 in value5) + { + if (!(item5 == null)) + { + if (excludeThumbRoot) + { + excludedBones.Add(item5); + } + else + { + excludedBones.Remove(item5); + } + } + } + } + bool[] boneIndexExcluded = new bool[bones.Length]; + for (int i = 0; i < bones.Length; i++) + { + Transform transform = bones[i]; + if (transform != null && excludedBones.Contains(transform)) + { + boneIndexExcluded[i] = true; + } + } + bool[] array = new bool[num]; + for (int j = 0; j < num; j++) + { + BoneWeight boneWeight = boneWeights[j]; + float excludedWeightSum = 0f; + AddExcludedWeight(boneWeight.weight0, boneWeight.boneIndex0); + AddExcludedWeight(boneWeight.weight1, boneWeight.boneIndex1); + AddExcludedWeight(boneWeight.weight2, boneWeight.boneIndex2); + AddExcludedWeight(boneWeight.weight3, boneWeight.boneIndex3); + array[j] = excludedWeightSum >= 0.8f; + void AddExcludedWeight(float w, int bi) + { + if (!(w <= 0f) && bi >= 0 && bi < boneIndexExcluded.Length && boneIndexExcluded[bi]) + { + excludedWeightSum += w; + } + } + } + clothInstance.excludedVertices = array; + BuildLegVertexMasks(clothInstance); + void AddHierarchy(Transform root, bool includeRoot) + { + if (!(root == null)) + { + Stack stack = new Stack(); + if (includeRoot) + { + stack.Push(root); + } + else + { + for (int k = 0; k < root.childCount; k++) + { + stack.Push(root.GetChild(k)); + } + } + while (stack.Count > 0) + { + Transform transform2 = stack.Pop(); + if (!(transform2 == null) && excludedBones.Add(transform2)) + { + for (int l = 0; l < transform2.childCount; l++) + { + stack.Push(transform2.GetChild(l)); + } + } + } + } + } + } + + public void BuildLegVertexMasks(ClothInstance clothInstance) + { + if (clothInstance == null || clothInstance.smr == null) + { + Debug.LogWarning("[ClothHumanoidMaskUtil] clothInstance / smr 가 null"); + return; + } + SkinnedMeshRenderer smr = clothInstance.smr; + Mesh mesh = clothInstance.editableMesh ?? smr.sharedMesh; + if (mesh == null) + { + Debug.LogWarning("[ClothHumanoidMaskUtil] mesh 가 null"); + return; + } + BoneWeight[] boneWeights = mesh.boneWeights; + Transform[] bones = smr.bones; + if (boneWeights == null || boneWeights.Length == 0 || bones == null || bones.Length == 0) + { + Debug.LogWarning("[ClothHumanoidMaskUtil] boneWeights 또는 bones 비어있음"); + return; + } + if (clothInstance.humanoidMatchedBones == null || clothInstance.humanoidMatchedBones.Count == 0) + { + Debug.LogWarning("[ClothHumanoidMaskUtil] humanoidMatchedBones 가 비어있음 (BodyToClothPoseApplier에서 매칭 필요)"); + return; + } + int num = mesh.vertexCount; + if (boneWeights.Length != num) + { + num = Mathf.Min(num, boneWeights.Length); + } + Dictionary> humanoidMatchedBones = clothInstance.humanoidMatchedBones; + HashSet hashSet = new HashSet(); + HashSet hashSet2 = new HashSet(); + if (humanoidMatchedBones.TryGetValue(HumanBodyBones.LeftUpperLeg, out var value) && value != null) + { + foreach (Transform item in value) + { + if (!(item == null)) + { + CollectHierarchy(item, hashSet); + } + } + } + if (humanoidMatchedBones.TryGetValue(HumanBodyBones.RightUpperLeg, out var value2) && value2 != null) + { + foreach (Transform item2 in value2) + { + if (!(item2 == null)) + { + CollectHierarchy(item2, hashSet2); + } + } + } + bool[] boneIsLeftLeg = new bool[bones.Length]; + bool[] boneIsRightLeg = new bool[bones.Length]; + for (int i = 0; i < bones.Length; i++) + { + Transform transform = bones[i]; + if (!(transform == null)) + { + if (hashSet.Contains(transform)) + { + boneIsLeftLeg[i] = true; + } + if (hashSet2.Contains(transform)) + { + boneIsRightLeg[i] = true; + } + } + } + bool[] array = new bool[num]; + bool[] array2 = new bool[num]; + for (int j = 0; j < num; j++) + { + BoneWeight boneWeight = boneWeights[j]; + float leftWeight = 0f; + float rightWeight = 0f; + Check(boneWeight.boneIndex0, boneWeight.weight0); + Check(boneWeight.boneIndex1, boneWeight.weight1); + Check(boneWeight.boneIndex2, boneWeight.weight2); + Check(boneWeight.boneIndex3, boneWeight.weight3); + if (leftWeight > 0.8f) + { + array[j] = true; + array2[j] = false; + } + else if (rightWeight > 0.8f) + { + array[j] = false; + array2[j] = true; + } + else + { + array[j] = false; + array2[j] = false; + } + void Check(int bi, float w) + { + if (bi >= 0 && bi < bones.Length && !(w <= 0f)) + { + if (boneIsLeftLeg[bi]) + { + leftWeight += w; + } + if (boneIsRightLeg[bi]) + { + rightWeight += w; + } + } + } + } + clothInstance.isLeftLegVertex = array; + clothInstance.isRightLegVertex = array2; + static void CollectHierarchy(Transform root, HashSet set) + { + if (!(root == null)) + { + Stack stack = new Stack(); + stack.Push(root); + while (stack.Count > 0) + { + Transform transform2 = stack.Pop(); + if (!(transform2 == null) && set.Add(transform2)) + { + for (int k = 0; k < transform2.childCount; k++) + { + stack.Push(transform2.GetChild(k)); + } + } + } + } + } + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothHumanoidMaskUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothHumanoidMaskUtil.cs.meta new file mode 100644 index 0000000..151ef4d --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothHumanoidMaskUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 47dc065d4616e574a92830eba48a0269 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstance.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstance.cs new file mode 100644 index 0000000..4fbd760 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstance.cs @@ -0,0 +1,539 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.ClothInstance +using System; +using System.Collections.Generic; +using Eden.AutoMorpher; +using UnityEngine; + +[Serializable] +public class ClothInstance +{ + public SkinnedMeshRenderer smr; + + public Mesh editableMesh; + + public Mesh bakedMesh; + + public Matrix4x4 worldNoScale; + + public Vector3[] worldVertices; + + public Vector3[] minDistanceVector; + + public float[] minDistance; + + public Vector3[] deltas; + + public Vector3[] deltasLocal; + + public List[] vertexAdjacency; + + public List> equivalentVertices; + + public bool[] isInsideVertex; + + public bool[] excludedVertices; + + public bool[] isLeftLegVertex; + + public bool[] isRightLegVertex; + + public Vector3[] bakedWorldNormals; + + public Vector4[] bakedWorldTangents; + + public Dictionary> humanoidMatchedBones; + + public ClothInstance(SkinnedMeshRenderer clotheSMR, bool duplicateMesh = true) + { + smr = clotheSMR; + if (duplicateMesh) + { + string name = smr.sharedMesh.name; + editableMesh = UnityEngine.Object.Instantiate(clotheSMR.sharedMesh); + editableMesh.name = name + "_EditableMesh"; + smr.sharedMesh = editableMesh; + } + else + { + editableMesh = smr.sharedMesh; + } + UpdateWorldVertices(); + int vertexCount = editableMesh.vertexCount; + minDistanceVector = new Vector3[vertexCount]; + deltas = new Vector3[vertexCount]; + minDistance = new float[vertexCount]; + isInsideVertex = new bool[vertexCount]; + excludedVertices = new bool[vertexCount]; + isLeftLegVertex = new bool[vertexCount]; + isRightLegVertex = new bool[vertexCount]; + humanoidMatchedBones = new Dictionary>(); + vertexAdjacency = BuildVertexAdjacency(editableMesh); + BuildEquivalentVerticesFromWorld(); + } + + public void UpdateWorldVertices() + { + if (bakedMesh == null) + { + bakedMesh = new Mesh(); + } + else + { + bakedMesh.Clear(); + } + smr.BakeMesh(bakedMesh); + int vertexCount = editableMesh.vertexCount; + Vector3 lossyScale = smr.transform.lossyScale; + Vector3 vector = new Vector3(1f / Mathf.Max(lossyScale.x, 1E-08f), 1f / Mathf.Max(lossyScale.y, 1E-08f), 1f / Mathf.Max(lossyScale.z, 1E-08f)); + worldNoScale = smr.transform.localToWorldMatrix * Matrix4x4.Scale(vector); + worldVertices = new Vector3[vertexCount]; + deltasLocal = new Vector3[vertexCount]; + for (int i = 0; i < vertexCount; i++) + { + worldVertices[i] = worldNoScale.MultiplyPoint3x4(bakedMesh.vertices[i]); + deltasLocal[i] = Vector3.zero; + } + Vector3[] normals = bakedMesh.normals; + Vector4[] tangents = bakedMesh.tangents; + bakedWorldNormals = new Vector3[vertexCount]; + if (tangents != null && tangents.Length == vertexCount) + { + bakedWorldTangents = new Vector4[vertexCount]; + } + else + { + bakedWorldTangents = null; + } + for (int j = 0; j < vertexCount; j++) + { + bakedWorldNormals[j] = smr.transform.TransformDirection(normals[j]).normalized; + if (bakedWorldTangents != null) + { + Vector3 normalized = smr.transform.TransformDirection(new Vector3(tangents[j].x, tangents[j].y, tangents[j].z)).normalized; + bakedWorldTangents[j] = new Vector4(normalized.x, normalized.y, normalized.z, tangents[j].w); + } + } + } + + public List[] BuildVertexAdjacency(Mesh mesh, float seamThreshold = 0.0001f, float proximityThreshold = 0f) + { + if (mesh == null) + { + throw new AutoMorpherException("Mesh is Missing", "[VertexAdjacencyUtil] BuildVertexAdjacency\n - mesh is null"); + } + int vertexCount = mesh.vertexCount; + int[] triangles = mesh.triangles; + Vector3[] vertices = mesh.vertices; + List[] array = new List[vertexCount]; + for (int i = 0; i < vertexCount; i++) + { + array[i] = new List(); + } + int num = triangles.Length / 3; + for (int j = 0; j < num; j++) + { + int num2 = triangles[j * 3]; + int num3 = triangles[j * 3 + 1]; + int num4 = triangles[j * 3 + 2]; + AddNeighbor(array, num2, num3); + AddNeighbor(array, num3, num2); + AddNeighbor(array, num3, num4); + AddNeighbor(array, num4, num3); + AddNeighbor(array, num4, num2); + AddNeighbor(array, num2, num4); + } + if (seamThreshold > 0f) + { + float num5 = seamThreshold * 2f; + float num6 = seamThreshold * seamThreshold; + Dictionary> dictionary = new Dictionary>(); + for (int k = 0; k < vertexCount; k++) + { + Vector3 vector = vertices[k]; + Vector3Int key = new Vector3Int(Mathf.FloorToInt(vector.x / num5), Mathf.FloorToInt(vector.y / num5), Mathf.FloorToInt(vector.z / num5)); + if (!dictionary.TryGetValue(key, out var value)) + { + value = (dictionary[key] = new List()); + } + value.Add(k); + } + foreach (KeyValuePair> item in dictionary) + { + Vector3Int key2 = item.Key; + List value2 = item.Value; + for (int l = -1; l <= 1; l++) + { + for (int m = -1; m <= 1; m++) + { + for (int n = -1; n <= 1; n++) + { + if (l < 0 || (l == 0 && m < 0) || (l == 0 && m == 0 && n < 0)) + { + continue; + } + Vector3Int vector3Int = new Vector3Int(key2.x + l, key2.y + m, key2.z + n); + if (!dictionary.TryGetValue(vector3Int, out var value3)) + { + continue; + } + if (vector3Int == key2) + { + for (int num7 = 0; num7 < value2.Count; num7++) + { + int num8 = value2[num7]; + Vector3 vector2 = vertices[num8]; + for (int num9 = num7 + 1; num9 < value2.Count; num9++) + { + int num10 = value2[num9]; + if ((vertices[num10] - vector2).sqrMagnitude <= num6) + { + AddNeighbor(array, num8, num10); + AddNeighbor(array, num10, num8); + } + } + } + continue; + } + for (int num11 = 0; num11 < value2.Count; num11++) + { + int num12 = value2[num11]; + Vector3 vector3 = vertices[num12]; + for (int num13 = 0; num13 < value3.Count; num13++) + { + int num14 = value3[num13]; + if ((vertices[num14] - vector3).sqrMagnitude <= num6) + { + AddNeighbor(array, num12, num14); + AddNeighbor(array, num14, num12); + } + } + } + } + } + } + } + } + if (proximityThreshold > 0f) + { + float num15 = proximityThreshold * 2f; + float num16 = proximityThreshold * proximityThreshold; + Dictionary> dictionary2 = new Dictionary>(); + for (int num17 = 0; num17 < vertexCount; num17++) + { + Vector3 vector4 = vertices[num17]; + Vector3Int key3 = new Vector3Int(Mathf.FloorToInt(vector4.x / num15), Mathf.FloorToInt(vector4.y / num15), Mathf.FloorToInt(vector4.z / num15)); + if (!dictionary2.TryGetValue(key3, out var value4)) + { + value4 = (dictionary2[key3] = new List()); + } + value4.Add(num17); + } + foreach (KeyValuePair> item2 in dictionary2) + { + Vector3Int key4 = item2.Key; + List value5 = item2.Value; + for (int num18 = -1; num18 <= 1; num18++) + { + for (int num19 = -1; num19 <= 1; num19++) + { + for (int num20 = -1; num20 <= 1; num20++) + { + if (num18 < 0 || (num18 == 0 && num19 < 0) || (num18 == 0 && num19 == 0 && num20 < 0)) + { + continue; + } + Vector3Int vector3Int2 = new Vector3Int(key4.x + num18, key4.y + num19, key4.z + num20); + if (!dictionary2.TryGetValue(vector3Int2, out var value6)) + { + continue; + } + if (vector3Int2 == key4) + { + for (int num21 = 0; num21 < value5.Count; num21++) + { + int num22 = value5[num21]; + Vector3 vector5 = vertices[num22]; + for (int num23 = num21 + 1; num23 < value5.Count; num23++) + { + int num24 = value5[num23]; + if ((vertices[num24] - vector5).sqrMagnitude <= num16) + { + AddNeighbor(array, num22, num24); + AddNeighbor(array, num24, num22); + } + } + } + continue; + } + for (int num25 = 0; num25 < value5.Count; num25++) + { + int num26 = value5[num25]; + Vector3 vector6 = vertices[num26]; + for (int num27 = 0; num27 < value6.Count; num27++) + { + int num28 = value6[num27]; + if ((vertices[num28] - vector6).sqrMagnitude <= num16) + { + AddNeighbor(array, num26, num28); + AddNeighbor(array, num28, num26); + } + } + } + } + } + } + } + } + return array; + static void AddNeighbor(List[] adj, int from, int to) + { + List list3 = adj[from]; + if (!list3.Contains(to)) + { + list3.Add(to); + } + } + } + + public void ApplyWorldVerticesToMesh() + { + if (editableMesh == null || smr == null || worldVertices == null) + { + return; + } + Mesh mesh = editableMesh; + int vertexCount = mesh.vertexCount; + Vector3[] vertices = mesh.vertices; + SkinningUtil skinningUtil = new SkinningUtil(); + Vector3[] array = null; + int blendShapeCount = mesh.blendShapeCount; + if (blendShapeCount > 0) + { + array = new Vector3[vertexCount]; + Vector3[] array2 = new Vector3[vertexCount]; + Vector3[] deltaNormals = new Vector3[vertexCount]; + Vector3[] deltaTangents = new Vector3[vertexCount]; + for (int i = 0; i < blendShapeCount; i++) + { + float blendShapeWeight = smr.GetBlendShapeWeight(i); + if (Mathf.Approximately(blendShapeWeight, 0f)) + { + continue; + } + int frameIndex = mesh.GetBlendShapeFrameCount(i) - 1; + float blendShapeFrameWeight = mesh.GetBlendShapeFrameWeight(i, frameIndex); + mesh.GetBlendShapeFrameVertices(i, frameIndex, array2, deltaNormals, deltaTangents); + float num = ((blendShapeFrameWeight != 0f) ? (blendShapeWeight / blendShapeFrameWeight) : 0f); + if (!Mathf.Approximately(num, 0f)) + { + for (int j = 0; j < vertexCount; j++) + { + array[j] += array2[j] * num; + } + } + } + } + for (int k = 0; k < vertexCount; k++) + { + Vector3 vector = skinningUtil.WorldPosToBindPos_Full(smr, mesh, k, worldVertices[k]); + if (array != null) + { + vector -= array[k]; + } + vertices[k] = vector; + } + mesh.vertices = vertices; + Vector3[] array3 = null; + Vector3[] array4 = null; + if (blendShapeCount > 0) + { + array3 = new Vector3[vertexCount]; + array4 = new Vector3[vertexCount]; + Vector3[] deltaVertices = new Vector3[vertexCount]; + Vector3[] array5 = new Vector3[vertexCount]; + Vector3[] array6 = new Vector3[vertexCount]; + for (int l = 0; l < blendShapeCount; l++) + { + float blendShapeWeight2 = smr.GetBlendShapeWeight(l); + if (Mathf.Approximately(blendShapeWeight2, 0f)) + { + continue; + } + int frameIndex2 = mesh.GetBlendShapeFrameCount(l) - 1; + float blendShapeFrameWeight2 = mesh.GetBlendShapeFrameWeight(l, frameIndex2); + mesh.GetBlendShapeFrameVertices(l, frameIndex2, deltaVertices, array5, array6); + float num2 = ((blendShapeFrameWeight2 != 0f) ? (blendShapeWeight2 / blendShapeFrameWeight2) : 0f); + if (!Mathf.Approximately(num2, 0f)) + { + for (int m = 0; m < vertexCount; m++) + { + array3[m] += array5[m] * num2; + array4[m] += array6[m] * num2; + } + } + } + } + if (bakedWorldNormals == null || bakedWorldNormals.Length != vertexCount) + { + return; + } + Vector3[] array7 = new Vector3[vertexCount]; + bool flag = bakedWorldTangents != null && bakedWorldTangents.Length == vertexCount; + Vector4[] array8 = (flag ? new Vector4[vertexCount] : null); + for (int n = 0; n < vertexCount; n++) + { + Vector3 vector2 = skinningUtil.WorldDirToBindDir_Full(smr, mesh, n, bakedWorldNormals[n]); + if (array3 != null) + { + vector2 -= array3[n]; + } + array7[n] = vector2.normalized; + if (flag) + { + Vector3 vector3 = skinningUtil.WorldDirToBindDir_Full(targetWorldDir: new Vector3(bakedWorldTangents[n].x, bakedWorldTangents[n].y, bakedWorldTangents[n].z), smr: smr, bindMesh: mesh, vertexIndex: n); + if (array4 != null) + { + vector3 -= array4[n]; + } + vector3 = vector3.normalized; + array8[n] = new Vector4(vector3.x, vector3.y, vector3.z, bakedWorldTangents[n].w); + } + } + mesh.normals = array7; + if (array8 != null) + { + mesh.tangents = array8; + } + } + + public void BuildEquivalentVerticesFromWorld(float maxDistance = 1E-05f) + { + if (worldVertices == null || worldVertices.Length == 0) + { + Debug.LogWarning("[ClothInstance] BuildEquivalentVerticesFromWorld: worldVertices is null or empty. Call UpdateWorldVertices() first."); + return; + } + int num = worldVertices.Length; + if (equivalentVertices == null) + { + equivalentVertices = new List>(); + } + else + { + equivalentVertices.Clear(); + } + float num2 = maxDistance * maxDistance; + Dictionary> dictionary = new Dictionary>(); + for (int i = 0; i < num; i++) + { + Vector3 vector = worldVertices[i]; + Vector3Int key = new Vector3Int(Mathf.FloorToInt(vector.x / maxDistance), Mathf.FloorToInt(vector.y / maxDistance), Mathf.FloorToInt(vector.z / maxDistance)); + if (!dictionary.TryGetValue(key, out var value)) + { + value = (dictionary[key] = new List()); + } + value.Add(i); + } + foreach (KeyValuePair> item in dictionary) + { + List value2 = item.Value; + int count = value2.Count; + if (count < 2) + { + continue; + } + int[] parent = new int[count]; + for (int j = 0; j < count; j++) + { + parent[j] = j; + } + for (int k = 0; k < count; k++) + { + int num3 = value2[k]; + Vector3 vector2 = worldVertices[num3]; + for (int l = k + 1; l < count; l++) + { + int num4 = value2[l]; + Vector3 vector3 = worldVertices[num4]; + if ((vector2 - vector3).sqrMagnitude <= num2) + { + Union(k, l); + } + } + } + Dictionary> dictionary2 = new Dictionary>(); + for (int m = 0; m < count; m++) + { + int key2 = Find(m); + if (!dictionary2.TryGetValue(key2, out var value3)) + { + value3 = (dictionary2[key2] = new List()); + } + value3.Add(value2[m]); + } + foreach (List value4 in dictionary2.Values) + { + if (value4.Count >= 2) + { + equivalentVertices.Add(value4); + } + } + int Find(int x) + { + if (parent[x] != x) + { + parent[x] = Find(parent[x]); + } + return parent[x]; + } + void Union(int a, int b) + { + int num5 = Find(a); + int num6 = Find(b); + if (num5 != num6) + { + parent[num6] = num5; + } + } + } + } + + ~ClothInstance() + { + Dispose(); + } + + public void Dispose() + { + if (bakedMesh != null) + { + UnityEngine.Object.DestroyImmediate(bakedMesh); + bakedMesh = null; + } + worldVertices = null; + minDistanceVector = null; + minDistance = null; + deltas = null; + deltasLocal = null; + isInsideVertex = null; + excludedVertices = null; + isLeftLegVertex = null; + isRightLegVertex = null; + vertexAdjacency = null; + if (equivalentVertices != null) + { + for (int i = 0; i < equivalentVertices.Count; i++) + { + equivalentVertices[i]?.Clear(); + } + equivalentVertices.Clear(); + equivalentVertices = null; + } + humanoidMatchedBones = null; + smr = null; + editableMesh = null; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstance.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstance.cs.meta new file mode 100644 index 0000000..eb24b81 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstance.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 40c5e34f6a4ef45489dc16cd660022ca \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstanceTotal.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstanceTotal.cs new file mode 100644 index 0000000..8930539 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstanceTotal.cs @@ -0,0 +1,307 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.ClothInstanceTotal +using System.Collections.Generic; +using Eden.AutoMorpher; +using UnityEngine; + +public class ClothInstanceTotal +{ + private readonly List clothInstances; + + public int TotalVertexCount { get; private set; } + + public Vector3[] GlobalPositions { get; private set; } + + public Vector3[] GlobalDeltas { get; private set; } + + public List[] GlobalAdjacencyTopology { get; private set; } + + public List[] GlobalAdjacencyDistance { get; private set; } + + public List[] GlobalAdjacencyMerged { get; private set; } + + public int[] VertexOffsets { get; private set; } + + public ClothInstanceTotal(List clothInstances, float distanceBuildRadius, int maxNeighborsPerVertex = 0) + { + if (clothInstances == null || clothInstances.Count == 0) + { + throw new AutoMorpherException("Cloth Instances are Missing", "[ClothInstanceTotal] ClothInstanceTotal\n - clothInstances is null or empty"); + } + if (distanceBuildRadius <= 0f) + { + throw new AutoMorpherException("Distance Build Radius is Invalid", "[ClothInstanceTotal] ClothInstanceTotal\n - distanceBuildRadius must be > 0"); + } + this.clothInstances = clothInstances; + BuildTopologyCache(); + BuildDistanceAdjacencyCandidates(distanceBuildRadius, maxNeighborsPerVertex); + BuildMergedAdjacency(); + } + + public void SetGlobalDeltas(Vector3[] globalDeltas) + { + if (globalDeltas == null || globalDeltas.Length != TotalVertexCount) + { + throw new AutoMorpherException("Global Deltas are Invalid", "[ClothInstanceTotal] SetGlobalDeltas\n - globalDeltas is null or length mismatch"); + } + GlobalDeltas = globalDeltas; + } + + public void UpdateGlobalBuffersFromClothInstances() + { + ValidateGlobalBufferReady("[ClothInstanceTotal] UpdateGlobalBuffersFromClothInstances"); + for (int i = 0; i < clothInstances.Count; i++) + { + ClothInstance clothInstance = clothInstances[i]; + if (clothInstance != null) + { + if (clothInstance.worldVertices == null || clothInstance.deltasLocal == null) + { + throw new AutoMorpherException("Cloth Instance Data is Missing", "[ClothInstanceTotal] UpdateGlobalBuffersFromClothInstances\n - worldVertices or deltasLocal is null"); + } + int num = VertexOffsets[i]; + int num2 = clothInstance.worldVertices.Length; + if (num2 != clothInstance.deltasLocal.Length) + { + throw new AutoMorpherException("Cloth Instance Array Length Mismatch", "[ClothInstanceTotal] UpdateGlobalBuffersFromClothInstances\n - worldVertices.Length != deltasLocal.Length"); + } + for (int j = 0; j < num2; j++) + { + GlobalPositions[num + j] = clothInstance.worldVertices[j]; + GlobalDeltas[num + j] = clothInstance.deltasLocal[j]; + } + } + } + } + + public void ApplyGlobalDeltasToClothInstances() + { + ValidateGlobalBufferReady("[ClothInstanceTotal] ApplyGlobalDeltasToClothInstances"); + for (int i = 0; i < clothInstances.Count; i++) + { + ClothInstance clothInstance = clothInstances[i]; + if (clothInstance != null) + { + if (clothInstance.deltasLocal == null) + { + throw new AutoMorpherException("Cloth Deltas are Missing", "[ClothInstanceTotal] ApplyGlobalDeltasToClothInstances\n - deltasLocal is null"); + } + int num = VertexOffsets[i]; + int num2 = clothInstance.deltasLocal.Length; + for (int j = 0; j < num2; j++) + { + clothInstance.deltasLocal[j] = GlobalDeltas[num + j]; + } + } + } + } + + private void ValidateGlobalBufferReady(string functionContext) + { + if (clothInstances == null || clothInstances.Count == 0) + { + throw new AutoMorpherException("Cloth Instances are Missing", functionContext + "\n - clothInstances is null or empty"); + } + if (TotalVertexCount <= 0 || GlobalPositions == null || GlobalDeltas == null || VertexOffsets == null || GlobalAdjacencyMerged == null) + { + throw new AutoMorpherException("Cloth Instance Total Cache is Not Ready", functionContext + "\n - total/global buffers/merged adjacency are not initialized"); + } + } + + private void BuildTopologyCache() + { + TotalVertexCount = CalculateTotalVertexCount(clothInstances); + if (TotalVertexCount <= 0) + { + throw new AutoMorpherException("Total Vertex Count is Zero", "[ClothInstanceTotal] BuildTopologyCache\n - TotalVertexCount <= 0"); + } + GlobalPositions = new Vector3[TotalVertexCount]; + GlobalDeltas = new Vector3[TotalVertexCount]; + GlobalAdjacencyTopology = new List[TotalVertexCount]; + VertexOffsets = new int[clothInstances.Count]; + int num = 0; + for (int i = 0; i < clothInstances.Count; i++) + { + ClothInstance clothInstance = clothInstances[i]; + VertexOffsets[i] = num; + if (clothInstance == null) + { + continue; + } + if (clothInstance.worldVertices == null || clothInstance.deltasLocal == null || clothInstance.vertexAdjacency == null) + { + throw new AutoMorpherException("Cloth Instance Data is Missing", "[ClothInstanceTotal] BuildTopologyCache\n - worldVertices/deltasLocal/vertexAdjacency is null"); + } + int num2 = clothInstance.worldVertices.Length; + if (num2 != clothInstance.deltasLocal.Length || num2 != clothInstance.vertexAdjacency.Length) + { + throw new AutoMorpherException("Cloth Instance Array Length Mismatch", "[ClothInstanceTotal] BuildTopologyCache\n - worldVertices/deltasLocal/vertexAdjacency lengths differ"); + } + for (int j = 0; j < num2; j++) + { + GlobalPositions[num + j] = clothInstance.worldVertices[j]; + GlobalDeltas[num + j] = clothInstance.deltasLocal[j]; + int capacity = clothInstance.vertexAdjacency[j]?.Count ?? 0; + GlobalAdjacencyTopology[num + j] = new List(capacity); + } + for (int k = 0; k < num2; k++) + { + List list = clothInstance.vertexAdjacency[k]; + if (list == null) + { + continue; + } + int num3 = num + k; + List list2 = GlobalAdjacencyTopology[num3]; + for (int l = 0; l < list.Count; l++) + { + int num4 = list[l]; + if ((uint)num4 >= (uint)num2) + { + throw new AutoMorpherException("Vertex Adjacency Index is Out of Range", "[ClothInstanceTotal] BuildTopologyCache\n - vertexAdjacency contains invalid neighbor index"); + } + list2.Add(num + num4); + } + } + num += num2; + } + } + + private void BuildDistanceAdjacencyCandidates(float radius, int maxNeighborsPerVertex) + { + if (GlobalPositions == null || GlobalPositions.Length == 0) + { + throw new AutoMorpherException("Reference Positions are Missing", "[ClothInstanceTotal] BuildDistanceAdjacencyCandidates\n - GlobalPositions is null or empty"); + } + GlobalAdjacencyDistance = BuildDistanceAdjacencyCandidatesInternal(GlobalPositions, radius, maxNeighborsPerVertex); + } + + private void BuildMergedAdjacency() + { + if (GlobalAdjacencyTopology == null || GlobalAdjacencyDistance == null) + { + throw new AutoMorpherException("Adjacency Inputs are Missing", "[ClothInstanceTotal] BuildMergedAdjacency\n - GlobalAdjacencyTopology or GlobalAdjacencyDistance is null"); + } + if (GlobalAdjacencyTopology.Length != GlobalAdjacencyDistance.Length) + { + throw new AutoMorpherException("Adjacency Length Mismatch", "[ClothInstanceTotal] BuildMergedAdjacency\n - topology and distance adjacency length differ"); + } + int num = GlobalAdjacencyTopology.Length; + GlobalAdjacencyMerged = new List[num]; + for (int i = 0; i < num; i++) + { + List list = new List((GlobalAdjacencyTopology[i]?.Count ?? 0) + (GlobalAdjacencyDistance[i]?.Count ?? 0)); + HashSet hashSet = new HashSet(); + List list2 = GlobalAdjacencyTopology[i]; + if (list2 != null) + { + for (int j = 0; j < list2.Count; j++) + { + int num2 = list2[j]; + if (num2 != i && hashSet.Add(num2)) + { + list.Add(num2); + } + } + } + List list3 = GlobalAdjacencyDistance[i]; + if (list3 != null) + { + for (int k = 0; k < list3.Count; k++) + { + int num3 = list3[k]; + if (num3 != i && hashSet.Add(num3)) + { + list.Add(num3); + } + } + } + GlobalAdjacencyMerged[i] = list; + } + } + + private int CalculateTotalVertexCount(List clothInstances) + { + int num = 0; + for (int i = 0; i < clothInstances.Count; i++) + { + ClothInstance clothInstance = clothInstances[i]; + if (clothInstance != null && clothInstance.worldVertices != null) + { + num += clothInstance.worldVertices.Length; + } + } + return num; + } + + private List[] BuildDistanceAdjacencyCandidatesInternal(Vector3[] referencePositions, float radius, int maxNeighborsPerVertex) + { + if (referencePositions == null || referencePositions.Length == 0) + { + throw new AutoMorpherException("Reference Positions are Missing", "[ClothInstanceTotal] BuildDistanceAdjacencyCandidatesInternal\n - referencePositions is null or empty"); + } + if (radius <= 0f) + { + throw new AutoMorpherException("Radius is Invalid", "[ClothInstanceTotal] BuildDistanceAdjacencyCandidatesInternal\n - radius must be > 0"); + } + int num = referencePositions.Length; + List[] array = new List[num]; + for (int i = 0; i < num; i++) + { + array[i] = new List(8); + } + float num2 = 1f / Mathf.Max(radius, 1E-12f); + float num3 = radius * radius; + Dictionary> dictionary = new Dictionary>(num); + for (int j = 0; j < num; j++) + { + Vector3 vector = referencePositions[j]; + Vector3Int key = new Vector3Int(Mathf.FloorToInt(vector.x * num2), Mathf.FloorToInt(vector.y * num2), Mathf.FloorToInt(vector.z * num2)); + if (!dictionary.TryGetValue(key, out var value)) + { + value = new List(16); + dictionary.Add(key, value); + } + value.Add(j); + } + for (int k = 0; k < num; k++) + { + Vector3 vector2 = referencePositions[k]; + Vector3Int vector3Int = new Vector3Int(Mathf.FloorToInt(vector2.x * num2), Mathf.FloorToInt(vector2.y * num2), Mathf.FloorToInt(vector2.z * num2)); + List list = array[k]; + for (int l = -1; l <= 1; l++) + { + for (int m = -1; m <= 1; m++) + { + for (int n = -1; n <= 1; n++) + { + Vector3Int key2 = new Vector3Int(vector3Int.x + l, vector3Int.y + m, vector3Int.z + n); + if (!dictionary.TryGetValue(key2, out var value2)) + { + continue; + } + for (int num4 = 0; num4 < value2.Count; num4++) + { + int num5 = value2[num4]; + if (num5 != k && !((referencePositions[num5] - vector2).sqrMagnitude > num3)) + { + list.Add(num5); + if (maxNeighborsPerVertex > 0 && list.Count >= maxNeighborsPerVertex) + { + break; + } + } + } + if (maxNeighborsPerVertex > 0 && list.Count >= maxNeighborsPerVertex) + { + break; + } + } + } + } + } + return array; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstanceTotal.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstanceTotal.cs.meta new file mode 100644 index 0000000..03bc812 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ClothInstanceTotal.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 565127c44f3596f4f88203da517ee42d \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher.cs new file mode 100644 index 0000000..0f42d6a --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher.cs @@ -0,0 +1,597 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.EdenAutoMorpher +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Eden.AutoMorpher; +using UnityEngine; + +public class EdenAutoMorpher : MonoBehaviour +{ + [SerializeField] + public GameObject sourceAvatarObject; + + [SerializeField] + public GameObject sourceClothesObject; + + [SerializeField] + private List sourceBodyMeshes = new List(); + + [SerializeField] + private IReadOnlyList sourceBodyMeshesReadOnly = new List(); + + [SerializeField] + public string profileName; + + [SerializeField] + public GameObject targetAvatarObject; + + [SerializeField] + private GameObject targetClothesObject; + + [SerializeField] + private GameObject targetClothesObjectOriginal; + + [SerializeField] + private List targetBodyMeshes = new List(); + + [SerializeField] + private IReadOnlyList targetBodyMeshesReadOnly = new List(); + + [SerializeField] + private float minMargin = 0.003f; + + [SerializeField] + private float worldRadius = 0.1f; + + [SerializeField] + private float sigma = 0.85f; + + [SerializeField] + private int smoothingIteration = 1; + + [SerializeField] + private int fittingExpandIteration = 10; + + [SerializeField] + private int fittingShrinkIteration = 12; + + [SerializeField] + public bool isBodyAutoSetup = true; + + [SerializeField] + public bool isReparentAccessoryBonesToTargetAvatar = true; + + [SerializeField] + public bool skipFootFitting; + + [SerializeField] + public bool transferWeightToAvatar; + + [SerializeField] + public bool addAnchorBone = true; + + [SerializeField] + public bool armatureBoneScaleCopy; + + [SerializeField] + public bool isRemoveAutoMorphedClothes = true; + + private Dictionary> clothesHumanoidMatchedBones; + + private Dictionary clothBoneTypeMap; + + private GameObject fittedTargetAvatar; + + private GameObject fittedTargetClothes; + + private const string EditedMeshRootFolder = "Assets/@Eden_Mesh"; + + private string tagEdenMorpehrCloth = "EDENAutoMorpher_EditedCloth"; + + private bool doProcess; + + private MorpherMode morpherMode; + + private MorpherState morpherState; + + private ProcessInfo processInfo; + + public ProcessInfo GetProcessInfo() + { + return processInfo; + } + + public void ChangeMode(MorpherMode newMode) + { + morpherMode = newMode; + morpherState = MorpherState.Idle; + } + + public void StopProcess() + { + doProcess = false; + switch (morpherState) + { + case MorpherState.Fitting_Doing: + morpherState = MorpherState.Idle; + break; + case MorpherState.Weighting_Doing: + morpherState = MorpherState.Fitting_End; + break; + default: + morpherState = MorpherState.Idle; + break; + } + } + + private EdenAutoMorpherConfig SetupConfigData(MorpherMode morpherMode) + { + EdenAutoMorpherConfig result = default(EdenAutoMorpherConfig); + switch (morpherMode) + { + case MorpherMode.AutoMorpher: + case MorpherMode.ManualMorpher: + result.sourceAvatarObject = sourceAvatarObject; + result.sourceClothesObject = sourceClothesObject; + result.sourceBodyMeshes = sourceBodyMeshesReadOnly; + result.profileName = string.Empty; + break; + case MorpherMode.ProfileMorpher: + result.sourceAvatarObject = null; + result.sourceClothesObject = sourceClothesObject; + result.sourceBodyMeshes = null; + result.profileName = profileName; + break; + default: + result.sourceAvatarObject = null; + result.sourceClothesObject = null; + result.sourceBodyMeshes = null; + result.profileName = string.Empty; + break; + } + result.targetAvatarObject = targetAvatarObject; + result.targetClothesObject = targetClothesObject; + result.targetBodyMeshes = targetBodyMeshesReadOnly; + result.minMargin = minMargin; + result.worldRadius = worldRadius; + result.sigma = sigma; + result.smoothingIteration = smoothingIteration; + result.fittingExpandIteration = fittingExpandIteration; + result.fittingShrinkIteration = fittingShrinkIteration; + result.isBodyAutoSetup = isBodyAutoSetup; + result.isReparentAccessoryBonesToTargetAvatar = isReparentAccessoryBonesToTargetAvatar; + result.skipFootFitting = skipFootFitting; + result.clothesHumanoidMatchedBones = clothesHumanoidMatchedBones; + result.clothBoneTypeMap = clothBoneTypeMap; + result.tagEdenMorpehrCloth = tagEdenMorpehrCloth; + result.transferWeightToAvatar = transferWeightToAvatar; + result.addAnchorBone = addAnchorBone; + result.armatureBoneScaleCopy = armatureBoneScaleCopy; + result.isRemoveAutoMorphedClothes = isRemoveAutoMorphedClothes; + return result; + } + + public void AutoPoseSetup(MorpherMode morpherMode) + { + TransformInfo transformInfo = new TransformInfo(sourceAvatarObject.transform); + TransformInfo transformInfo2 = new TransformInfo(targetAvatarObject.transform); + try + { + EdenAutoMorpher_SetUpUtil edenAutoMorpher_SetUpUtil = new EdenAutoMorpher_SetUpUtil(); + sourceBodyMeshesReadOnly = edenAutoMorpher_SetUpUtil.SetupBodyMeshes(sourceAvatarObject, isBodyAutoSetup, sourceBodyMeshes, "Source ").AsReadOnly(); + targetBodyMeshesReadOnly = edenAutoMorpher_SetUpUtil.SetupBodyMeshes(targetAvatarObject, isBodyAutoSetup, targetBodyMeshes, "Target ").AsReadOnly(); + targetAvatarObject.transform.SetParent(null, worldPositionStays: true); + targetAvatarObject.transform.localRotation = Quaternion.identity; + sourceAvatarObject.transform.SetParent(null, worldPositionStays: true); + sourceAvatarObject.transform.localRotation = Quaternion.identity; + EdenAutoMorpherConfig config = SetupConfigData(morpherMode); + new EdenAutoMorpher_SetUpUtil().AutoSetup(ref config, out clothesHumanoidMatchedBones, out clothBoneTypeMap); + targetClothesObject = config.targetClothesObject; + targetClothesObjectOriginal = targetClothesObject; + } + finally + { + transformInfo.ApplyToTransform(sourceAvatarObject.transform, applyParent: true, applyPosition: false, applyRotation: true, applyScale: true); + transformInfo2.ApplyToTransform(targetAvatarObject.transform, applyParent: true, applyPosition: false, applyRotation: true, applyScale: true); + } + } + + public void ProfileSetup(MorpherMode morpherMode) + { + TransformInfo transformInfo = new TransformInfo(sourceClothesObject.transform); + TransformInfo transformInfo2 = new TransformInfo(targetAvatarObject.transform); + try + { + EdenAutoMorpher_SetUpUtil edenAutoMorpher_SetUpUtil = new EdenAutoMorpher_SetUpUtil(); + targetBodyMeshesReadOnly = edenAutoMorpher_SetUpUtil.SetupBodyMeshes(targetAvatarObject, isBodyAutoSetup, targetBodyMeshes, "Target ").AsReadOnly(); + targetAvatarObject.transform.SetParent(null, worldPositionStays: true); + targetAvatarObject.transform.localRotation = Quaternion.identity; + sourceClothesObject.transform.SetParent(null, worldPositionStays: true); + sourceClothesObject.transform.localScale = Vector3.one; + sourceClothesObject.transform.localRotation = Quaternion.identity; + EdenAutoMorpherConfig config = SetupConfigData(morpherMode); + new EdenAutoMorpher_SetUpUtil().ProfileAutoSetup(ref config, out clothesHumanoidMatchedBones, out clothBoneTypeMap); + targetClothesObject = config.targetClothesObject; + targetClothesObjectOriginal = targetClothesObject; + } + finally + { + transformInfo.ApplyToTransform(sourceClothesObject.transform, applyParent: true, applyPosition: false, applyRotation: true, applyScale: true); + transformInfo2.ApplyToTransform(targetAvatarObject.transform, applyParent: true, applyPosition: false, applyRotation: true, applyScale: true); + } + } + + public IEnumerator AutoMorphingEnumerator(MorpherMode morpherMode) + { + doProcess = true; + yield return null; + ProcessInfoDebug(morpherMode, "Run ALL"); + morpherState = MorpherState.Fitting_Doing; + IEnumerator fitEnum = null; + this.morpherMode = morpherMode; + yield return null; + Debug.Log("[Eden Auto Morpher] Run ALL - Start Fitting"); + switch (morpherMode) + { + case MorpherMode.AutoMorpher: + fitEnum = AutoFitting(morpherMode); + break; + case MorpherMode.ManualMorpher: + fitEnum = ManualFitting(morpherMode); + break; + case MorpherMode.ProfileMorpher: + fitEnum = ProfileFitting(morpherMode); + break; + default: + Debug.LogError("Unknown Mode Index"); + break; + } + while (fitEnum != null && fitEnum.MoveNext() && doProcess) + { + yield return fitEnum.Current; + } + morpherState = MorpherState.Fitting_End; + yield return null; + morpherState = MorpherState.Weighting_Doing; + yield return null; + Debug.Log("[Eden Auto Morpher] Run ALL - Start Weighting"); + using (EdenAutoMorpherManager eamManager = new EdenAutoMorpherManager()) + { + EdenAutoMorpherConfig config = SetupConfigData(morpherMode); + IEnumerator eamFitting = eamManager.WeightingEnumerator(config); + while (eamFitting.MoveNext() && doProcess) + { + ref string title = ref processInfo.title; + ref string text = ref processInfo.text; + ref float progress = ref processInfo.progress; + (title, text, progress) = eamManager.GetProgressInfo(); + yield return eamFitting.Current; + } + } + yield return null; + doProcess = false; + morpherState = MorpherState.Idle; + Debug.Log("[Eden Auto Morpher] Ended Run ALL"); + yield return null; + } + + public IEnumerator FittingEnumerator(MorpherMode morpherMode) + { + doProcess = true; + ref string title = ref processInfo.title; + ref string text = ref processInfo.text; + ref float progress = ref processInfo.progress; + title = "Setup Avatar Pose"; + text = "Calculating avatar shape and skeletal data."; + progress = 0f; + yield return null; + ProcessInfoDebug(morpherMode, "Fitting"); + yield return null; + morpherState = MorpherState.Fitting_Doing; + IEnumerator fitEnum = null; + this.morpherMode = morpherMode; + yield return null; + switch (morpherMode) + { + case MorpherMode.AutoMorpher: + fitEnum = AutoFitting(morpherMode); + break; + case MorpherMode.ManualMorpher: + fitEnum = ManualFitting(morpherMode); + break; + case MorpherMode.ProfileMorpher: + fitEnum = ProfileFitting(morpherMode); + break; + default: + Debug.LogError("Unknown Mode Index"); + break; + } + while (fitEnum != null && fitEnum.MoveNext() && doProcess) + { + yield return null; + } + morpherState = MorpherState.Fitting_End; + yield return null; + doProcess = false; + Debug.Log("[Eden Auto Morpher] Ended Fitting"); + yield return null; + } + + public IEnumerator WeightingEnumerator() + { + morpherState = MorpherState.Weighting_Doing; + doProcess = true; + yield return null; + ProcessInfoDebug(morpherMode, "Weighting"); + using (EdenAutoMorpherManager eamManager = new EdenAutoMorpherManager()) + { + EdenAutoMorpherConfig config = SetupConfigData(MorpherMode.ETC); + IEnumerator eamFitting = eamManager.WeightingEnumerator(config); + while (eamFitting.MoveNext() && doProcess) + { + ref string title = ref processInfo.title; + ref string text = ref processInfo.text; + ref float progress = ref processInfo.progress; + (title, text, progress) = eamManager.GetProgressInfo(); + yield return eamFitting.Current; + } + } + yield return null; + doProcess = false; + morpherState = MorpherState.Idle; + Debug.Log("[Eden Auto Morpher] Ended Weighting"); + yield return null; + } + + public IEnumerator AutoFitting(MorpherMode morpherMode) + { + TransformInfo sourceAvatarInfo = new TransformInfo(sourceAvatarObject.transform); + TransformInfo targetAvatarInfo = new TransformInfo(targetAvatarObject.transform); + try + { + EdenAutoMorpher_SetUpUtil edenAutoMorpher_SetUpUtil = new EdenAutoMorpher_SetUpUtil(); + sourceBodyMeshesReadOnly = edenAutoMorpher_SetUpUtil.SetupBodyMeshes(sourceAvatarObject, isBodyAutoSetup, sourceBodyMeshes, "Source ").AsReadOnly(); + targetBodyMeshesReadOnly = edenAutoMorpher_SetUpUtil.SetupBodyMeshes(targetAvatarObject, isBodyAutoSetup, targetBodyMeshes, "Target ").AsReadOnly(); + targetAvatarObject.transform.SetParent(null, worldPositionStays: true); + targetAvatarObject.transform.localRotation = Quaternion.identity; + sourceAvatarObject.transform.SetParent(null, worldPositionStays: true); + sourceAvatarObject.transform.localRotation = Quaternion.identity; + EdenAutoMorpherConfig configData = SetupConfigData(morpherMode); + new EdenAutoMorpher_SetUpUtil().AutoSetup(ref configData, out clothesHumanoidMatchedBones, out clothBoneTypeMap); + targetClothesObject = configData.targetClothesObject; + targetClothesObjectOriginal = targetClothesObject; + yield return null; + fittedTargetAvatar = null; + fittedTargetClothes = null; + yield return null; + configData = SetupConfigData(morpherMode); + using (EdenAutoMorpherManager eamManager = new EdenAutoMorpherManager()) + { + IEnumerator eamFitting = eamManager.FittingIteration(configData, morpherMode); + while (eamFitting.MoveNext() && doProcess) + { + ref string title = ref processInfo.title; + ref string text = ref processInfo.text; + ref float progress = ref processInfo.progress; + (title, text, progress) = eamManager.GetProgressInfo(); + yield return eamFitting.Current; + } + } + fittedTargetAvatar = configData.targetAvatarObject; + fittedTargetClothes = configData.targetClothesObject; + } + finally + { + EdenAutoMorpher edenAutoMorpher = this; + sourceAvatarInfo.ApplyToTransform(edenAutoMorpher.sourceAvatarObject.transform, applyParent: true, applyPosition: false, applyRotation: true, applyScale: true); + targetAvatarInfo.ApplyToTransform(edenAutoMorpher.targetAvatarObject.transform, applyParent: true, applyPosition: false, applyRotation: true, applyScale: true); + } + yield return null; + } + + public IEnumerator ManualFitting(MorpherMode morpherMode) + { + TransformInfo sourceAvatarInfo = new TransformInfo(sourceAvatarObject.transform); + TransformInfo targetAvatarInfo = new TransformInfo(targetAvatarObject.transform); + try + { + EdenAutoMorpher_SetUpUtil edenAutoMorpher_SetUpUtil = new EdenAutoMorpher_SetUpUtil(); + sourceBodyMeshesReadOnly = edenAutoMorpher_SetUpUtil.SetupBodyMeshes(sourceAvatarObject, isBodyAutoSetup, sourceBodyMeshes, "Source ").AsReadOnly(); + targetBodyMeshesReadOnly = edenAutoMorpher_SetUpUtil.SetupBodyMeshes(targetAvatarObject, isBodyAutoSetup, targetBodyMeshes, "Target ").AsReadOnly(); + targetClothesObject = targetClothesObjectOriginal; + EdenAutoMorpherConfig configData = SetupConfigData(morpherMode); + new EdenAutoMorpher_SetUpUtil().ManulSetup(ref configData, out clothesHumanoidMatchedBones, out clothBoneTypeMap); + yield return null; + fittedTargetAvatar = null; + fittedTargetClothes = null; + configData = SetupConfigData(morpherMode); + using (EdenAutoMorpherManager eamManager = new EdenAutoMorpherManager()) + { + IEnumerator eamFitting = eamManager.FittingIteration(configData, morpherMode); + while (eamFitting.MoveNext() && doProcess) + { + ref string title = ref processInfo.title; + ref string text = ref processInfo.text; + ref float progress = ref processInfo.progress; + (title, text, progress) = eamManager.GetProgressInfo(); + yield return eamFitting.Current; + } + } + fittedTargetAvatar = configData.targetAvatarObject; + fittedTargetClothes = configData.targetClothesObject; + } + finally + { + EdenAutoMorpher edenAutoMorpher = this; + sourceAvatarInfo.ApplyToTransform(edenAutoMorpher.sourceAvatarObject.transform, applyParent: true, applyPosition: false, applyRotation: true, applyScale: true); + targetAvatarInfo.ApplyToTransform(edenAutoMorpher.targetAvatarObject.transform, applyParent: true, applyPosition: false, applyRotation: true, applyScale: true); + } + yield return null; + } + + public IEnumerator ProfileFitting(MorpherMode morpherMode) + { + TransformInfo sourceClothesInfo = new TransformInfo(sourceClothesObject.transform); + TransformInfo targetAvatarInfo = new TransformInfo(targetAvatarObject.transform); + try + { + EdenAutoMorpher_SetUpUtil edenAutoMorpher_SetUpUtil = new EdenAutoMorpher_SetUpUtil(); + targetBodyMeshesReadOnly = edenAutoMorpher_SetUpUtil.SetupBodyMeshes(targetAvatarObject, isBodyAutoSetup, targetBodyMeshes, "Target ").AsReadOnly(); + targetAvatarObject.transform.SetParent(null, worldPositionStays: true); + targetAvatarObject.transform.localRotation = Quaternion.identity; + sourceClothesObject.transform.SetParent(null, worldPositionStays: true); + sourceClothesObject.transform.localScale = Vector3.one; + sourceClothesObject.transform.localRotation = Quaternion.identity; + EdenAutoMorpherConfig configData = SetupConfigData(morpherMode); + new EdenAutoMorpher_SetUpUtil().ProfileAutoSetup(ref configData, out clothesHumanoidMatchedBones, out clothBoneTypeMap); + targetClothesObject = configData.targetClothesObject; + targetClothesObjectOriginal = targetClothesObject; + yield return null; + fittedTargetAvatar = null; + fittedTargetClothes = null; + yield return null; + configData = SetupConfigData(morpherMode); + using (EdenAutoMorpherManager eamManager = new EdenAutoMorpherManager()) + { + IEnumerator eamFitting = eamManager.FittingIteration(configData, morpherMode); + while (eamFitting.MoveNext() && doProcess) + { + ref string title = ref processInfo.title; + ref string text = ref processInfo.text; + ref float progress = ref processInfo.progress; + (title, text, progress) = eamManager.GetProgressInfo(); + yield return eamFitting.Current; + } + } + fittedTargetAvatar = configData.targetAvatarObject; + fittedTargetClothes = configData.targetClothesObject; + yield return null; + } + finally + { + EdenAutoMorpher edenAutoMorpher = this; + sourceClothesInfo.ApplyToTransform(edenAutoMorpher.sourceClothesObject.transform, applyParent: true, applyPosition: false, applyRotation: true, applyScale: true); + targetAvatarInfo.ApplyToTransform(edenAutoMorpher.targetAvatarObject.transform, applyParent: true, applyPosition: false, applyRotation: true, applyScale: true); + } + } + + public bool IsWeightingReady(bool isAutoMode) + { + bool flag = morpherState == MorpherState.Fitting_End && fittedTargetAvatar != null && fittedTargetAvatar == targetAvatarObject; + if (!isAutoMode) + { + flag = flag && fittedTargetClothes == targetClothesObjectOriginal; + } + return flag; + } + + private void ProcessInfoDebug(MorpherMode morpherMode, string processType) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine($"Start Process [{processType} / {morpherMode}]"); + EdenAutoMorpherConfig edenAutoMorpherConfig = SetupConfigData(morpherMode); + string text = ((edenAutoMorpherConfig.sourceAvatarObject != null) ? edenAutoMorpherConfig.sourceAvatarObject.name : "null"); + string text2 = ((edenAutoMorpherConfig.sourceClothesObject != null) ? edenAutoMorpherConfig.sourceClothesObject.name : "null"); + string text3 = ((edenAutoMorpherConfig.targetAvatarObject != null) ? edenAutoMorpherConfig.targetAvatarObject.name : "null"); + string text4 = ((edenAutoMorpherConfig.targetClothesObject != null) ? edenAutoMorpherConfig.targetClothesObject.name : "null"); + stringBuilder.AppendLine("[Objects]"); + if (morpherMode == MorpherMode.ProfileMorpher) + { + stringBuilder.AppendLine("- profileName: " + (string.IsNullOrEmpty(edenAutoMorpherConfig.profileName) ? "null/empty" : edenAutoMorpherConfig.profileName)); + } + stringBuilder.AppendLine("- sourceAvatarObject: " + text); + stringBuilder.AppendLine("- sourceClothesObject: " + text2); + if (edenAutoMorpherConfig.sourceClothesObject != null) + { + SkinnedMeshRenderer[] componentsInChildren = edenAutoMorpherConfig.sourceClothesObject.GetComponentsInChildren(includeInactive: true); + stringBuilder.AppendLine($" - sourceClothes SkinnedMeshRenderers Count: {componentsInChildren.Length}"); + int num = Mathf.Min(componentsInChildren.Length, 10); + for (int i = 0; i < num; i++) + { + SkinnedMeshRenderer skinnedMeshRenderer = componentsInChildren[i]; + string text5 = ((skinnedMeshRenderer != null) ? skinnedMeshRenderer.name : "null"); + string text6 = ((skinnedMeshRenderer != null && skinnedMeshRenderer.rootBone != null) ? skinnedMeshRenderer.rootBone.name : "null"); + string text7 = ((skinnedMeshRenderer != null && skinnedMeshRenderer.sharedMesh != null) ? skinnedMeshRenderer.sharedMesh.name : "null"); + stringBuilder.AppendLine($" - [{i}] SMR: {text5} / rootBone: {text6} / mesh: {text7}"); + } + if (componentsInChildren.Length > num) + { + stringBuilder.AppendLine($" - ... ({componentsInChildren.Length - num} more)"); + } + } + stringBuilder.AppendLine("- targetAvatarObject: " + text3); + stringBuilder.AppendLine("- targetClothesObject: " + text4); + if (edenAutoMorpherConfig.targetClothesObject != null) + { + SkinnedMeshRenderer[] componentsInChildren2 = edenAutoMorpherConfig.targetClothesObject.GetComponentsInChildren(includeInactive: true); + stringBuilder.AppendLine($" - targetClothes SkinnedMeshRenderers Count: {componentsInChildren2.Length}"); + int num2 = Mathf.Min(componentsInChildren2.Length, 10); + for (int j = 0; j < num2; j++) + { + SkinnedMeshRenderer skinnedMeshRenderer2 = componentsInChildren2[j]; + string text8 = ((skinnedMeshRenderer2 != null) ? skinnedMeshRenderer2.name : "null"); + string text9 = ((skinnedMeshRenderer2 != null && skinnedMeshRenderer2.rootBone != null) ? skinnedMeshRenderer2.rootBone.name : "null"); + string text10 = ((skinnedMeshRenderer2 != null && skinnedMeshRenderer2.sharedMesh != null) ? skinnedMeshRenderer2.sharedMesh.name : "null"); + stringBuilder.AppendLine($" - [{j}] SMR: {text8} / rootBone: {text9} / mesh: {text10}"); + } + if (componentsInChildren2.Length > num2) + { + stringBuilder.AppendLine($" - ... ({componentsInChildren2.Length - num2} more)"); + } + } + stringBuilder.AppendLine(); + stringBuilder.AppendLine("[Body Meshes]"); + stringBuilder.AppendLine($"- isBodyAutoSetup: {edenAutoMorpherConfig.isBodyAutoSetup}"); + if (!edenAutoMorpherConfig.isBodyAutoSetup) + { + if (edenAutoMorpherConfig.sourceBodyMeshes == null) + { + stringBuilder.AppendLine("- sourceBodyMeshes: null"); + } + else + { + stringBuilder.AppendLine($"- sourceBodyMeshes Count: {sourceBodyMeshes.Count}"); + for (int k = 0; k < sourceBodyMeshes.Count; k++) + { + SkinnedMeshRenderer skinnedMeshRenderer3 = sourceBodyMeshes[k]; + string text11 = ((skinnedMeshRenderer3 != null) ? skinnedMeshRenderer3.name : "null"); + string text12 = ((skinnedMeshRenderer3 != null && skinnedMeshRenderer3.rootBone != null) ? skinnedMeshRenderer3.rootBone.name : "null"); + string text13 = ((skinnedMeshRenderer3 != null && skinnedMeshRenderer3.sharedMesh != null) ? skinnedMeshRenderer3.sharedMesh.name : "null"); + stringBuilder.AppendLine($" - [{k}] SMR: {text11} / rootBone: {text12} / mesh: {text13}"); + } + } + if (targetBodyMeshes == null) + { + stringBuilder.AppendLine("- targetBodyMeshes: null"); + } + else + { + stringBuilder.AppendLine($"- targetBodyMeshes Count: {targetBodyMeshes.Count}"); + for (int l = 0; l < targetBodyMeshes.Count; l++) + { + SkinnedMeshRenderer skinnedMeshRenderer4 = targetBodyMeshes[l]; + string text14 = ((skinnedMeshRenderer4 != null) ? skinnedMeshRenderer4.name : "null"); + string text15 = ((skinnedMeshRenderer4 != null && skinnedMeshRenderer4.rootBone != null) ? skinnedMeshRenderer4.rootBone.name : "null"); + string text16 = ((skinnedMeshRenderer4 != null && skinnedMeshRenderer4.sharedMesh != null) ? skinnedMeshRenderer4.sharedMesh.name : "null"); + stringBuilder.AppendLine($" - [{l}] SMR: {text14} / rootBone: {text15} / mesh: {text16}"); + } + } + } + stringBuilder.AppendLine(); + stringBuilder.AppendLine("[Parameters]"); + stringBuilder.AppendLine($"- minMargin: {edenAutoMorpherConfig.minMargin}"); + stringBuilder.AppendLine($"- worldRadius: {edenAutoMorpherConfig.worldRadius}"); + stringBuilder.AppendLine($"- sigma: {edenAutoMorpherConfig.sigma}"); + stringBuilder.AppendLine($"- smoothingIteration: {edenAutoMorpherConfig.smoothingIteration}"); + stringBuilder.AppendLine($"- fittingExpandIteration: {edenAutoMorpherConfig.fittingExpandIteration}"); + stringBuilder.AppendLine($"- fittingShrinkIteration: {edenAutoMorpherConfig.fittingShrinkIteration}"); + stringBuilder.AppendLine(); + stringBuilder.AppendLine("[Options]"); + stringBuilder.AppendLine($"- isReparentAccessoryBonesToTargetAvatar: {edenAutoMorpherConfig.isReparentAccessoryBonesToTargetAvatar}"); + stringBuilder.AppendLine($"- skipFootFitting: {edenAutoMorpherConfig.skipFootFitting}"); + Debug.Log(stringBuilder.ToString()); + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher.cs.meta new file mode 100644 index 0000000..1d551a3 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 50552370e41d31140a52da99bcc7ab74 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherConfig.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherConfig.cs new file mode 100644 index 0000000..853e3e0 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherConfig.cs @@ -0,0 +1,56 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.EdenAutoMorpherConfig +using System.Collections.Generic; +using Eden.AutoMorpher; +using UnityEngine; + +public struct EdenAutoMorpherConfig +{ + public GameObject sourceAvatarObject; + + public GameObject sourceClothesObject; + + public IReadOnlyList sourceBodyMeshes; + + public GameObject targetAvatarObject; + + public GameObject targetClothesObject; + + public IReadOnlyList targetBodyMeshes; + + public string profileName; + + public float minMargin; + + public float worldRadius; + + public float sigma; + + public int smoothingIteration; + + public int fittingExpandIteration; + + public int fittingShrinkIteration; + + public bool isBodyAutoSetup; + + public bool isReparentAccessoryBonesToTargetAvatar; + + public bool skipFootFitting; + + public bool isRemoveAutoMorphedClothes; + + public bool transferWeightToAvatar; + + public bool addAnchorBone; + + public bool armatureBoneScaleCopy; + + public Dictionary> clothesHumanoidMatchedBones; + + public Dictionary clothBoneTypeMap; + + public string tagEdenMorpehrCloth; +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherConfig.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherConfig.cs.meta new file mode 100644 index 0000000..8f82d4e --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherConfig.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 36c114e39fc870440b90581fce2b9b1c \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherManager.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherManager.cs new file mode 100644 index 0000000..f8fad4a --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherManager.cs @@ -0,0 +1,396 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.EdenAutoMorpherManager +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using Eden.AutoMorpher; +using Eden.AutoMorpher.profile; +using UnityEditor; +using UnityEngine; + +public class EdenAutoMorpherManager : IDisposable +{ + private string displayTitle; + + private string displayText; + + private float displayProgress; + + private List clothInstances; + + public (string, string, float) GetProgressInfo() + { + return (displayTitle, displayText, displayProgress); + } + + private void SetupProgress(string title, string text, float progress) + { + displayTitle = title; + displayText = text; + displayProgress = progress; + } + + private IEnumerator SetupClothInstances(GameObject targetClothesObject, Dictionary> clothesHumanoidMatchedBones, bool duplicateMesh = true) + { + clothInstances = new List(); + SkinnedMeshRenderer[] targetClothesSMRs = targetClothesObject.GetComponentsInChildren(includeInactive: false); + if (targetClothesSMRs.Length == 0) + { + throw new AutoMorpherException("There is NO Skinned Mesh Renderer in Clothes", "[EdenAutoMorpherManager] SetupClothInstance\n - There is NO Skinned Mesh Renderer in Clothes"); + } + EdenAutoMorpher_SetUpUtil setupUtil = new EdenAutoMorpher_SetUpUtil(); + for (int rendereri = 0; rendereri < targetClothesSMRs.Length; rendereri++) + { + SetupProgress("Set Up", $"Setup Clothes Info [{rendereri}/{targetClothesSMRs.Length}] - ({targetClothesSMRs[rendereri].name})", Mathf.Lerp(0.05f, 0.1f, rendereri / targetClothesSMRs.Length)); + yield return null; + ClothInstance clothInstance = setupUtil.SetupClothInstance(targetClothesSMRs[rendereri], clothesHumanoidMatchedBones, duplicateMesh); + if (clothInstance != null) + { + clothInstances.Add(clothInstance); + } + } + if (clothInstances.Count == 0) + { + throw new AutoMorpherException(LanguageManager.Get("UI.Exception.title.ClothInstanceAllocateFail"), LanguageManager.Get("UI.Exception.message.ClothInstanceAllocateFail")); + } + } + + private IEnumerator SetupClothInstances(GameObject targetClothesObject, Dictionary> clothesHumanoidMatchedBones, GameObject sourceClothesObject, MeshMatcher meshMatcher) + { + clothInstances = new List(); + SkinnedMeshRenderer[] targetClothesSMRs = targetClothesObject.GetComponentsInChildren(includeInactive: false); + if (targetClothesSMRs.Length == 0) + { + throw new AutoMorpherException("There is NO Skinned Mesh Renderer in Clothes", "[EdenAutoMorpherManager] SetupClothInstance\n - There is NO Skinned Mesh Renderer in Clothes"); + } + SkinnedMeshRenderer[] sourceClothesSMRs = sourceClothesObject.GetComponentsInChildren(includeInactive: false); + if (sourceClothesSMRs.Length == 0 || targetClothesSMRs.Length != sourceClothesSMRs.Length) + { + throw new AutoMorpherException("Clothes Skinned Mesh Renderer is Not Matched", "[EdenAutoMorpherManager] SetupClothInstance\n - Skinned Mesh Renderer of Source Clothes and Target Clothes is Not Matched"); + } + EdenAutoMorpher_SetUpUtil setupUtil = new EdenAutoMorpher_SetUpUtil(); + for (int rendereri = 0; rendereri < targetClothesSMRs.Length; rendereri++) + { + SetupProgress("Set Up", $"Setup Clothes Info [{rendereri}/{targetClothesSMRs.Length}] - ({targetClothesSMRs[rendereri].name})", Mathf.Lerp(0.07f, 0.1f, rendereri / targetClothesSMRs.Length)); + yield return null; + ClothInstance clothInstance = setupUtil.SetupClothInstance(targetClothesSMRs[rendereri], sourceClothesSMRs[rendereri], meshMatcher, clothesHumanoidMatchedBones, duplicateMesh: true); + if (clothInstance != null) + { + clothInstances.Add(clothInstance); + } + } + if (clothInstances.Count == 0) + { + throw new AutoMorpherException(LanguageManager.Get("UI.Exception.title.ClothInstanceAllocateFail"), LanguageManager.Get("UI.Exception.message.ClothInstanceAllocateFail")); + } + } + + public IEnumerator FittingIteration(EdenAutoMorpherConfig config, MorpherMode morpherMode) + { + SetupProgress("Set Up", "Setup Target Avatar Info", 0.03f); + yield return null; + MeshMatcher meshMatcher = new MeshMatcher(); + BvhTriangleMesh targetBodyBVH = meshMatcher.BuildBvhMulti(config.targetBodyMeshes, config.targetAvatarObject.GetComponent()); + if (targetBodyBVH == null) + { + throw new AutoMorpherException("targetBodyBVH is Null", "[EdenAutoMorpherManater] FittingIteration\n - targetBodyBVH is null"); + } + yield return null; + SetupProgress("Set Up", "Setup Source Avatar Info", 0.05f); + yield return null; + BvhTriangleMesh sourceBodyBVH = null; + switch (morpherMode) + { + case MorpherMode.AutoMorpher: + case MorpherMode.ManualMorpher: + sourceBodyBVH = meshMatcher.BuildBvhMulti(config.sourceBodyMeshes, config.sourceAvatarObject.GetComponent()); + break; + case MorpherMode.ProfileMorpher: + { + ProfileLoader profileLoader = new ProfileLoader(); + sourceBodyBVH = profileLoader.LoadBvhWithRootTransform(config.sourceClothesObject.transform, config.profileName); + break; + } + default: + Debug.LogError("Unknown MorpherMode"); + break; + } + yield return null; + if (sourceBodyBVH == null) + { + throw new AutoMorpherException("sourceBodyBVH is Null", "[EdenAutoMorpherManater] FittingIteration\n - sourceBodyBVH is null"); + } + meshMatcher.bodyBVH = sourceBodyBVH; + SetupProgress("Set Up", "Setup Clothes Info", 0.07f); + yield return null; + IEnumerator setupClothEnum = SetupClothInstances(config.targetClothesObject, config.clothesHumanoidMatchedBones, config.sourceClothesObject, meshMatcher); + while (setupClothEnum.MoveNext()) + { + yield return setupClothEnum.Current; + } + yield return null; + float fittingRadius = config.worldRadius; + meshMatcher.bodyBVH = targetBodyBVH; + VertexFittingUtil vertexFittingUtil = new VertexFittingUtil(); + int fittingI; + for (fittingI = 0; fittingI < config.fittingExpandIteration; fittingI++) + { + foreach (ClothInstance clothInstance in clothInstances) + { + SetupProgress("Fitting", $"Phase 1 Expanding [{fittingI + 1}/{config.fittingExpandIteration}] - {clothInstance.smr.transform.name}", Mathf.Lerp(0.1f, 0.4f, (float)fittingI / (float)config.fittingExpandIteration)); + yield return null; + vertexFittingUtil.ExpandClothes_World(clothInstance, clothInstances, meshMatcher, config, fittingRadius); + yield return null; + } + if (fittingI % 2 == 0) + { + fittingRadius *= 0.97f; + } + } + fittingRadius = config.worldRadius; + for (fittingI = 0; fittingI < config.fittingShrinkIteration; fittingI++) + { + foreach (ClothInstance clothInstance in clothInstances) + { + SetupProgress("Fitting", $"Phase 2 Fitting [{fittingI + 1}/{config.fittingShrinkIteration}] - {clothInstance.smr.transform.name}", Mathf.Lerp(0.4f, 0.8f, (float)fittingI / (float)config.fittingShrinkIteration)); + yield return null; + vertexFittingUtil.ShrinkClothes_World(clothInstance, clothInstances, meshMatcher, config, fittingRadius); + yield return null; + vertexFittingUtil.ExpandClothes_World(clothInstance, clothInstances, meshMatcher, config, fittingRadius); + yield return null; + } + if (fittingI % 2 == 0) + { + fittingRadius *= 0.97f; + } + } + fittingRadius = config.worldRadius / 2f; + for (fittingI = 0; fittingI < config.fittingExpandIteration; fittingI++) + { + foreach (ClothInstance clothInstance in clothInstances) + { + SetupProgress("Fitting", $"Phase 3 Fine-tuning [{fittingI + 1}/{config.fittingExpandIteration}] - {clothInstance.smr.transform.name}", Mathf.Lerp(0.8f, 0.9f, (float)fittingI / (float)config.fittingExpandIteration)); + yield return null; + vertexFittingUtil.ExpandClothes_World(clothInstance, clothInstances, meshMatcher, config, fittingRadius); + yield return null; + } + if (fittingI % 2 == 0) + { + fittingRadius *= 0.97f; + } + } + yield return null; + SetupProgress("Fitting", "Apply Transform Bone", 0.93f); + yield return null; + BoneAlignmentUtil boneAlignmentUtil = new BoneAlignmentUtil(); + boneAlignmentUtil.CleanupTempBones(config.targetClothesObject.transform); + MorpherMode morpherMode2 = morpherMode; + if (morpherMode2 == MorpherMode.AutoMorpher || morpherMode2 == MorpherMode.ProfileMorpher) + { + config.targetClothesObject.transform.localScale = Vector3.one; + boneAlignmentUtil.AlignClothBonesToAvatar(clothInstances[0], config.targetAvatarObject.GetComponent()); + } + yield return null; + SetupProgress("Fitting", "Apply Transform Mesh", 0.99f); + yield return null; + fittingI = 0; + while (fittingI < clothInstances.Count) + { + ClothInstance clothInstance = clothInstances[fittingI]; + SetupProgress("Fitting", $"Apply to Mesh [{fittingI + 1}/{clothInstances.Count}] - {clothInstance.smr.transform.name}", Mathf.Lerp(0.93f, 0.98f, (float)fittingI / (float)clothInstances.Count)); + yield return null; + clothInstance.ApplyWorldVerticesToMesh(); + clothInstance.smr.localBounds = new Bounds(Vector3.zero, Vector3.one * 2f); + EditorUtility.SetDirty(clothInstance.smr.sharedMesh); + EditorUtility.SetDirty(clothInstance.smr); + yield return null; + int num = fittingI + 1; + fittingI = num; + } + yield return null; + SetupProgress("Saving", "Saving Mesh Data", 0.99f); + yield return null; + SaveEditedMeshesToAssets(clothInstances, config); + yield return null; + SetupProgress("Finishing", "Finishing", 1f); + foreach (ClothInstance clothInstance2 in clothInstances) + { + clothInstance2.Dispose(); + } + clothInstances = null; + yield return null; + } + + public IEnumerator WeightingEnumerator(EdenAutoMorpherConfig config) + { + SetupProgress("Set Up", "Setup Target Avatar Info", 0f); + yield return null; + if (new MeshMatcher().BuildBvhMulti(config.targetBodyMeshes, config.targetAvatarObject.GetComponent()) == null) + { + throw new AutoMorpherException("targetBodyBVH is Null", "[EdenAutoMorpherManater] FittingIteration\n - targetBodyBVH is null"); + } + yield return null; + List bakedTargetBodyMeshes = new List(); + foreach (SkinnedMeshRenderer bodyMesh in config.targetBodyMeshes) + { + SetupProgress("Set Up", "Setup Target Avatar BodyMesh Info", Mathf.Lerp(0.02f, 0.05f, bakedTargetBodyMeshes.Count / config.targetBodyMeshes.Count)); + yield return null; + Mesh mesh = new Mesh(); + bodyMesh.BakeMesh(mesh); + mesh.triangles = bodyMesh.sharedMesh.triangles; + mesh.boneWeights = bodyMesh.sharedMesh.boneWeights; + bakedTargetBodyMeshes.Add(mesh); + } + yield return null; + IEnumerator setupClothEnum = SetupClothInstances(config.targetClothesObject, config.clothesHumanoidMatchedBones, duplicateMesh: false); + while (setupClothEnum.MoveNext()) + { + yield return setupClothEnum.Current; + } + yield return null; + Dictionary clonedBoneMap = null; + if (!config.transferWeightToAvatar) + { + new EdenAutoMorpher_SetUpUtil().CreateClothesArmature(config, out clonedBoneMap); + } + yield return null; + WeightTransferUtil weightTransferUtil = new WeightTransferUtil(); + WeightTransferUtil.Settings wtSettings = new WeightTransferUtil.Settings + { + maxDistance = 0.07f, + maxNormalAngleDeg = 35f, + allowFlippedNormal = true, + enableSmoothing = true, + smoothingIterations = 4, + smoothingAlpha = 0.25f, + enforceFourBoneLimit = true, + weightInClothes = !config.transferWeightToAvatar + }; + yield return null; + int weightingI = 0; + while (weightingI < clothInstances.Count) + { + ClothInstance clothInstance = clothInstances[weightingI]; + float num = 0.1f; + float num2 = (0.95f - num) / (float)Mathf.Max(1, clothInstances.Count); + float num3 = num + num2 * (float)weightingI; + float num4 = 0.8f; + float weightingStart = num3; + float weightingEnd = num3 + num2 * num4; + float applyStart = num3 + num2 * num4; + int step = 1; + SetupProgress("Weighting", $"Weighting | Step {step} [{weightingI + 1}/{clothInstances.Count}] - {clothInstance.smr.transform.name}", weightingStart); + yield return null; + IEnumerator weightingEnum = weightTransferUtil.RetargetAndTransfer(clothInstance, config.targetAvatarObject.GetComponent(), config.targetBodyMeshes, bakedTargetBodyMeshes, 0, wtSettings, config.clothBoneTypeMap, clonedBoneMap); + while (weightingEnum.MoveNext()) + { + yield return null; + step++; + float t = 1f - 1f / ((float)step + 1f); + float progress = Mathf.Lerp(weightingStart, weightingEnd, t); + SetupProgress("Weighting", $"Weighting | Step {step} [{weightingI + 1}/{clothInstances.Count}] - {clothInstance.smr.transform.name}", progress); + yield return weightingEnum.Current; + } + yield return null; + step++; + SetupProgress("Weighting", $"Weighting | Step {step} - Apply to Mesh [{weightingI + 1}/{clothInstances.Count}] - {clothInstance.smr.transform.name}", applyStart); + yield return null; + clothInstance.ApplyWorldVerticesToMesh(); + clothInstance.smr.localBounds = new Bounds(Vector3.zero, Vector3.one * 2f); + EditorUtility.SetDirty(clothInstance.smr.sharedMesh); + EditorUtility.SetDirty(clothInstance.smr); + yield return null; + int num5 = weightingI + 1; + weightingI = num5; + } + SetupProgress("Weighting", "Allocate Bones", 0.95f); + yield return null; + if (config.transferWeightToAvatar && config.isReparentAccessoryBonesToTargetAvatar) + { + new BoneAlignmentUtil().ReparentAccessoryBonesToAvatar(config); + } + SetupProgress("Finishing", "Finishing", 1f); + yield return null; + foreach (ClothInstance clothInstance2 in clothInstances) + { + clothInstance2.Dispose(); + } + clothInstances = null; + yield return null; + } + + private void SaveEditedMeshesToAssets(List clothInstances, EdenAutoMorpherConfig config) + { + if (clothInstances == null || clothInstances.Count == 0) + { + Debug.LogWarning("[EdenAutoMorpher] 저장할 ClothInstance가 없습니다."); + throw new AutoMorpherException("Can't Save Mesh", "Therer is no ClothInstance to save mesh"); + } + if (config.targetClothesObject == null || config.targetAvatarObject == null) + { + Debug.LogWarning("[EdenAutoMorpher] targetClothesObject 또는 targetAvatarObject 가 null 입니다. 메쉬 저장 경로를 만들 수 없습니다."); + throw new AutoMorpherException("Can't Save Mesh", "Target Clothes Object or Target Avatar Object is null.\nCan't Create Mesh Folder"); + } + string text = "Assets/@Eden_Mesh"; + if (!AssetDatabase.IsValidFolder(text)) + { + AssetDatabase.CreateFolder("Assets", "@Eden_Mesh"); + } + string text2 = SanitizeForAssetName(config.targetAvatarObject.name); + string text3 = text + "/" + text2; + if (!AssetDatabase.IsValidFolder(text3)) + { + AssetDatabase.CreateFolder(text, text2); + } + string text4 = SanitizeForAssetName(config.targetClothesObject.name); + string text5 = text3 + "/" + text4; + if (!AssetDatabase.IsValidFolder(text5)) + { + AssetDatabase.CreateFolder(text3, text4); + } + string text6 = AssetDatabase.GenerateUniqueAssetPath(text5 + "/" + text4 + "_Meshes.asset"); + int num = 0; + AssetDatabase.CreateAsset(new Mesh + { + name = text4 + "_RootMesh" + }, text6); + foreach (ClothInstance clothInstance in clothInstances) + { + if (clothInstance != null && !(clothInstance.smr == null)) + { + Mesh sharedMesh = clothInstance.smr.sharedMesh; + if (!(sharedMesh == null)) + { + Mesh mesh = UnityEngine.Object.Instantiate(sharedMesh); + mesh.name = SanitizeForAssetName(clothInstance.smr.gameObject.name + "_EditedMesh"); + AssetDatabase.AddObjectToAsset(mesh, text6); + clothInstance.smr.sharedMesh = mesh; + clothInstance.editableMesh = clothInstance.smr.sharedMesh; + EditorUtility.SetDirty(clothInstance.smr); + EditorUtility.SetDirty(clothInstance.smr.sharedMesh); + EditorUtility.SetDirty(clothInstance.smr.gameObject); + num++; + } + } + } + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + Debug.Log($"[EdenAutoMorpher] 편집된 메쉬 {num}개를 " + "'" + text6 + "' 에 서브 에셋으로 저장했습니다. (원본 프리팹은 변경하지 않음)"); + static string SanitizeForAssetName(string rawName) + { + char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); + foreach (char oldChar in invalidFileNameChars) + { + rawName = rawName.Replace(oldChar, '_'); + } + return rawName.Trim(); + } + } + + public void Dispose() + { + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherManager.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherManager.cs.meta new file mode 100644 index 0000000..38886f8 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpherManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a0a53153b37b0594c8d32b305724f9b9 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher_SetUpUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher_SetUpUtil.cs new file mode 100644 index 0000000..b87db38 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher_SetUpUtil.cs @@ -0,0 +1,676 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.EdenAutoMorpher_SetUpUtil +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Eden.AutoMorpher; +using Eden.AutoMorpher.profile; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; + +public class EdenAutoMorpher_SetUpUtil +{ + public GameObject InstantiateTargetClothes(GameObject sourceClothesObject, GameObject targetAvatarObject, string tagEdenMorpherCloth, bool removeAutoMorphedClothes) + { + EnsureTagExists(tagEdenMorpherCloth); + if (removeAutoMorphedClothes) + { + List list = new List(); + Transform[] componentsInChildren = targetAvatarObject.GetComponentsInChildren(includeInactive: true); + foreach (Transform transform in componentsInChildren) + { + if (!(transform.gameObject == targetAvatarObject) && transform.CompareTag(tagEdenMorpherCloth)) + { + list.Add(transform.gameObject); + } + } + foreach (GameObject item in list) + { + UnityEngine.Object.DestroyImmediate(item); + } + } + GameObject gameObject = UnityEngine.Object.Instantiate(sourceClothesObject, targetAvatarObject.transform); + gameObject.SetActive(value: true); + gameObject.transform.localPosition = Vector3.zero; + gameObject.transform.localRotation = Quaternion.identity; + gameObject.transform.localScale = Vector3.one; + gameObject.tag = tagEdenMorpherCloth; + return gameObject; + } + + private static void EnsureTagExists(string tag) + { + if (string.IsNullOrEmpty(tag)) + { + throw new AutoMorpherException("Clothes Tag is Invalid", "[EdenAutoMorpherManager] EnsureTagExists\n - tagEdenMorpherCloth is null or empty"); + } + string[] tags = InternalEditorUtility.tags; + for (int i = 0; i < tags.Length; i++) + { + if (tags[i] == tag) + { + return; + } + } + SerializedObject serializedObject = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]); + SerializedProperty serializedProperty = serializedObject.FindProperty("tags"); + int arraySize = serializedProperty.arraySize; + serializedProperty.InsertArrayElementAtIndex(arraySize); + serializedProperty.GetArrayElementAtIndex(arraySize).stringValue = tag; + serializedObject.ApplyModifiedProperties(); + } + + public void AutoSetup(ref EdenAutoMorpherConfig config, out Dictionary> clothesHumanoidMatchedBones, out Dictionary clothBoneTypeMap) + { + config.targetClothesObject = InstantiateTargetClothes(config.sourceClothesObject, config.targetAvatarObject, config.tagEdenMorpehrCloth, config.isRemoveAutoMorphedClothes); + if (config.targetClothesObject == null) + { + throw new AutoMorpherException("Target Clothes Object is Null", "[EdenAutoMorpher_SetUpUtil] AutoSetup\n - config.targetClothesObject is null"); + } + BoneMatchUtil boneMatchUtil = new BoneMatchUtil(); + List bodyRootLocalBones = boneMatchUtil.GetBodyRootLocalBones(config.sourceAvatarObject.transform, config.sourceAvatarObject.GetComponent(), config.sourceBodyMeshes.ToList()); + List rootLocalBones = boneMatchUtil.GetRootLocalBones(config.targetClothesObject.transform, boneMatchUtil.GetMeshBones(config.targetClothesObject.GetComponentsInChildren().ToList())); + boneMatchUtil.MatchClothesToBodyBones(bodyRootLocalBones, rootLocalBones, out clothesHumanoidMatchedBones, out clothBoneTypeMap, out var clothToBodyMatched); + Dictionary sourceToProxy; + GameObject gameObject = new BodyPoseMatchUtil().AutoAdjustBodyPose(config.sourceAvatarObject, config.sourceBodyMeshes, config.targetAvatarObject, config.targetBodyMeshes, out sourceToProxy); + if (gameObject == null) + { + throw new AutoMorpherException(LanguageManager.Get("UI.Exception.title.BodyProxyNull"), LanguageManager.Get("UI.Exception.message.BodyProxyNull")); + } + try + { + if (clothesHumanoidMatchedBones == null || clothesHumanoidMatchedBones.Count == 0) + { + throw new AutoMorpherException("Clothes Humanoid Matched Bones is Null", "[EdenAutoMorpher_SetUpUtil] AutoSetup\n - clothesHumanoidMatchedBones is null\n - Please check whether the outfit’s bones match the bones of the source avatar."); + } + if (clothBoneTypeMap == null) + { + throw new AutoMorpherException("Cloth Bone Type Map is Null", "[EdenAutoMorpher_SetUpUtil] AutoSetup\n - clothBoneTypeMap is null\n - Please check whether the outfit’s bones match the bones of the source avatar."); + } + if (AutoMorpherDev.isDeveloperMode) + { + DebugPrintSameNameBonePaths(clothToBodyMatched, sourceToProxy); + } + new BodyPoseToClothApplier().ApplyBodyPoseToClothes(gameObject.transform, config.targetClothesObject.transform, clothToBodyMatched, sourceToProxy); + } + finally + { + UnityEngine.Object.DestroyImmediate(gameObject); + } + } + + public void DebugPrintSameNameBonePaths(Dictionary clothToBodyMatched, Dictionary sourceToProxy) + { + if (clothToBodyMatched == null) + { + throw new AutoMorpherException("clothToBodyMatched is Missing", "[DebugBonePath] DebugPrintSameNameBonePaths\n - clothToBodyMatched is null"); + } + if (sourceToProxy == null) + { + throw new AutoMorpherException("sourceToProxy is Missing", "[DebugBonePath] DebugPrintSameNameBonePaths\n - sourceToProxy is null"); + } + Dictionary> dictionary = new Dictionary>(); + foreach (KeyValuePair item in sourceToProxy) + { + Transform key = item.Key; + if (!(key == null)) + { + if (!dictionary.TryGetValue(key.name, out var value)) + { + value = new List(); + dictionary.Add(key.name, value); + } + value.Add(key); + } + } + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("[DebugBonePath] Same-name bone path comparison"); + stringBuilder.AppendLine("--------------------------------------------------"); + foreach (KeyValuePair item2 in clothToBodyMatched) + { + _ = item2.Key; + Transform transform = GetSourceTransform(item2.Value); + if (transform == null) + { + continue; + } + string name = transform.name; + if (!dictionary.TryGetValue(name, out var value2)) + { + continue; + } + stringBuilder.AppendLine("[Bone Name] " + name); + stringBuilder.AppendLine(" - BoneRootLocalData Source Path: " + GetPath(transform)); + foreach (Transform item3 in value2) + { + stringBuilder.AppendLine(" - sourceToProxy Source Path : " + GetPath(item3)); + } + stringBuilder.AppendLine("--------------------------------------------------"); + } + Debug.Log(stringBuilder.ToString()); + static string GetPath(Transform t) + { + if (t == null) + { + return "(null)"; + } + StringBuilder stringBuilder2 = new StringBuilder(); + while (t != null) + { + stringBuilder2.Insert(0, t.name); + t = t.parent; + if (t != null) + { + stringBuilder2.Insert(0, "/"); + } + } + return stringBuilder2.ToString(); + } + static Transform GetSourceTransform(BoneMatchUtil.BoneRootLocalData data) + { + if (data == null) + { + return null; + } + FieldInfo[] fields = data.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + foreach (FieldInfo fieldInfo in fields) + { + if (typeof(Transform).IsAssignableFrom(fieldInfo.FieldType)) + { + return fieldInfo.GetValue(data) as Transform; + } + } + return null; + } + } + + public void ManulSetup(ref EdenAutoMorpherConfig config, out Dictionary> clothesHumanoidMatchedBones, out Dictionary clothBoneTypeMap) + { + BoneMatchUtil boneMatchUtil = new BoneMatchUtil(); + List bodyRootLocalBones = boneMatchUtil.GetBodyRootLocalBones(config.sourceAvatarObject.transform, config.sourceAvatarObject.GetComponent(), config.sourceBodyMeshes.ToList()); + List rootLocalBones = boneMatchUtil.GetRootLocalBones(config.sourceClothesObject.transform, boneMatchUtil.GetMeshBones(config.sourceClothesObject.GetComponentsInChildren().ToList())); + boneMatchUtil.MatchClothesToBodyBones(bodyRootLocalBones, rootLocalBones, out var clothHumanBones, out var clothBoneTypeMap2, out var _); + boneMatchUtil.RemapSourceClothMatchToTargetCloth(config.sourceClothesObject.transform, config.targetClothesObject.transform, clothHumanBones, clothBoneTypeMap2, out clothesHumanoidMatchedBones, out clothBoneTypeMap); + if (clothesHumanoidMatchedBones == null || clothesHumanoidMatchedBones.Count == 0) + { + throw new AutoMorpherException("Clothes Humanoid Matched Bones is Null", "[EdenAutoMorpher_SetUpUtil] ManulSetup\n - clothesHumanoidMatchedBones is null\n - Please check whether the Source Clothes’s bones match the bones of the source avatar."); + } + if (clothBoneTypeMap == null) + { + throw new AutoMorpherException("Cloth Bone Type Map is Null", "[EdenAutoMorpher_SetUpUtil] ManulSetup\n - clothBoneTypeMap is null\n - Please check whether the Source Clothes’s bones match the bones of the source avatar."); + } + MeshClassifier meshClassifier = new MeshClassifier(); + Dictionary> humanBoneMap = meshClassifier.MeshHumanoidBoneMatcher(config.sourceAvatarObject.GetComponent(), config.sourceBodyMeshes); + Dictionary> humanBoneMap2 = meshClassifier.MeshHumanoidBoneMatcher(config.targetAvatarObject.GetComponent(), config.sourceBodyMeshes); + BodyPoseMatchSetupUtil bodyPoseMatchSetupUtil = new BodyPoseMatchSetupUtil(); + bodyPoseMatchSetupUtil.AdjustAvatarScaleByNeck(config.sourceAvatarObject.transform, humanBoneMap, 1.5f); + bodyPoseMatchSetupUtil.AdjustAvatarScaleByNeck(config.targetAvatarObject.transform, humanBoneMap2, 1.5f); + } + + public void ProfileAutoSetup(ref EdenAutoMorpherConfig config, out Dictionary> clothesHumanoidMatchedBones, out Dictionary clothBoneTypeMap) + { + config.targetClothesObject = InstantiateTargetClothes(config.sourceClothesObject, config.targetAvatarObject, config.tagEdenMorpehrCloth, config.isRemoveAutoMorphedClothes); + if (config.targetClothesObject == null) + { + throw new AutoMorpherException("Target Clothes Object is Null", "[EdenAutoMorpher_SetUpUtil] ProfileAutoSetup\n - config.targetClothesObject is null"); + } + ProfileData profileData = new ProfileLoader().LoadProfileData(config.profileName); + if (profileData == null) + { + throw new AutoMorpherException("Clothes Humanoid Matched Bones is Null", "[EdenAutoMorpher_SetUpUtil] ProfileAutoSetup\n - profile Data is null"); + } + BoneMatchUtil boneMatchUtil = new BoneMatchUtil(); + List bodyBones = boneMatchUtil.ConvertProfileBoneDataToRootLocalData(profileData.bones); + List rootLocalBones = boneMatchUtil.GetRootLocalBones(config.targetClothesObject.transform, boneMatchUtil.GetMeshBones(config.targetClothesObject.GetComponentsInChildren().ToList())); + boneMatchUtil.MatchClothesToBodyBones(bodyBones, rootLocalBones, out clothesHumanoidMatchedBones, out clothBoneTypeMap, out var _); + if (clothesHumanoidMatchedBones == null || clothesHumanoidMatchedBones.Count == 0) + { + throw new AutoMorpherException("Cloth Body Bones is Null", "[EdenAutoMorpher_SetUpUtil] ProfileAutoSetup\n - clothesHumanoidMatchedBones is null\n - Please check whether you have selected the correct profile for the outfit."); + } + if (clothBoneTypeMap == null) + { + throw new AutoMorpherException("Cloth Bone Type Map is Null", "[EdenAutoMorpher_SetUpUtil] ProfileAutoSetup\n - clothBoneTypeMap is null\n - Please check whether you have selected the correct profile for the outfit."); + } + new ProfilePoseMatchUtil().ProfilePoseMatcher(config.targetAvatarObject, config.targetBodyMeshes, config.targetClothesObject, profileData, clothesHumanoidMatchedBones, clothBoneTypeMap); + } + + public List SetupBodyMeshes(GameObject bodyObject, bool isBodyAutoSetup, List userAllocatedBoeyMeshes, string errorPreFix = "") + { + new EdenAutoMorpherManager(); + List list = (isBodyAutoSetup ? GetBodyMeshes(bodyObject) : userAllocatedBoeyMeshes); + if (list == null) + { + throw new AutoMorpherException(errorPreFix + "Body Mesh Is Null or Empty", errorPreFix + "Body Mesh list is null or empty"); + } + foreach (SkinnedMeshRenderer item in list) + { + if (item == null) + { + throw new AutoMorpherException(errorPreFix + "Body Mesh Is Null", "Null Mesh in " + errorPreFix + " Body Meshes"); + } + } + return list; + } + + public List GetBodyMeshes(GameObject avatarObject) + { + MeshClassifier meshClassifier = new MeshClassifier(); + List list = new List(); + Animator component = avatarObject.GetComponent(); + if (component != null && component.avatar != null && component.avatar.isHuman) + { + list.Add(meshClassifier.GetBodyMesh(avatarObject.transform, component)); + { + foreach (SkinnedMeshRenderer item in list) + { + if (item == null) + { + throw new AutoMorpherException(LanguageManager.Get("UI.Exception.title.BodyMeshNull"), LanguageManager.GetFormat("UI.Exception.message.BodyMeshNull", new object[1] { avatarObject.name })); + } + } + return list; + } + } + throw new AutoMorpherException(LanguageManager.Get("UI.Exception.title.BodyMeshNull"), LanguageManager.GetFormat("UI.Exception.message.BodyMeshNull", new object[1] { avatarObject.name })); + } + + public ClothInstance SetupClothInstance(SkinnedMeshRenderer clothSMR, Dictionary> clothesHumanoidMatchedBones, bool duplicateMesh) + { + ClothHumanoidMaskUtil clothHumanoidMaskUtil = new ClothHumanoidMaskUtil(); + if (clothSMR == null || clothSMR.sharedMesh == null) + { + throw new AutoMorpherException(LanguageManager.Get("UI.Exception.title.TargetRendererNull"), LanguageManager.Get("UI.Exception.message.TargetRendererNull")); + } + ClothInstance clothInstance = new ClothInstance(clothSMR, duplicateMesh); + clothInstance.humanoidMatchedBones = clothesHumanoidMatchedBones; + clothHumanoidMaskUtil.BuildExcludedVertexMaskForHandsAndHead(clothInstance); + return clothInstance; + } + + public ClothInstance SetupClothInstance(SkinnedMeshRenderer clothSMR, SkinnedMeshRenderer coupledSMR, MeshMatcher meshMatcher, Dictionary> clothesHumanoidMatchedBones, bool duplicateMesh) + { + if (coupledSMR == null) + { + throw new AutoMorpherException("Coupled Skinned Mesh Renderer is Null", "[EdenAutoMorpher_SetupUtil] SetupClothInstance\n\n - Coupled Skinned Mesh Renderer is null"); + } + if (coupledSMR.sharedMesh == null || coupledSMR.sharedMesh.vertexCount == 0) + { + throw new AutoMorpherException("Coupled Skinned Mesh Renderer's Mesh is Null or empty", "[EdenAutoMorpher_SetupUtil] SetupClothInstance\n\n - Coupled Skinned Mesh Renderer's mesh is null or no vertices"); + } + ClothInstance clothInstance = SetupClothInstance(clothSMR, clothesHumanoidMatchedBones, duplicateMesh); + if (clothInstance == null) + { + return null; + } + Vector3[] worldVertices = new WorldVertexUtil().GetWorldVertices(coupledSMR); + clothInstance.minDistanceVector = meshMatcher.GetMinDistanceToBody(worldVertices); + if (clothInstance.minDistanceVector == null || clothInstance.minDistanceVector.Length == 0) + { + throw new AutoMorpherException("Min Distance is Null", "[EdenAutoMorpher_SetupUtil] SetupClothInstance\n - minDistanceVector is null or empty"); + } + if (clothInstance.minDistanceVector != null) + { + clothInstance.minDistance = clothInstance.minDistanceVector.Select((Vector3 v) => v.magnitude).ToArray(); + } + clothInstance.isInsideVertex = meshMatcher.GetBodyInsideFlags(worldVertices); + if (clothInstance.isInsideVertex == null || clothInstance.isInsideVertex.Length == 0) + { + throw new AutoMorpherException("isInsideVertex is Null", "[EdenAutoMorpher_SetupUtil] SetupClothInstance\n - isInsideVertex is null or empty"); + } + return clothInstance; + } + + public Transform CreateClothesArmature(EdenAutoMorpherConfig config, out Dictionary clonedBoneMap) + { + clonedBoneMap = null; + if (config.targetClothesObject == null) + { + throw new AutoMorpherException("Target Clothes Object is Missing", "[EdenAutoMorpherManager] CreateClothesArmature\n - config.targetClothesObject is null"); + } + if (config.targetAvatarObject == null) + { + throw new AutoMorpherException("Target Avatar Object is Missing", "[EdenAutoMorpherManager] CreateClothesArmature\n - config.targetAvatarObject is null"); + } + Animator component = config.targetAvatarObject.GetComponent(); + if (component == null || !component.isHuman) + { + throw new AutoMorpherException("Target Avatar Animator is Invalid", "[EdenAutoMorpherManager] CreateClothesArmature\n - targetAvatarAnimator is null or not a Humanoid"); + } + Transform boneTransform = component.GetBoneTransform(HumanBodyBones.Hips); + if (boneTransform == null) + { + throw new AutoMorpherException("Humanoid Root Bone is Missing", "[EdenAutoMorpherManager] CreateClothesArmature\n - Animator.GetBoneTransform(HumanBodyBones.Hips) returned null"); + } + if (config.targetBodyMeshes == null || config.targetBodyMeshes.Count == 0) + { + throw new AutoMorpherException("Target Body Meshes are Missing", "[EdenAutoMorpherManager] CreateClothesArmature\n - config.targetBodyMeshes is null or empty"); + } + Transform transform = config.targetClothesObject.transform; + Transform transform2 = FindArmature(config, config.targetClothesObject.transform); + if (transform2 == null) + { + transform2 = new GameObject("Armature.1").transform; + transform2.SetParent(transform, worldPositionStays: false); + transform2.localPosition = Vector3.zero; + transform2.localRotation = Quaternion.identity; + transform2.localScale = Vector3.one; + } + if (transform2.name == "Armature") + { + transform2.name = "Armature.1"; + } + Dictionary dictionary = new Dictionary(); + if (config.clothesHumanoidMatchedBones != null && config.clothesHumanoidMatchedBones.Count > 0) + { + foreach (KeyValuePair> clothesHumanoidMatchedBone in config.clothesHumanoidMatchedBones) + { + HumanBodyBones key = clothesHumanoidMatchedBone.Key; + HashSet value = clothesHumanoidMatchedBone.Value; + if (value == null || value.Count == 0) + { + continue; + } + Transform boneTransform2 = component.GetBoneTransform(key); + if (boneTransform2 == null) + { + continue; + } + Transform transform3 = null; + int num = int.MaxValue; + foreach (Transform item in value) + { + if (!(item == null)) + { + int num2 = 0; + Transform transform4 = item; + while (transform4 != null) + { + num2++; + transform4 = transform4.parent; + } + if (num2 < num) + { + num = num2; + transform3 = item; + } + else if (num2 == num && transform3 != null && string.Compare(item.name, transform3.name, StringComparison.Ordinal) < 0) + { + transform3 = item; + } + } + } + if (!(transform3 == null) && !dictionary.ContainsKey(boneTransform2)) + { + dictionary.Add(boneTransform2, transform3); + } + } + } + MeshClassifier meshClassifier = new MeshClassifier(); + HashSet hashSet = new HashSet(); + for (int i = 0; i < config.targetBodyMeshes.Count; i++) + { + SkinnedMeshRenderer skinnedMeshRenderer = config.targetBodyMeshes[i]; + if (skinnedMeshRenderer == null) + { + continue; + } + HashSet activeBones = meshClassifier.GetActiveBones(skinnedMeshRenderer); + if (activeBones == null) + { + continue; + } + foreach (Transform item2 in activeBones) + { + if (item2 != null) + { + hashSet.Add(item2); + } + } + } + HashSet hashSet2 = new HashSet(hashSet); + foreach (Transform item3 in hashSet) + { + if (item3 == null) + { + continue; + } + Transform transform5 = item3; + while (transform5 != null) + { + hashSet2.Add(transform5); + if (transform5 == boneTransform) + { + break; + } + transform5 = transform5.parent; + } + } + Dictionary dictionary2 = new Dictionary(hashSet2.Count); + Dictionary dictionary3 = new Dictionary(hashSet2.Count); + foreach (Transform item4 in hashSet2) + { + if (item4 == null) + { + continue; + } + Transform transform6 = null; + Transform transform7 = null; + if (dictionary.TryGetValue(item4, out var value2) && value2 != null) + { + transform6 = value2; + if (config.addAnchorBone) + { + transform7 = new GameObject(item4.name).transform; + transform7.SetParent(transform2, worldPositionStays: false); + transform7.position = item4.position; + transform7.rotation = item4.rotation; + transform7.localScale = Vector3.one; + transform6.SetParent(transform7, worldPositionStays: true); + transform6.name = item4.name + "_clothes"; + transform6.localPosition = Vector3.zero; + transform6.localScale = Vector3.one; + } + else + { + transform6.name = item4.name; + transform6.position = item4.position; + ChildBonePositionPreserve(transform6, item4); + if (config.armatureBoneScaleCopy) + { + transform6.localScale = item4.localScale; + } + else + { + transform6.localScale = Vector3.one; + } + } + } + else + { + transform6 = new GameObject(item4.name).transform; + transform6.SetParent(transform2, worldPositionStays: false); + transform6.position = item4.position; + transform6.rotation = item4.rotation; + if (config.armatureBoneScaleCopy) + { + transform6.localScale = item4.localScale; + } + else + { + transform6.localScale = Vector3.one; + } + } + if (!(transform6 == null)) + { + dictionary2[item4] = transform6; + dictionary3[item4] = ((transform7 != null) ? transform7 : transform6); + } + } + foreach (Transform item5 in hashSet2) + { + if (item5 == null) + { + continue; + } + if (!dictionary2.TryGetValue(item5, out var value3) || value3 == null) + { + throw new AutoMorpherException("Mapped Clothes Bone is Missing", "[EdenAutoMorpherManager] CreateClothesArmature\n - clonedByOriginal does not contain originalBone or mappedClothesBone is null\n - originalBone: " + item5.name); + } + if (!dictionary3.TryGetValue(item5, out var value4) || value4 == null) + { + throw new AutoMorpherException("Mapped Anchor Bone is Missing", "[EdenAutoMorpherManager] CreateClothesArmature\n - parentAnchorByOriginal does not contain originalBone or mappedAnchorBone is null\n - originalBone: " + item5.name); + } + if (item5 == boneTransform) + { + value4.SetParent(transform2, worldPositionStays: true); + continue; + } + Transform parent = item5.parent; + Transform value5; + if (parent == null) + { + value4.SetParent(transform2, worldPositionStays: true); + } + else if (dictionary3.TryGetValue(parent, out value5) && value5 != null) + { + value4.SetParent(value5, worldPositionStays: true); + } + else + { + value4.SetParent(transform2, worldPositionStays: true); + } + } + if (!dictionary3.TryGetValue(boneTransform, out var value6) || value6 == null) + { + throw new AutoMorpherException("Cloned Humanoid Root Bone is Missing", "[EdenAutoMorpherManager] CreateClothesArmature\n - failed to map clone for HumanBodyBones.Hips"); + } + value6.SetParent(transform2, worldPositionStays: true); + clonedBoneMap = dictionary2; + return transform2; + } + + private Transform FindArmature(EdenAutoMorpherConfig config, Transform clothesRoot) + { + if (clothesRoot == null) + { + throw new AutoMorpherException("Armature Root Resolve Failed", "[EdenAutoMorpherManager] FindSingleArmatureRootFromHumanoidMatchedBones\n - config is null or clothesRoot is null"); + } + if (config.clothesHumanoidMatchedBones == null || config.clothesHumanoidMatchedBones.Count == 0) + { + return null; + } + Transform transform = null; + bool flag = false; + foreach (KeyValuePair> clothesHumanoidMatchedBone in config.clothesHumanoidMatchedBones) + { + HashSet value = clothesHumanoidMatchedBone.Value; + if (value == null || value.Count == 0) + { + continue; + } + foreach (Transform item in value) + { + if (!(item == null) && (!(item != clothesRoot) || item.IsChildOf(clothesRoot))) + { + flag = true; + Transform topChildUnderRoot = GetTopChildUnderRoot(item, clothesRoot); + if (topChildUnderRoot == null || topChildUnderRoot == clothesRoot) + { + return null; + } + if (transform == null) + { + transform = topChildUnderRoot; + } + else if (transform != topChildUnderRoot) + { + return null; + } + } + } + } + if (!flag) + { + return null; + } + if (transform == null || transform == clothesRoot) + { + return null; + } + return transform; + } + + private Transform GetTopChildUnderRoot(Transform bone, Transform clothesRoot) + { + if (bone == null || clothesRoot == null) + { + return null; + } + if (bone == clothesRoot) + { + return clothesRoot; + } + Transform transform = bone; + while (transform.parent != null && transform.parent != clothesRoot) + { + transform = transform.parent; + } + if (transform.parent == clothesRoot) + { + return transform; + } + return clothesRoot; + } + + private void ChildBonePositionPreserve(Transform reusableClothBone, Transform referenceAvatarBone) + { + if (reusableClothBone == null || referenceAvatarBone == null) + { + throw new AutoMorpherException("Reusable Bone Sync Failed", "[EdenAutoMorpherManager] SyncReusableBoneRotationPreserveChildrenWorld\n - reusableClothBone or referenceAvatarBone is null"); + } + List list = new List(); + List list2 = new List(); + List list3 = new List(); + for (int i = 0; i < reusableClothBone.childCount; i++) + { + Transform child = reusableClothBone.GetChild(i); + if (!(child == null)) + { + CollectDescendants(child, list); + } + } + for (int j = 0; j < list.Count; j++) + { + Transform transform = list[j]; + list2.Add(transform.position); + list3.Add(transform.rotation); + } + reusableClothBone.localRotation = referenceAvatarBone.localRotation; + for (int k = 0; k < list.Count; k++) + { + list[k].SetPositionAndRotation(list2[k], list3[k]); + } + } + + private void CollectDescendants(Transform root, List resultList) + { + if (root == null) + { + return; + } + resultList.Add(root); + for (int i = 0; i < root.childCount; i++) + { + Transform child = root.GetChild(i); + if (!(child == null)) + { + CollectDescendants(child, resultList); + } + } + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher_SetUpUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher_SetUpUtil.cs.meta new file mode 100644 index 0000000..29833a5 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/EdenAutoMorpher_SetUpUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ee9b28aa8763b004c9d0f631d797bb28 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshClassifier.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshClassifier.cs new file mode 100644 index 0000000..ef37df7 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshClassifier.cs @@ -0,0 +1,277 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.MeshClassifier +using System; +using System.Collections.Generic; +using Eden.AutoMorpher; +using UnityEngine; + +public class MeshClassifier +{ + private HumanBodyBones[] bodyBones = new HumanBodyBones[28] + { + HumanBodyBones.Hips, + HumanBodyBones.Spine, + HumanBodyBones.Chest, + HumanBodyBones.Neck, + HumanBodyBones.LeftShoulder, + HumanBodyBones.LeftUpperArm, + HumanBodyBones.LeftLowerArm, + HumanBodyBones.LeftHand, + HumanBodyBones.LeftThumbProximal, + HumanBodyBones.LeftIndexProximal, + HumanBodyBones.LeftMiddleProximal, + HumanBodyBones.LeftRingProximal, + HumanBodyBones.LeftLittleProximal, + HumanBodyBones.RightShoulder, + HumanBodyBones.RightUpperArm, + HumanBodyBones.RightLowerArm, + HumanBodyBones.RightHand, + HumanBodyBones.RightThumbProximal, + HumanBodyBones.RightIndexProximal, + HumanBodyBones.RightMiddleProximal, + HumanBodyBones.RightRingProximal, + HumanBodyBones.RightLittleProximal, + HumanBodyBones.LeftUpperLeg, + HumanBodyBones.LeftLowerLeg, + HumanBodyBones.LeftFoot, + HumanBodyBones.RightUpperLeg, + HumanBodyBones.RightLowerLeg, + HumanBodyBones.RightFoot + }; + + private HumanBodyBones[] headBones = new HumanBodyBones[3] + { + HumanBodyBones.Head, + HumanBodyBones.LeftEye, + HumanBodyBones.RightEye + }; + + public SkinnedMeshRenderer GetBodyMesh(Transform root, Animator animator) + { + List list = HumanBodyBonesTrsnforms(bodyBones, animator); + if (list.Count != bodyBones.Length) + { + if (AutoMorpherDev.isDeveloperMode) + { + Debug.LogWarning("[Body Mesh] Animator Bone is not enough"); + } + return null; + } + return GetBoneMatchedMesh(root, list); + } + + public SkinnedMeshRenderer GetHeadMesh(Transform root, Animator animator) + { + List list = HumanBodyBonesTrsnforms(headBones, animator); + if (list.Count != headBones.Length) + { + return null; + } + return GetBoneMatchedMesh(root, list); + } + + private List HumanBodyBonesTrsnforms(HumanBodyBones[] humanBonesList, Animator animator) + { + List list = new List(); + List list2 = new List(); + foreach (HumanBodyBones humanBodyBones in humanBonesList) + { + Transform boneTransform = animator.GetBoneTransform(humanBodyBones); + if (boneTransform == null) + { + list2.Add(humanBodyBones); + } + else + { + list.Add(boneTransform); + } + } + if (list2.Count > 0) + { + string text = string.Join(", ", list2); + throw new AutoMorpherException("[Body Mesh Finding] Required Humanoid Bones are Missing", "[BodyMeshUtil] HumanBodyBonesTrsnforms\n - Missing Humanoid Bones: [" + text + "]\n - Animator Humanoid mapping may be broken\n - Please check whether the missing humanoid bones are correctly assigned in [Animator → Avatar → Configure]."); + } + return list; + } + + private SkinnedMeshRenderer GetBoneMatchedMesh(Transform root, List humanBoneTransforms) + { + SkinnedMeshRenderer[] componentsInChildren = root.GetComponentsInChildren(includeInactive: false); + foreach (SkinnedMeshRenderer skinnedMeshRenderer in componentsInChildren) + { + bool flag = true; + HashSet activeBones = GetActiveBones(skinnedMeshRenderer); + if (AutoMorpherDev.isDeveloperMode) + { + Debug.Log($"[Body Mesh] {skinnedMeshRenderer.gameObject.name} have bone Set {activeBones.Count}"); + } + foreach (Transform humanBoneTransform in humanBoneTransforms) + { + if (!activeBones.Contains(humanBoneTransform) && !BoneExistsByPosition(humanBoneTransform, activeBones)) + { + flag = false; + if (AutoMorpherDev.isDeveloperMode) + { + Debug.Log("[Body Mesh] " + skinnedMeshRenderer.gameObject.name + " Doesn't hav bone " + humanBoneTransform.name); + } + break; + } + } + if (flag) + { + return skinnedMeshRenderer; + } + } + return null; + } + + private bool BoneExistsByPosition(Transform boneToCheck, HashSet smrBoneSet, float posTolerance = 0.0001f) + { + foreach (Transform item in smrBoneSet) + { + if ((item.position - boneToCheck.position).sqrMagnitude <= posTolerance * posTolerance) + { + return true; + } + } + return false; + } + + public HashSet GetActiveBones(SkinnedMeshRenderer smr, float weightThreshold = 0.0001f) + { + Mesh sharedMesh = smr.sharedMesh; + if (sharedMesh == null) + { + Debug.LogWarning("SkinnedMeshRenderer에 연결된 Mesh가 없습니다."); + return new HashSet(); + } + Transform[] bones = smr.bones; + BoneWeight[] boneWeights = sharedMesh.boneWeights; + HashSet hashSet = new HashSet(); + BoneWeight[] array = boneWeights; + for (int i = 0; i < array.Length; i++) + { + BoneWeight boneWeight = array[i]; + if (boneWeight.weight0 > weightThreshold) + { + hashSet.Add(boneWeight.boneIndex0); + } + if (boneWeight.weight1 > weightThreshold) + { + hashSet.Add(boneWeight.boneIndex1); + } + if (boneWeight.weight2 > weightThreshold) + { + hashSet.Add(boneWeight.boneIndex2); + } + if (boneWeight.weight3 > weightThreshold) + { + hashSet.Add(boneWeight.boneIndex3); + } + } + HashSet hashSet2 = new HashSet(); + foreach (int item in hashSet) + { + if (item >= 0 && item < bones.Length) + { + hashSet2.Add(bones[item]); + } + } + return hashSet2; + } + + public Dictionary> MeshHumanoidBoneMatcher(Animator animator, IReadOnlyList bodyMeshes, float posTolerance = 0.0001f, float weightThreshold = 0.0001f) + { + Dictionary> dictionary = new Dictionary>(); + if (animator == null) + { + throw new AutoMorpherException("Animator is Missing", "[MeshHumanoidBoneMatcher] MeshHumanoidBoneMatcher\n - animator is null"); + } + HashSet hashSet = new HashSet(); + if (bodyMeshes != null) + { + foreach (SkinnedMeshRenderer bodyMesh in bodyMeshes) + { + if (bodyMesh == null) + { + continue; + } + foreach (Transform activeBone in GetActiveBones(bodyMesh, weightThreshold)) + { + if (activeBone != null) + { + hashSet.Add(activeBone); + } + } + } + } + for (int i = 0; i < 55; i++) + { + HumanBodyBones humanBodyBones = (HumanBodyBones)i; + Transform boneTransform = animator.GetBoneTransform(humanBodyBones); + if (boneTransform == null) + { + continue; + } + HashSet hashSet2 = new HashSet(); + hashSet2.Add(boneTransform); + foreach (Transform item in FindBonesByPosition(boneTransform, hashSet, posTolerance)) + { + hashSet2.Add(item); + } + dictionary[humanBodyBones] = hashSet2; + } + return dictionary; + } + + private List FindBonesByPosition(Transform boneToCheck, HashSet smrBoneSet, float posTolerance = 0.0001f) + { + List list = new List(); + if (boneToCheck == null) + { + return list; + } + float num = posTolerance * posTolerance; + Vector3 position = boneToCheck.position; + foreach (Transform item in smrBoneSet) + { + if (!(item == null) && !(item == boneToCheck) && NameMatches(item.gameObject.name, boneToCheck.gameObject.name) && (item.position - position).sqrMagnitude <= num) + { + list.Add(item); + } + } + return list; + } + + private string[] TokenizeBoneName(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + return Array.Empty(); + } + char[] separator = new char[5] { '-', '_', ':', '.', '|' }; + name = name.Trim(); + return name.Split(separator, StringSplitOptions.RemoveEmptyEntries); + } + + private bool NameMatches(string boneToCheckName, string candidateName) + { + string[] array = TokenizeBoneName(boneToCheckName); + string[] array2 = TokenizeBoneName(candidateName); + if (array.Length == 0 || array2.Length == 0) + { + return false; + } + if (!array[0].Equals(array2[0], StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (array.Length > 1 && array2.Length > 1 && !array[1].Equals(array2[1], StringComparison.OrdinalIgnoreCase)) + { + return false; + } + return true; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshClassifier.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshClassifier.cs.meta new file mode 100644 index 0000000..22f17cc --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshClassifier.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ff16c8420ee112443862fd5a7b3c0903 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshMatcher.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshMatcher.cs new file mode 100644 index 0000000..fa9d5c8 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshMatcher.cs @@ -0,0 +1,246 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.MeshMatcher +using System.Collections.Generic; +using Eden.AutoMorpher; +using UnityEngine; + +public class MeshMatcher +{ + public struct ClosestHit + { + public Vector3 closestP; + + public Vector3 direction; + + public Vector3 moveVector; + + public float distance; + } + + public BvhTriangleMesh bodyBVH; + + private readonly HashSet LeftLegBones = new HashSet + { + HumanBodyBones.LeftUpperLeg, + HumanBodyBones.LeftLowerLeg, + HumanBodyBones.LeftFoot, + HumanBodyBones.LeftToes + }; + + private readonly HashSet RightLegBones = new HashSet + { + HumanBodyBones.RightUpperLeg, + HumanBodyBones.RightLowerLeg, + HumanBodyBones.RightFoot, + HumanBodyBones.RightToes + }; + + public BvhTriangleMesh BuildBvhMulti(IReadOnlyList bodies, Animator bodyAnimator) + { + if (bodies == null || bodies.Count == 0) + { + throw new AutoMorpherException("Body Meshes are Missing", "[BuildBvhMulti] BuildBvhMulti\n - bodies is null or empty"); + } + BvhTriangleMesh bvhTriangleMesh = new BvhTriangleMesh().BuildFromSkinnedMeshes(bodies, bodyAnimator); + if (bvhTriangleMesh == null || bvhTriangleMesh.triangles == null) + { + Debug.LogError("Failed to build multi-body BVH (no triangles)."); + throw new AutoMorpherException(LanguageManager.Get("UI.Exception.title.BodyBVHFail"), LanguageManager.GetFormat("UI.Exception.message.BodyBVHFail", new object[3] + { + bodyAnimator.gameObject.name, + bvhTriangleMesh == null, + bvhTriangleMesh.triangles == null + })); + } + return bvhTriangleMesh; + } + + public Vector3[] ExpandVertexMatch(ClothInstance clothInstance, float defaultMinDist = 0.005f, bool skipFootFitting = false, float maxMatchDistance = 0.1f) + { + Vector3[] worldVertices = clothInstance.worldVertices; + float[] minDistance = clothInstance.minDistance; + if (bodyBVH == null) + { + throw new AutoMorpherException("Body BVH is Missing", "[ExpandVertexMatch] ExpandVertexMatch\n - bodyBVH is null"); + } + if (worldVertices == null) + { + throw new AutoMorpherException("Cloth World Vertices are Missing", "[ExpandVertexMatch] ExpandVertexMatch\n - clothInstance.worldVertices is null"); + } + if (worldVertices.Length == 0) + { + Debug.LogWarning("clothes mesh has no vertices"); + return null; + } + if (minDistance == null) + { + Debug.LogWarning("minDists is null"); + } + if (minDistance.Length != worldVertices.Length) + { + Debug.LogWarning("minDists.Length != worldVertexs.Length"); + } + Vector3[] array = new Vector3[worldVertices.Length]; + float num = maxMatchDistance * maxMatchDistance; + for (int i = 0; i < worldVertices.Length; i++) + { + if (clothInstance.excludedVertices[i]) + { + array[i] = Vector3.zero; + continue; + } + if (clothInstance.isInsideVertex[i]) + { + array[i] = Vector3.zero; + continue; + } + float num2 = minDistance[i] + defaultMinDist; + BvhTriangleMesh.ClosestHit closestHit = (clothInstance.isLeftLegVertex[i] ? bodyBVH.QueryClosest(worldVertices[i], LeftLegBones) : ((!clothInstance.isRightLegVertex[i]) ? bodyBVH.QueryClosest(worldVertices[i]) : bodyBVH.QueryClosest(worldVertices[i], RightLegBones))); + if (skipFootFitting && (closestHit.mainHumanBone == HumanBodyBones.LeftFoot || closestHit.mainHumanBone == HumanBodyBones.RightFoot || closestHit.mainHumanBone == HumanBodyBones.RightToes || closestHit.mainHumanBone == HumanBodyBones.LeftToes)) + { + array[i] = Vector3.zero; + continue; + } + if (closestHit.sqrDistance > num) + { + array[i] = Vector3.zero; + continue; + } + Vector3 normalized = (closestHit.closestPoint - worldVertices[i]).normalized; + float num3 = Vector3.Dot(normalized, closestHit.normal.normalized); + if (num3 > 0.7f) + { + array[i] = closestHit.closestPoint + normalized * num2 - worldVertices[i]; + } + else if (num3 < -0.7f) + { + if (closestHit.sqrDistance < num2 * num2) + { + array[i] = closestHit.closestPoint - normalized * num2 - worldVertices[i]; + } + } + else + { + array[i] = Vector3.zero; + } + } + return array; + } + + public Vector3[] ShrinkVertexMatch(ClothInstance clothInstance, float defaultMinDist = 0.005f, float maxMatchDistance = 0.1f) + { + Vector3[] worldVertices = clothInstance.worldVertices; + float[] minDistance = clothInstance.minDistance; + if (bodyBVH == null) + { + throw new AutoMorpherException("Body BVH is Missing", "[ShrinkVertexMatch] ShrinkVertexMatch\n - bodyBVH is null"); + } + if (worldVertices == null) + { + throw new AutoMorpherException("Cloth World Vertices are Missing", "[ShrinkVertexMatch] ShrinkVertexMatch\n - clothInstance.worldVertices is null"); + } + if (worldVertices.Length == 0) + { + Debug.LogWarning("clothes mesh has no vertices"); + return null; + } + if (minDistance == null) + { + Debug.LogWarning("minDists is null"); + } + if (minDistance.Length != worldVertices.Length) + { + Debug.LogWarning("minDists.Length != worldVertexs.Length"); + } + Vector3[] array = new Vector3[worldVertices.Length]; + float num = maxMatchDistance * maxMatchDistance; + _ = clothInstance.isLeftLegVertex; + _ = clothInstance.isRightLegVertex; + for (int i = 0; i < worldVertices.Length; i++) + { + if (clothInstance.excludedVertices[i]) + { + array[i] = Vector3.zero; + continue; + } + if (clothInstance.isInsideVertex[i]) + { + array[i] = Vector3.zero; + continue; + } + float num2 = minDistance[i] + defaultMinDist; + BvhTriangleMesh.ClosestHit closestHit = (clothInstance.isLeftLegVertex[i] ? bodyBVH.QueryClosest(worldVertices[i], LeftLegBones) : ((!clothInstance.isRightLegVertex[i]) ? bodyBVH.QueryClosest(worldVertices[i]) : bodyBVH.QueryClosest(worldVertices[i], RightLegBones))); + if (closestHit.sqrDistance > num) + { + array[i] = Vector3.zero; + continue; + } + if (closestHit.mainHumanBone == HumanBodyBones.LeftFoot || closestHit.mainHumanBone == HumanBodyBones.RightFoot || closestHit.mainHumanBone == HumanBodyBones.RightToes || closestHit.mainHumanBone == HumanBodyBones.LeftToes) + { + array[i] = Vector3.zero; + continue; + } + Vector3 normalized = (closestHit.closestPoint - worldVertices[i]).normalized; + float num3 = Vector3.Dot(normalized, closestHit.normal.normalized); + if (num3 < -0.7f) + { + array[i] = closestHit.closestPoint - normalized * num2 - worldVertices[i]; + } + else if (num3 < -0.7f) + { + if (closestHit.sqrDistance < num2 * num2) + { + array[i] = closestHit.closestPoint + normalized * num2 - worldVertices[i]; + } + } + else + { + array[i] = Vector3.zero; + } + } + return array; + } + + public Vector3[] GetMinDistanceToBody(Vector3[] clothesVertices) + { + if (bodyBVH == null) + { + throw new AutoMorpherException("sourceBodyBVH is null", "[MeshMatcher] GetMinDistanceToBodysourceBodyBVH is null"); + } + if (clothesVertices == null || clothesVertices.Length == 0) + { + throw new AutoMorpherException("Source Vertices is null", "[MeshMatcher] GetMinDistanceToBodySource Vertices is null or no vertices"); + } + Vector3[] array = new Vector3[clothesVertices.Length]; + for (int i = 0; i < clothesVertices.Length; i++) + { + Vector3 vector = clothesVertices[i]; + Vector3 vector2 = bodyBVH.QueryClosest(vector).closestPoint - vector; + array[i] = vector2; + } + return array; + } + + public bool[] GetBodyInsideFlags(Vector3[] worldVertices) + { + if (bodyBVH == null) + { + throw new AutoMorpherException("sourceBodyBVH is null", "[MeshMatcher] GetBodyInsideFlagssourceBodyBVH is null"); + } + if (worldVertices == null || worldVertices.Length == 0) + { + Debug.LogError("clothes is null"); + throw new AutoMorpherException("Source Vertices is null", "[MeshMatcher] GetMinDistanceToBodySource Vertices is null or no vertices"); + } + bool[] array = new bool[worldVertices.Length]; + for (int i = 0; i < worldVertices.Length; i++) + { + BvhTriangleMesh.ClosestHit closestHit = bodyBVH.QueryClosest(worldVertices[i]); + float num = Vector3.Dot((closestHit.closestPoint - worldVertices[i]).normalized, closestHit.normal.normalized); + array[i] = num > 0f; + } + return array; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshMatcher.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshMatcher.cs.meta new file mode 100644 index 0000000..3a97c5d --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MeshMatcher.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ff91f6ef8c92bde45be01d6a9a72de14 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherMode.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherMode.cs new file mode 100644 index 0000000..1d7e77e --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherMode.cs @@ -0,0 +1,11 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.MorpherMode +public enum MorpherMode +{ + AutoMorpher = 0, + ManualMorpher = 1, + ProfileMorpher = 2, + ETC = 99 +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherMode.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherMode.cs.meta new file mode 100644 index 0000000..97a8598 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherMode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4034d900027327e4f852772dcfe8e365 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherState.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherState.cs new file mode 100644 index 0000000..26a65e7 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherState.cs @@ -0,0 +1,12 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.MorpherState +public enum MorpherState +{ + Idle, + Fitting_Doing, + Fitting_End, + Weighting_Doing, + Weighting_End +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherState.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherState.cs.meta new file mode 100644 index 0000000..a46ce9a --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/MorpherState.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: bc2c9a1228ed1e048ad890b466973f9b \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/PcaUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/PcaUtil.cs new file mode 100644 index 0000000..7f9ec97 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/PcaUtil.cs @@ -0,0 +1,162 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.PcaUtil +using System.Collections.Generic; +using Eden.AutoMorpher; +using UnityEngine; + +public class PcaUtil +{ + public RegionStats ComputeRegionStats(IList points) + { + RegionStats result = default(RegionStats); + if (points == null || points.Count == 0) + { + return result; + } + int count = points.Count; + Vector3 zero = Vector3.zero; + for (int i = 0; i < count; i++) + { + zero += points[i]; + } + zero /= (float)count; + float num = 0f; + float num2 = 0f; + float num3 = 0f; + float num4 = 0f; + float num5 = 0f; + float num6 = 0f; + for (int j = 0; j < count; j++) + { + Vector3 vector = points[j] - zero; + num += vector.x * vector.x; + num2 += vector.x * vector.y; + num3 += vector.x * vector.z; + num4 += vector.y * vector.y; + num5 += vector.y * vector.z; + num6 += vector.z * vector.z; + } + float num7 = 1f / (float)count; + num *= num7; + num2 *= num7; + num3 *= num7; + num4 *= num7; + num5 *= num7; + num6 *= num7; + JacobiEigenDecomposition3x3(num, num2, num3, num4, num5, num6, out var eigenValues, out var eigenVectors); + int num8 = 0; + if (eigenValues[1] > eigenValues[num8]) + { + num8 = 1; + } + if (eigenValues[2] > eigenValues[num8]) + { + num8 = 2; + } + Vector3 normalized = eigenVectors[num8].normalized; + float num9 = float.PositiveInfinity; + float num10 = float.NegativeInfinity; + float num11 = 0f; + for (int k = 0; k < count; k++) + { + float num12 = Vector3.Dot(points[k] - zero, normalized); + if (num12 < num9) + { + num9 = num12; + } + if (num12 > num10) + { + num10 = num12; + } + Vector3 vector2 = zero + normalized * num12; + float magnitude = (points[k] - vector2).magnitude; + num11 += magnitude; + } + result.center = zero; + result.principalAxis = normalized; + result.length = num10 - num9; + result.avgRadius = num11 / (float)count; + return result; + } + + private void JacobiEigenDecomposition3x3(float c00, float c01, float c02, float c11, float c12, float c22, out float[] eigenValues, out Vector3[] eigenVectors) + { + float[,] array = new float[3, 3] + { + { c00, c01, c02 }, + { c01, c11, c12 }, + { c02, c12, c22 } + }; + float[,] array2 = new float[3, 3] + { + { 1f, 0f, 0f }, + { 0f, 1f, 0f }, + { 0f, 0f, 1f } + }; + for (int i = 0; i < 32; i++) + { + int num = 0; + int num2 = 1; + float num3 = Mathf.Abs(array[0, 1]); + float num4 = Mathf.Abs(array[0, 2]); + if (num4 > num3) + { + num3 = num4; + num = 0; + num2 = 2; + } + float num5 = Mathf.Abs(array[1, 2]); + if (num5 > num3) + { + num3 = num5; + num = 1; + num2 = 2; + } + if (num3 < 1E-10f) + { + break; + } + float num6 = array[num, num]; + float num7 = array[num2, num2]; + float num8 = array[num, num2]; + float f = 0.5f * Mathf.Atan2(2f * num8, num7 - num6); + float num9 = Mathf.Cos(f); + float num10 = Mathf.Sin(f); + for (int j = 0; j < 3; j++) + { + if (j != num && j != num2) + { + float num11 = array[j, num]; + float num12 = array[j, num2]; + array[j, num] = num9 * num11 - num10 * num12; + array[num, j] = array[j, num]; + array[j, num2] = num10 * num11 + num9 * num12; + array[num2, j] = array[j, num2]; + } + } + float num13 = num9 * num9 * num6 - 2f * num10 * num9 * num8 + num10 * num10 * num7; + float num14 = num10 * num10 * num6 + 2f * num10 * num9 * num8 + num9 * num9 * num7; + array[num, num] = num13; + array[num2, num2] = num14; + array[num, num2] = 0f; + array[num2, num] = 0f; + for (int k = 0; k < 3; k++) + { + float num15 = array2[k, num]; + float num16 = array2[k, num2]; + array2[k, num] = num9 * num15 - num10 * num16; + array2[k, num2] = num10 * num15 + num9 * num16; + } + } + eigenValues = new float[3]; + eigenVectors = new Vector3[3]; + eigenValues[0] = array[0, 0]; + eigenValues[1] = array[1, 1]; + eigenValues[2] = array[2, 2]; + eigenVectors[0] = new Vector3(array2[0, 0], array2[1, 0], array2[2, 0]); + eigenVectors[1] = new Vector3(array2[0, 1], array2[1, 1], array2[2, 1]); + eigenVectors[2] = new Vector3(array2[0, 2], array2[1, 2], array2[2, 2]); + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/PcaUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/PcaUtil.cs.meta new file mode 100644 index 0000000..ea5a54e --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/PcaUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e4e18ad2e82437b4e9ac73ad1a2f08df \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProcessInfo.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProcessInfo.cs new file mode 100644 index 0000000..e3370d4 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProcessInfo.cs @@ -0,0 +1,12 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.ProcessInfo +public struct ProcessInfo +{ + public string title; + + public string text; + + public float progress; +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProcessInfo.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProcessInfo.cs.meta new file mode 100644 index 0000000..5a0fe37 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProcessInfo.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a1fa6f19a17c1cc4cbec5095fc7f64e5 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfileLoader.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfileLoader.cs new file mode 100644 index 0000000..efd1060 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfileLoader.cs @@ -0,0 +1,298 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.profile.ProfileLoader +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Eden.AutoMorpher; +using Eden.AutoMorpher.profile; +using UnityEngine; + +public class ProfileLoader +{ + private ProfileData loadedProfileData; + + private const int HEX32_W = 8; + + public List GetProfileList() + { + ProfileUtils profileUtils = new ProfileUtils(); + string text = Path.Combine(Application.dataPath, profileUtils.GetProfileBasePath()); + List list = new List(); + if (!Directory.Exists(text)) + { + Debug.LogWarning("[ProfileUtils] Profile base path does not exist: " + text); + return list; + } + string[] directories = Directory.GetDirectories(text); + foreach (string text2 in directories) + { + string fileName = Path.GetFileName(text2); + if (!string.IsNullOrEmpty(fileName)) + { + string path = Path.Combine(text2, fileName + ".json"); + string path2 = Path.Combine(text2, fileName + ".eb"); + if (File.Exists(path) && File.Exists(path2)) + { + list.Add(fileName); + } + } + } + return list; + } + + public ProfileData LoadProfileData(string profileName) + { + ProfileUtils profileUtils = new ProfileUtils(); + string path = Path.Combine(Application.dataPath, profileUtils.GetProfileBasePath()); + path = Path.Combine(path, profileName); + path = Path.Combine(path, profileName + ".json"); + if (string.IsNullOrWhiteSpace(path)) + { + throw new AutoMorpherException("Profile File Path is Invalid", "[ProfileLoader] LoadProfileData\n - Profile Path is null, empty, or whitespace"); + } + if (!File.Exists(path)) + { + throw new AutoMorpherException("Profile File Does Not Exist", "[ProfileLoader] LoadProfileData\n - profile file does not exist\n - path : " + path); + } + string json = File.ReadAllText(path); + loadedProfileData = JsonUtility.FromJson(json); + if (loadedProfileData == null) + { + throw new AutoMorpherException("Failed to Load Profile Data", "[ProfileLoader] LoadProfileData\n - Can't Load Profile Data\n - Please place a valid profile data file at " + path); + } + return loadedProfileData; + } + + public BvhTriangleMesh LoadBvhWithRootTransform(Transform rootTransform, string profileName) + { + ProfileUtils profileUtils = new ProfileUtils(); + string path = Path.Combine(Application.dataPath, profileUtils.GetProfileBasePath()); + path = Path.Combine(path, profileName); + path = Path.Combine(path, profileName + ".eb"); + if (rootTransform == null) + { + throw new AutoMorpherException("Root Transform is Null", "[ProfileLoader] LoadBvhWithRootTransform\n - rootTransform is null"); + } + if (string.IsNullOrEmpty(path)) + { + throw new AutoMorpherException("Profile BVH Path is Invalid", "[ProfileLoader] LoadBvhWithRootTransform\n - profileBvhPath is null or empty"); + } + int version; + ProfileBVH profileBVH = LoadProfileBVHData(path, out version); + if (profileBVH == null || profileBVH.vertices == null || profileBVH.datas == null || profileBVH.nodes == null || profileBVH.dataIndices == null) + { + throw new AutoMorpherException("Profile BVH Data is Invalid", "[ProfileLoader] LoadBvhWithRootTransform\n - profile or one of its internal data fields is null"); + } + Matrix4x4 localToWorldMatrix = rootTransform.localToWorldMatrix; + int num = profileBVH.datas.Length; + BvhTriangleMesh bvhTriangleMesh = new BvhTriangleMesh + { + triangles = new BvhTriangle[num], + triIndices = new int[profileBVH.dataIndices.Length], + nodes = new BvhNode[profileBVH.nodes.Length] + }; + for (int i = 0; i < num; i++) + { + profileBVHData profileBVHData2 = profileBVH.datas[i]; + Vector3 point = profileBVH.vertices[profileBVHData2.verA]; + Vector3 point2 = profileBVH.vertices[profileBVHData2.verB]; + Vector3 point3 = profileBVH.vertices[profileBVHData2.verC]; + Vector3 a = localToWorldMatrix.MultiplyPoint3x4(point); + Vector3 b = localToWorldMatrix.MultiplyPoint3x4(point2); + Vector3 c = localToWorldMatrix.MultiplyPoint3x4(point3); + Vector3 normal = ComputeTriangleNormal(a, b, c); + bvhTriangleMesh.triangles[i] = new BvhTriangle + { + a = a, + b = b, + c = c, + normal = normal, + mainHumanBone = HumanBodyBones.LastBone + }; + } + Array.Copy(profileBVH.dataIndices, bvhTriangleMesh.triIndices, profileBVH.dataIndices.Length); + for (int j = 0; j < profileBVH.nodes.Length; j++) + { + profileBVHNode profileBVHNode2 = profileBVH.nodes[j]; + bvhTriangleMesh.nodes[j] = new BvhNode + { + isLeaf = profileBVHNode2.isLeaf, + leftChild = profileBVHNode2.leftChild, + rightChild = profileBVHNode2.rightChild, + start = profileBVHNode2.start, + count = profileBVHNode2.count, + bounds = TransformBoundsToWorldAABB(localToWorldMatrix, profileBVHNode2.bounds) + }; + } + return bvhTriangleMesh; + } + + private ProfileBVH LoadProfileBVHData(string path, out int version) + { + version = 0; + int num = 0; + if (!File.Exists(path)) + { + throw new AutoMorpherException("Profile BVH File Not Found", "[ProfileLoader] LoadProfileBVHData\n - file not found\n - path : " + path); + } + string text = File.ReadAllText(path, Encoding.UTF8); + int num2 = 0; + ProfileUtils profileUtils = new ProfileUtils(); + string profileMagic = profileUtils.GetProfileMagic(); + if (text.Length < profileMagic.Length || text.Substring(0, profileMagic.Length) != profileMagic) + { + throw new AutoMorpherException("Profile BVH Magic Mismatch", "[ProfileLoader] LoadProfileBVHData\n - magic string mismatch\n - invalid or corrupted BVH file"); + } + num2 += profileMagic.Length; + version = ReadHexIntSafe(text, ref num2); + num = ReadHexIntSafe(text, ref num2); + int num3 = ReadHexIntSafe(text, ref num2); + int num4 = ReadHexIntSafe(text, ref num2); + int num5 = ReadHexIntSafe(text, ref num2); + int num6 = ReadHexIntSafe(text, ref num2); + if (num3 < 0 || num4 < 0 || num5 < 0 || num6 < 0) + { + throw new AutoMorpherException("Profile BVH Count Data is Invalid", "[ProfileLoader] LoadProfileBVHData\n - one or more count values are negative"); + } + Vector3[] array = new Vector3[num3]; + for (int i = 0; i < num3; i++) + { + array[i] = ReadHexVec3Safe(text, ref num2); + } + int version2; + int countInFile; + BaseKey3[] array2 = new ProfileUtils_VertexUtil().LoadTable(profileUtils.GetBaseDataPath(), out version2, out countInFile); + if (array2 == null || array2.Length == 0) + { + throw new AutoMorpherException("Profile BVH Base Vertex Table is Invalid", "[ProfileLoader] LoadProfileBVHData\n - base vertex table is null or empty"); + } + ProfileUtil_IndexUtil profileUtil_IndexUtil = new ProfileUtil_IndexUtil(); + profileUtil_IndexUtil.Build(num); + Vector3[] array3 = new Vector3[num3]; + profileUtil_IndexUtil.DecodeInto(array, array3); + Vector3[] array4 = new Vector3[num3]; + for (int j = 0; j < num3; j++) + { + array4[j] = TransformVec3(array3[j], array2[j % array2.Length]); + } + ProfileBVH profileBVH = new ProfileBVH + { + vertices = new List(num3), + datas = new profileBVHData[num4], + nodes = new profileBVHNode[num5], + dataIndices = new int[num6] + }; + profileBVH.vertices.AddRange(array4); + for (int k = 0; k < num4; k++) + { + profileBVH.datas[k] = new profileBVHData + { + verA = ReadHexIntSafe(text, ref num2), + verB = ReadHexIntSafe(text, ref num2), + verC = ReadHexIntSafe(text, ref num2) + }; + } + for (int l = 0; l < num5; l++) + { + Vector3 center = ReadHexVec3Safe(text, ref num2); + Vector3 vector = ReadHexVec3Safe(text, ref num2); + profileBVH.nodes[l] = new profileBVHNode + { + bounds = new Bounds(center, vector * 2f), + leftChild = ReadHexIntSafe(text, ref num2), + rightChild = ReadHexIntSafe(text, ref num2), + start = ReadHexIntSafe(text, ref num2), + count = ReadHexIntSafe(text, ref num2), + isLeaf = (num2 < text.Length && text[num2++] == '1') + }; + } + for (int m = 0; m < num6; m++) + { + profileBVH.dataIndices[m] = ReadHexIntSafe(text, ref num2); + } + return profileBVH; + } + + private int ReadHexIntSafe(string s, ref int p) + { + if (p + 8 > s.Length) + { + p = s.Length; + throw new AutoMorpherException("Profile BVH ReadHexInt Out of Range", "[ProfileBVH] ReadHexIntSafe\n - read position exceeds string length"); + } + uint result = Convert.ToUInt32(s.Substring(p, 8), 16); + p += 8; + return (int)result; + } + + private Vector3 ReadHexVec3Safe(string s, ref int p) + { + return new Vector3(ReadHexFloatSafe(s, ref p), ReadHexFloatSafe(s, ref p), ReadHexFloatSafe(s, ref p)); + } + + private float ReadHexFloatSafe(string s, ref int p) + { + if (p + 8 > s.Length) + { + p = s.Length; + throw new AutoMorpherException("Profile BVH ReadHexFloat Out of Range", "[ProfileBVH] ReadHexFloat\n - read position exceeds string length"); + } + uint value = Convert.ToUInt32(s.Substring(p, 8), 16); + p += 8; + return BitConverter.Int32BitsToSingle((int)value); + } + + private Vector3 TransformVec3(Vector3 v, BaseKey3 k) + { + return new Vector3(TransformFloatBits(v.x, k.x), TransformFloatBits(v.y, k.y), TransformFloatBits(v.z, k.z)); + } + + private float TransformFloatBits(float a, uint keyBits) + { + return BitConverter.Int32BitsToSingle(BitConverter.SingleToInt32Bits(a) ^ (int)keyBits); + } + + private Vector3 ComputeTriangleNormal(Vector3 a, Vector3 b, Vector3 c) + { + Vector3 vector = Vector3.Cross(b - a, c - a); + float magnitude = vector.magnitude; + if (magnitude > 1E-08f) + { + return vector / magnitude; + } + return Vector3.up; + } + + private Bounds TransformBoundsToWorldAABB(Matrix4x4 l2w, Bounds localBounds) + { + Vector3 center = localBounds.center; + Vector3 extents = localBounds.extents; + Vector3 vector = l2w.MultiplyPoint3x4(center + new Vector3(0f - extents.x, 0f - extents.y, 0f - extents.z)); + Vector3 p = l2w.MultiplyPoint3x4(center + new Vector3(0f - extents.x, 0f - extents.y, extents.z)); + Vector3 p2 = l2w.MultiplyPoint3x4(center + new Vector3(0f - extents.x, extents.y, 0f - extents.z)); + Vector3 p3 = l2w.MultiplyPoint3x4(center + new Vector3(0f - extents.x, extents.y, extents.z)); + Vector3 p4 = l2w.MultiplyPoint3x4(center + new Vector3(extents.x, 0f - extents.y, 0f - extents.z)); + Vector3 p5 = l2w.MultiplyPoint3x4(center + new Vector3(extents.x, 0f - extents.y, extents.z)); + Vector3 p6 = l2w.MultiplyPoint3x4(center + new Vector3(extents.x, extents.y, 0f - extents.z)); + Vector3 p7 = l2w.MultiplyPoint3x4(center + new Vector3(extents.x, extents.y, extents.z)); + Vector3 min = vector; + Vector3 max = vector; + Encapsulate(ref min, ref max, p); + Encapsulate(ref min, ref max, p2); + Encapsulate(ref min, ref max, p3); + Encapsulate(ref min, ref max, p4); + Encapsulate(ref min, ref max, p5); + Encapsulate(ref min, ref max, p6); + Encapsulate(ref min, ref max, p7); + return new Bounds((min + max) * 0.5f, max - min); + } + + private void Encapsulate(ref Vector3 min, ref Vector3 max, Vector3 p) + { + min = Vector3.Min(min, p); + max = Vector3.Max(max, p); + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfileLoader.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfileLoader.cs.meta new file mode 100644 index 0000000..2bbe8ba --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfileLoader.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 28844442e65718447b17317ed2155ea6 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfilePoseMatchUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfilePoseMatchUtil.cs new file mode 100644 index 0000000..e387eda --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfilePoseMatchUtil.cs @@ -0,0 +1,80 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.profile.ProfilePoseMatchUtil +using System.Collections.Generic; +using Eden.AutoMorpher; +using Eden.AutoMorpher.profile; +using UnityEngine; + +public class ProfilePoseMatchUtil +{ + private WorldVertexUtil _worldVertexUtil; + + private MeshClassifier meshClassifier; + + public ProfilePoseMatchUtil() + { + _worldVertexUtil = new WorldVertexUtil(); + meshClassifier = new MeshClassifier(); + } + + public void ProfilePoseMatcher(GameObject targetAvatar, IReadOnlyList targetBodyMeshes, GameObject targetCloth, ProfileData profileData, Dictionary> clothHumanBonesMap, Dictionary clothBoneTypeMap, float neckTargetHeight = 1.5f) + { + if (targetAvatar == null) + { + throw new AutoMorpherException("Target Avatar is Null", "[ProfilePoseMatchUtil] ProfilePoseMatcher\n - targetAvatar is null"); + } + if (targetAvatar.GetComponent() == null) + { + throw new AutoMorpherException("Target Avatar Animator is Null", "[ProfilePoseMatchUtil] ProfilePoseMatcher\n - targetAvatar has no animator"); + } + if (targetBodyMeshes == null || targetBodyMeshes.Count == 0) + { + throw new AutoMorpherException("Target Body Meshes are Missing", "[ProfilePoseMatchUtil] ProfilePoseMatcher\n - targetBodyMeshes is null or empty"); + } + if (targetCloth == null) + { + throw new AutoMorpherException("Target Cloth is Null", "[ProfilePoseMatchUtil] ProfilePoseMatcher\n - targetCloth is null"); + } + if (profileData == null) + { + throw new AutoMorpherException("Profile Data is Null", "[ProfilePoseMatchUtil] ProfilePoseMatcher\n - profileData is null"); + } + if (clothHumanBonesMap == null || clothHumanBonesMap.Count == 0 || clothBoneTypeMap == null || clothBoneTypeMap.Count == 0) + { + throw new AutoMorpherException("Clothing Bone Match Data is Missing", "[ProfilePoseMatchUtil] ProfilePoseMatcher\n - profile Clothe Bone Match Data is null or empty"); + } + Transform transform = targetAvatar.transform; + Transform transform2 = targetCloth.transform; + Transform parent = transform.parent; + transform.SetParent(null, worldPositionStays: true); + Transform parent2 = transform2.parent; + transform2.SetParent(null, worldPositionStays: true); + transform2.position = transform.position; + Dictionary> dictionary = meshClassifier.MeshHumanoidBoneMatcher(targetAvatar.GetComponent(), targetBodyMeshes); + if (dictionary == null || dictionary.Count == 0) + { + throw new AutoMorpherException("Target Avatar Bone Match Data is Missing", "[ProfilePoseMatchUtil] ProfilePoseMatcher\n - target Avatar Bone Match Data is null or empty"); + } + BodyPoseMatchSetupUtil bodyPoseMatchSetupUtil = new BodyPoseMatchSetupUtil(); + Vector3 comprehensiveScale = bodyPoseMatchSetupUtil.GetComprehensiveScale(transform2, clothHumanBonesMap, profileData); + Debug.Log($"ComprehensiveScale: {comprehensiveScale}"); + bodyPoseMatchSetupUtil.AdjustAvatarScaleByNeck(transform, dictionary, neckTargetHeight); + bodyPoseMatchSetupUtil.AdjustAvatarScaleByNeck(transform2, clothHumanBonesMap, neckTargetHeight); + List list = new List(); + foreach (SkinnedMeshRenderer targetBodyMesh in targetBodyMeshes) + { + list.Add(new BakedBodyMesh(targetBodyMesh)); + } + new BodyPoseMatch_Torso().AlignTorsoByNeck(transform, list, dictionary, clothHumanBonesMap, transform2, profileData, comprehensiveScale); + BodyPoseMatch_Arm bodyPoseMatch_Arm = new BodyPoseMatch_Arm(); + bodyPoseMatch_Arm.AlignUpperArmByArmPcaCenters(list, dictionary, clothHumanBonesMap, transform2, profileData, comprehensiveScale); + bodyPoseMatch_Arm.ScalingBothArmsLength(list, dictionary, clothHumanBonesMap, profileData, comprehensiveScale); + BodyPoseMatch_Leg bodyPoseMatch_Leg = new BodyPoseMatch_Leg(); + bodyPoseMatch_Leg.AlignBothUpperLegs(transform, list, dictionary, transform2, clothHumanBonesMap, profileData, comprehensiveScale); + bodyPoseMatch_Leg.ScalingBothLegsAndFoots(transform, list, dictionary, transform2, clothHumanBonesMap, profileData, comprehensiveScale); + transform.SetParent(parent, worldPositionStays: true); + transform2.SetParent(parent2, worldPositionStays: true); + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfilePoseMatchUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfilePoseMatchUtil.cs.meta new file mode 100644 index 0000000..3f9e1fe --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/ProfilePoseMatchUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a0ba475a2b0c45145b7cf6306c6bddf7 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/RegionStats.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/RegionStats.cs new file mode 100644 index 0000000..9e79049 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/RegionStats.cs @@ -0,0 +1,16 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.RegionStats +using UnityEngine; + +public struct RegionStats +{ + public Vector3 center; + + public Vector3 principalAxis; + + public float length; + + public float avgRadius; +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/RegionStats.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/RegionStats.cs.meta new file mode 100644 index 0000000..fa529ff --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/RegionStats.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f15328957f9a0184783cda194c88bc85 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/SkinningUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/SkinningUtil.cs new file mode 100644 index 0000000..6038695 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/SkinningUtil.cs @@ -0,0 +1,274 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.SkinningUtil +using UnityEngine; + +public class SkinningUtil +{ + public Vector3 WorldPosToBindPos(SkinnedMeshRenderer smr, Mesh bindMesh, int vertexIndex, Vector3 targetWorld) + { + if (smr == null || bindMesh == null) + { + return Vector3.zero; + } + BoneWeight[] boneWeights = bindMesh.boneWeights; + Matrix4x4[] bindposes = bindMesh.bindposes; + Transform[] bones = smr.bones; + if (boneWeights == null || bindposes == null || bones == null) + { + return smr.transform.InverseTransformPoint(targetWorld); + } + if (vertexIndex < 0 || vertexIndex >= boneWeights.Length) + { + return smr.transform.InverseTransformPoint(targetWorld); + } + if (bindposes.Length != bones.Length) + { + return smr.transform.InverseTransformPoint(targetWorld); + } + BoneWeight boneWeight = boneWeights[vertexIndex]; + bool flag = boneWeight.boneIndex0 >= 0 && boneWeight.boneIndex0 < bones.Length && bones[boneWeight.boneIndex0] != null && boneWeight.weight0 > 0f; + bool flag2 = boneWeight.boneIndex1 >= 0 && boneWeight.boneIndex1 < bones.Length && bones[boneWeight.boneIndex1] != null && boneWeight.weight1 > 0f; + bool flag3 = boneWeight.boneIndex2 >= 0 && boneWeight.boneIndex2 < bones.Length && bones[boneWeight.boneIndex2] != null && boneWeight.weight2 > 0f; + bool num = boneWeight.boneIndex3 >= 0 && boneWeight.boneIndex3 < bones.Length && bones[boneWeight.boneIndex3] != null && boneWeight.weight3 > 0f; + float num2 = (flag ? boneWeight.weight0 : 0f); + float num3 = (flag2 ? boneWeight.weight1 : 0f); + float num4 = (flag3 ? boneWeight.weight2 : 0f); + float num5 = (num ? boneWeight.weight3 : 0f); + float num6 = num2 + num3 + num4 + num5; + if (num6 <= 1E-08f) + { + return smr.transform.InverseTransformPoint(targetWorld); + } + num2 /= num6; + num3 /= num6; + num4 /= num6; + num5 /= num6; + Vector3 vector = smr.transform.InverseTransformPoint(targetWorld); + Matrix4x4 objWorldInv = smr.transform.worldToLocalMatrix; + Matrix4x4 acc = Matrix4x4.zero; + if (num2 > 0f) + { + Acc(ref acc, boneWeight.boneIndex0, num2); + } + if (num3 > 0f) + { + Acc(ref acc, boneWeight.boneIndex1, num3); + } + if (num4 > 0f) + { + Acc(ref acc, boneWeight.boneIndex2, num4); + } + if (num5 > 0f) + { + Acc(ref acc, boneWeight.boneIndex3, num5); + } + if (Mathf.Abs(acc.m00 * (acc.m11 * acc.m22 - acc.m12 * acc.m21) - acc.m01 * (acc.m10 * acc.m22 - acc.m12 * acc.m20) + acc.m02 * (acc.m10 * acc.m21 - acc.m11 * acc.m20)) < 1E-12f) + { + return vector; + } + acc.m33 = 1f; + Vector3 vector2 = Matrix4x4.Inverse(acc).MultiplyPoint3x4(vector); + Debug.Log($"Diff{smr.transform.TransformPoint(vector2) - targetWorld}"); + return vector2; + void Acc(ref Matrix4x4 reference, int boneIndex, float w) + { + if (!(w <= 0f)) + { + Matrix4x4 matrix4x = objWorldInv * bones[boneIndex].localToWorldMatrix * bindposes[boneIndex]; + reference.m00 += matrix4x.m00 * w; + reference.m01 += matrix4x.m01 * w; + reference.m02 += matrix4x.m02 * w; + reference.m10 += matrix4x.m10 * w; + reference.m11 += matrix4x.m11 * w; + reference.m12 += matrix4x.m12 * w; + reference.m20 += matrix4x.m20 * w; + reference.m21 += matrix4x.m21 * w; + reference.m22 += matrix4x.m22 * w; + } + } + } + + public Vector3 WorldPosToBindPos_Full(SkinnedMeshRenderer smr, Mesh bindMesh, int vertexIndex, Vector3 targetWorld) + { + if (smr == null || bindMesh == null) + { + return Vector3.zero; + } + BoneWeight[] boneWeights = bindMesh.boneWeights; + Matrix4x4[] bindposes = bindMesh.bindposes; + Transform[] bones = smr.bones; + if (boneWeights == null || bindposes == null || bones == null) + { + return smr.transform.InverseTransformPoint(targetWorld); + } + if (vertexIndex < 0 || vertexIndex >= boneWeights.Length) + { + return smr.transform.InverseTransformPoint(targetWorld); + } + if (bindposes.Length != bones.Length) + { + return smr.transform.InverseTransformPoint(targetWorld); + } + BoneWeight boneWeight = boneWeights[vertexIndex]; + float[] array = new float[4]; + int[] array2 = new int[4]; + array[0] = boneWeight.weight0; + array2[0] = boneWeight.boneIndex0; + array[1] = boneWeight.weight1; + array2[1] = boneWeight.boneIndex1; + array[2] = boneWeight.weight2; + array2[2] = boneWeight.boneIndex2; + array[3] = boneWeight.weight3; + array2[3] = boneWeight.boneIndex3; + float num = 0f; + for (int i = 0; i < 4; i++) + { + if (array2[i] >= 0 && array2[i] < bones.Length && bones[array2[i]] != null) + { + num += array[i]; + } + } + if (num < 1E-08f) + { + return smr.transform.InverseTransformPoint(targetWorld); + } + for (int j = 0; j < 4; j++) + { + array[j] /= num; + } + Vector3 result = smr.transform.InverseTransformPoint(targetWorld); + Vector4 vector = new Vector4(result.x, result.y, result.z, 1f); + Matrix4x4 objWorldInv = smr.transform.worldToLocalMatrix; + Matrix4x4 acc = Matrix4x4.zero; + for (int k = 0; k < 4; k++) + { + if (array[k] > 0f && array2[k] >= 0 && array2[k] < bones.Length) + { + Accumulate(ref acc, array2[k], array[k]); + } + } + if (Mathf.Abs(acc.m00 * (acc.m11 * acc.m22 - acc.m12 * acc.m21) - acc.m01 * (acc.m10 * acc.m22 - acc.m12 * acc.m20) + acc.m02 * (acc.m10 * acc.m21 - acc.m11 * acc.m20)) < 1E-12f) + { + return result; + } + return Matrix4x4.Inverse(acc).MultiplyPoint3x4(vector); + void Accumulate(ref Matrix4x4 reference, int bi, float w) + { + if (!(w <= 0f)) + { + Matrix4x4 matrix4x = objWorldInv * bones[bi].localToWorldMatrix * bindposes[bi]; + reference.m00 += matrix4x.m00 * w; + reference.m01 += matrix4x.m01 * w; + reference.m02 += matrix4x.m02 * w; + reference.m03 += matrix4x.m03 * w; + reference.m10 += matrix4x.m10 * w; + reference.m11 += matrix4x.m11 * w; + reference.m12 += matrix4x.m12 * w; + reference.m13 += matrix4x.m13 * w; + reference.m20 += matrix4x.m20 * w; + reference.m21 += matrix4x.m21 * w; + reference.m22 += matrix4x.m22 * w; + reference.m23 += matrix4x.m23 * w; + reference.m30 += matrix4x.m30 * w; + reference.m31 += matrix4x.m31 * w; + reference.m32 += matrix4x.m32 * w; + reference.m33 += matrix4x.m33 * w; + } + } + } + + public Vector3 WorldDirToBindDir_Full(SkinnedMeshRenderer smr, Mesh bindMesh, int vertexIndex, Vector3 targetWorldDir) + { + if (smr == null || bindMesh == null) + { + return Vector3.zero; + } + BoneWeight[] boneWeights = bindMesh.boneWeights; + Matrix4x4[] bindposes = bindMesh.bindposes; + Transform[] bones = smr.bones; + if (boneWeights == null || bindposes == null || bones == null) + { + return smr.transform.InverseTransformDirection(targetWorldDir).normalized; + } + if (vertexIndex < 0 || vertexIndex >= boneWeights.Length) + { + return smr.transform.InverseTransformDirection(targetWorldDir).normalized; + } + if (bindposes.Length != bones.Length) + { + return smr.transform.InverseTransformDirection(targetWorldDir).normalized; + } + BoneWeight boneWeight = boneWeights[vertexIndex]; + float[] array = new float[4]; + int[] array2 = new int[4]; + array[0] = boneWeight.weight0; + array2[0] = boneWeight.boneIndex0; + array[1] = boneWeight.weight1; + array2[1] = boneWeight.boneIndex1; + array[2] = boneWeight.weight2; + array2[2] = boneWeight.boneIndex2; + array[3] = boneWeight.weight3; + array2[3] = boneWeight.boneIndex3; + float num = 0f; + for (int i = 0; i < 4; i++) + { + if (array2[i] >= 0 && array2[i] < bones.Length && bones[array2[i]] != null) + { + num += array[i]; + } + } + if (num < 1E-08f) + { + return smr.transform.InverseTransformDirection(targetWorldDir).normalized; + } + for (int j = 0; j < 4; j++) + { + array[j] /= num; + } + Vector3 vector = smr.transform.InverseTransformDirection(targetWorldDir); + if (vector.sqrMagnitude < 1E-12f) + { + return Vector3.up; + } + Matrix4x4 objWorldInv = smr.transform.worldToLocalMatrix; + Matrix4x4 acc = Matrix4x4.zero; + for (int k = 0; k < 4; k++) + { + if (array[k] > 0f && array2[k] >= 0 && array2[k] < bones.Length) + { + Accumulate(ref acc, array2[k], array[k]); + } + } + Vector3 vector2 = acc.transpose.MultiplyVector(vector); + if (vector2.sqrMagnitude < 1E-12f) + { + return vector.normalized; + } + return vector2.normalized; + void Accumulate(ref Matrix4x4 reference, int bi, float w) + { + if (!(w <= 0f)) + { + Matrix4x4 matrix4x = objWorldInv * bones[bi].localToWorldMatrix * bindposes[bi]; + reference.m00 += matrix4x.m00 * w; + reference.m01 += matrix4x.m01 * w; + reference.m02 += matrix4x.m02 * w; + reference.m03 += matrix4x.m03 * w; + reference.m10 += matrix4x.m10 * w; + reference.m11 += matrix4x.m11 * w; + reference.m12 += matrix4x.m12 * w; + reference.m13 += matrix4x.m13 * w; + reference.m20 += matrix4x.m20 * w; + reference.m21 += matrix4x.m21 * w; + reference.m22 += matrix4x.m22 * w; + reference.m23 += matrix4x.m23 * w; + reference.m30 += matrix4x.m30 * w; + reference.m31 += matrix4x.m31 * w; + reference.m32 += matrix4x.m32 * w; + reference.m33 += matrix4x.m33 * w; + } + } + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/SkinningUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/SkinningUtil.cs.meta new file mode 100644 index 0000000..344d161 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/SkinningUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c723a3f7b3bf302429eb5f6b80030ed2 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TempBoneMarker.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TempBoneMarker.cs new file mode 100644 index 0000000..f04c52e --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TempBoneMarker.cs @@ -0,0 +1,15 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// TempBoneMarker +using System.Collections.Generic; +using UnityEngine; + +public class TempBoneMarker : MonoBehaviour +{ + public Transform originalParent; + + public string additionalInfo = ""; + + public List wrappedChildNames = new List(); +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TempBoneMarker.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TempBoneMarker.cs.meta new file mode 100644 index 0000000..4a22ff1 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TempBoneMarker.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9f6f7aa7d54dbcc409c4af15cf457d7b \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TransformInfo.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TransformInfo.cs new file mode 100644 index 0000000..8cdd95e --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TransformInfo.cs @@ -0,0 +1,72 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.TransformInfo +using UnityEngine; + +public class TransformInfo +{ + public Transform parentTransform; + + public Vector3 position; + + public Vector3 localPosition; + + public Quaternion rotation; + + public Quaternion localRotation; + + public Vector3 localScale; + + public string name; + + public TransformInfo(Transform t) + { + parentTransform = t.parent; + position = t.position; + localPosition = t.localPosition; + rotation = t.rotation; + localRotation = t.localRotation; + localScale = t.localScale; + name = t.name; + } + + public void ApplyToTransform(Transform t, bool applyParent, bool applyPosition, bool applyRotation, bool applyScale, bool applyName = false) + { + if (applyParent) + { + t.SetParent(parentTransform, worldPositionStays: true); + if (applyPosition) + { + t.localPosition = localPosition; + } + if (applyRotation) + { + t.localRotation = localRotation; + } + if (applyScale) + { + t.localScale = localScale; + } + } + else + { + if (applyPosition) + { + t.position = position; + } + if (applyRotation) + { + t.rotation = rotation; + } + if (applyScale) + { + t.localScale = localScale; + } + } + if (applyName) + { + t.name = name; + } + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TransformInfo.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TransformInfo.cs.meta new file mode 100644 index 0000000..2a770b8 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TransformInfo.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 076524bdea516c44384a07cc7c600c74 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TriangleUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TriangleUtil.cs new file mode 100644 index 0000000..1d8ed1a --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TriangleUtil.cs @@ -0,0 +1,57 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.TriangleUtil +using UnityEngine; + +public class TriangleUtil +{ + public Vector3 ClosestPointOnTriangle(Vector3 p, Vector3 a, Vector3 b, Vector3 c) + { + Vector3 vector = b - a; + Vector3 vector2 = c - a; + Vector3 rhs = p - a; + float num = Vector3.Dot(vector, rhs); + float num2 = Vector3.Dot(vector2, rhs); + if (num <= 0f && num2 <= 0f) + { + return a; + } + Vector3 rhs2 = p - b; + float num3 = Vector3.Dot(vector, rhs2); + float num4 = Vector3.Dot(vector2, rhs2); + if (num3 >= 0f && num4 <= num3) + { + return b; + } + float num5 = num * num4 - num3 * num2; + if (num5 <= 0f && num >= 0f && num3 <= 0f) + { + float num6 = num / (num - num3); + return a + num6 * vector; + } + Vector3 rhs3 = p - c; + float num7 = Vector3.Dot(vector, rhs3); + float num8 = Vector3.Dot(vector2, rhs3); + if (num8 >= 0f && num7 <= num8) + { + return c; + } + float num9 = num7 * num2 - num * num8; + if (num9 <= 0f && num2 >= 0f && num8 <= 0f) + { + float num10 = num2 / (num2 - num8); + return a + num10 * vector2; + } + float num11 = num3 * num8 - num7 * num4; + if (num11 <= 0f && num4 - num3 >= 0f && num7 - num8 >= 0f) + { + float num12 = (num4 - num3) / (num4 - num3 + (num7 - num8)); + return b + num12 * (c - b); + } + float num13 = 1f / (num11 + num9 + num5); + float num14 = num9 * num13; + float num15 = num5 * num13; + return a + vector * num14 + vector2 * num15; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TriangleUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TriangleUtil.cs.meta new file mode 100644 index 0000000..0c0813e --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/TriangleUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f0f259ffb91dc6e4387819375a0d8353 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexFittingUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexFittingUtil.cs new file mode 100644 index 0000000..5f8629b --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexFittingUtil.cs @@ -0,0 +1,675 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.VertexFittingUtil +using System.Collections.Generic; +using System.Diagnostics; +using Eden.AutoMorpher; +using UnityEngine; + +public class VertexFittingUtil +{ + private struct Node + { + public int index; + + public int depth; + } + + public void ExpandClothes_World_Test(List clothInstances, MeshMatcher meshMatcher, EdenAutoMorpherConfig config, ClothInstanceTotal clothInstanceTotal) + { + using (AutoMorpherDev.Profile("[Expand] BVH Mesh Matching time")) + { + foreach (ClothInstance clothInstance in clothInstances) + { + clothInstance.deltas = meshMatcher.ExpandVertexMatch(clothInstance, config.minMargin, config.skipFootFitting); + } + } + foreach (ClothInstance clothInstance2 in clothInstances) + { + clothInstance2.deltasLocal = clothInstance2.deltas; + } + Vector3[] deltas = clothInstanceTotal.GlobalDeltas; + clothInstanceTotal.UpdateGlobalBuffersFromClothInstances(); + SmoothDeltasByDistance(clothInstanceTotal.GlobalPositions, ref deltas, clothInstanceTotal.GlobalAdjacencyMerged, config.worldRadius, 1, config.smoothingIteration, 0.5f, config.sigma); + clothInstanceTotal.SetGlobalDeltas(deltas); + clothInstanceTotal.ApplyGlobalDeltasToClothInstances(); + foreach (ClothInstance clothInstance3 in clothInstances) + { + for (int i = 0; i < clothInstance3.worldVertices.Length; i++) + { + if (!clothInstance3.excludedVertices[i] || !clothInstance3.isInsideVertex[i]) + { + clothInstance3.worldVertices[i] += clothInstance3.deltasLocal[i]; + } + clothInstance3.deltasLocal[i] = Vector3.zero; + } + foreach (List equivalentVertex in clothInstance3.equivalentVertices) + { + Vector3 zero = Vector3.zero; + for (int j = 0; j < equivalentVertex.Count; j++) + { + zero += clothInstance3.worldVertices[equivalentVertex[j]]; + } + zero /= (float)equivalentVertex.Count; + for (int k = 0; k < equivalentVertex.Count; k++) + { + clothInstance3.worldVertices[equivalentVertex[k]] = zero; + } + } + } + } + + public void ShrinkClothes_World_Test(List clothInstances, MeshMatcher meshMatcher, EdenAutoMorpherConfig config, ClothInstanceTotal clothInstanceTotal) + { + using (AutoMorpherDev.Profile("[Expand] BVH Mesh Matching time")) + { + foreach (ClothInstance clothInstance in clothInstances) + { + clothInstance.deltas = meshMatcher.ShrinkVertexMatch(clothInstance, config.minMargin); + } + } + foreach (ClothInstance clothInstance2 in clothInstances) + { + clothInstance2.deltasLocal = clothInstance2.deltas; + } + Vector3[] deltas = clothInstanceTotal.GlobalDeltas; + clothInstanceTotal.UpdateGlobalBuffersFromClothInstances(); + SmoothDeltasByDistance(clothInstanceTotal.GlobalPositions, ref deltas, clothInstanceTotal.GlobalAdjacencyMerged, config.worldRadius, 3, config.smoothingIteration, 0.5f, config.sigma); + clothInstanceTotal.SetGlobalDeltas(deltas); + clothInstanceTotal.ApplyGlobalDeltasToClothInstances(); + SmoothAllClothesDeltasByDistance(clothInstances, config.worldRadius, config.smoothingIteration, config.sigma); + foreach (ClothInstance clothInstance3 in clothInstances) + { + for (int i = 0; i < clothInstance3.worldVertices.Length; i++) + { + if (!clothInstance3.excludedVertices[i] || !clothInstance3.isInsideVertex[i]) + { + clothInstance3.worldVertices[i] += clothInstance3.deltasLocal[i]; + } + clothInstance3.deltasLocal[i] = Vector3.zero; + } + foreach (List equivalentVertex in clothInstance3.equivalentVertices) + { + Vector3 zero = Vector3.zero; + for (int j = 0; j < equivalentVertex.Count; j++) + { + zero += clothInstance3.worldVertices[equivalentVertex[j]]; + } + zero /= (float)equivalentVertex.Count; + for (int k = 0; k < equivalentVertex.Count; k++) + { + clothInstance3.worldVertices[equivalentVertex[k]] = zero; + } + } + } + } + + public void ExpandClothes_World(ClothInstance clothInstance, List clothInstances, MeshMatcher meshMatcher, EdenAutoMorpherConfig config, float fittingRadius) + { + if (AutoMorpherDev.isDeveloperMode) + { + UnityEngine.Debug.Log("[Expand] Start " + clothInstance.smr.gameObject.name + "//==============================================================================================================="); + } + Stopwatch stopwatch = (AutoMorpherDev.isDeveloperMode ? Stopwatch.StartNew() : null); + Stopwatch.StartNew(); + using (AutoMorpherDev.Profile("[Expand] BVH Mesh Matching time")) + { + clothInstance.deltas = meshMatcher.ExpandVertexMatch(clothInstance, config.minMargin, config.skipFootFitting); + } + List list = null; + using (AutoMorpherDev.Profile("[Expand] Select Anchors time")) + { + list = SelectAnchors(clothInstance, fittingRadius); + } + if (AutoMorpherDev.isDeveloperMode) + { + UnityEngine.Debug.Log($"[Expand] Anchor Count {list.Count}"); + } + if (list.Count == 0) + { + if (AutoMorpherDev.isDeveloperMode) + { + stopwatch.Stop(); + UnityEngine.Debug.Log($"[Expand] TOTAL ShrinkClothes time: {stopwatch.ElapsedMilliseconds} ms"); + UnityEngine.Debug.Log("[Expand] End " + clothInstance.smr.gameObject.name + "//==============================================================================================================="); + } + return; + } + Stopwatch stopwatch2 = (AutoMorpherDev.isDeveloperMode ? Stopwatch.StartNew() : null); + float num = 0f; + float num2 = 0f; + VertexMoverUtil vertexMoverUtil = new VertexMoverUtil(); + foreach (int item in list) + { + if (clothInstance.excludedVertices[item]) + { + continue; + } + Stopwatch stopwatch3 = (AutoMorpherDev.isDeveloperMode ? Stopwatch.StartNew() : null); + if (AutoMorpherDev.isDeveloperMode) + { + UnityEngine.Debug.Log($"[Expand] Selected Expand Vertes: {clothInstance.smr.gameObject.name} / {item}"); + UnityEngine.Debug.Log($"[Expand] SelectedCEnter {item} {clothInstance.deltas[item]} {clothInstance.worldVertices[item]}"); + } + Vector3[] array = vertexMoverUtil.MoveVertices(clothInstance, item, clothInstance.isLeftLegVertex[item], clothInstance.isRightLegVertex[item], fittingRadius, config.sigma, clothInstance.deltas[item]); + if (array == null) + { + if (AutoMorpherDev.isDeveloperMode) + { + stopwatch3.Stop(); + num += (float)stopwatch3.ElapsedMilliseconds; + } + continue; + } + for (int i = 0; i < clothInstance.worldVertices.Length && i < array.Length; i++) + { + if (array[i] != Vector3.zero) + { + clothInstance.deltasLocal[i] += array[i]; + } + } + if (AutoMorpherDev.isDeveloperMode) + { + stopwatch3.Stop(); + num += (float)stopwatch3.ElapsedMilliseconds; + } + Stopwatch stopwatch4 = (AutoMorpherDev.isDeveloperMode ? Stopwatch.StartNew() : null); + foreach (ClothInstance clothInstance2 in clothInstances) + { + if (clothInstance2 == clothInstance) + { + continue; + } + Vector3[] array2 = vertexMoverUtil.MoveVertices(clothInstance2, clothInstance.worldVertices[item], clothInstance.isLeftLegVertex[item], clothInstance.isRightLegVertex[item], fittingRadius, config.sigma, clothInstance.deltas[item]); + if (array2 == null) + { + continue; + } + for (int j = 0; j < clothInstance2.worldVertices.Length && j < array2.Length; j++) + { + if (array2[j] != Vector3.zero) + { + clothInstance2.deltasLocal[j] += array2[j]; + } + } + } + if (AutoMorpherDev.isDeveloperMode) + { + stopwatch4.Stop(); + num2 += (float)stopwatch4.ElapsedMilliseconds; + } + } + SmoothAllClothesDeltasByDistance(clothInstances, fittingRadius, config.smoothingIteration, config.sigma); + foreach (ClothInstance clothInstance3 in clothInstances) + { + for (int k = 0; k < clothInstance3.worldVertices.Length; k++) + { + if (!clothInstance3.excludedVertices[k]) + { + clothInstance3.worldVertices[k] += clothInstance3.deltasLocal[k]; + } + clothInstance3.deltasLocal[k] = Vector3.zero; + } + foreach (List equivalentVertex in clothInstance3.equivalentVertices) + { + Vector3 zero = Vector3.zero; + for (int l = 0; l < equivalentVertex.Count; l++) + { + zero += clothInstance3.worldVertices[equivalentVertex[l]]; + } + zero /= (float)equivalentVertex.Count; + for (int m = 0; m < equivalentVertex.Count; m++) + { + clothInstance3.worldVertices[equivalentVertex[m]] = zero; + } + } + } + if (AutoMorpherDev.isDeveloperMode) + { + stopwatch2.Stop(); + UnityEngine.Debug.Log($"[Expand] Renderer Move Vector Calculate: {clothInstance.smr.gameObject.name} : {num}"); + UnityEngine.Debug.Log($"[Expand] Other Renderer Move Vector Calculate: {clothInstance.smr.gameObject.name} : {num2}"); + UnityEngine.Debug.Log($"[Expand] Total Move Vector Calculate: {clothInstance.smr.gameObject.name} : {stopwatch2.ElapsedMilliseconds}"); + stopwatch.Stop(); + UnityEngine.Debug.Log($"[Expand] TOTAL ShrinkClothes time: {stopwatch.ElapsedMilliseconds} ms"); + UnityEngine.Debug.Log("[Expand] End " + clothInstance.smr.gameObject.name + "//==============================================================================================================="); + } + } + + public void ShrinkClothes_World(ClothInstance clothInstance, List clothInstances, MeshMatcher meshMatcher, EdenAutoMorpherConfig config, float fittingRadius) + { + if (AutoMorpherDev.isDeveloperMode) + { + UnityEngine.Debug.Log("[Shrink] Start " + clothInstance.smr.gameObject.name + "//==============================================================================================================="); + } + Stopwatch stopwatch = (AutoMorpherDev.isDeveloperMode ? Stopwatch.StartNew() : null); + using (AutoMorpherDev.Profile("[Shrink] BVH Mesh Matching time")) + { + clothInstance.deltas = meshMatcher.ShrinkVertexMatch(clothInstance, config.minMargin); + } + List list = null; + using (AutoMorpherDev.Profile("[Shrink] Select Anchors time")) + { + list = SelectAnchors(clothInstance, fittingRadius); + } + if (AutoMorpherDev.isDeveloperMode) + { + UnityEngine.Debug.Log($"[Shrink] Anchor Count {list.Count}"); + } + if (list.Count == 0) + { + if (AutoMorpherDev.isDeveloperMode) + { + stopwatch.Stop(); + UnityEngine.Debug.Log($"[Shrink] TOTAL ShrinkClothes time: {stopwatch.ElapsedMilliseconds} ms"); + UnityEngine.Debug.Log("[Shrink] End " + clothInstance.smr.gameObject.name + "//==============================================================================================================="); + } + return; + } + Stopwatch stopwatch2 = (AutoMorpherDev.isDeveloperMode ? Stopwatch.StartNew() : null); + float num = 0f; + float num2 = 0f; + VertexMoverUtil vertexMoverUtil = new VertexMoverUtil(); + foreach (int item in list) + { + if (clothInstance.excludedVertices[item]) + { + continue; + } + Stopwatch stopwatch3 = (AutoMorpherDev.isDeveloperMode ? Stopwatch.StartNew() : null); + if (AutoMorpherDev.isDeveloperMode) + { + UnityEngine.Debug.Log($"[Shrink] Selected Expand Vertes: {clothInstance.smr.gameObject.name} / {item}"); + UnityEngine.Debug.Log($"[Shrink] SelectedCEnter {item} {clothInstance.deltas[item]} {clothInstance.worldVertices[item]}"); + } + Vector3[] array = vertexMoverUtil.MoveVertices(clothInstance, item, clothInstance.isLeftLegVertex[item], clothInstance.isRightLegVertex[item], fittingRadius, config.sigma, clothInstance.deltas[item]); + if (array == null) + { + stopwatch3.Stop(); + num += (float)stopwatch3.ElapsedMilliseconds; + continue; + } + for (int i = 0; i < clothInstance.worldVertices.Length && i < array.Length; i++) + { + if (array[i] != Vector3.zero) + { + clothInstance.deltasLocal[i] += array[i]; + } + } + if (AutoMorpherDev.isDeveloperMode) + { + stopwatch3.Stop(); + num += (float)stopwatch3.ElapsedMilliseconds; + } + Stopwatch stopwatch4 = (AutoMorpherDev.isDeveloperMode ? Stopwatch.StartNew() : null); + foreach (ClothInstance clothInstance2 in clothInstances) + { + if (clothInstance2 == clothInstance) + { + continue; + } + Vector3[] array2 = vertexMoverUtil.MoveVertices(clothInstance2, clothInstance.worldVertices[item], clothInstance.isLeftLegVertex[item], clothInstance.isRightLegVertex[item], fittingRadius, config.sigma, clothInstance.deltas[item]); + if (array2 == null) + { + continue; + } + for (int j = 0; j < clothInstance2.worldVertices.Length && j < array2.Length; j++) + { + if (array2[j] != Vector3.zero) + { + clothInstance2.deltasLocal[j] += array2[j]; + } + } + } + if (AutoMorpherDev.isDeveloperMode) + { + stopwatch4.Stop(); + num2 += (float)stopwatch4.ElapsedMilliseconds; + } + } + SmoothAllClothesDeltasByDistance(clothInstances, fittingRadius, config.smoothingIteration, config.sigma); + foreach (ClothInstance clothInstance3 in clothInstances) + { + for (int k = 0; k < clothInstance3.worldVertices.Length; k++) + { + if (!clothInstance3.excludedVertices[k]) + { + clothInstance3.worldVertices[k] += clothInstance3.deltasLocal[k]; + } + clothInstance3.deltasLocal[k] = Vector3.zero; + } + foreach (List equivalentVertex in clothInstance3.equivalentVertices) + { + Vector3 zero = Vector3.zero; + for (int l = 0; l < equivalentVertex.Count; l++) + { + zero += clothInstance3.worldVertices[equivalentVertex[l]]; + } + zero /= (float)equivalentVertex.Count; + for (int m = 0; m < equivalentVertex.Count; m++) + { + clothInstance3.worldVertices[equivalentVertex[m]] = zero; + } + } + } + if (AutoMorpherDev.isDeveloperMode) + { + stopwatch2.Stop(); + UnityEngine.Debug.Log($"[Shrink] Renderer Move Vector Calculate: {clothInstance.smr.gameObject.name} : {num}"); + UnityEngine.Debug.Log($"[Shrink] Other Renderer Move Vector Calculate: {clothInstance.smr.gameObject.name} : {num2}"); + UnityEngine.Debug.Log($"[Shrink] Total Move Vector Calculate: {clothInstance.smr.gameObject.name} : {stopwatch2.ElapsedMilliseconds}"); + stopwatch.Stop(); + UnityEngine.Debug.Log($"[Shrink] TOTAL ShrinkClothes time: {stopwatch.ElapsedMilliseconds} ms"); + UnityEngine.Debug.Log("[Shrink] End " + clothInstance.smr.gameObject.name + "//==============================================================================================================="); + } + } + + public void ExpandFitClothes_World(ClothInstance clothInstance, List clothInstances, MeshMatcher meshMatcher, float worldRadius, float sigma, float minMargin) + { + UnityEngine.Debug.Log("[Expand] " + clothInstance.smr.gameObject.name + "//==============================================================================================================="); + Stopwatch stopwatch = Stopwatch.StartNew(); + Stopwatch stopwatch2 = Stopwatch.StartNew(); + clothInstance.deltas = meshMatcher.ExpandVertexMatch(clothInstance, minMargin); + stopwatch2.Stop(); + UnityEngine.Debug.Log($"[Expand] BVH Mesh Matching time: {stopwatch2.ElapsedMilliseconds} ms"); + for (int i = 0; i < clothInstance.worldVertices.Length; i++) + { + clothInstance.worldVertices[i] += clothInstance.deltas[i]; + clothInstance.deltasLocal[i] = Vector3.zero; + } + foreach (List equivalentVertex in clothInstance.equivalentVertices) + { + Vector3 zero = Vector3.zero; + for (int j = 0; j < equivalentVertex.Count; j++) + { + zero += clothInstance.worldVertices[equivalentVertex[j]]; + } + zero /= (float)equivalentVertex.Count; + for (int k = 0; k < equivalentVertex.Count; k++) + { + clothInstance.worldVertices[equivalentVertex[k]] = zero; + } + } + stopwatch.Stop(); + UnityEngine.Debug.Log($"[Expand] TOTAL ShrinkClothes time: {stopwatch.ElapsedMilliseconds} ms"); + UnityEngine.Debug.Log("[Expand] " + clothInstance.smr.gameObject.name + "//==============================================================================================================="); + } + + private List SelectAnchors(ClothInstance clothInstance, float worldRadius, float minDeltaSq = 1E-05f, float maxDelta = 0.1f) + { + List list = new List(); + List list2 = new List(); + int num = clothInstance.worldVertices.Length; + for (int i = 0; i < num; i++) + { + if (!clothInstance.isInsideVertex[i] && clothInstance.deltas[i].sqrMagnitude > minDeltaSq) + { + list2.Add(i); + } + } + if (list2.Count == 0) + { + return list; + } + list2.Sort(delegate(int a, int b) + { + float sqrMagnitude = clothInstance.deltas[a].sqrMagnitude; + float num9 = clothInstance.deltas[b].sqrMagnitude - sqrMagnitude; + if (Mathf.Abs(num9) > 1E-06f) + { + if (!(num9 > 0f)) + { + return -1; + } + return 1; + } + float num10 = clothInstance.minDistance[a]; + float value3 = clothInstance.minDistance[b]; + return num10.CompareTo(value3); + }); + float num2 = worldRadius * 2f; + float num3 = num2 * num2; + float cellSize = num2; + Dictionary> dictionary = new Dictionary>(); + foreach (int item in list2) + { + Vector3 p = clothInstance.worldVertices[item]; + Vector3Int key = GetCellIndex(p); + bool flag = false; + for (int num4 = -1; num4 <= 1; num4++) + { + if (flag) + { + break; + } + for (int num5 = -1; num5 <= 1; num5++) + { + if (flag) + { + break; + } + for (int num6 = -1; num6 <= 1; num6++) + { + if (flag) + { + break; + } + Vector3Int key2 = new Vector3Int(key.x + num4, key.y + num5, key.z + num6); + if (!dictionary.TryGetValue(key2, out var value)) + { + continue; + } + for (int num7 = 0; num7 < value.Count; num7++) + { + int num8 = value[num7]; + if ((clothInstance.worldVertices[item] - clothInstance.worldVertices[num8]).sqrMagnitude < num3) + { + flag = true; + break; + } + } + } + } + } + if (!flag) + { + list.Add(item); + if (!dictionary.TryGetValue(key, out var value2)) + { + value2 = (dictionary[key] = new List()); + } + value2.Add(item); + } + } + return list; + Vector3Int GetCellIndex(Vector3 vector) + { + return new Vector3Int(Mathf.FloorToInt(vector.x / cellSize), Mathf.FloorToInt(vector.y / cellSize), Mathf.FloorToInt(vector.z / cellSize)); + } + } + + public void SmoothDeltasByDistance(Vector3[] positions, ref Vector3[] deltas, List[] adjacency, float radius, int maxDepth = 3, int iterations = 1, float smoothFactor = 0.5f, float gaussianSigma = -1f) + { + if (positions == null || deltas == null || adjacency == null || positions.Length != deltas.Length || positions.Length != adjacency.Length) + { + return; + } + int num = positions.Length; + if (num == 0) + { + return; + } + if (gaussianSigma <= 0f) + { + gaussianSigma = radius * 0.5f; + } + float num2 = radius * radius; + float num3 = 2f * gaussianSigma * gaussianSigma; + Vector3[] array = new Vector3[num]; + Queue queue = new Queue(); + List list = new List(32); + int[] array2 = new int[num]; + int num4 = 1; + for (int i = 0; i < iterations; i++) + { + for (int j = 0; j < num; j++) + { + if (deltas[j] == Vector3.zero) + { + array[j] = Vector3.zero; + continue; + } + list.Clear(); + queue.Clear(); + num4 = (array2[j] = num4 + 1); + queue.Enqueue(new Node + { + index = j, + depth = 0 + }); + Vector3 vector = positions[j]; + while (queue.Count > 0) + { + Node node = queue.Dequeue(); + int index = node.index; + int depth = node.depth; + Vector3 vector2 = positions[index]; + float sqrMagnitude = (vector2 - vector).sqrMagnitude; + if (index != j && sqrMagnitude <= num2) + { + list.Add(index); + } + if (depth >= maxDepth) + { + continue; + } + List list2 = adjacency[index]; + if (list2 == null) + { + continue; + } + for (int k = 0; k < list2.Count; k++) + { + int num5 = list2[k]; + if (array2[num5] != num4 && !((positions[num5] - vector2).sqrMagnitude > num2)) + { + array2[num5] = num4; + queue.Enqueue(new Node + { + index = num5, + depth = depth + 1 + }); + } + } + } + if (list.Count == 0) + { + array[j] = deltas[j]; + continue; + } + Vector3 zero = Vector3.zero; + float num6 = 0f; + for (int l = 0; l < list.Count; l++) + { + int num7 = list[l]; + float sqrMagnitude2 = (positions[num7] - vector).sqrMagnitude; + if (!(sqrMagnitude2 > num2)) + { + float num8 = Mathf.Exp((0f - sqrMagnitude2) / num3); + zero += deltas[num7] * num8; + num6 += num8; + } + } + if (num6 > 1E-08f) + { + Vector3 b = zero / num6; + array[j] = Vector3.Lerp(deltas[j], b, smoothFactor); + } + else + { + array[j] = deltas[j]; + } + } + for (int m = 0; m < num; m++) + { + deltas[m] = array[m]; + } + } + } + + public void SmoothAllClothesDeltasByDistance(List clothesInstances, float radius, int iterations = 1, float gaussianSigma = -1f, int maxDepth = 3, float smoothFactor = 0.5f) + { + if (clothesInstances == null || clothesInstances.Count == 0) + { + return; + } + int num = 0; + for (int i = 0; i < clothesInstances.Count; i++) + { + ClothInstance clothInstance = clothesInstances[i]; + if (clothInstance != null && clothInstance.worldVertices != null) + { + num += clothInstance.worldVertices.Length; + } + } + if (num == 0) + { + return; + } + Vector3[] array = new Vector3[num]; + Vector3[] deltas = new Vector3[num]; + List[] array2 = new List[num]; + int[] array3 = new int[clothesInstances.Count]; + int num2 = 0; + for (int j = 0; j < clothesInstances.Count; j++) + { + ClothInstance clothInstance2 = clothesInstances[j]; + if (clothInstance2 == null) + { + continue; + } + array3[j] = num2; + Vector3[] worldVertices = clothInstance2.worldVertices; + Vector3[] deltasLocal = clothInstance2.deltasLocal; + List[] vertexAdjacency = clothInstance2.vertexAdjacency; + int num3 = worldVertices.Length; + for (int k = 0; k < num3; k++) + { + array[num2 + k] = worldVertices[k]; + deltas[num2 + k] = deltasLocal[k]; + array2[num2 + k] = new List(); + } + for (int l = 0; l < num3; l++) + { + List list = vertexAdjacency[l]; + if (list != null) + { + int num4 = num2 + l; + List list2 = array2[num4]; + for (int m = 0; m < list.Count; m++) + { + int num5 = list[m]; + int item = num2 + num5; + list2.Add(item); + } + } + } + num2 += num3; + } + SmoothDeltasByDistance(array, ref deltas, array2, radius, maxDepth, iterations, smoothFactor, gaussianSigma); + for (int n = 0; n < clothesInstances.Count; n++) + { + ClothInstance clothInstance3 = clothesInstances[n]; + if (clothInstance3 != null) + { + int num6 = array3[n]; + int num7 = clothInstance3.deltasLocal.Length; + for (int num8 = 0; num8 < num7; num8++) + { + clothInstance3.deltasLocal[num8] = deltas[num6 + num8]; + } + } + } + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexFittingUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexFittingUtil.cs.meta new file mode 100644 index 0000000..d28b3bb --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexFittingUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1b54ca9533d091a428ae9f61c754901c \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexMoverUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexMoverUtil.cs new file mode 100644 index 0000000..60d41eb --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexMoverUtil.cs @@ -0,0 +1,173 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.VertexMoverUtil +using Eden.AutoMorpher; +using UnityEngine; + +public class VertexMoverUtil +{ + public Vector3[] MoveVertices(ClothInstance cloth, Vector3 targetPoint, bool centerIsLeftLeg, bool centerIsRightLeg, float maxRadius, float param, Vector3 vertexMovement) + { + if (cloth == null || cloth.worldVertices == null) + { + throw new AutoMorpherException("Cloth or World Vertices are Missing", "[MoveVertices] MoveVertices\n - cloth is null or cloth.worldVertices is null"); + } + Vector3[] worldVertices = cloth.worldVertices; + int num = worldVertices.Length; + bool[] isInboundVerts; + float[] array = ComputeEuclideanDistances(worldVertices, targetPoint, maxRadius, out isInboundVerts); + if (array == null) + { + return null; + } + float[] array2 = ComputeKernelWeights(array, isInboundVerts, maxRadius, WeightKernel.Gaussian, param); + Vector3[] array3 = new Vector3[num]; + bool[] isLeftLegVertex = cloth.isLeftLegVertex; + bool[] isRightLegVertex = cloth.isRightLegVertex; + for (int i = 0; i < num; i++) + { + if (!isInboundVerts[i]) + { + array3[i] = Vector3.zero; + continue; + } + if (centerIsLeftLeg && IsRightLegVertex(isRightLegVertex, i)) + { + array3[i] = Vector3.zero; + continue; + } + if (centerIsRightLeg && IsLeftLegVertex(isLeftLegVertex, i)) + { + array3[i] = Vector3.zero; + continue; + } + float num2 = array2[i]; + array3[i] = vertexMovement * num2; + } + return array3; + } + + public Vector3[] MoveVertices(ClothInstance cloth, int targetVertexIdx, bool centerIsLeftLeg, bool centerIsRightLeg, float maxRadius, float param, Vector3 vertexMovement) + { + if (cloth == null || cloth.worldVertices == null) + { + throw new AutoMorpherException("Cloth or World Vertices are Missing", "[MoveVertices] MoveVertices\n - cloth is null or cloth.worldVertices is null"); + } + Vector3[] worldVertices = cloth.worldVertices; + int num = worldVertices.Length; + bool[] isInboundVerts; + float[] array = ComputeEuclideanDistances(worldVertices, targetVertexIdx, maxRadius, out isInboundVerts); + if (array == null) + { + return null; + } + float[] array2 = ComputeKernelWeights(array, isInboundVerts, maxRadius, WeightKernel.Gaussian, param); + Vector3[] array3 = new Vector3[num]; + bool[] isLeftLegVertex = cloth.isLeftLegVertex; + bool[] isRightLegVertex = cloth.isRightLegVertex; + for (int i = 0; i < num; i++) + { + if (!isInboundVerts[i]) + { + array3[i] = Vector3.zero; + continue; + } + if (centerIsLeftLeg && IsRightLegVertex(isRightLegVertex, i)) + { + array3[i] = Vector3.zero; + continue; + } + if (centerIsRightLeg && IsLeftLegVertex(isLeftLegVertex, i)) + { + array3[i] = Vector3.zero; + continue; + } + float num2 = array2[i]; + array3[i] = vertexMovement * num2; + } + return array3; + } + + private float[] ComputeEuclideanDistances(Vector3[] verts, int targetVertIdx, float maxRadius, out bool[] isInboundVerts) + { + return ComputeEuclideanDistances(verts, verts[targetVertIdx], maxRadius, out isInboundVerts); + } + + private float[] ComputeEuclideanDistances(Vector3[] verts, Vector3 targetVert, float maxRadius, out bool[] isInboundVerts) + { + int num = verts.Length; + float[] array = new float[num]; + isInboundVerts = new bool[num]; + bool flag = false; + for (int i = 0; i < num; i++) + { + array[i] = Vector3.Distance(verts[i], targetVert); + isInboundVerts[i] = array[i] <= maxRadius; + flag |= isInboundVerts[i]; + } + if (!flag) + { + return null; + } + return array; + } + + private float ComputeGaussianWeight(float distance, float alpah) + { + return Mathf.Exp((0f - distance * distance) / (2f * alpah * alpah)); + } + + private float ComputeLaplacianWeight(float distance, float beta) + { + return Mathf.Exp(0f - distance / beta); + } + + private float[] ComputeKernelWeights(float[] dist, bool[] isInboundVerts, float maxRadius, WeightKernel kernel, float param) + { + int num = dist.Length; + float[] array = new float[num]; + float num2 = Mathf.Max(1E-08f, param); + for (int i = 0; i < num; i++) + { + if (!isInboundVerts[i]) + { + array[i] = 0f; + continue; + } + switch (kernel) + { + case WeightKernel.Gaussian: + array[i] = ComputeGaussianWeight(dist[i], num2); + break; + case WeightKernel.Laplacian: + array[i] = ComputeLaplacianWeight(dist[i], num2); + break; + default: + array[i] = 0f; + break; + } + float num3 = 1f - dist[i] / maxRadius; + array[i] *= num3 * num3; + } + return array; + } + + private bool IsLeftLegVertex(bool[] leftMask, int index) + { + if (leftMask != null && index >= 0 && index < leftMask.Length) + { + return leftMask[index]; + } + return false; + } + + private bool IsRightLegVertex(bool[] rightMask, int index) + { + if (rightMask != null && index >= 0 && index < rightMask.Length) + { + return rightMask[index]; + } + return false; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexMoverUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexMoverUtil.cs.meta new file mode 100644 index 0000000..0a9e04c --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/VertexMoverUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 432823d8c328a744d9b31f59f2b2e418 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightKernel.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightKernel.cs new file mode 100644 index 0000000..f6abe4e --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightKernel.cs @@ -0,0 +1,9 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.WeightKernel +public enum WeightKernel +{ + Gaussian, + Laplacian +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightKernel.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightKernel.cs.meta new file mode 100644 index 0000000..6597da2 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightKernel.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ef9fd1d3011dd444e8020b45711c9d71 \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightTransferUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightTransferUtil.cs new file mode 100644 index 0000000..32ce3a7 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightTransferUtil.cs @@ -0,0 +1,6 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.WeightTransferUtil + +Decompilation was cancelled. diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightTransferUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightTransferUtil.cs.meta new file mode 100644 index 0000000..1d7fbd2 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WeightTransferUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7e945f3a1117baf4bbd34cb57786343a \ No newline at end of file diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WorldVertexUtil.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WorldVertexUtil.cs new file mode 100644 index 0000000..f2be74c --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WorldVertexUtil.cs @@ -0,0 +1,68 @@ +// Warning: Some assembly references could not be resolved automatically. This might lead to incorrect decompilation of some parts, +// for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies. +// EdenAutoMorpherScript, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// Eden.AutoMorpher.WorldVertexUtil +using Eden.AutoMorpher; +using UnityEngine; + +public class WorldVertexUtil +{ + public Vector3[] GetWorldVertices(SkinnedMeshRenderer smr) + { + Mesh bakedMesh = new Mesh(); + return GetWorldVertices(smr, ref bakedMesh); + } + + public Vector3[] GetWorldVertices(SkinnedMeshRenderer smr, ref Mesh bakedMesh) + { + if (smr == null) + { + throw new AutoMorpherException("SkinnedMeshRenderer is Missing", "[SkinnedMeshWorldUtil] GetWorldVertices\n - smr is null"); + } + if (bakedMesh == null) + { + bakedMesh = new Mesh(); + } + else + { + bakedMesh.Clear(); + } + smr.BakeMesh(bakedMesh); + Transform transform = smr.transform; + Vector3 lossyScale = transform.lossyScale; + Vector3 vector = new Vector3(1f / Mathf.Max(lossyScale.x, 1E-08f), 1f / Mathf.Max(lossyScale.y, 1E-08f), 1f / Mathf.Max(lossyScale.z, 1E-08f)); + Matrix4x4 matrix4x = transform.localToWorldMatrix * Matrix4x4.Scale(vector); + Vector3[] vertices = bakedMesh.vertices; + int num = vertices.Length; + Vector3[] array = new Vector3[num]; + for (int i = 0; i < num; i++) + { + array[i] = matrix4x.MultiplyPoint3x4(vertices[i]); + } + return array; + } + + public Vector3[] GetWorldVerticesWithBakedMesh(SkinnedMeshRenderer smr, Mesh bakedMesh) + { + if (smr == null) + { + throw new AutoMorpherException("SkinnedMeshRenderer is Missing", "[SkinnedMeshWorldUtil] GetWorldVerticesWithBakedMesh\n - smr is null"); + } + if (bakedMesh == null) + { + return null; + } + Transform transform = smr.transform; + Vector3 lossyScale = transform.lossyScale; + Vector3 vector = new Vector3(1f / Mathf.Max(lossyScale.x, 1E-08f), 1f / Mathf.Max(lossyScale.y, 1E-08f), 1f / Mathf.Max(lossyScale.z, 1E-08f)); + Matrix4x4 matrix4x = transform.localToWorldMatrix * Matrix4x4.Scale(vector); + Vector3[] vertices = bakedMesh.vertices; + int num = vertices.Length; + Vector3[] array = new Vector3[num]; + for (int i = 0; i < num; i++) + { + array[i] = matrix4x.MultiplyPoint3x4(vertices[i]); + } + return array; + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WorldVertexUtil.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WorldVertexUtil.cs.meta new file mode 100644 index 0000000..02ef85b --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/Script/WorldVertexUtil.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8615d10a36c958c4aa71fb09914a7630 \ No newline at end of file