11
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
1110
Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothEditorManager.cs
Normal file
1110
Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothEditorManager.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
1161
Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothEditorUtility.cs
Normal file
1161
Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothEditorUtility.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
949
Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothPainter.cs
Normal file
949
Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothPainter.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
232
Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothPresetUtility.cs
Normal file
232
Assets/MagicaCloth2/Scripts/Editor/Cloth/ClothPresetUtility.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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を設定すると異なる値の場合は「ー」で表記されるようになるが、
|
||||
// カプセルコライダーでは1つのVector3に3つの異なるスライダーを割り当てているため、
|
||||
// 各スライダーの値が同じ場合でも「ー」表記になってしまう
|
||||
// そうならないようにアクティブなコンポーネントの数値のみを表示するように、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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
951
Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaClothEditor.cs
Normal file
951
Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaClothEditor.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
40
Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaEditorBase.cs
Normal file
40
Assets/MagicaCloth2/Scripts/Editor/Cloth/MagicaEditorBase.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user