This commit is contained in:
2026-06-24 17:43:37 +09:00
parent 24f5f729e4
commit 122d59b0f5
2297 changed files with 517608 additions and 605 deletions

View File

@@ -0,0 +1,41 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEditor;
namespace MagicaCloth2
{
/// <summary>
/// インポートアセットの判定
/// </summary>
public class ClothAssetPostprocessor : AssetPostprocessor
{
/// <summary>
/// データベースにアセットが追加/削除/移動/更新された場合にその完了後に一度呼び出される
/// </summary>
/// <param name="importedAssets"></param>
/// <param name="deletedAssets"></param>
/// <param name="movedAssets"></param>
/// <param name="movedFromAssetPaths"></param>
protected static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
#if false
foreach (string str in importedAssets)
{
Debug.Log("@Reimported Asset: " + str);
}
foreach (string str in deletedAssets)
{
Debug.Log("@Deleted Asset: " + str);
}
for (int i = 0; i < movedAssets.Length; i++)
{
Debug.Log("@Moved Asset: " + movedAssets[i] + " from: " + movedFromAssetPaths[i]);
}
#endif
// アセットがインポートされたことによる編集用メッシュの更新判定
ClothEditorManager.UpdateFromAssetImport(importedAssets);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 62851d07720bcd84f89c596c9a32c8a6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothAssetPostprocessor.cs
uploadId: 893596

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 905d98a228d9c6c4eb4d5e240f30378a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothEditorManager.cs
uploadId: 893596

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f11b17a94f5361246b6c56a0fb52570e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothEditorUtility.cs
uploadId: 893596

View File

@@ -0,0 +1,76 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEditor;
using UnityEngine;
namespace MagicaCloth2
{
public static class ClothInspectorUtility
{
//===============================================================================
/// <summary>
/// 折りたたみ制御
/// </summary>
/// <param name="foldKey">折りたたみ保存キー</param>
/// <param name="title"></param>
/// <param name="drawAct">内容描画アクション</param>
/// <param name="enableAct">有効フラグアクション(null=無効)</param>
/// <param name="enable">現在の有効フラグ</param>
public static void Foldout(
string foldKey,
string title = null,
System.Action drawAct = null,
System.Action<bool> enableAct = null,
bool enable = false,
bool warning = false
)
{
var style = new GUIStyle("ShurikenModuleTitle");
style.font = new GUIStyle(EditorStyles.label).font;
style.border = new RectOffset(15, 7, 4, 4);
style.fixedHeight = 22;
style.contentOffset = new Vector2(20f, -2f);
var rect = GUILayoutUtility.GetRect(16f, 22f, style);
GUI.backgroundColor = warning ? Color.yellow : Color.white;
GUI.Box(rect, title, style);
GUI.backgroundColor = Color.white;
var e = Event.current;
bool foldOut = EditorPrefs.GetBool(foldKey);
if (enableAct == null)
{
if (e.type == EventType.Repaint)
{
var toggleRect = new Rect(rect.x + 4f, rect.y + 2f, 13f, 13f);
EditorStyles.foldout.Draw(toggleRect, false, false, foldOut, false);
}
}
else
{
// 有効チェック
var toggleRect = new Rect(rect.x + 4f, rect.y + 4f, 13f, 13f);
bool sw = GUI.Toggle(toggleRect, enable, string.Empty, new GUIStyle("ShurikenCheckMark"));
if (sw != enable)
{
enableAct(sw);
}
}
if (e.type == EventType.MouseDown && rect.Contains(e.mousePosition))
{
foldOut = !foldOut;
EditorPrefs.SetBool(foldKey, foldOut);
e.Use();
}
if (foldOut && drawAct != null)
{
drawAct();
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 434cdfa441f5efc49954b9ba7d8bb241
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothInspectorUtility.cs
uploadId: 893596

View File

@@ -0,0 +1,949 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// 頂点ペイントウインドウ
/// </summary>
[InitializeOnLoad]
public class ClothPainter
{
public enum PaintMode
{
None,
/// <summary>
/// Move/Fixed/Ignore/Invalid
/// </summary>
Attribute,
/// <summary>
/// Max Distance/Backstop
/// </summary>
Motion,
}
static PaintMode paintMode = PaintMode.None;
/// <summary>
/// 編集対象のクロスコンポーネント
/// </summary>
static MagicaCloth cloth = null;
/// <summary>
/// 編集対象のクロスEditorクラス
/// </summary>
static MagicaClothEditor clothEditor = null;
/// <summary>
/// 編集対象のエディットメッシュ
/// </summary>
static VirtualMeshContainer editMeshContainer = null;
/// <summary>
/// 編集対象のセレクションデータ
/// </summary>
static SelectionData selectionData = null;
/// <summary>
/// 編集開始時のセレクションデータ(コピー)
/// </summary>
static SelectionData initSelectionData = null;
internal const int WindowWidth = 200;
internal const int WindowHeight = 200;
//=========================================================================================
const int PointFlag_Selecting = 1; // 選択中
internal struct Point : IComparable<Point>
{
public int vindex;
public float distance;
public BitField32 flag;
public int CompareTo(Point other)
{
if (distance != other.distance)
return distance < other.distance ? 1 : -1;
else if (vindex != other.vindex)
return vindex < other.vindex ? 1 : -1;
else
return 0;
}
}
static NativeList<Point> dispPointList;
static NativeArray<float3> pointWorldPositions;
static VirtualMeshRaycastHit rayhit = default;
static bool oldShowAll = false;
static bool forceUpdate = false;
//=========================================================================================
static ClothPainter()
{
// シーンビューにGUIを描画するためのコールバック
SceneView.duringSceneGui += OnGUI;
}
/// <summary>
/// ペイント開始
/// </summary>
/// <param name="clothComponent"></param>
public static void EnterPaint(PaintMode mode, MagicaClothEditor editor, MagicaCloth clothComponent, VirtualMeshContainer cmesh, SelectionData sdata)
{
Develop.DebugLog($"EnterPaint");
paintMode = mode;
cloth = clothComponent;
clothEditor = editor;
editMeshContainer = cmesh;
selectionData = sdata;
initSelectionData = sdata.Clone();
rayhit = default;
forceUpdate = true;
// ポイントバッファ
dispPointList = new NativeList<Point>(cmesh.shareVirtualMesh.VertexCount, Allocator.Persistent);
pointWorldPositions = new NativeArray<float3>(cmesh.shareVirtualMesh.VertexCount, Allocator.Persistent);
// UndoRedoコールバック
Undo.undoRedoPerformed += UndoRedoCallback;
}
/// <summary>
/// ペイント終了
/// </summary>
public static void ExitPaint()
{
Develop.DebugLog($"ExitPaint");
cloth = null;
clothEditor = null;
editMeshContainer = null;
selectionData = null;
initSelectionData = null;
rayhit = default;
if (dispPointList.IsCreated)
dispPointList.Dispose();
if (pointWorldPositions.IsCreated)
pointWorldPositions.Dispose();
// UndoRedoコールバック
Undo.undoRedoPerformed -= UndoRedoCallback;
}
/// <summary>
/// Undo/Redo実行後のコールバック
/// </summary>
static void UndoRedoCallback()
{
//Develop.DebugLog($"Undo Redo!");
if (EditorApplication.isPlaying)
return;
if (cloth == null || editMeshContainer == null || selectionData == null || editMeshContainer.shareVirtualMesh.IsSuccess == false)
return;
// セレクションデータを取り直す
selectionData = clothEditor.GetSelectionData(cloth, editMeshContainer.shareVirtualMesh);
forceUpdate = true;
}
//=========================================================================================
/// <summary>
/// 指定クロスコンポーネントを編集中かどうか
/// </summary>
/// <param name="clothComponent"></param>
/// <returns></returns>
public static bool HasEditCloth(MagicaCloth clothComponent)
{
return cloth == clothComponent;
}
/// <summary>
/// 編集中かどうか
/// </summary>
/// <returns></returns>
public static bool IsPainting()
{
return cloth != null;
}
//=========================================================================================
static void OnGUI(SceneView sceneView)
{
if (EditorApplication.isPlaying)
return;
// アクティブシーンビュー判定
if (SceneView.lastActiveSceneView != sceneView)
return;
if (cloth == null || editMeshContainer == null || editMeshContainer.shareVirtualMesh == null || selectionData == null)
return;
if (editMeshContainer.shareVirtualMesh.IsSuccess == false || selectionData.IsValid() == false)
return;
var windata = ScriptableSingleton<ClothPainterWindowData>.instance;
// シーンビューカメラ
Camera cam = SceneView.currentDrawingSceneView.camera;
Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
Vector3 spos = ray.origin;
Vector3 epos = spos + ray.direction * 1000.0f;
// 透過カーソル用
float rayAngle = Vector3.Angle(cam.transform.forward, ray.direction);
float nearPlaneDistance = cam.orthographic ? cam.nearClipPlane : cam.nearClipPlane / math.cos(math.radians(rayAngle));
float nearPlaneVSize = cam.orthographic ? cam.orthographicSize : math.tan(math.radians(cam.fieldOfView * 0.5f)) * cam.nearClipPlane * 2;
float brushSizeThrough = nearPlaneVSize * windata.brushSizeThrough;
// カーソルのviewportポイント
var sp = HandleUtility.GUIPointToScreenPixelCoordinate(Event.current.mousePosition);
var cursorViewpos = cam.ScreenToViewportPoint(sp);
cursorViewpos = math.remap(new float3(0), new float3(1), new float3(-1), new float3(1), cursorViewpos); // 射影行列(-1 ~ +1)に変換
// カメラ座標をローカル空間に変換する
var t = cloth.ClothTransform; // コンポーネント空間
bool repaint = false;
bool updatePoint = false;
// ポイントサイズ
float pointSize = windata.drawPointSize;
bool showAll = windata.backFaceCulling == false;
// マウス移動中は常にメッシュとの交差判定
if (Event.current.type == EventType.MouseMove || Event.current.type == EventType.MouseDrag || oldShowAll != showAll || forceUpdate)
{
oldShowAll = showAll;
forceUpdate = false;
updatePoint = true;
}
// マウス選択
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && !Event.current.alt)
{
// ペイント適用
ApplyPaint(windata, dispPointList);
// シーンビューのエリア選択を出さないために、とりあえずこうするらしい
int controlId = GUIUtility.GetControlID(FocusType.Passive);
GUIUtility.hotControl = controlId;
Event.current.Use(); // ?
}
if (Event.current.type == EventType.MouseDrag && Event.current.button == 0 && !Event.current.alt)
{
// ペイント適用
ApplyPaint(windata, dispPointList);
Event.current.Use();
}
if (Event.current.type == EventType.MouseUp && Event.current.button == 0 && !Event.current.alt)
{
// ペイント結果を適用する
ApplySelectionData();
// マウスボタンUPでコントロールロックを解除するらしい
GUIUtility.hotControl = 0;
Event.current.Use(); // ?
}
// Handlesの描画はGUIの前でなければならない.理由は不明
if (Event.current.type == EventType.Repaint)
{
// ディスクの描画
if (windata.through)
{
// 透過モード
Handles.matrix = Matrix4x4.identity;
Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
var dpos = spos + ray.direction * nearPlaneDistance;
var dnor = cam.transform.forward;
Handles.color = new Color(0.6f, 0.2f, 1.0f, 0.3f);
Handles.DrawSolidDisc(dpos, dnor, brushSizeThrough);
}
else if (rayhit.IsValid())
{
Handles.matrix = Matrix4x4.identity;
Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
var dpos = t.TransformPoint(rayhit.position);
var dnor = t.TransformDirection(rayhit.normal);
Handles.color = new Color(0.3f, 0.5f, 1.0f, 0.4f);
Handles.DrawSolidDisc(dpos, dnor, windata.brushSize);
}
// ポイント表示
Handles.lighting = true;
//Handles.zTest = showAll ? UnityEngine.Rendering.CompareFunction.Always : UnityEngine.Rendering.CompareFunction.LessEqual;
//Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
Handles.zTest = windata.zTest ? UnityEngine.Rendering.CompareFunction.LessEqual : UnityEngine.Rendering.CompareFunction.Always;
Handles.matrix = Matrix4x4.identity;
int cnt = dispPointList.Length;
for (int i = 0; i < cnt; i++)
{
var point = dispPointList[i];
Color col = Color.black;
var attr = selectionData.attributes[point.vindex];
switch (paintMode)
{
case PaintMode.Attribute:
if (attr.IsMove()) col = Color.green;
else if (attr.IsFixed()) col = Color.red;
//else if (attr.IsIgnore()) col = Color.blue;
else col = Color.gray;
break;
case PaintMode.Motion:
if (attr.IsMotion()) col = Color.cyan;
else col = Color.gray;
break;
}
// 選択中
if (point.flag.IsSet(PointFlag_Selecting))
{
col = Color.yellow;
}
var pos = pointWorldPositions[point.vindex];
Handles.color = col;
Handles.SphereHandleCap(0, pos, Quaternion.identity, windata.drawPointSize, EventType.Repaint);
}
// ペイント中のギズモ表示
if (windata.showShape)
ClothEditorUtility.DrawClothEditor(editMeshContainer, ClothEditorUtility.PaintSettings, cloth.SerializeData, true, windata.backFaceCulling, true);
}
// GUI
//sceneView.BeginWindows();
string title = string.Empty;
switch (paintMode)
{
case PaintMode.Attribute:
title = "Movement attribute paint";
break;
case PaintMode.Motion:
title = "Max Distance/Backstop paint";
break;
}
var rect = GUILayout.Window(71903439, windata.windowRect, DoWindow, title);
//Debug.Log(windata.windowRect);
// ウインドウをSceneViewの領域内にクランプする
// sceneView.positionでrectが取れる
rect.x = Mathf.Clamp(rect.x, 0, Mathf.Max(sceneView.position.width - rect.width, 0));
rect.y = Mathf.Clamp(rect.y, 0, Mathf.Max(sceneView.position.height - rect.height - 25, 0));
rect.width = WindowWidth;
rect.height = WindowHeight;
windata.windowRect = rect;
//sceneView.EndWindows();
// ポイントデータ更新
if (updatePoint)
{
UpdatePoint(
windata.through,
t, cam, ray, showAll,
windata.through ? windata.brushSizeThrough : windata.brushSize,
windata.through ? cursorViewpos : t.TransformPoint(rayhit.position),
windata.drawPointSize
);
repaint = true;
}
// 画面リフレッシュ
if (repaint)
sceneView.Repaint();
}
/// <summary>
/// ポイントデータを更新する
/// </summary>
/// <param name="ct"></param>
/// <param name="cam"></param>
/// <param name="ray"></param>
/// <param name="showAll"></param>
/// <param name="brushSize"></param>
static void UpdatePoint(bool through, Transform ct, Camera cam, Ray ray, bool showAll, float brushSize, float3 brushPos, float pointSize)
{
// 表示頂点選別
CreateDispPointList(through, ct, cam, showAll, brushSize, brushPos).Complete();
// サーフェース交差判定
//rayhit = editMesh.IntersectRayMesh(ray.origin, ray.direction, showAll, pointSize);
rayhit = editMeshContainer.shareVirtualMesh.IntersectRayMesh(ray.origin, ray.direction, showAll, pointSize);
}
static JobHandle CreateDispPointList(
bool through, Transform ct, Camera cam,
bool showAll, float brushSize, float3 brushPosition, JobHandle jobHandle = default
)
{
dispPointList.Clear();
var editMesh = editMeshContainer.shareVirtualMesh;
int vcnt = editMesh.VertexCount;
if (vcnt == 0)
return jobHandle;
// ローカルカメラ
var localCameraDirection = ct.InverseTransformDirection(cam.transform.forward);
// 頂点のワールド変換と表示頂点の選別
var job = new CreateDispPointListJob()
{
through = through,
LtoW = ct.localToWorldMatrix,
showAll = showAll,
cameraPosition = cam.transform.position,
cameraDirection = localCameraDirection,
cameraProjectionMatrix = cam.projectionMatrix,
worldToCameraMatrix = cam.worldToCameraMatrix,
cameraAspectRatio = cam.aspect,
useBrush = through || rayhit.IsValid(),
brushPosition = brushPosition,
brushSize = brushSize,
localPositions = editMesh.localPositions.GetNativeArray(),
localNormals = editMesh.localNormals.GetNativeArray(),
vertexToTriangles = editMesh.vertexToTriangles,
pointWorldPositions = pointWorldPositions,
dispPointList = dispPointList.AsParallelWriter(),
};
jobHandle = job.Schedule(vcnt, 16, jobHandle);
// 距離ソート
var job2 = new SortDispPointJob()
{
dispPointList = dispPointList,
};
jobHandle = job2.Schedule(jobHandle);
return jobHandle;
}
[BurstCompile]
struct CreateDispPointListJob : IJobParallelFor
{
public bool through;
public float4x4 LtoW;
public bool showAll;
public float3 cameraPosition;
public float3 cameraDirection;
public float4x4 cameraProjectionMatrix;
public float4x4 worldToCameraMatrix;
public float cameraAspectRatio;
public bool useBrush;
public float3 brushPosition;
public float brushSize;
[Unity.Collections.ReadOnly]
public NativeArray<float3> localPositions;
[Unity.Collections.ReadOnly]
public NativeArray<float3> localNormals;
[Unity.Collections.ReadOnly]
public NativeArray<FixedList32Bytes<uint>> vertexToTriangles;
[Unity.Collections.WriteOnly]
public NativeArray<float3> pointWorldPositions;
[Unity.Collections.WriteOnly]
public NativeList<Point>.ParallelWriter dispPointList;
public void Execute(int vindex)
{
// ワールド座標
var lpos = localPositions[vindex];
var wpos = math.transform(LtoW, lpos);
pointWorldPositions[vindex] = wpos;
// カメラ距離
float dist = math.distance(cameraPosition, wpos);
// 表示選別
bool show;
if (showAll)
{
show = true;
}
else
{
// 頂点がトライアングルに属さない場合は無条件で表示する
if (vertexToTriangles[vindex].Length == 0)
show = true;
else
{
// トライアングルに属する頂点は法線が画面に向いているもののみ表示
show = math.dot(cameraDirection, localNormals[vindex]) < 0.0f;
}
}
if (show)
{
var flag = new BitField32();
// ブラシ範囲内に存在するか判定
if (useBrush)
{
if (through)
{
// 透過モード
// 射影変換
var cpos = math.mul(worldToCameraMatrix, new float4(wpos, 1));
var vpos = math.mul(cameraProjectionMatrix, new float4(cpos.xyz, 1));
vpos /= vpos.w; // wで割る必要あり
// ブラシはアスペクト比を考慮した楕円で判定する必要あり
// brushPositionはviewport座標を指す
float2 v = vpos.xy - brushPosition.xy;
v.x *= cameraAspectRatio;
var bdist = math.length(v);
if (bdist <= brushSize)
flag.SetBits(PointFlag_Selecting, true);
}
else
{
// サーフェス選択モード
var bdist = math.distance(brushPosition, wpos);
if (bdist <= brushSize)
flag.SetBits(PointFlag_Selecting, true);
}
}
dispPointList.AddNoResize(new Point() { vindex = vindex, distance = dist, flag = flag });
}
}
}
[BurstCompile]
struct SortDispPointJob : IJob
{
public NativeList<Point> dispPointList;
public void Execute()
{
if (dispPointList.Length > 1)
dispPointList.Sort();
}
}
/// <summary>
/// 渡されたpointListをカメラからの距離の降順にソートして返す
/// </summary>
/// <param name="positions"></param>
/// <param name="positonOffset"></param>
/// <param name="LtoW">positionをワールド座標変換するマトリックス</param>
/// <param name="camPos">カメラワールド座標</param>
/// <param name="pointList"></param>
internal static void CalcPointCameraDistance(NativeArray<float3> positions, int positonOffset, float4x4 LtoW, float3 camPos, NativeList<Point> pointList)
{
// ポイントのカメラからの距離を求める
int cnt = pointList.Length;
if (cnt <= 1)
return;
var job = new CalcCameraDistanceJob()
{
LtoW = LtoW,
cameraPosition = camPos,
positions = positions,
pointList = pointList,
positionOffset = positonOffset,
};
job.Run(cnt);
// 距離ソート
var job2 = new SortDispPointJob()
{
dispPointList = pointList,
};
job2.Run();
}
[BurstCompile]
struct CalcCameraDistanceJob : IJobParallelFor
{
public float4x4 LtoW;
public float3 cameraPosition;
[Unity.Collections.ReadOnly]
public NativeArray<float3> positions;
[NativeDisableParallelForRestriction]
public NativeList<Point> pointList;
public int positionOffset;
public void Execute(int index)
{
var point = pointList[index];
// ワールド座標
var lpos = positions[positionOffset + point.vindex];
var wpos = math.transform(LtoW, lpos);
// カメラ距離
float dist = math.distance(cameraPosition, wpos);
point.distance = dist;
pointList[index] = point;
}
}
/// <summary>
/// 選択中ポイントに属性を付与する
/// </summary>
/// <param name="attribute"></param>
static void ApplyPaint(ClothPainterWindowData windata, NativeList<Point> applyPointList)
{
int cnt = applyPointList.Length;
for (int i = 0; i < cnt; i++)
{
var point = applyPointList[i];
if (point.flag.IsSet(PointFlag_Selecting) == false)
continue;
var attr = selectionData.attributes[point.vindex];
bool change = false;
switch (paintMode)
{
case PaintMode.Attribute:
// 移動/固定
if (windata.editAttribute == 0 && attr.IsMove() == false)
{
attr.SetFlag(VertexAttribute.Flag_Move, true);
attr.SetFlag(VertexAttribute.Flag_Fixed, false);
//attr.SetFlag(VertexAttribute.Flag_Ignore, false);
change = true;
}
else if (windata.editAttribute == 1 && attr.IsFixed() == false)
{
attr.SetFlag(VertexAttribute.Flag_Move, false);
attr.SetFlag(VertexAttribute.Flag_Fixed, true);
//attr.SetFlag(VertexAttribute.Flag_Ignore, false);
change = true;
}
//else if (windata.editAttribute == 2 && attr.IsIgnore() == false)
//{
// attr.SetFlag(VertexAttribute.Flag_Move, false);
// attr.SetFlag(VertexAttribute.Flag_Fixed, false);
// attr.SetFlag(VertexAttribute.Flag_Ignore, true);
// change = true;
//}
else if (windata.editAttribute == 3 && attr.IsInvalid() == false)
{
attr.SetFlag(VertexAttribute.Flag_Move, false);
attr.SetFlag(VertexAttribute.Flag_Fixed, false);
//attr.SetFlag(VertexAttribute.Flag_Ignore, false);
change = true;
}
break;
case PaintMode.Motion:
// MaxDistance/Backstop
if (windata.editMotion == 0 && attr.IsMotion() == false)
{
attr.SetFlag(VertexAttribute.Flag_InvalidMotion, false);
change = true;
}
else if (windata.editMotion == 1 && attr.IsMotion())
{
attr.SetFlag(VertexAttribute.Flag_InvalidMotion, true);
change = true;
}
break;
}
if (change)
selectionData.attributes[point.vindex] = attr;
}
}
/// <summary>
/// セレクションデータをシリアライズする
/// </summary>
static void ApplySelectionData()
{
// セレクションデータデータをシリアライズ化する
selectionData.userEdit = true; // ユーザー編集フラグを立てる
if (clothEditor != null)
clothEditor.ApplyClothPainter(selectionData);
// Undo/Redoの状態を切り替えるためセレクションデータのクローンを作成して切り替える
selectionData = selectionData.Clone();
}
/// <summary>
/// 塗りつぶし
/// </summary>
/// <param name="windata"></param>
static void Fill(ClothPainterWindowData windata)
{
// 塗りつぶし用のポイントデータを作成する
//int vcnt = editMesh.VertexCount;
int vcnt = editMeshContainer.shareVirtualMesh.VertexCount;
using var fillDispPointList = new NativeList<Point>(vcnt, Allocator.TempJob);
BitField32 flag = new();
flag.SetBits(PointFlag_Selecting, true);
for (int i = 0; i < vcnt; i++)
{
var p = new Point()
{
vindex = i,
distance = 0,
flag = flag,
};
fillDispPointList.Add(p);
}
// セレクションデータへ反映
ApplyPaint(windata, fillDispPointList);
// シリアライズ
ApplySelectionData();
}
static void DoWindow(int unusedWindowID)
{
var windata = ScriptableSingleton<ClothPainterWindowData>.instance;
//Debug.Log(windata.windowRect);
//float lineHight = EditorGUIUtility.singleLineHeight;
// ポイントサイズスライダー
const float sliderLableWidth = 80;
const float sliderWidth = 180;
using (var h = new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Point Size", GUILayout.Width(sliderLableWidth));
windata.drawPointSize = EditorGUILayout.Slider(GUIContent.none, windata.drawPointSize, 0.001f, 0.1f, GUILayout.Width(sliderWidth));
}
// ブラシサイズスライダー
using (var h = new EditorGUILayout.HorizontalScope())
{
if (windata.through)
{
EditorGUILayout.LabelField("Brush Size T", GUILayout.Width(sliderLableWidth));
windata.brushSizeThrough = EditorGUILayout.Slider(GUIContent.none, windata.brushSizeThrough, 0.01f, 0.3f, GUILayout.Width(sliderWidth));
}
else
{
EditorGUILayout.LabelField("Brush Size", GUILayout.Width(sliderLableWidth));
windata.brushSize = EditorGUILayout.Slider(GUIContent.none, windata.brushSize, 0.001f, 0.2f, GUILayout.Width(sliderWidth));
}
}
EditorGUILayout.Space();
const float toggleLableWidth = 80;
const float toggleButtonWidth = 30;
using (var h = new EditorGUILayout.HorizontalScope())
{
// 形状表示
EditorGUILayout.LabelField("Shape", GUILayout.Width(toggleLableWidth));
windata.showShape = EditorGUILayout.Toggle(GUIContent.none, windata.showShape, GUILayout.Width(toggleButtonWidth));
// 全表示
EditorGUILayout.LabelField("Culling", GUILayout.Width(toggleLableWidth));
windata.backFaceCulling = EditorGUILayout.Toggle(GUIContent.none, windata.backFaceCulling, GUILayout.Width(toggleButtonWidth));
}
using (var h = new EditorGUILayout.HorizontalScope())
{
// Zテスト
EditorGUILayout.LabelField("Z-Test", GUILayout.Width(toggleLableWidth));
windata.zTest = EditorGUILayout.Toggle(GUIContent.none, windata.zTest, GUILayout.Width(toggleButtonWidth));
// 透過
EditorGUILayout.LabelField("Through", GUILayout.Width(toggleLableWidth));
windata.through = EditorGUILayout.Toggle(GUIContent.none, windata.through, GUILayout.Width(toggleButtonWidth));
}
// 属性ボタン
EditorGUILayout.Space();
EditorGUILayout.LabelField("Paint Attribute");
Color bcol = GUI.backgroundColor;
using (new EditorGUILayout.HorizontalScope())
{
if (paintMode == PaintMode.Attribute)
{
// Move/Fixed
int nowAttr = windata.editAttribute;
for (int i = 0; i < 4; i++)
{
// !現在Ignoreはオミット
if (i == 2)
continue;
Color col = Color.black;
string title = string.Empty;
switch (i)
{
case 0: // move
col = Color.green;
title = "Move";
break;
case 1: // fixed
col = Color.red;
title = "Fixed";
break;
case 2: // ignore
col = Color.blue;
title = "Ignore";
break;
case 3: // invalid
col = Color.gray;
title = "Invalid";
break;
}
GUI.backgroundColor = i == nowAttr ? col : Color.gray;
bool ret = GUILayout.Toggle(i == nowAttr, title, EditorStyles.miniButton);
if (ret)
{
nowAttr = i;
}
}
windata.editAttribute = nowAttr;
}
else if (paintMode == PaintMode.Motion)
{
// MaxDistance/Backstop
int nowAttr = windata.editMotion;
for (int i = 0; i < 2; i++)
{
// カラー
var col = i == 0 ? Color.cyan : Color.red;
GUI.backgroundColor = i == nowAttr ? col : Color.gray;
bool ret = GUILayout.Toggle(i == nowAttr, i == 0 ? "Valid" : "Invalid", EditorStyles.miniButton);
if (ret)
{
nowAttr = i;
}
}
windata.editMotion = nowAttr;
}
}
GUI.backgroundColor = bcol;
// 適用/キャンセルボタン
EditorGUILayout.Space();
using (new EditorGUILayout.HorizontalScope())
{
// 塗りつぶしボタン
if (GUILayout.Button("Fill", GUILayout.Width(70)))
{
Fill(windata);
}
GUILayout.FlexibleSpace();
// ペイント終了ボタン
if (GUILayout.Button("Exit"))
{
// 初期化データの保存確認
ClothEditorManager.ApplyInitData(cloth, global: true);
// ペイント終了フラグ
cloth = null;
// この編集によりセレクションデータに変更があった場合はProxyMeshをリビルドする
if (initSelectionData.Compare(selectionData) == false)
{
Develop.DebugLog($"Change selection data!");
if (clothEditor != null)
clothEditor.UpdateEditMesh();
}
ExitPaint();
}
EditorGUILayout.Space();
}
GUI.DragWindow();
}
}
//=============================================================================================
/// <summary>
/// ペイントウインドウの保持データ
/// ScriptableSingletonを利用することによりエディタ終了時までデータを保持できる
/// </summary>
public class ClothPainterWindowData : ScriptableSingleton<ClothPainterWindowData>
{
/// <summary>
/// ウインドウの位置とサイズ
/// </summary>
public Rect windowRect = new Rect(100, 100, ClothPainter.WindowWidth, ClothPainter.WindowHeight);
/// <summary>
/// 表示ポイントサイズ
/// </summary>
public float drawPointSize = 0.02f;
/// <summary>
/// ブラシサイズ
/// </summary>
public float brushSize = 0.05f;
/// <summary>
/// ブラシサイズ(透過モード時)
/// シーンカメラ垂直サイズの(%)
/// </summary>
public float brushSizeThrough = 0.1f;
/// <summary>
/// 裏面カリング
/// </summary>
public bool backFaceCulling = true;
/// <summary>
/// 形状を表示
/// </summary>
public bool showShape = true;
/// <summary>
/// Zテスト
/// </summary>
public bool zTest = false;
/// <summary>
/// 透過モード
/// </summary>
public bool through = false;
/// <summary>
/// 現在アクティブなポイント属性(0=Move/1=Fixed/2=Ignore/3=Invalid)
/// VertexAttributeとは異なるので注意
/// </summary>
public int editAttribute = 0;
/// <summary>
/// 現在アクティブなモーション制約(0=Valid, 1=Invalid)
/// </summary>
public int editMotion = 0;
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 3d9ac74274185454f8b6f05bc4f2825e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothPainter.cs
uploadId: 893596

View File

@@ -0,0 +1,232 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// プリセットに関するユーティリティ
/// </summary>
public static class ClothPresetUtility
{
const string prefix = "MC2_Preset";
const string configName = "MC2 preset folder";
public static void DrawPresetButton(MagicaCloth cloth, ClothSerializeData sdata)
{
bool save = false;
bool load = false;
using (var horizontalScope = new GUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Parameters", EditorStyles.boldLabel);
GUI.backgroundColor = Color.green;
if (EditorGUILayout.DropdownButton(new GUIContent("Preset"), FocusType.Keyboard, GUILayout.Width(70), GUILayout.Height(16)))
{
CreatePresetPopupMenu(cloth, sdata);
GUI.backgroundColor = Color.white;
//GUIUtility.ExitGUI();
}
GUI.backgroundColor = Color.white;
if (GUILayout.Button("Save", GUILayout.Width(40), GUILayout.Height(16)))
{
//SavePreset(sdata);
//GUIUtility.ExitGUI();
save = true;
}
if (GUILayout.Button("Load", GUILayout.Width(40), GUILayout.Height(16)))
{
//LoadPreset(cloth, sdata);
//GUIUtility.ExitGUI();
load = true;
}
}
if (save)
SavePreset(sdata);
if (load)
LoadPreset(cloth, sdata);
}
static string GetComponentTypeName(ClothSerializeData sdata)
{
//if (sdata.clothType == ClothProcess.ClothType.BoneCloth)
// return prefix + "BoneCloth";
//else if (sdata.clothType == ClothProcess.ClothType.MeshCloth)
// return prefix + "MeshCloth";
//return prefix;
return prefix;
}
class PresetInfo
{
public string presetPath;
public string presetName;
public TextAsset text;
}
private static void CreatePresetPopupMenu(MagicaCloth cloth, ClothSerializeData sdata)
{
var guidArray = AssetDatabase.FindAssets($"{prefix} t:{nameof(TextAsset)}");
if (guidArray == null)
return;
Dictionary<string, List<PresetInfo>> dict = new Dictionary<string, List<PresetInfo>>();
foreach (var guid in guidArray)
{
var filePath = AssetDatabase.GUIDToAssetPath(guid);
// json確認
if (filePath.EndsWith(".json") == false)
continue;
var text = AssetDatabase.LoadAssetAtPath<TextAsset>(filePath);
if (text)
{
var info = new PresetInfo();
info.presetPath = filePath;
var fname = Path.GetFileNameWithoutExtension(filePath);
fname = fname.Replace(prefix, "");
if (fname.StartsWith("_"))
fname = fname.Remove(0, 1); // 頭の_は削除する
info.presetName = fname;
info.text = text;
// ディレクトリごとに記録する
var dirName = Path.GetDirectoryName(filePath);
if (dict.ContainsKey(dirName) == false)
{
dict.Add(dirName, new List<PresetInfo>());
}
dict[dirName].Add(info);
}
}
// ポップアップメニューの作成
// ディレクトリごとにセパレータで分けて表示する
var menu = new GenericMenu();
int line = 0;
foreach (var kv in dict)
{
if (line > 0)
{
menu.AddSeparator("");
}
foreach (var info in kv.Value)
{
var textAsset = info.text;
var presetName = info.presetName;
var presetPath = info.presetPath;
menu.AddItem(new GUIContent(presetName), false, () =>
{
// load
Develop.Log("Load preset file:" + presetPath);
if (sdata.ImportJson(textAsset.text))
Develop.Log("Completed.");
else
Develop.LogError("Preset load error!");
LoadPresetFinish(cloth);
});
}
line++;
}
menu.ShowAsContext();
}
/// <summary>
/// プリセットファイル保存
/// </summary>
/// <param name="clothParam"></param>
private static void SavePreset(ClothSerializeData sdata)
{
// フォルダを読み込み
string folder = EditorUserSettings.GetConfigValue(configName);
// 接頭語
string presetTypeName = GetComponentTypeName(sdata);
// 保存ダイアログ
string path = EditorUtility.SaveFilePanelInProject(
"Save Preset",
$"{presetTypeName}_(name)",
"json",
"Enter a name for the preset json.",
folder
);
if (string.IsNullOrEmpty(path))
return;
// フォルダを記録
folder = Path.GetDirectoryName(path);
EditorUserSettings.SetConfigValue(configName, folder);
Develop.Log("Save preset file:" + path);
// export json
string json = sdata.ExportJson();
// save
File.WriteAllText(path, json);
AssetDatabase.Refresh();
Develop.Log("Completed.");
}
/// <summary>
/// プリセットファイル読み込み
/// </summary>
/// <param name="clothParam"></param>
private static void LoadPreset(MagicaCloth cloth, ClothSerializeData sdata)
{
// フォルダを読み込み
string folder = EditorUserSettings.GetConfigValue(configName);
// 読み込みダイアログ
string path = EditorUtility.OpenFilePanel("Load Preset", folder, "json");
if (string.IsNullOrEmpty(path))
return;
// フォルダを記録
folder = Path.GetDirectoryName(path);
EditorUserSettings.SetConfigValue(configName, folder);
// import json
Develop.Log("Load preset file:" + path);
string json = File.ReadAllText(path);
// load
if (sdata.ImportJson(json))
Develop.Log("Completed.");
else
Develop.LogError("Preset load error!");
LoadPresetFinish(cloth);
}
/// <summary>
/// プリセットファイル読み込み後処理
/// </summary>
/// <param name="cloth"></param>
private static void LoadPresetFinish(MagicaCloth cloth)
{
if (EditorApplication.isPlaying)
{
// パラメータ更新通知
cloth.SetParameterChange();
}
else
{
// シリアライズ変更通知
EditorUtility.SetDirty(cloth);
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 92b2c2c365167f34e90c74c01638b1eb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothPresetUtility.cs
uploadId: 893596

View File

@@ -0,0 +1,118 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEditor;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// CapsuleColliderのエディタ拡張
/// </summary>
[CustomEditor(typeof(MagicaCapsuleCollider))]
[CanEditMultipleObjects]
public class MagicaCapsuleColliderEditor : MagicaEditorBase
{
public override void OnInspectorGUI()
{
var scr = target as MagicaCapsuleCollider;
const string undoName = "CapsuleCollider";
serializedObject.Update();
Undo.RecordObject(scr, undoName);
// separation
var separationValue = serializedObject.FindProperty("radiusSeparation");
// direction
var directionValue = serializedObject.FindProperty("direction");
EditorGUILayout.PropertyField(directionValue);
// reverse direction
EditorGUILayout.PropertyField(serializedObject.FindProperty("reverseDirection"));
// aligned on center
var alignedOnCenterValue = serializedObject.FindProperty("alignedOnCenter");
EditorGUILayout.PropertyField(alignedOnCenterValue);
var sizeValue = serializedObject.FindProperty("size");
var size = sizeValue.vector3Value;
// マルチ選択時について
// CapsuleColliderではEditorGUI.showMixedValueは設定しない
// EditorGUI.showMixedValueを設定すると異なる値の場合は「ー」で表記されるようになるが、
// カプセルコライダーではつのVector3につの異なるスライダーを割り当てているため、
// 各スライダーの値が同じ場合でも「ー」表記になってしまう
// そうならないようにアクティブなコンポーネントの数値のみを表示するように、showMixedValueは常にfalseにしておく
// length
using (var check = new EditorGUI.ChangeCheckScope())
{
var newSize = EditorGUILayout.Slider("Length", size.z, 0.0f, 2.0f);
ApplyMultiSelection<MagicaCapsuleCollider>(check.changed, undoName, x => x.SetSizeZ(newSize));
}
// radius
float lineHight = EditorGUIUtility.singleLineHeight;
// start
{
Rect r = EditorGUILayout.GetControlRect();
// ラベルを描画
var positionA = EditorGUI.PrefixLabel(r, GUIUtility.GetControlID(FocusType.Passive), new GUIContent(scr.radiusSeparation ? "Start Radius" : "Radius"));
// 矩形を計算
float w = positionA.width;
var buttonRect = new Rect(positionA.x + w - 30, r.y, 30, lineHight);
var sliderRect = new Rect(positionA.x, r.y, Mathf.Max(w - 35, 0), lineHight);
// Slider
using (var check = new EditorGUI.ChangeCheckScope())
{
var newSize = EditorGUI.Slider(sliderRect, size.x, 0.001f, 0.5f);
ApplyMultiSelection<MagicaCapsuleCollider>(check.changed, undoName, x => x.SetSizeX(newSize));
}
// 分割ボタン
if (GUI.Button(buttonRect, scr.radiusSeparation ? "X" : "S"))
{
// 切り替え
separationValue.boolValue = !separationValue.boolValue;
}
}
// end
if (separationValue.boolValue)
{
Rect r = EditorGUILayout.GetControlRect();
// ラベルを描画
var positionA = EditorGUI.PrefixLabel(r, GUIUtility.GetControlID(FocusType.Passive), new GUIContent("End Radius"));
// 矩形を計算
float w = positionA.width;
var sliderRect = new Rect(positionA.x, r.y, Mathf.Max(w - 35, 0), lineHight);
// Slider
using (var check = new EditorGUI.ChangeCheckScope())
{
var newSize = EditorGUI.Slider(sliderRect, size.y, 0.001f, 0.5f);
ApplyMultiSelection<MagicaCapsuleCollider>(check.changed, undoName, x => x.SetSizeY(newSize));
}
}
// center
EditorGUILayout.PropertyField(serializedObject.FindProperty("center"));
// Symmetry
EditorGUILayout.Space();
var symmetryModeProperty = serializedObject.FindProperty("symmetryMode");
EditorGUILayout.PropertyField(symmetryModeProperty);
if (symmetryModeProperty.enumValueIndex >= (int)ColliderSymmetryMode.AutomaticTarget)
EditorGUILayout.PropertyField(serializedObject.FindProperty("symmetryTarget"));
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 89239f89e837c394ca8ebe2515680a5e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaCapsuleColliderEditor.cs
uploadId: 893596

View File

@@ -0,0 +1,951 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEditor;
using UnityEngine;
namespace MagicaCloth2
{
/// <summary>
/// MagicaClothコンポーネントのエディタ拡張
/// </summary>
[CustomEditor(typeof(MagicaCloth))]
[CanEditMultipleObjects]
public class MagicaClothEditor : MagicaEditorBase
{
protected void OnEnable()
{
//Debug.Log("MagicaClothEditor.OnEnable");
ClothEditorManager.OnEditMeshBuildComplete += OnEditMeshBuildComplete;
}
protected void OnDisable()
{
//Debug.Log("MagicaClothEditor.OnDisable");
ClothEditorManager.OnEditMeshBuildComplete -= OnEditMeshBuildComplete;
ClothPainter.ExitPaint();
}
//=========================================================================================
int oldAcitve = -1;
//=========================================================================================
/// <summary>
/// 編集用のセレクションデータを取得する
/// </summary>
/// <param name="cloth"></param>
/// <param name="editMesh"></param>
/// <returns></returns>
public SelectionData GetSelectionData(MagicaCloth cloth, VirtualMesh editMesh)
{
// すでにセレクションデータが存在し、かつユーザー編集データならばコンバートする
var selectionData = ClothEditorManager.CreateAutoSelectionData(cloth, cloth.SerializeData, editMesh);
if (cloth.GetSerializeData2().selectionData != null && cloth.GetSerializeData2().selectionData.userEdit)
{
//Debug.Log($"セレクションデータコンバート!");
selectionData.ConvertFrom(cloth.GetSerializeData2().selectionData);
selectionData.userEdit = true;
}
return selectionData;
}
/// <summary>
/// エディットメッシュの構築完了通知(成否問わず)
/// </summary>
void OnEditMeshBuildComplete()
{
//Debug.Log($"MagicaClothInspector. OnEditMeshBuildComplete.");
Repaint();
}
/// <summary>
/// インスペクターGUI
/// </summary>
public override void OnInspectorGUI()
{
var cloth = target as MagicaCloth;
// 状態
DispVersion();
DispStatus();
DispProxyMesh();
// 設定
serializedObject.Update();
Undo.RecordObject(cloth, "MagicaCloth2");
EditorGUILayout.Space();
ClothMainInspector();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.Space();
ClothParameterInspector();
EditorGUILayout.Space();
EditorGUILayout.Space();
GizmoInspector();
EditorGUILayout.Space();
ClothPreBuildInspector();
serializedObject.ApplyModifiedProperties();
//DrawDefaultInspector();
// アクティブが変更された場合は編集メッシュを再構築する
int nowActive = cloth.isActiveAndEnabled ? 1 : 0;
if (nowActive != oldAcitve)
{
oldAcitve = nowActive;
// ただしコンポーネントがProjectビューで選択されている場合は再構築しない
// Hierarchyおよびプレハブモードはこれに該当しない
#if UNITY_6000_3_OR_NEWER
bool inProject = AssetDatabase.Contains(cloth.gameObject.GetEntityId());
#else
bool inProject = AssetDatabase.Contains(cloth.gameObject.GetInstanceID());
#endif
if (inProject == false)
{
//Develop.Log($"[{cloth.name}] rebuild. active:{nowActive}, inProject:{inProject}");
ClothEditorManager.RegisterComponent(cloth, nowActive > 0 ? GizmoType.Active : 0, true);
}
}
}
/// <summary>
/// クロスペイントの適用
/// </summary>
/// <param name="selectiondata"></param>
internal void ApplyClothPainter(SelectionData selectionData)
{
if (selectionData == null || selectionData.IsValid() == false)
return;
var cloth = target as MagicaCloth;
// セレクションデータ格納
ClothEditorManager.ApplySelectionData(cloth, selectionData);
}
/// <summary>
/// クロスペイントの変更による編集メッシュの再構築
/// </summary>
internal void UpdateEditMesh()
{
var cloth = target as MagicaCloth;
// 編集用メッシュの再構築
ClothEditorManager.RegisterComponent(cloth, GizmoType.Active, true); // 強制更新
}
//=========================================================================================
void DispVersion()
{
EditorGUILayout.LabelField($"Version {AboutMenu.MagicaClothVersion}");
}
void DispStatus()
{
var cloth = target as MagicaCloth;
if (EditorApplication.isPlaying)
{
StaticStringBuilder.Clear();
StaticStringBuilder.AppendLine("[State]");
StaticStringBuilder.Append(cloth.Process.IsState(ClothProcess.State_UsePreBuild) ? "Pre-Build Construction" : "Runtime Construction");
DispClothStatus(StaticStringBuilder.ToString(), cloth.Process.Result, true);
}
else
{
var result = ClothEditorManager.GetResultCode(cloth);
var preBuildData = cloth.GetSerializeData2().preBuildData;
if (preBuildData.enabled)
{
// pre-build
if (result.IsError() == false)
result = preBuildData.DataValidate();
DispClothStatus("[Pre-Build Construction]", result, false);
}
else
{
// runtime
DispClothStatus("[Runtime Construction]", result, true);
}
}
}
void DispClothStatus(string title, ResultCode result, bool dispWarning)
{
StaticStringBuilder.Clear();
StaticStringBuilder.AppendLine(title);
// normal / error
MessageType mtype = MessageType.Info;
if (result.IsError())
mtype = MessageType.Error;
var infoMessage = result.GetResultInformation();
if (infoMessage != null)
{
StaticStringBuilder.AppendLine(result.GetResultString());
StaticStringBuilder.AppendLine(infoMessage);
}
else
{
StaticStringBuilder.AppendLine(result.GetResultString());
}
EditorGUILayout.HelpBox(StaticStringBuilder.ToString(), mtype);
// warning
if (dispWarning && result.IsWarning())
{
mtype = MessageType.Warning;
infoMessage = result.GetWarningInformation();
if (infoMessage != null)
EditorGUILayout.HelpBox($"{result.GetWarningString()}\n{infoMessage}", mtype);
else
EditorGUILayout.HelpBox(result.GetWarningString(), mtype);
}
}
void DispProxyMesh()
{
var cloth = target as MagicaCloth;
VirtualMeshContainer cmesh;
if (EditorApplication.isPlaying)
{
cmesh = cloth.Process?.ProxyMeshContainer;
}
else
{
cmesh = ClothEditorManager.GetEditMeshContainer(cloth);
}
if (cmesh == null || cmesh.shareVirtualMesh == null)
return;
var vmesh = cmesh.shareVirtualMesh;
StaticStringBuilder.Clear();
// 初期化データ
StaticStringBuilder.AppendLine("[Init Data]");
int initVersion = cloth.GetSerializeData2().initData?.initVersion ?? 0;
if (initVersion > 0)
{
StaticStringBuilder.Append($"v{initVersion}: ");
}
if (EditorApplication.isPlaying)
{
StaticStringBuilder.Append($"{cloth.Process.InitDataResult.GetResultString()}");
}
else
{
StaticStringBuilder.Append($"{cloth.GetSerializeData2().initData.HasData()}");
}
// Proxyメッシュ
StaticStringBuilder.AppendLine();
if (EditorApplication.isPlaying)
StaticStringBuilder.AppendLine("[Proxy Mesh]");
else
StaticStringBuilder.AppendLine("[Edit Mesh]");
if (EditorApplication.isPlaying)
{
StaticStringBuilder.AppendLine($"Camera Visible: {!cloth.Process.IsCameraCullingInvisible()}");
StaticStringBuilder.AppendLine($"Distance Visible: {!cloth.Process.IsDistanceCullingInvisible()}");
}
StaticStringBuilder.AppendLine($"Vertex: {vmesh.VertexCount}");
StaticStringBuilder.AppendLine($"Edge: {vmesh.EdgeCount}");
StaticStringBuilder.AppendLine($"Triangle: {vmesh.TriangleCount}");
StaticStringBuilder.AppendLine($"SkinBoneCount: {vmesh.SkinBoneCount}");
StaticStringBuilder.Append($"TransformCount: {cmesh.GetTransformCount()}");
EditorGUILayout.HelpBox(StaticStringBuilder.ToString(), MessageType.Info);
}
void ClothMainInspector()
{
var cloth = target as MagicaCloth;
var clothType = cloth.SerializeData.clothType;
bool isBoneSpring = clothType == ClothProcess.ClothType.BoneSpring;
bool runtime = EditorApplication.isPlaying;
// 同期状態
bool sync = EditorApplication.isPlaying && cloth.SyncPartnerCloth != null;
EditorGUILayout.LabelField("Main", EditorStyles.boldLabel);
// Cloth
{
var clothTypeProperty = serializedObject.FindProperty("serializeData.clothType");
using (new EditorGUI.DisabledScope(runtime))
{
EditorGUILayout.PropertyField(clothTypeProperty, new GUIContent("Cloth Type"));
}
var paintMode = serializedObject.FindProperty("serializeData.paintMode");
using (new EditorGUI.IndentLevelScope())
{
if (clothType == ClothProcess.ClothType.BoneCloth)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.rootBones"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.connectionMode"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.rootRotation"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.rotationalInterpolation"));
}
else if (clothType == ClothProcess.ClothType.BoneSpring)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.rootBones"));
// BoneSpringでは接続モードは指定させない。内部ではLineで固定される。
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("ConnectionMode");
EditorGUILayout.LabelField("[Line]");
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.rootRotation"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.rotationalInterpolation"));
}
else if (clothType == ClothProcess.ClothType.MeshCloth)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.sourceRenderers"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.meshWriteMode"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.reductionSetting"));
}
EditorGUILayout.Space();
if (sync == false)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.updateMode"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.disableMode"));
}
else
{
// 同期中は操作不可
using (new EditorGUI.DisabledScope(true))
{
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Update Mode");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Disable Mode");
EditorGUILayout.LabelField("(Synchronizing)");
}
}
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.animationPoseRatio"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.blendWeight"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.normalAxis"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.normalAlignmentSetting.alignmentMode"), new GUIContent("Normal Alignment"));
if (cloth.SerializeData.normalAlignmentSetting.alignmentMode == NormalAlignmentSettings.AlignmentMode.Transform)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.normalAlignmentSetting.adjustmentTransform"));
}
if (clothType == ClothProcess.ClothType.MeshCloth)
{
EditorGUILayout.Space();
EditorGUILayout.PropertyField(paintMode);
if (paintMode.enumValueIndex != 0)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.paintMaps"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.paintMapUvChannel"));
}
}
}
// ペイントボタン
if (paintMode.enumValueIndex == 0)
{
EditorGUILayout.Space();
PaintButton(ClothPainter.PaintMode.Attribute);
}
else
EditorGUILayout.Space();
}
// Custom Skinning
if (isBoneSpring == false)
{
Foldout("Custom Skinning", serializedObject.FindProperty("serializeData.customSkinningSetting.enable"), null, () =>
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.customSkinningSetting.skinningBones"));
});
}
// Culling
Foldout("Culling", null, () =>
{
if (sync == false)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.cullingSettings.cameraCullingMode"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.cullingSettings.cameraCullingMethod"));
if (cloth.SerializeData.cullingSettings.cameraCullingMethod == CullingSettings.CameraCullingMethod.ManualRenderer)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.cullingSettings.cameraCullingRenderers"));
}
EditorGUILayout.Space();
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.cullingSettings.distanceCullingLength"));
using (new EditorGUI.DisabledScope(cloth.SerializeData.cullingSettings.distanceCullingLength.use == false))
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.cullingSettings.distanceCullingFadeRatio"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.cullingSettings.distanceCullingReferenceObject"));
EditorGUILayout.HelpBox("If Reference Object is [None], the main camera is referred.", MessageType.None);
}
}
else
{
// 同期中は操作不可
using (new EditorGUI.DisabledScope(true))
{
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Camera Culling Mode");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Camera Culling Method");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Distance Culling Length");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Distance Culling Fade Ratio");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Distance Culling Reference Object");
EditorGUILayout.LabelField("(Synchronizing)");
}
}
}
});
}
void ClothPreBuildInspector()
{
var cloth = target as MagicaCloth;
bool generation = false;
using (new EditorGUI.DisabledScope(EditorApplication.isPlaying))
{
Foldout("Pre-Build", serializedObject.FindProperty("serializeData2.preBuildData.enabled"), null, () =>
{
// information
var preBuildData = cloth.GetSerializeData2().preBuildData;
if (preBuildData.UsePreBuild())
{
DispClothStatus("[Pre-Build Construction]", preBuildData.DataValidate(), false);
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData2.preBuildData.buildId"), new GUIContent("Build ID"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData2.preBuildData.preBuildScriptableObject"), new GUIContent("Write Object"));
using (var horizontalScope = new GUILayout.HorizontalScope())
{
EditorGUILayout.Space();
GUI.backgroundColor = Color.red;
if (GUILayout.Button("Create PreBuild Data"))
{
generation = true;
}
GUI.backgroundColor = Color.white;
EditorGUILayout.Space();
}
});
}
if (generation)
{
// PreBuildデータ構築実行
Develop.Log($"Start PreBuild data creation.");
var preBuildResult = PreBuildDataCreation.CreatePreBuildData(cloth);
Develop.Log($"PreBuild data creation completed. [{cloth.GetSerializeData2().preBuildData.buildId}] : {preBuildResult.GetResultString()}");
}
}
void ClothParameterInspector()
{
var cloth = target as MagicaCloth;
var clothType = cloth.SerializeData.clothType;
bool isBoneSpring = clothType == ClothProcess.ClothType.BoneSpring;
// 同期状態
bool sync = EditorApplication.isPlaying && cloth.SyncPartnerCloth != null;
ClothPresetUtility.DrawPresetButton(cloth, cloth.SerializeData);
// Force
Foldout("Force", null, () =>
{
if (isBoneSpring == false)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.gravity"), new GUIContent("Gravity"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.gravityDirection"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.gravityFalloff"));
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.damping"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.stablizationTimeAfterReset"), new GUIContent("Stablization Time"));
});
// Spring
if (isBoneSpring)
{
Foldout("Spring", serializedObject.FindProperty("serializeData.springConstraint.useSpring"), null, () =>
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.springConstraint.springPower"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.springConstraint.limitDistance"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.springConstraint.normalLimitRatio"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.springConstraint.springNoise"));
});
}
// Angle Restoration
Foldout("Angle Restoration", serializedObject.FindProperty("serializeData.angleRestorationConstraint.useAngleRestoration"), null, () =>
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.angleRestorationConstraint.stiffness"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.angleRestorationConstraint.velocityAttenuation"));
if (isBoneSpring == false)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.angleRestorationConstraint.gravityFalloff"));
}
}
);
// Angle Limit
Foldout("Angle Limit", serializedObject.FindProperty("serializeData.angleLimitConstraint.useAngleLimit"), null, () =>
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.angleLimitConstraint.limitAngle"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.angleLimitConstraint.stiffness"));
}
);
// Shape
// BoneSpringではすべて定数なので隠蔽する
if (isBoneSpring == false)
{
Foldout("Shape Restoration", null, () =>
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.distanceConstraint.stiffness"), new GUIContent("Distance Stiffness"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.tetherConstraint.distanceCompression"), new GUIContent("Tether Compression"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.triangleBendingConstraint.stiffness"), new GUIContent("Triangle Bending Stiffness"));
});
}
// Inertia
Foldout("Inertia", null, () =>
{
if (sync == false)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.anchor"));
if (cloth.SerializeData.inertiaConstraint.anchor != null)
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.anchorInertia"));
EditorGUILayout.Space();
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.worldInertia"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.movementInertiaSmoothing"), new GUIContent("World Inertia Smoothing"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.movementSpeedLimit"), new GUIContent("World Movement Speed Limit"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.rotationSpeedLimit"), new GUIContent("World Rotation Speed Limit"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.teleportMode"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.teleportDistance"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.teleportRotation"));
}
else
{
// 同期中は操作不可
using (new EditorGUI.DisabledScope(true))
{
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Anchor");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Anchor Inertia");
EditorGUILayout.LabelField("(Synchronizing)");
}
EditorGUILayout.Space();
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("World Inertia");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("World Inertia Smoothing");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("World Movement Speed Limit");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("World Rotation Speed Limit");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Teleport Mode");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Teleport Distance");
EditorGUILayout.LabelField("(Synchronizing)");
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Teleport Rotation");
EditorGUILayout.LabelField("(Synchronizing)");
}
}
}
EditorGUILayout.Space();
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.localInertia"), new GUIContent("Local Inertia"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.localMovementSpeedLimit"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.localRotationSpeedLimit"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.depthInertia"), new GUIContent("Local Depth Inertia"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.centrifualAcceleration"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.inertiaConstraint.particleSpeedLimit"));
});
// Motion
if (isBoneSpring == false)
{
Foldout("Movement Limit", null, () =>
{
var useMaxDistance = serializedObject.FindProperty("serializeData.motionConstraint.useMaxDistance");
var useBackstop = serializedObject.FindProperty("serializeData.motionConstraint.useBackstop");
EditorGUILayout.PropertyField(useMaxDistance);
using (new EditorGUI.DisabledScope(!useMaxDistance.boolValue))
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.motionConstraint.maxDistance"));
}
EditorGUILayout.PropertyField(useBackstop);
using (new EditorGUI.DisabledScope(!useBackstop.boolValue))
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.motionConstraint.backstopRadius"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.motionConstraint.backstopDistance"));
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.motionConstraint.stiffness"));
var paintMode = serializedObject.FindProperty("serializeData.paintMode");
if (paintMode.enumValueIndex == 0)
PaintButton(ClothPainter.PaintMode.Motion);
}
);
}
// Collider Collision
Foldout("Collider Collision", null, () =>
{
if (isBoneSpring)
{
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Mode");
EditorGUILayout.LabelField("[Point]");
}
}
else
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.colliderCollisionConstraint.mode"));
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.radius"));
if (clothType == ClothProcess.ClothType.BoneSpring)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.colliderCollisionConstraint.limitDistance"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.colliderCollisionConstraint.collisionBones"));
}
else
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.colliderCollisionConstraint.friction"));
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.colliderCollisionConstraint.colliderList"));
}
);
// Self Collision
if (isBoneSpring == false)
{
Foldout("Self Collision", "Self Collision (Beta2)", () =>
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.selfCollisionConstraint.selfMode"));
var syncMode = serializedObject.FindProperty("serializeData.selfCollisionConstraint.syncMode");
EditorGUILayout.PropertyField(syncMode);
if (syncMode.enumValueIndex != 0)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.selfCollisionConstraint.syncPartner"));
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.selfCollisionConstraint.surfaceThickness"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.selfCollisionConstraint.clothMass"));
}
);
}
// Wind
Foldout("Wind", null, () =>
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.wind.influence"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.wind.frequency"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.wind.turbulence"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.wind.blend"), new GUIContent("Noise Blend"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.wind.synchronization"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.wind.depthWeight"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("serializeData.wind.movingWind"));
});
}
/// <summary>
/// 各プロパティの設定範囲.デフォルトは(0.0 ~ 1.0)
/// </summary>
/// <param name="propertyName"></param>
/// <returns></returns>
public static Vector2 GetPropertyMinMax(string propertyName)
{
var minmax = new Vector2(0.0f, 1.0f);
switch (propertyName)
{
case "radius":
minmax.Set(0.001f, 0.5f);
break;
case "limitAngle":
minmax.Set(0.0f, 180.0f);
break;
case "maxDistance":
minmax.Set(0.0f, 5.0f);
break;
case "surfaceThickness":
minmax.Set(Define.System.SelfCollisionThicknessMin, Define.System.SelfCollisionThicknessMax);
break;
case "movementSpeedLimit":
case "localMovementSpeedLimit":
minmax.Set(0.0f, Define.System.MaxMovementSpeedLimit);
break;
case "rotationSpeedLimit":
case "localRotationSpeedLimit":
minmax.Set(0.0f, Define.System.MaxRotationSpeedLimit);
break;
case "particleSpeedLimit":
minmax.Set(0.0f, Define.System.MaxParticleSpeedLimit);
break;
case "distanceCullingLength":
minmax.Set(0.0f, Define.System.DistanceCullingMaxLength);
break;
}
return minmax;
}
void GizmoInspector()
{
#if MC2_DEBUG
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData"));
#else
FoldOut("Gizmos", null, () =>
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.always"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.enable"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.ztest"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.position"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.axis"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.shape"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.baseLine"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.depth"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.collider"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.animatedPosition"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.animatedAxis"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.animatedShape"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.inertiaCenter"));
//EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.basicPosition"));
//EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.basicAxis"));
//EditorGUILayout.PropertyField(serializedObject.FindProperty("gizmoSerializeData.clothDebugSettings.basicShape"));
});
#endif
}
//=========================================================================================
/// <summary>
/// 折りたたみ制御
/// </summary>
/// <param name="foldKey">折りたたみ保存キー</param>
/// <param name="title"></param>
/// <param name="drawAct">内容描画アクション</param>
/// <param name="enableAct">有効フラグアクション(null=無効)</param>
/// <param name="enable">現在の有効フラグ</param>
public void Foldout(
string foldKey,
string title = null,
System.Action drawAct = null,
System.Action<bool> enableAct = null,
bool enable = true
)
{
var style = new GUIStyle("ShurikenModuleTitle");
style.font = new GUIStyle(EditorStyles.label).font;
style.border = new RectOffset(15, 7, 4, 4);
style.fixedHeight = 22;
style.contentOffset = new Vector2(20f, -2f);
var rect = GUILayoutUtility.GetRect(16f, 22f, style);
GUI.backgroundColor = Color.white;
GUI.Box(rect, title ?? foldKey, style);
var e = Event.current;
bool foldOut = EditorPrefs.GetBool(foldKey);
if (enableAct == null)
{
if (e.type == EventType.Repaint)
{
var arrowRect = new Rect(rect.x + 4f, rect.y + 2f, 13f, 13f);
EditorStyles.foldout.Draw(arrowRect, false, false, foldOut, false);
}
}
else
{
// 有効チェック
var toggleRect = new Rect(rect.x + 4f, rect.y + 4f, 13f, 13f);
bool sw = GUI.Toggle(toggleRect, enable, string.Empty, new GUIStyle("ShurikenCheckMark"));
if (sw != enable)
{
enableAct(sw);
}
}
if (e.type == EventType.MouseDown && rect.Contains(e.mousePosition))
{
foldOut = !foldOut;
EditorPrefs.SetBool(foldKey, foldOut);
e.Use();
}
if (foldOut && drawAct != null)
{
using (new EditorGUI.IndentLevelScope())
{
using (new EditorGUI.DisabledScope(!enable))
{
drawAct();
}
}
}
}
/// <summary>
/// 折りたたみ制御Boolプロパティによるチェックあり
/// </summary>
/// <param name="foldKey"></param>
/// <param name="boolProperty"></param>
/// <param name="title"></param>
/// <param name="drawAct"></param>
public void Foldout(
string foldKey,
SerializedProperty boolProperty,
string title = null,
System.Action drawAct = null
)
{
Foldout(
foldKey, title, drawAct,
(sw) => boolProperty.boolValue = sw,
boolProperty.boolValue
);
}
void FoldOut(string key, string title = null, System.Action drawAct = null)
{
bool foldOut1 = EditorPrefs.GetBool(key);
bool foldOut2 = EditorGUILayout.Foldout(foldOut1, title ?? key);
if (foldOut2)
{
using (new EditorGUI.IndentLevelScope())
{
drawAct?.Invoke();
}
}
if (foldOut1 != foldOut2)
{
EditorPrefs.SetBool(key, foldOut2);
}
}
void PaintButton(ClothPainter.PaintMode paintMode)
{
if (EditorApplication.isPlaying)
return;
var cloth = target as MagicaCloth;
using (new EditorGUILayout.HorizontalScope())
{
switch (paintMode)
{
case ClothPainter.PaintMode.Attribute:
GUI.backgroundColor = new Color(0.5f, 1.0f, 0.5f);
break;
case ClothPainter.PaintMode.Motion:
GUI.backgroundColor = new Color(0.0f, 1.0f, 1.0f);
break;
}
EditorGUILayout.Space();
bool edit = ClothPainter.HasEditCloth(cloth);
//var icon = edit ? EditorGUIUtility.IconContent("winbtn_win_close") : EditorGUIUtility.IconContent("d_editicon.sml");
//var icon = EditorGUIUtility.IconContent("d_Grid.PaintTool");// 良い
var icon = EditorGUIUtility.IconContent("d_editicon.sml");
if (GUILayout.Button(icon, GUILayout.Width(40)))
{
if (edit == false)
{
// 最新の編集メッシュからセレクションデータを生成する
var editMeshContainer = ClothEditorManager.GetEditMeshContainer(cloth);
if (editMeshContainer != null && editMeshContainer.shareVirtualMesh != null)
{
// すでにセレクションデータが存在し、かつユーザー編集データならばコンバートする
var selectionData = GetSelectionData(cloth, editMeshContainer.shareVirtualMesh);
// セレクションデータにメッシュの最大接続距離を記録する
selectionData.maxConnectionDistance = editMeshContainer.shareVirtualMesh.maxVertexDistance.Value;
// ペイント開始
ClothPainter.EnterPaint(paintMode, this, cloth, editMeshContainer, selectionData);
SceneView.RepaintAll();
}
}
else
{
// 初期化データの保存確認
ClothEditorManager.ApplyInitData(cloth, global: true);
ClothPainter.ExitPaint();
SceneView.RepaintAll();
}
}
EditorGUILayout.Space();
GUI.backgroundColor = Color.white;
}
EditorGUILayout.Space(10);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a43a416802744754899e8dc139f244c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaClothEditor.cs
uploadId: 893596

View File

@@ -0,0 +1,40 @@
// Magica Cloth 2.
// Copyright (c) 2024 MagicaSoft.
// https://magicasoft.jp
using System;
using UnityEditor;
namespace MagicaCloth2
{
/// <summary>
/// インスペクター拡張のベースクラス
/// </summary>
public class MagicaEditorBase : Editor
{
/// <summary>
/// マルチ選択編集の適用
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="changed"></param>
/// <param name="act"></param>
protected void ApplyMultiSelection<T>(bool changed, string undoName, Action<T> act) where T : ClothBehaviour
{
if (changed)
{
foreach (var obj in targets)
{
var tscr = obj as T;
// Undo
Undo.RecordObject(tscr, undoName);
act(tscr);
EditorUtility.SetDirty(tscr);
// OnValidate()手動呼び出し
tscr.GetType().GetMethod("OnValidate", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.Invoke(tscr, null);
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 3126cd1eb42b4af4aa38cea3c3836c3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaEditorBase.cs
uploadId: 893596

View File

@@ -0,0 +1,35 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEditor;
namespace MagicaCloth2
{
/// <summary>
/// PlaneColliderのインスペクター拡張
/// </summary>
[CustomEditor(typeof(MagicaPlaneCollider))]
[CanEditMultipleObjects]
public class MagicaPlaneColliderEditor : MagicaEditorBase
{
public override void OnInspectorGUI()
{
var scr = target as MagicaPlaneCollider;
serializedObject.Update();
Undo.RecordObject(scr, "PlaneCollider");
// center
EditorGUILayout.PropertyField(serializedObject.FindProperty("center"));
// Symmetry
EditorGUILayout.Space();
var symmetryModeProperty = serializedObject.FindProperty("symmetryMode");
EditorGUILayout.PropertyField(symmetryModeProperty);
if (symmetryModeProperty.enumValueIndex >= (int)ColliderSymmetryMode.AutomaticTarget)
EditorGUILayout.PropertyField(serializedObject.FindProperty("symmetryTarget"));
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7c2eaf60c91e70a4198cb71bbaf68678
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaPlaneColliderEditor.cs
uploadId: 893596

View File

@@ -0,0 +1,37 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEditor;
namespace MagicaCloth2
{
[CustomEditor(typeof(MagicaSettings))]
[CanEditMultipleObjects]
public class MagicaSettingsEditor : MagicaEditorBase
{
//=========================================================================================
/// <summary>
/// インスペクターGUI
/// </summary>
public override void OnInspectorGUI()
{
var comp = target as MagicaSettings;
serializedObject.Update();
Undo.RecordObject(comp, "MagicaSettings");
SettingsInspector();
serializedObject.ApplyModifiedProperties();
}
void SettingsInspector()
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("refreshMode"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("simulationFrequency"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("maxSimulationCountPerFrame"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("initializationLocation"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("updateLocation"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("monitorPlayerLoop"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("splitProxyMeshVertexCount"));
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 53b8f563506b5fa4691d2d29c3d3c185
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaSettingsEditor.cs
uploadId: 893596

View File

@@ -0,0 +1,50 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEditor;
namespace MagicaCloth2
{
/// <summary>
/// SphereColliderのインスペクター拡張
/// </summary>
[CustomEditor(typeof(MagicaSphereCollider))]
[CanEditMultipleObjects]
public class MagicaSphereColliderEditor : MagicaEditorBase
{
public override void OnInspectorGUI()
{
var scr = target as MagicaSphereCollider;
const string undoName = "SphereCollider";
serializedObject.Update();
Undo.RecordObject(scr, undoName);
using (var check = new EditorGUI.ChangeCheckScope())
{
// radius
var sizeValue = serializedObject.FindProperty("size");
//EditorGUI.showMixedValue = sizeValue.hasMultipleDifferentValues;
var size = sizeValue.vector3Value;
float newSize = EditorGUILayout.Slider("Radius", size.x, 0.001f, 0.5f);
//EditorGUI.showMixedValue = false;
ApplyMultiSelection<MagicaSphereCollider>(check.changed, undoName, x => x.SetSize(newSize));
}
// center
EditorGUILayout.PropertyField(serializedObject.FindProperty("center"));
// Symmetry
EditorGUILayout.Space();
var symmetryModeProperty = serializedObject.FindProperty("symmetryMode");
EditorGUILayout.PropertyField(symmetryModeProperty);
if (symmetryModeProperty.enumValueIndex >= (int)ColliderSymmetryMode.AutomaticTarget)
EditorGUILayout.PropertyField(serializedObject.FindProperty("symmetryTarget"));
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d26018c2091e9054ea0bf517157ca1c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaSphereColliderEditor.cs
uploadId: 893596

View File

@@ -0,0 +1,77 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
using UnityEditor;
using UnityEngine;
namespace MagicaCloth2
{
[CustomEditor(typeof(MagicaWindZone))]
[CanEditMultipleObjects]
public class MagicaWindZoneEditor : MagicaEditorBase
{
//=========================================================================================
/// <summary>
/// インスペクターGUI
/// </summary>
public override void OnInspectorGUI()
{
var wind = target as MagicaWindZone;
serializedObject.Update();
Undo.RecordObject(wind, "MagicaWindZone");
WindInspector();
serializedObject.ApplyModifiedProperties();
}
void WindInspector()
{
var wind = target as MagicaWindZone;
var modeProperty = serializedObject.FindProperty("mode");
EditorGUILayout.PropertyField(modeProperty);
//var mode = (MagicaWindZone.Mode)modeProperty.enumValueIndex;
if (wind.mode == MagicaWindZone.Mode.SphereDirection || wind.mode == MagicaWindZone.Mode.SphereRadial)
{
var radiusProperty = serializedObject.FindProperty("radius");
var radius = radiusProperty.floatValue;
float nr = EditorGUILayout.FloatField(new GUIContent("Radius"), radius);
nr = Mathf.Max(nr, 0.0f);
if (radius != nr)
{
radiusProperty.floatValue = nr;
}
}
else if (wind.mode == MagicaWindZone.Mode.BoxDirection)
{
var sizeProperty = serializedObject.FindProperty("size");
var size = sizeProperty.vector3Value;
var ns = EditorGUILayout.Vector3Field(new GUIContent("Box Size"), size);
ns = Vector3.Max(ns, Vector3.zero);
if (size != ns)
{
sizeProperty.vector3Value = ns;
}
}
EditorGUILayout.Space();
EditorGUILayout.PropertyField(serializedObject.FindProperty("main"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("turbulence"));
if (wind.IsDirection())
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("directionAngleX"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("directionAngleY"));
}
else
{
EditorGUILayout.CurveField(serializedObject.FindProperty("attenuation"), Color.green, new Rect(0, 0, 1, 1));
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("isAddition"));
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 56e75137613813345a058b4bb6748854
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaWindZoneEditor.cs
uploadId: 893596

View File

@@ -0,0 +1,538 @@
// Magica Cloth 2.
// Copyright (c) 2023 MagicaSoft.
// https://magicasoft.jp
#if MC2_DEBUG
using System.Collections.Generic;
using Unity.Collections;
using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
#endif
namespace MagicaCloth2
{
#if MC2_DEBUG
public static partial class VirtualMeshEditorUtility
{
//=========================================================================================
static List<Vector3> positionBuffer0 = new List<Vector3>(1024);
static List<Vector3> positionBuffer1 = new List<Vector3>(1024);
static List<Vector3> positionBuffer2 = new List<Vector3>(1024);
static List<int> segmentBuffer0 = new List<int>(2048);
static List<int> segmentBuffer1 = new List<int>(2048);
static List<int> segmentBuffer2 = new List<int>(2048);
//=========================================================================================
/// <summary>
/// VirtualMeshのデバッグ表示編集用
/// </summary>
/// <param name="vmesh"></param>
/// <param name="debugSettings"></param>
public static void DrawGizmos(VirtualMeshContainer cmesh, VirtualMeshDebugSettings debugSettings, bool selected, bool useHandles)
{
if (debugSettings.enable == false)
return;
if (cmesh == null || cmesh.shareVirtualMesh == null || cmesh.shareVirtualMesh.IsSuccess == false || cmesh.shareVirtualMesh.VertexCount == 0)
return;
var vmesh = cmesh.shareVirtualMesh;
var t = cmesh.GetCenterTransform();
if (t == null)
return;
using NativeArray<quaternion> dummyRotations = new NativeArray<quaternion>(0, Allocator.TempJob);
DrawGizmosInternal(
useHandles,
true,
selected,
cmesh,
debugSettings,
t,
0,
vmesh.attributes.GetNativeArray(),
vmesh.localPositions.GetNativeArray(),
dummyRotations,
vmesh.localNormals.GetNativeArray(),
vmesh.localTangents.GetNativeArray(),
0,
vmesh.boneWeights.GetNativeArray(),
vmesh.skinBoneTransformIndices.GetNativeArray(),
vmesh.uv.GetNativeArray()
);
}
/// <summary>
/// Proxy/Mappingメッシュのデバッグ表示ランタイム用
/// </summary>
/// <param name="cprocess"></param>
/// <param name="vmesh"></param>
/// <param name="debugSettings"></param>
public static void DrawRuntimeGizmos(ClothProcess cprocess, bool isMapping, VirtualMeshContainer cmesh, VirtualMeshDebugSettings debugSettings, bool selected, bool useHandles)
{
if (cprocess == null || cprocess.IsValid() == false)
return;
if (debugSettings.enable == false)
return;
if (MagicaManager.Team == null)
return;
if (MagicaManager.Team.ContainsTeamData(cprocess.TeamId) == false)
return;
if (cmesh == null || cmesh.shareVirtualMesh == null || cmesh.shareVirtualMesh.IsSuccess == false)
return;
var t = cmesh.GetCenterTransform();
if (t == null)
return;
// チームデータ
ref var tdata = ref MagicaManager.Team.GetTeamDataRef(cprocess.TeamId);
var tm = MagicaManager.Team;
var vm = MagicaManager.VMesh;
// メッシュタイプにより参照するデータを切り替える
var vmesh = cmesh.shareVirtualMesh;
var attributes = vmesh.IsMapping ? vm.mappingAttributes : vm.attributes;
var positions = vmesh.IsMapping ? vm.mappingPositions : vm.positions;
var rotations = vmesh.IsMapping ? null : vm.rotations;
int boneWeightOffset = vmesh.IsMapping ? 0 : tdata.proxySkinBoneChunk.startIndex + tdata.proxyTransformChunk.startIndex;
var boneWeights = vmesh.IsMapping ? vm.mappingBoneWeights : vm.boneWeights;
var skinBoneTransformIndices = vmesh.IsMapping ? vm.skinBoneTransformIndices : vm.skinBoneTransformIndices;
var uvs = vmesh.IsMapping ? null : vmesh.uv;
int vstart = vmesh.IsMapping ? tm.mappingDataArray[vmesh.mappingId].mappingCommonChunk.startIndex : tdata.proxyCommonChunk.startIndex;
using NativeArray<quaternion> dummyRotations = new NativeArray<quaternion>(0, Allocator.TempJob);
using NativeArray<float2> dummyUvs = new NativeArray<float2>(0, Allocator.TempJob);
DrawGizmosInternal(
useHandles,
isMapping ? true : false,
selected,
cmesh,
debugSettings,
t,
vstart,
attributes.GetNativeArray(),
positions.GetNativeArray(),
rotations != null ? rotations.GetNativeArray() : dummyRotations,
vmesh.localNormals.GetNativeArray(),
vmesh.localTangents.GetNativeArray(),
boneWeightOffset,
boneWeights.GetNativeArray(),
skinBoneTransformIndices.GetNativeArray(),
uvs != null ? uvs.GetNativeArray() : dummyUvs
);
}
static void DrawGizmosInternal(
bool useHandles,
bool isLocal,
bool selected,
VirtualMeshContainer cmesh,
VirtualMeshDebugSettings debugSettings,
Transform center,
int vstart,
NativeArray<VertexAttribute> attributes,
NativeArray<float3> positions,
NativeArray<quaternion> rotations,
NativeArray<float3> normals,
NativeArray<float3> tangents,
int boneWeightOffset,
NativeArray<VirtualMeshBoneWeight> boneWeights,
NativeArray<int> skinBoneTransformIndices,
NativeArray<float2> uvs
)
{
// 座標空間に合わせる
if (isLocal)
{
Handles.matrix = center.localToWorldMatrix;
if (useHandles == false)
Gizmos.matrix = center.localToWorldMatrix;
}
else
{
Handles.matrix = Matrix4x4.identity;
Gizmos.matrix = Matrix4x4.identity;
}
// シーンカメラ
var scam = SceneView.currentDrawingSceneView?.camera;
if (scam == null)
return;
quaternion camRot = scam.transform.rotation;
//quaternion invCamRot = math.inverse(camRot);
var vmesh = cmesh.shareVirtualMesh;
int vcnt = vmesh.VertexCount;
// 表示スケール調整
var scl = isLocal ? 1.0f / (center.lossyScale.magnitude / 1.7305f) : 1.0f;
var drawPointSize = debugSettings.pointSize * scl;
float drawAxisSize = debugSettings.lineSize * scl;
bool hasRotation = rotations.IsCreated && rotations.Length > 0;
bool hasUv = uvs.IsCreated && uvs.Length > 0;
float colorScale = selected ? 1 : 0.5f;
// position
if (debugSettings.position)
{
for (int i = 0; i < vcnt; i++)
{
if (i < debugSettings.vertexMinIndex || i > debugSettings.vertexMaxIndex)
continue;
var pos = positions[vstart + i];
var attr = attributes[vstart + i];
var col = Color.black;
if (attr.IsFixed()) col = Color.red;
if (attr.IsMove()) col = Color.green;
GizmoUtility.SetColor(col * colorScale, useHandles);
GizmoUtility.DrawSphere(pos, drawPointSize, useHandles);
}
}
// rotation axis
if (debugSettings.axis && hasRotation)
{
positionBuffer0.Clear();
positionBuffer1.Clear();
positionBuffer2.Clear();
segmentBuffer0.Clear();
segmentBuffer1.Clear();
segmentBuffer2.Clear();
for (int i = 0; i < vcnt; i++)
{
if (i < debugSettings.vertexMinIndex || i > debugSettings.vertexMaxIndex)
continue;
var pos = positions[vstart + i];
var y = hasRotation ? MathUtility.ToNormal(rotations[vstart + i]) : normals[vstart + i];
var z = hasRotation ? MathUtility.ToTangent(rotations[vstart + i]) : tangents[vstart + i];
var x = math.cross(y, z);
positionBuffer0.Add(pos);
positionBuffer0.Add(pos + x * drawAxisSize);
segmentBuffer0.Add(i * 2);
segmentBuffer0.Add(i * 2 + 1);
positionBuffer1.Add(pos);
positionBuffer1.Add(pos + y * drawAxisSize);
segmentBuffer1.Add(i * 2);
segmentBuffer1.Add(i * 2 + 1);
positionBuffer2.Add(pos);
positionBuffer2.Add(pos + z * drawAxisSize);
segmentBuffer2.Add(i * 2);
segmentBuffer2.Add(i * 2 + 1);
}
// x
Handles.color = Color.red * colorScale;
Handles.DrawLines(positionBuffer0.ToArray(), segmentBuffer0.ToArray());
// y
Handles.color = Color.green * colorScale;
Handles.DrawLines(positionBuffer1.ToArray(), segmentBuffer1.ToArray());
// z
Handles.color = Color.blue * colorScale;
Handles.DrawLines(positionBuffer2.ToArray(), segmentBuffer2.ToArray());
}
// bone weight
if (debugSettings.boneWeight)
{
for (int i = 0; i < vcnt; i++)
{
if (i < debugSettings.vertexMinIndex || i > debugSettings.vertexMaxIndex)
continue;
var pos = positions[vstart + i];
var bw = boneWeights[vstart + i];
bw.boneIndices += boneWeightOffset; // グローバルインデックスに変換
bw.boneIndices.x = skinBoneTransformIndices[bw.boneIndices.x];
bw.boneIndices.y = skinBoneTransformIndices[bw.boneIndices.y];
bw.boneIndices.z = skinBoneTransformIndices[bw.boneIndices.z];
bw.boneIndices.w = skinBoneTransformIndices[bw.boneIndices.w];
Handles.Label(pos, $"[{bw.boneIndices.x},{bw.boneIndices.y},{bw.boneIndices.z},{bw.boneIndices.w}] w({bw.weights.x:0.###}, {bw.weights.y:0.###}, {bw.weights.z:0.###}, {bw.weights.w:0.###})");
}
}
// number
else if (debugSettings.indexNumber)
{
for (int i = 0; i < vcnt; i++)
{
if (i < debugSettings.vertexMinIndex || i > debugSettings.vertexMaxIndex)
continue;
var pos = positions[vstart + i];
Handles.Label(pos, i.ToString());
}
}
// uv
else if (debugSettings.uv)
{
if (vmesh.IsProxy)
{
// プロキシのみ
for (int i = 0; i < vcnt; i++)
{
if (i < debugSettings.vertexMinIndex || i > debugSettings.vertexMaxIndex)
continue;
var pos = positions[vstart + i];
var uv = vmesh.uv[i];
Handles.Label(pos, $"({uv.x:0.####}, {uv.y:0.####})");
}
}
}
// depth
else if (debugSettings.depth)
{
if (vmesh.IsProxy)
{
if (vmesh.vertexDepths.IsCreated)
{
for (int i = 0; i < vcnt; i++)
{
if (i < debugSettings.vertexMinIndex || i > debugSettings.vertexMaxIndex)
continue;
var pos = positions[vstart + i];
var depth = vmesh.vertexDepths[i];
Handles.Label(pos, $"{depth:0.##}");
}
}
}
}
// root index
else if (debugSettings.rootIndex)
{
if (vmesh.IsProxy)
{
if (vmesh.vertexRootIndices.IsCreated)
{
for (int i = 0; i < vcnt; i++)
{
if (i < debugSettings.vertexMinIndex || i > debugSettings.vertexMaxIndex)
continue;
var pos = positions[vstart + i];
int rootIndex = vmesh.vertexRootIndices[i];
if (rootIndex >= 0)
Handles.Label(pos, $"{rootIndex}");
}
}
}
}
// parent index
else if (debugSettings.parentIndex)
{
if (vmesh.IsProxy)
{
if (vmesh.vertexParentIndices.IsCreated)
{
for (int i = 0; i < vcnt; i++)
{
if (i < debugSettings.vertexMinIndex || i > debugSettings.vertexMaxIndex)
continue;
var pos = positions[vstart + i];
int parentIndex = vmesh.vertexParentIndices[i];
//if (parentIndex < 0)
Handles.Label(pos, $"{parentIndex}");
}
}
}
}
// triangls
if (debugSettings.triangle)
{
positionBuffer0.Clear();
segmentBuffer0.Clear();
switch (vmesh.meshType)
{
case VirtualMesh.MeshType.NormalMesh:
GizmoUtility.SetColor(Color.magenta * colorScale, useHandles);
break;
case VirtualMesh.MeshType.NormalBoneMesh:
GizmoUtility.SetColor(Color.magenta * colorScale, useHandles);
break;
case VirtualMesh.MeshType.ProxyMesh:
GizmoUtility.SetColor(new Color(0.8666f, 0.627f, 0.8666f) * colorScale, useHandles);
break;
case VirtualMesh.MeshType.ProxyBoneMesh:
GizmoUtility.SetColor(new Color(0.8666f, 0.627f, 0.8666f) * colorScale, useHandles);
break;
case VirtualMesh.MeshType.Mapping:
GizmoUtility.SetColor(new Color(0.851f, 0.644f, 0.125f) * colorScale, useHandles);
break;
}
for (int i = 0; i < vmesh.TriangleCount; i++)
{
if (i < debugSettings.triangleMinIndex || i > debugSettings.triangleMaxIndex)
continue;
int3 tri = vmesh.triangles[i];
var pos1 = positions[vstart + tri.x];
var pos2 = positions[vstart + tri.y];
var pos3 = positions[vstart + tri.z];
int index = positionBuffer0.Count;
positionBuffer0.Add(pos1);
positionBuffer0.Add(pos2);
positionBuffer0.Add(pos3);
segmentBuffer0.Add(index);
segmentBuffer0.Add(index + 1);
segmentBuffer0.Add(index + 1);
segmentBuffer0.Add(index + 2);
segmentBuffer0.Add(index + 2);
segmentBuffer0.Add(index);
}
Handles.DrawLines(positionBuffer0.ToArray(), segmentBuffer0.ToArray());
}
// line
if (debugSettings.line)
{
positionBuffer0.Clear();
segmentBuffer0.Clear();
GizmoUtility.SetColor(Color.cyan * colorScale, useHandles);
for (int i = 0; i < vmesh.LineCount; i++)
{
int2 line = vmesh.lines[i];
var pos1 = positions[vstart + line.x];
var pos2 = positions[vstart + line.y];
positionBuffer0.Add(pos1);
positionBuffer0.Add(pos2);
segmentBuffer0.Add(i * 2);
segmentBuffer0.Add(i * 2 + 1);
}
Handles.DrawLines(positionBuffer0.ToArray(), segmentBuffer0.ToArray());
}
// edge number
if (debugSettings.edgeNumber)
{
int ecnt = vmesh.EdgeCount;
for (int i = 0; i < ecnt; i++)
{
if (i < debugSettings.edgeMinIndex || i > debugSettings.edgeMaxIndex)
continue;
int2 edge = vmesh.edges[i];
var pos1 = positions[vstart + edge.x];
var pos2 = positions[vstart + edge.y];
var c = (pos1 + pos2) * 0.5f;
Handles.Label(c, i.ToString());
}
}
// triangle normal
if (debugSettings.triangleNormal || debugSettings.triangleNumber || debugSettings.triangleTangent)
{
positionBuffer0.Clear();
segmentBuffer0.Clear();
positionBuffer1.Clear();
segmentBuffer1.Clear();
Color colF = Color.yellow;
Color colB = new Color(1.0f, 0.3f, 0.3f, 1.0f);
for (int i = 0; i < vmesh.TriangleCount; i++)
{
if (i < debugSettings.triangleMinIndex || i > debugSettings.triangleMaxIndex)
continue;
int3 tri = vmesh.triangles[i];
var pos1 = positions[vstart + tri.x];
var pos2 = positions[vstart + tri.y];
var pos3 = positions[vstart + tri.z];
var n = MathUtility.TriangleNormal(pos1, pos2, pos3);
var c = MathUtility.TriangleCenter(pos1, pos2, pos3);
if (debugSettings.triangleNormal)
{
//var dir = math.mul(invCamRot, n);
//GizmoUtility.SetColor((dir.z < 0.0f ? colF : colB) * colorScale, useHandles);
//GizmoUtility.DrawLine(c, c + n * drawAxisSize, useHandles);
positionBuffer0.Add(c);
positionBuffer0.Add(c + n * drawAxisSize);
segmentBuffer0.Add(i * 2);
segmentBuffer0.Add(i * 2 + 1);
}
if (debugSettings.triangleTangent && hasUv)
{
var uv1 = uvs[vstart + tri.x];
var uv2 = uvs[vstart + tri.y];
var uv3 = uvs[vstart + tri.z];
var tn = MathUtility.TriangleTangent(pos1, pos2, pos3, uv1, uv2, uv3);
positionBuffer1.Add(c);
positionBuffer1.Add(c + tn * drawAxisSize);
segmentBuffer1.Add(i * 2);
segmentBuffer1.Add(i * 2 + 1);
}
if (debugSettings.triangleNumber)
Handles.Label(c, i.ToString());
}
if (debugSettings.triangleNormal)
{
GizmoUtility.SetColor(colF * colorScale, useHandles);
Handles.DrawLines(positionBuffer0.ToArray(), segmentBuffer0.ToArray());
}
if (debugSettings.triangleTangent && hasUv)
{
GizmoUtility.SetColor(colB * colorScale, useHandles);
Handles.DrawLines(positionBuffer1.ToArray(), segmentBuffer1.ToArray());
}
}
// base line
if (vmesh.IsProxy)
{
// プロキシのみ
if (debugSettings.baseLine && vmesh.BaseLineCount > 0)
{
GizmoUtility.SetColor(new Color(1.0f, 0.27f, 0.0f) * colorScale, useHandles);
positionBuffer0.Clear();
segmentBuffer0.Clear();
for (int i = 0; i < vcnt; i++)
{
int pindex = vmesh.vertexParentIndices[i];
if (pindex >= 0)
{
var pos = positions[vstart + i];
var ppos = positions[vstart + pindex];
//GizmoUtility.DrawLine(pos, ppos, useHandles);
positionBuffer0.Add(pos);
positionBuffer0.Add(ppos);
segmentBuffer0.Add(i * 2);
segmentBuffer0.Add(i * 2 + 1);
}
}
Handles.DrawLines(positionBuffer0.ToArray(), segmentBuffer0.ToArray());
}
}
// 空間を戻す
Handles.matrix = Matrix4x4.identity;
if (useHandles == false)
Gizmos.matrix = Matrix4x4.identity;
// bone name
if (debugSettings.boneName)
{
//int cnt = vmesh.transformData.Count;
int cnt = cmesh.GetTransformCount();
for (int i = 0; i < cnt; i++)
{
//var t = vmesh.transformData.GetTransformFromIndex(i);
var t = cmesh.GetTransformFromIndex(i);
if (t)
{
var pos = t.position;
Handles.Label(pos, $"[{i}] {t.name}");
}
}
}
}
}
#endif // MC_DEBUG
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 2e052cf3e2e3eca4f9c71f84cb7712ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 242307
packageName: Magica Cloth 2
packageVersion: 2.18.1
assetPath: Assets/MagicaCloth2/Scripts/Editor/Cloth/VirtualMeshEditorUtility.cs
uploadId: 893596