// 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);
}
}
}