diff options
Diffstat (limited to 'Assets/ThirdParty/VRM/MeshUtility/Editor')
8 files changed, 868 insertions, 0 deletions
diff --git a/Assets/ThirdParty/VRM/MeshUtility/Editor/BoneMeshEraserWizard.cs b/Assets/ThirdParty/VRM/MeshUtility/Editor/BoneMeshEraserWizard.cs new file mode 100644 index 00000000..63bf9045 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Editor/BoneMeshEraserWizard.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; + + +namespace MeshUtility +{ + [CustomPropertyDrawer(typeof(BoneMeshEraser.EraseBone))] + public class EraseBoneDrawer : PropertyDrawer + { + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + //EditorGUI.BeginProperty(position, label, property); + + var leftWidth = 0.6f; + var rightWidth = 1.0f - leftWidth; + + var leftSide = new Rect(position.x, position.y, position.width * leftWidth, position.height); + var rightSide = new Rect(position.width * leftWidth, position.y, position.width * rightWidth, position.height); + { + EditorGUI.PropertyField(leftSide, property.FindPropertyRelative("Bone"), new GUIContent("", "")); + EditorGUI.PropertyField(rightSide, property.FindPropertyRelative("Erase")); + } + + //EditorGUI.EndProperty(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + var height = base.GetPropertyHeight(property, label); + return height; + } + } + + public class BoneMeshEraserWizard : ScriptableWizard + { + const string ASSET_SUFFIX = ".asset"; + + [SerializeField] + SkinnedMeshRenderer m_skinnedMesh; + + [SerializeField] + Animator m_animator; + + [SerializeField] + Transform EraseRoot; + + [SerializeField] + BoneMeshEraser.EraseBone[] m_eraseBones; + + [MenuItem(MeshUtility.MENU_PARENT + "BoneMeshEraser Wizard", priority = 4)] + static void CreateWizard() + { + ScriptableWizard.DisplayWizard<BoneMeshEraserWizard>("BoneMeshEraser", "Erase triangles by bone", "Erase"); + } + + private void OnEnable() + { + var root = Selection.activeGameObject; + if (root != null) + { + m_animator = root.GetComponent<Animator>(); + m_skinnedMesh = root.GetComponent<SkinnedMeshRenderer>(); + OnValidate(); + } + } + + void OnValidate() + { + //Debug.Log("OnValidate"); + if (m_skinnedMesh == null) + { + m_eraseBones = new BoneMeshEraser.EraseBone[] { }; + return; + } + + if (EraseRoot == null) + { + if (m_animator != null) + { + EraseRoot = m_animator.GetBoneTransform(HumanBodyBones.Head); + //Debug.LogFormat("head: {0}", EraseRoot); + } + } + + m_eraseBones = m_skinnedMesh.bones.Select(x => + { + var eb = new BoneMeshEraser.EraseBone + { + Bone = x, + }; + + if (EraseRoot != null) + { + // 首の子孫を消去 + if (eb.Bone.Ancestor().Any(y => y == EraseRoot)) + { + //Debug.LogFormat("erase {0}", x); + eb.Erase = true; + } + } + + return eb; + }) + .ToArray(); + } + + void OnWizardUpdate() + { + helpString = "select target skinnedMesh and animator"; + } + + + + static int IndexOf(Transform[] list, Transform target) + { + for (int i = 0; i < list.Length; ++i) + { + if (list[i] == target) + { + return i; + } + } + return -1; + } + + SkinnedMeshRenderer _Erase(GameObject go) + { + if (go == null) + { + Debug.LogWarning("select root object in hierarchy"); + return null; + } + if (m_skinnedMesh == null) + { + Debug.LogWarning("no skinnedmesh"); + return null; + } + + var bones = m_skinnedMesh.bones; + var eraseBones = m_eraseBones + .Where(x => x.Erase) + .Select(x => Array.IndexOf(bones, x.Bone)) + .ToArray(); + + var meshNode = new GameObject("BoneMeshEraser"); + meshNode.transform.SetParent(go.transform, false); + + var erased = meshNode.AddComponent<SkinnedMeshRenderer>(); + erased.sharedMesh = BoneMeshEraser.CreateErasedMesh(m_skinnedMesh.sharedMesh, eraseBones); + erased.sharedMaterials = m_skinnedMesh.sharedMaterials; + erased.bones = m_skinnedMesh.bones; + + return erased; + } + + void Erase() + { + var go = Selection.activeGameObject; + var renderer = _Erase(go); + if (renderer == null) + { + return; + } + + // save mesh to Assets + var assetPath = string.Format("{0}{1}", go.name, ASSET_SUFFIX); + var prefab = MeshUtility.GetPrefab(go); + if (prefab != null) + { + var prefabPath = AssetDatabase.GetAssetPath(prefab); + assetPath = string.Format("{0}/{1}{2}", + Path.GetDirectoryName(prefabPath), + Path.GetFileNameWithoutExtension(prefabPath), + ASSET_SUFFIX + ); + } + + Debug.LogFormat("CreateAsset: {0}", assetPath); + AssetDatabase.CreateAsset(renderer.sharedMesh, assetPath); + } + + void OnWizardCreate() + { + //Debug.Log("OnWizardCreate"); + Erase(); + + // close + } + + void OnWizardOtherButton() + { + //Debug.Log("OnWizardOtherButton"); + Erase(); + } + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Editor/BoneMeshEraserWizard.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Editor/BoneMeshEraserWizard.cs.meta new file mode 100644 index 00000000..ef9f3976 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Editor/BoneMeshEraserWizard.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 881b00db73f639c48a3f043a775fa61a +timeCreated: 1518503829 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Editor/HumanoidEditor.cs b/Assets/ThirdParty/VRM/MeshUtility/Editor/HumanoidEditor.cs new file mode 100644 index 00000000..f9a99cfc --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Editor/HumanoidEditor.cs @@ -0,0 +1,324 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using UnityEditor; +using UnityEngine; +using System; + +namespace MeshUtility +{ + [CustomEditor(typeof(Humanoid))] + public class HumanoidEditor : Editor + { + const int LABEL_WIDTH = 100; + + Humanoid m_target; + + SerializedProperty m_Hips; + #region leg + SerializedProperty m_LeftUpperLeg; + SerializedProperty m_RightUpperLeg; + SerializedProperty m_LeftLowerLeg; + SerializedProperty m_RightLowerLeg; + SerializedProperty m_LeftFoot; + SerializedProperty m_RightFoot; + SerializedProperty m_LeftToes; + SerializedProperty m_RightToes; + + #endregion + + #region spine + SerializedProperty m_Spine; + SerializedProperty m_Chest; + SerializedProperty m_UpperChest; + SerializedProperty m_Neck; + SerializedProperty m_Head; + SerializedProperty m_LeftEye; + SerializedProperty m_RightEye; + SerializedProperty m_Jaw; + + #endregion + + #region arm + SerializedProperty m_LeftShoulder; + SerializedProperty m_RightShoulder; + SerializedProperty m_LeftUpperArm; + SerializedProperty m_RightUpperArm; + SerializedProperty m_LeftLowerArm; + SerializedProperty m_RightLowerArm; + SerializedProperty m_LeftHand; + SerializedProperty m_RightHand; + + #endregion + + #region fingers + SerializedProperty m_LeftThumbProximal; + SerializedProperty m_LeftThumbIntermediate; + SerializedProperty m_LeftThumbDistal; + SerializedProperty m_LeftIndexProximal; + SerializedProperty m_LeftIndexIntermediate; + SerializedProperty m_LeftIndexDistal; + SerializedProperty m_LeftMiddleProximal; + SerializedProperty m_LeftMiddleIntermediate; + SerializedProperty m_LeftMiddleDistal; + SerializedProperty m_LeftRingProximal; + SerializedProperty m_LeftRingIntermediate; + SerializedProperty m_LeftRingDistal; + SerializedProperty m_LeftLittleProximal; + SerializedProperty m_LeftLittleIntermediate; + SerializedProperty m_LeftLittleDistal; + SerializedProperty m_RightThumbProximal; + SerializedProperty m_RightThumbIntermediate; + SerializedProperty m_RightThumbDistal; + SerializedProperty m_RightIndexProximal; + SerializedProperty m_RightIndexIntermediate; + SerializedProperty m_RightIndexDistal; + SerializedProperty m_RightMiddleProximal; + SerializedProperty m_RightMiddleIntermediate; + SerializedProperty m_RightMiddleDistal; + SerializedProperty m_RightRingProximal; + SerializedProperty m_RightRingIntermediate; + SerializedProperty m_RightRingDistal; + SerializedProperty m_RightLittleProximal; + SerializedProperty m_RightLittleIntermediate; + SerializedProperty m_RightLittleDistal; + + #endregion + + void OnEnable() + { + m_target = target as Humanoid; + m_Hips = serializedObject.FindProperty($"m_{nameof(Humanoid.Hips)}"); + + #region legs + m_LeftUpperLeg = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftUpperLeg)}"); + m_RightUpperLeg = serializedObject.FindProperty($"m_{nameof(Humanoid.RightUpperLeg)}"); + m_LeftLowerLeg = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftLowerLeg)}"); + m_RightLowerLeg = serializedObject.FindProperty($"m_{nameof(Humanoid.RightLowerLeg)}"); + m_LeftFoot = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftFoot)}"); + m_RightFoot = serializedObject.FindProperty($"m_{nameof(Humanoid.RightFoot)}"); + m_LeftToes = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftToes)}"); + m_RightToes = serializedObject.FindProperty($"m_{nameof(Humanoid.RightToes)}"); + #endregion + + #region spine + m_Spine = serializedObject.FindProperty($"m_{nameof(Humanoid.Spine)}"); + m_Chest = serializedObject.FindProperty($"m_{nameof(Humanoid.Chest)}"); + m_UpperChest = serializedObject.FindProperty($"m_{nameof(Humanoid.UpperChest)}"); + m_Neck = serializedObject.FindProperty($"m_{nameof(Humanoid.Neck)}"); + m_Head = serializedObject.FindProperty($"m_{nameof(Humanoid.Head)}"); + m_LeftEye = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftEye)}"); + m_RightEye = serializedObject.FindProperty($"m_{nameof(Humanoid.RightEye)}"); + m_Jaw = serializedObject.FindProperty($"m_{nameof(Humanoid.Jaw)}"); + + #endregion + + #region arm + m_LeftShoulder = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftShoulder)}"); + m_RightShoulder = serializedObject.FindProperty($"m_{nameof(Humanoid.RightShoulder)}"); + m_LeftUpperArm = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftUpperArm)}"); + m_RightUpperArm = serializedObject.FindProperty($"m_{nameof(Humanoid.RightUpperArm)}"); + m_LeftLowerArm = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftLowerArm)}"); + m_RightLowerArm = serializedObject.FindProperty($"m_{nameof(Humanoid.RightLowerArm)}"); + m_LeftHand = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftHand)}"); + m_RightHand = serializedObject.FindProperty($"m_{nameof(Humanoid.RightHand)}"); + + #endregion + + #region fingers + m_LeftThumbProximal = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftThumbProximal)}"); + m_LeftThumbIntermediate = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftThumbIntermediate)}"); + m_LeftThumbDistal = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftThumbDistal)}"); + m_LeftIndexProximal = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftIndexProximal)}"); + m_LeftIndexIntermediate = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftIndexIntermediate)}"); + m_LeftIndexDistal = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftIndexDistal)}"); + m_LeftMiddleProximal = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftMiddleProximal)}"); + m_LeftMiddleIntermediate = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftMiddleIntermediate)}"); + m_LeftMiddleDistal = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftMiddleDistal)}"); + m_LeftRingProximal = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftRingProximal)}"); + m_LeftRingIntermediate = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftRingIntermediate)}"); + m_LeftRingDistal = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftRingDistal)}"); + m_LeftLittleProximal = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftLittleProximal)}"); + m_LeftLittleIntermediate = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftLittleIntermediate)}"); + m_LeftLittleDistal = serializedObject.FindProperty($"m_{nameof(Humanoid.LeftLittleDistal)}"); + m_RightThumbProximal = serializedObject.FindProperty($"m_{nameof(Humanoid.RightThumbProximal)}"); + m_RightThumbIntermediate = serializedObject.FindProperty($"m_{nameof(Humanoid.RightThumbIntermediate)}"); + m_RightThumbDistal = serializedObject.FindProperty($"m_{nameof(Humanoid.RightThumbDistal)}"); + m_RightIndexProximal = serializedObject.FindProperty($"m_{nameof(Humanoid.RightIndexProximal)}"); + m_RightIndexIntermediate = serializedObject.FindProperty($"m_{nameof(Humanoid.RightIndexIntermediate)}"); + m_RightIndexDistal = serializedObject.FindProperty($"m_{nameof(Humanoid.RightIndexDistal)}"); + m_RightMiddleProximal = serializedObject.FindProperty($"m_{nameof(Humanoid.RightMiddleProximal)}"); + m_RightMiddleIntermediate = serializedObject.FindProperty($"m_{nameof(Humanoid.RightMiddleIntermediate)}"); + m_RightMiddleDistal = serializedObject.FindProperty($"m_{nameof(Humanoid.RightMiddleDistal)}"); + m_RightRingProximal = serializedObject.FindProperty($"m_{nameof(Humanoid.RightRingProximal)}"); + m_RightRingIntermediate = serializedObject.FindProperty($"m_{nameof(Humanoid.RightRingIntermediate)}"); + m_RightRingDistal = serializedObject.FindProperty($"m_{nameof(Humanoid.RightRingDistal)}"); + m_RightLittleProximal = serializedObject.FindProperty($"m_{nameof(Humanoid.RightLittleProximal)}"); + m_RightLittleIntermediate = serializedObject.FindProperty($"m_{nameof(Humanoid.RightLittleIntermediate)}"); + m_RightLittleDistal = serializedObject.FindProperty($"m_{nameof(Humanoid.RightLittleDistal)}"); + #endregion + } + + struct Horizontal : IDisposable + { + public static Horizontal Using() + { + EditorGUILayout.BeginHorizontal(); + return default; + } + public void Dispose() + { + EditorGUILayout.EndHorizontal(); + } + } + + static void HorizontalFields(string label, params SerializedProperty[] props) + { + try + { + EditorGUILayout.BeginHorizontal(); + + GUILayout.Label(label, GUILayout.Width(LABEL_WIDTH)); + GUILayout.FlexibleSpace(); + + foreach (var prop in props) + { + EditorGUILayout.PropertyField(prop, GUIContent.none, true, GUILayout.MinWidth(100)); + } + } + finally + { + EditorGUILayout.EndHorizontal(); + } + } + + static bool s_spineFold; + static bool s_legFold; + static bool s_armFold; + static bool s_fingerFold; + static string GetDialogDir(UnityEngine.Object obj) + { + var prefab = PrefabUtility.GetCorrespondingObjectFromSource(obj); + if (prefab == null) + { + return null; + } + return UnityPath.FromAsset(prefab).FullPath; + } + + public override void OnInspectorGUI() + { + foreach (var validation in m_target.Validate()) + { + EditorGUILayout.HelpBox(validation.Message, validation.IsError ? MessageType.Error : MessageType.Warning); + } + + // prefer + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Hips); + + s_spineFold = EditorGUILayout.Foldout(s_spineFold, "Body"); + if (s_spineFold) + { + EditorGUILayout.PropertyField(m_Spine); + EditorGUILayout.PropertyField(m_Chest); + EditorGUILayout.PropertyField(m_UpperChest); + EditorGUILayout.PropertyField(m_Neck); + EditorGUILayout.PropertyField(m_Head); + EditorGUILayout.PropertyField(m_Jaw); + HorizontalFields("Eye", m_LeftEye, m_RightEye); + } + + s_legFold = EditorGUILayout.Foldout(s_legFold, "Leg"); + if (s_legFold) + { + HorizontalFields("UpperLeg", m_LeftUpperLeg, m_RightUpperLeg); + HorizontalFields("LowerLeg", m_LeftLowerLeg, m_RightLowerLeg); + HorizontalFields("Foot", m_LeftFoot, m_RightFoot); + HorizontalFields("Toes", m_LeftToes, m_RightToes); + } + + s_armFold = EditorGUILayout.Foldout(s_armFold, "Arm"); + if (s_armFold) + { + HorizontalFields("Shoulder", m_LeftShoulder, m_RightShoulder); + HorizontalFields("UpperArm", m_LeftUpperArm, m_RightUpperArm); + HorizontalFields("LowerArm", m_LeftLowerArm, m_RightLowerArm); + HorizontalFields("Hand", m_LeftHand, m_RightHand); + } + + s_fingerFold = EditorGUILayout.Foldout(s_fingerFold, "Finger"); + if (s_fingerFold) + { + HorizontalFields("LeftThumb", m_LeftThumbProximal, m_LeftThumbIntermediate, m_LeftThumbDistal); + HorizontalFields("LeftIndex", m_LeftIndexProximal, m_LeftIndexIntermediate, m_LeftIndexDistal); + HorizontalFields("LeftMiddle", m_LeftMiddleProximal, m_LeftMiddleIntermediate, m_LeftMiddleDistal); + HorizontalFields("LeftRing", m_LeftRingProximal, m_LeftRingIntermediate, m_LeftRingDistal); + HorizontalFields("LeftLittle", m_LeftLittleProximal, m_LeftLittleIntermediate, m_LeftLittleDistal); + HorizontalFields("RightThumb", m_RightThumbProximal, m_RightThumbIntermediate, m_RightThumbDistal); + HorizontalFields("RightIndex", m_RightIndexProximal, m_RightIndexIntermediate, m_RightIndexDistal); + HorizontalFields("RightMiddle", m_RightMiddleProximal, m_RightMiddleIntermediate, m_RightMiddleDistal); + HorizontalFields("RightRing", m_RightRingProximal, m_RightRingIntermediate, m_RightRingDistal); + HorizontalFields("RightLittle", m_RightLittleProximal, m_RightLittleIntermediate, m_RightLittleDistal); + } + + serializedObject.ApplyModifiedProperties(); + + // create avatar + if (GUILayout.Button("Create UnityEngine.Avatar")) + { + var path = EditorUtility.SaveFilePanel( + "Save avatar", + GetDialogDir(m_target), + string.Format("{0}.avatar.asset", serializedObject.targetObject.name), + "asset"); + if (!string.IsNullOrEmpty(path)) + { + var avatar = m_target.CreateAvatar(); + if (avatar != null) + { + var unityPath = UnityPath.FromFullpath(path); + avatar.name = "avatar"; + Debug.LogFormat("Create avatar {0}", unityPath); + AssetDatabase.CreateAsset(avatar, unityPath.Value); + AssetDatabase.ImportAsset(unityPath.Value); + + // replace + var animator = m_target.GetComponent<Animator>(); + if (animator == null) + { + animator = m_target.gameObject.AddComponent<Animator>(); + } + animator.avatar = avatar; + + Selection.activeObject = avatar; + } + } + } + } + + void OnSceneGUI() + { + // var bones = m_target.Bones; + // if (bones != null) + // { + // for (int i = 0; i < bones.Length; ++i) + // { + // DrawBone((HumanBodyBones)i, bones[i]); + // } + // foreach (var x in m_bones) + // { + // x.Draw(); + // } + // } + + var forward = m_target.GetForward(); + + var begin = m_target.transform.position; + var end = begin + forward; + Handles.DrawLine(begin, end); + } + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Editor/HumanoidEditor.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Editor/HumanoidEditor.cs.meta new file mode 100644 index 00000000..dffaaa18 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Editor/HumanoidEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57cc7b16eb4146c4ab5631f538e2489f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.Editor.asmdef b/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.Editor.asmdef new file mode 100644 index 00000000..9854cd8f --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.Editor.asmdef @@ -0,0 +1,16 @@ +{ + "name": "MeshUtility.Editor", + "references": [ + "MeshUtility" + ], + "optionalUnityReferences": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +}
\ No newline at end of file diff --git a/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.Editor.asmdef.meta b/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.Editor.asmdef.meta new file mode 100644 index 00000000..ad84160c --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: eedb868807606df4db953419ab9e0780 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs b/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs new file mode 100644 index 00000000..045879c6 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs @@ -0,0 +1,286 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; +using UnityEditor; + +namespace MeshUtility +{ + public class MeshUtility + { + public const string MENU_PARENT = "Mesh Utility/"; + public const int MENU_PRIORITY = 11; + + private const string ASSET_SUFFIX = ".mesh.asset"; + private const string MENU_NAME = MENU_PARENT + "MeshSeparator"; + private static readonly Vector3 ZERO_MOVEMENT = Vector3.zero; + + public static Object GetPrefab(GameObject instance) + { +#if UNITY_2018_2_OR_NEWER + return PrefabUtility.GetCorrespondingObjectFromSource(instance); +#else + return PrefabUtility.GetPrefabParent(go); +#endif + } + + private enum BlendShapeLogic + { + WithBlendShape, + WithoutBlendShape, + } + + [MenuItem(MENU_NAME, validate = true)] + private static bool ShowLogValidation() + { + if (Selection.activeTransform == null) + return false; + else + return true; + } + + [MenuItem(MENU_NAME, priority = 2)] + public static void SeparateSkinnedMeshContainedBlendShape() + { + var go = Selection.activeTransform.gameObject; + + if (go.GetComponentsInChildren<SkinnedMeshRenderer>().Length > 0) + { + SeparationProcessing(go); + go.SetActive(false); + } + else + { + EditorUtility.DisplayDialog("Error", "No skinnedMeshRenderer contained", "ok"); + } + } + + [MenuItem("Mesh Utility/MeshSeparator Docs", priority = MeshUtility.MENU_PRIORITY)] + public static void LinkToMeshSeparatorDocs() + { + Application.OpenURL("https://github.com/vrm-c/UniVRM/tree/master/Assets/MeshUtility"); + } + + private static void SeparationProcessing(GameObject go) + { + var outputObject = GameObject.Instantiate(go); + var skinnedMeshRenderers = outputObject.GetComponentsInChildren<SkinnedMeshRenderer>(); + foreach (var skinnedMeshRenderer in skinnedMeshRenderers) + { + if (skinnedMeshRenderer.sharedMesh.blendShapeCount > 0) + { + SeparatePolyWithBlendShape(skinnedMeshRenderer); + } + } + } + + private static void SeparatePolyWithBlendShape(SkinnedMeshRenderer skinnedMeshRendererInput) + { + var indicesUsedByBlendShape = new Dictionary<int, int>(); + var mesh = skinnedMeshRendererInput.sharedMesh; + + // retrieve the original BlendShape data + for (int i = 0; i < mesh.blendShapeCount; ++i) + { + var deltaVertices = new Vector3[mesh.vertexCount]; + var deltaNormals = new Vector3[mesh.vertexCount]; + var deltaTangents = new Vector3[mesh.vertexCount]; + mesh.GetBlendShapeFrameVertices(i, 0, deltaVertices, deltaNormals, deltaTangents); + + for (int j = 0; j < deltaVertices.Length; j++) + { + if (!deltaVertices[j].Equals(ZERO_MOVEMENT)) + { + if (!indicesUsedByBlendShape.Values.Contains(j)) + { + indicesUsedByBlendShape.Add(indicesUsedByBlendShape.Count, j); + } + } + } + } + + var subMeshCount = mesh.subMeshCount; + var submeshesWithBlendShape = new Dictionary<int, int[]>(); + var submeshesWithoutBlendShape = new Dictionary<int, int[]>(); + var vertexIndexWithBlendShape = new Dictionary<int, int>(); + var vertexCounterWithBlendShape = 0; + var vertexIndexWithoutBlendShape = new Dictionary<int, int>(); + var vertexCounterWithoutBlendShape = 0; + + // check blendshape's vertex index from submesh + for (int i = 0; i < subMeshCount; i++) + { + var triangle = mesh.GetTriangles(i); + var submeshWithBlendShape = new List<int>(); + var submeshWithoutBlendShape = new List<int>(); + + for (int j = 0; j < triangle.Length; j += 3) + { + if (indicesUsedByBlendShape.Values.Contains(triangle[j]) || + indicesUsedByBlendShape.Values.Contains(triangle[j + 1]) || + indicesUsedByBlendShape.Values.Contains(triangle[j + 2])) + { + BuildNewTriangleList(vertexIndexWithBlendShape, triangle, j, submeshWithBlendShape, ref vertexCounterWithBlendShape); + } + else + { + BuildNewTriangleList(vertexIndexWithoutBlendShape, triangle, j, submeshWithoutBlendShape, ref vertexCounterWithoutBlendShape); + } + } + if (submeshWithBlendShape.Count > 0) + submeshesWithBlendShape.Add(i, submeshWithBlendShape.ToArray()); + if (submeshWithoutBlendShape.Count > 0) + submeshesWithoutBlendShape.Add(i, submeshWithoutBlendShape.ToArray()); ; + } + + // check if any BlendShape exists + if (submeshesWithoutBlendShape.Count > 0) + { + // put the mesh without BlendShape in a new SkinnedMeshRenderer + var srcGameObject = skinnedMeshRendererInput.gameObject; + var srcTransform = skinnedMeshRendererInput.transform.parent; + var targetObjectForMeshWithoutBS = GameObject.Instantiate(srcGameObject); + targetObjectForMeshWithoutBS.name = srcGameObject.name + "_WithoutBlendShape"; + targetObjectForMeshWithoutBS.transform.SetParent(srcTransform); + var skinnedMeshRendererWithoutBS = targetObjectForMeshWithoutBS.GetComponent<SkinnedMeshRenderer>(); + + // build meshes with/without BlendShape + BuildNewMesh(skinnedMeshRendererInput, vertexIndexWithBlendShape, submeshesWithBlendShape, BlendShapeLogic.WithBlendShape); + BuildNewMesh(skinnedMeshRendererWithoutBS, vertexIndexWithoutBlendShape, submeshesWithoutBlendShape, BlendShapeLogic.WithoutBlendShape); + } + } + + private static void BuildNewTriangleList(Dictionary<int, int> newVerticesListLookUp, int[] triangleList, int index, + List<int> newTriangleList, ref int vertexCounter) + { + // build new vertex list and triangle list + // vertex 1 + if (!newVerticesListLookUp.Keys.Contains(triangleList[index])) + { + newVerticesListLookUp.Add(triangleList[index], vertexCounter); + newTriangleList.Add(vertexCounter); + vertexCounter++; + } + else + { + var newVertexIndex = newVerticesListLookUp[triangleList[index]]; + newTriangleList.Add(newVertexIndex); + } + // vertex 2 + if (!newVerticesListLookUp.Keys.Contains(triangleList[index + 1])) + { + newVerticesListLookUp.Add(triangleList[index + 1], vertexCounter); + newTriangleList.Add(vertexCounter); + vertexCounter++; + } + else + { + var newVertexIndex = newVerticesListLookUp[triangleList[index + 1]]; + newTriangleList.Add(newVertexIndex); + } + // vertex 3 + if (!newVerticesListLookUp.Keys.Contains(triangleList[index + 2])) + { + newVerticesListLookUp.Add(triangleList[index + 2], vertexCounter); + newTriangleList.Add(vertexCounter); + vertexCounter++; + } + else + { + var newVertexIndex = newVerticesListLookUp[triangleList[index + 2]]; + newTriangleList.Add(newVertexIndex); + } + } + + private static void BuildNewMesh(SkinnedMeshRenderer skinnedMeshRenderer, Dictionary<int, int> newIndexLookUpDict, + Dictionary<int, int[]> subMeshes, BlendShapeLogic blendShapeLabel) + { + // get original mesh data + var materialList = new List<Material>(); + skinnedMeshRenderer.GetSharedMaterials(materialList); + var mesh = skinnedMeshRenderer.sharedMesh; + var meshVertices = mesh.vertices; + var meshNormals = mesh.normals; + var meshTangents = mesh.tangents; + var meshColors = mesh.colors; + var meshBoneWeights = mesh.boneWeights; + var meshUVs = mesh.uv; + + // build new mesh + var materialListNew = new List<Material>(); + var newMesh = new Mesh(); + + if (mesh.vertexCount > ushort.MaxValue) + { +#if UNITY_2017_3_OR_NEWER + Debug.LogFormat("exceed 65535 vertices: {0}", mesh.vertexCount); + newMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; +#else + throw new NotImplementedException(String.Format("exceed 65535 vertices: {0}", integrator.Positions.Count.ToString())); +#endif + } + + var newDataLength = newIndexLookUpDict.Count; + var newIndexLookUp = newIndexLookUpDict.Keys.ToArray(); + + newMesh.vertices = newIndexLookUp.Select(x => meshVertices[x]).ToArray(); + if (meshNormals.Length > 0) newMesh.normals = newIndexLookUp.Select(x => meshNormals[x]).ToArray(); + if (meshTangents.Length > 0) newMesh.tangents = newIndexLookUp.Select(x => meshTangents[x]).ToArray(); + if (meshColors.Length > 0) newMesh.colors = newIndexLookUp.Select(x => meshColors[x]).ToArray(); + if (meshBoneWeights.Length > 0) newMesh.boneWeights = newIndexLookUp.Select(x => meshBoneWeights[x]).ToArray(); + if (meshUVs.Length > 0) newMesh.uv = newIndexLookUp.Select(x => meshUVs[x]).ToArray(); + newMesh.bindposes = mesh.bindposes; + + // add BlendShape data + if (blendShapeLabel == BlendShapeLogic.WithBlendShape) + { + for (int i = 0; i < mesh.blendShapeCount; i++) + { + // get original BlendShape data + var srcVertices = new Vector3[mesh.vertexCount]; + var srcNormals = new Vector3[mesh.vertexCount]; + var srcTangents = new Vector3[mesh.vertexCount]; + mesh.GetBlendShapeFrameVertices(i, 0, srcVertices, srcNormals, srcTangents); + + // declare the size for the destination array + var dstVertices = new Vector3[newDataLength]; + var dstNormals = new Vector3[newDataLength]; + var dstTangents = new Vector3[newDataLength]; + + dstVertices = newIndexLookUp.Select(x => srcVertices[x]).ToArray(); + dstNormals = newIndexLookUp.Select(x => srcNormals[x]).ToArray(); + dstTangents = newIndexLookUp.Select(x => srcTangents[x]).ToArray(); + newMesh.AddBlendShapeFrame(mesh.GetBlendShapeName(i), mesh.GetBlendShapeFrameWeight(i, 0), + dstVertices, dstNormals, dstTangents); + } + } + + newMesh.subMeshCount = subMeshes.Count; + var cosMaterialIndex = subMeshes.Keys.ToArray(); + + // build material list + for (int i = 0; i < subMeshes.Count; i++) + { + newMesh.SetTriangles(subMeshes[cosMaterialIndex[i]], i); + materialListNew.Add(materialList[cosMaterialIndex[i]]); + } + skinnedMeshRenderer.sharedMaterials = materialListNew.ToArray(); + skinnedMeshRenderer.sharedMesh = newMesh; + + // save mesh as asset + var assetPath = string.Format("{0}{1}", Path.GetFileNameWithoutExtension(mesh.name), ASSET_SUFFIX); + Debug.Log(assetPath); + if (!string.IsNullOrEmpty((AssetDatabase.GetAssetPath(mesh)))) + { + var directory = Path.GetDirectoryName(AssetDatabase.GetAssetPath(mesh)).Replace("\\", "/"); + assetPath = string.Format("{0}/{1}{2}", directory, Path.GetFileNameWithoutExtension(mesh.name) + "_" + blendShapeLabel.ToString(), ASSET_SUFFIX); + } + else + { + assetPath = string.Format("Assets/{0}{1}", Path.GetFileNameWithoutExtension(mesh.name) + "_" + blendShapeLabel.ToString(), ASSET_SUFFIX); + } + Debug.LogFormat("CreateAsset: {0}", assetPath); + AssetDatabase.CreateAsset(newMesh, assetPath); + } + } +}
\ No newline at end of file diff --git a/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs.meta new file mode 100644 index 00000000..2fb0aadd --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c1bbe850b95e44740bbbb44064e17d25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: |