// Magica Cloth. // Copyright (c) MagicaSoft, 2020-2022. // https://magicasoft.jp using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace MagicaCloth { /// /// クロス用のエディタ拡張 /// public abstract class ClothEditor : Editor { /// /// ポイントセレクタークラス /// PointSelector pointSelector = new PointSelector(); /// /// 現在編集中の選択データ /// List selectorData = new List(); /// /// アクティブなエディタメッシュインターフェース /// IEditorMesh editorMesh; //========================================================================================= protected virtual void OnEnable() { pointSelector.EnableEdit(); } protected virtual void OnDisable() { pointSelector.DisableEdit(this); } /// /// 選択データの初期化 /// 配列はすでに頂点数分が確保されゼロクリアされています。 /// すでに選択データがある場合はここでselectorDataにデータをセットしてください。 /// /// protected virtual void OnResetSelector(List selectorData) { } /// /// 選択データの決定 /// /// protected virtual void OnFinishSelector(List selectorData) { } /// /// ポイント選択GUIの表示と制御 /// /// /// protected void DrawInspectorGUI(IEditorMesh editorMesh) { this.editorMesh = editorMesh; if (editorMesh == null) return; pointSelector.DrawInspectorGUI(this, StartEdit, EndEdit); } /// /// 共有選択データを初期化する /// protected void InitSelectorData() { // メッシュデータ List wposList; List wnorList; List wtanList; int meshVertexCount = editorMesh.GetEditorPositionNormalTangent(out wposList, out wnorList, out wtanList); // 選択データ初期化 selectorData.Clear(); for (int i = 0; i < meshVertexCount; i++) selectorData.Add(0); // Invalid // 基本設定 OnResetSelector(selectorData); // 共有データ作成 OnFinishSelector(selectorData); } //============================================================================================= /// /// 作成を実行できるか判定する /// /// protected abstract bool CheckCreate(); //============================================================================================= /// /// ポイント選択開始 /// /// void StartEdit(PointSelector pointSelector) { // 毎回初期化する // 各ポイントのタイプ情報を設定 pointSelector.AddPointType("Move Point", Color.green, SelectionData.Move); pointSelector.AddPointType("Fixed Point", Color.red, SelectionData.Fixed); pointSelector.AddPointType("Invalid Point", Color.gray, SelectionData.Invalid); // メッシュデータ List wposList; List wnorList; List wtanList; int meshVertexCount = editorMesh.GetEditorPositionNormalTangent(out wposList, out wnorList, out wtanList); // 選択データ初期化 selectorData.Clear(); for (int i = 0; i < meshVertexCount; i++) selectorData.Add(0); // Invalid OnResetSelector(selectorData); if (meshVertexCount == 0) return; // 各ポイントデータをセレクタークラスへ流し込む for (int i = 0; i < meshVertexCount; i++) { pointSelector.AddPoint(wposList[i], i, selectorData[i]); } } /// /// ポイント選択終了 /// /// void EndEdit(PointSelector pointSelector) { // 現在のポイント内容をデータに反映 var pointList = pointSelector.GetPointList(); foreach (var p in pointList) { selectorData[p.index] = p.value; } // 確定 OnFinishSelector(selectorData); } /// /// 新規選択クラスを作成して返す /// /// /// /// protected SelectionData CreateSelection(MonoBehaviour obj, string property) { string dataname = "SelectionData_" + obj.name; var selection = ShareDataObject.CreateShareData(dataname); return selection; } /// /// 選択クラスをプロパティに保存する /// /// /// /// protected void ApplySelection(MonoBehaviour obj, string property, SelectionData selectionData) { var so = new SerializedObject(obj); var sel = so.FindProperty(property); sel.objectReferenceValue = selectionData; so.ApplyModifiedProperties(); } //========================================================================================= /// /// チーム項目インスペクタ /// protected void TeamBasicInspector() { BaseCloth scr = target as BaseCloth; EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.PropertyField(serializedObject.FindProperty("updateMode")); EditorGUILayout.Slider(serializedObject.FindProperty("userBlendWeight"), 0.0f, 1.0f, "Blend Weight"); } protected void CullingInspector() { BaseCloth scr = target as BaseCloth; EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.LabelField("Culling", EditorStyles.boldLabel); EditorGUILayout.PropertyField(serializedObject.FindProperty("cullingMode")); if (scr is MagicaBoneCloth || scr is MagicaBoneSpring) { if (scr.CullingMode != PhysicsTeam.TeamCullingMode.Off) { // warning if (scr.GetCullRenderListCount() == 0) { EditorGUILayout.HelpBox("If you want to cull, you need to register the renderer that makes the display decision here.\nIf not registered, culling will not be performed.", MessageType.Warning); } //EditorGUILayout.PropertyField(serializedObject.FindProperty("cullRendererList")); EditorInspectorUtility.DrawObjectList( serializedObject.FindProperty("cullRendererList"), scr.gameObject, true, true, SearchBoneClothRenderer, "Auto Select" ); } } } /// /// BoneCloth/Springのボーンの対象となっているレンダラーを検索する /// /// private Renderer[] SearchBoneClothRenderer() { BaseCloth scr = target as BaseCloth; if (scr is MagicaBoneCloth || scr is MagicaBoneSpring) { var rendererList = new List(); var skinRendererSet = new HashSet(); // search all bone var boneSet = new HashSet(); var property = serializedObject.FindProperty("clothTarget.rootList"); for (int i = 0; i < property.arraySize; i++) { var boneRoot = property.GetArrayElementAtIndex(i).objectReferenceValue as Transform; if (boneRoot) { // all transform var tlist = boneRoot.GetComponentsInChildren(); if (tlist != null) { foreach (var t in tlist) boneSet.Add(t); } // mesh renderer var rlist = boneRoot.GetComponentsInChildren(); if (rlist != null) rendererList.AddRange(rlist); // skin renderer Transform root = boneRoot; while (root.parent) { if (root.GetComponent() != null || root.GetComponent() != null) break; root = root.parent; } var srlist = root.GetComponentsInChildren(); if (srlist != null) { foreach (var skin in srlist) skinRendererSet.Add(skin); } } } //foreach (var t in boneSet) // Debug.Log(t); // skinrenderer foreach (var skin in skinRendererSet) { //Debug.Log(skin); var useBoneList = MeshUtility.GetUseBoneTransformList(skin.bones, skin.sharedMesh); foreach (var bone in useBoneList) { if (boneSet.Contains(bone)) { rendererList.Add(skin); break; } } } return rendererList.ToArray(); } else return null; } /// /// コライダー設定インスペクタ /// protected void ColliderInspector() { PhysicsTeam scr = target as PhysicsTeam; EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.LabelField("Collider", EditorStyles.boldLabel); EditorGUILayout.PropertyField(serializedObject.FindProperty("teamData.mergeAvatarCollider")); EditorInspectorUtility.DrawObjectList( serializedObject.FindProperty("teamData.colliderList"), scr.gameObject, true, true, () => scr.gameObject.transform.root.GetComponentsInChildren() ); } /// /// スキニング設定インスペクタ /// protected void SkinningInspector() { PhysicsTeam scr = target as PhysicsTeam; var mode = serializedObject.FindProperty("skinningMode"); //var boneList = serializedObject.FindProperty("teamData.skinningBoneList"); EditorGUILayout.Space(); EditorGUILayout.LabelField("Skinning", EditorStyles.boldLabel); EditorGUILayout.PropertyField(mode, new GUIContent("Skinning Mode"), true); //if (scr.SkinningMode == PhysicsTeam.TeamSkinningMode.GenerateFromBones) //{ // var updateFixed = serializedObject.FindProperty("skinningUpdateFixed"); // EditorGUILayout.PropertyField(updateFixed, new GUIContent("Update Fixed"), true); //} //EditorGUILayout.PropertyField(boneList, new GUIContent("Skinning Bone List"), true); } /// /// 古いパラメータを最新アルゴリズム用にコンバートする /// protected void ConvertToLatestAlgorithmParameters() { BaseCloth cloth = target as BaseCloth; Debug.Log($"[{cloth.name}] Convert Parameters."); Undo.RecordObject(cloth, "Convert Parameters"); cloth.Params.ConvertToLatestAlgorithmParameter(); serializedObject.ApplyModifiedProperties(); EditorUtility.SetDirty(cloth); } } }