// Magica Cloth 2. // Copyright (c) 2023 MagicaSoft. // https://magicasoft.jp using Unity.Mathematics; using UnityEditor; using UnityEngine; namespace MagicaCloth2 { public static class GizmoUtility { // ギズモカラー定義 public static readonly Color ColorCollider = new Color(0.0f, 1.0f, 0.0f); public static readonly Color ColorSymmetryCollider = new Color(0.0f, 1.0f, 1.0f); public static readonly Color ColorNonSelectedCollider = new Color(0.5f, 0.3f, 0.0f); public static readonly Color ColorSkinningBone = new Color(1.0f, 0.5f, 0.0f); public static readonly Color ColorWindZone = new Color(1f, 1f, 1f); public static readonly Color ColorWindArrow = new Color(1f, 1f, 0f); public static readonly Quaternion FlipZ = Quaternion.AngleAxis(180.0f, Vector3.up); //========================================================================================= public static void SetColor(Color col, bool useHandles) { if (useHandles) Handles.color = col; else Gizmos.color = col; } public static void DrawSphere(Vector3 pos, float radius, bool useHandles) { if (useHandles) { // ソリッド球のサイズ指定は半径ではなく直径なので2倍する Handles.SphereHandleCap(0, pos, Quaternion.identity, radius * 2, EventType.Repaint); } else Gizmos.DrawSphere(pos, radius); } public static void DrawWireSphere(Vector3 pos, Quaternion rot, float radius, Quaternion camRot, bool useHandles) { if (useHandles) { Handles.CircleHandleCap(0, pos, camRot, radius, EventType.Repaint); Handles.CircleHandleCap(0, pos, rot, radius, EventType.Repaint); Handles.CircleHandleCap(0, pos, rot * Quaternion.Euler(90, 0, 0), radius, EventType.Repaint); Handles.CircleHandleCap(0, pos, rot * Quaternion.Euler(0, 90, 0), radius, EventType.Repaint); } else Gizmos.DrawWireSphere(pos, radius); } public static void DrawSimpleWireSphere(Vector3 pos, float radius, Quaternion camRot, bool useHandles) { if (useHandles) { Handles.CircleHandleCap(0, pos, camRot, radius, EventType.Repaint); //Handles.CircleHandleCap(0, pos, rot, radius, EventType.Repaint); //Handles.CircleHandleCap(0, pos, rot * Quaternion.Euler(90, 0, 0), radius, EventType.Repaint); //Handles.CircleHandleCap(0, pos, rot * Quaternion.Euler(0, 90, 0), radius, EventType.Repaint); } else Gizmos.DrawWireSphere(pos, radius); } public static void DrawLine(Vector3 from, Vector3 to, bool useHandles) { if (useHandles) Handles.DrawLine(from, to); else Gizmos.DrawLine(from, to); } public static void DrawWireCapsule(Vector3 pos, Quaternion rot, Vector3 dir, Vector3 up, float sradius, float eradius, float len, bool alignedCenter, Quaternion camRot, bool useHandles) { if (useHandles) { //float slen = len * 0.5f; //float elen = len * 0.5f; float slen = alignedCenter ? len * 0.5f : 0.0f; float elen = alignedCenter ? len * 0.5f : (len - sradius); slen = Mathf.Max(slen - sradius, 0.0f); elen = Mathf.Max(elen - eradius, 0.0f); Vector3 sl = dir * slen; Vector3 el = -dir * elen; var spos = pos + rot * sl; var epos = pos + rot * el; DrawWireSphere(spos, rot, sradius, camRot, true); DrawWireSphere(epos, rot, eradius, camRot, true); for (int i = 0; i < 360; i += 45) { var q = Quaternion.AngleAxis(i, dir); var up1 = q * (up * sradius); var up2 = q * (up * eradius); Handles.DrawLine(spos + up1, epos + up2); } } else { } } //public static void DrawWireCapsule(Vector3 spos, Vector3 epos, Quaternion rot, float sradius, float eradius, Quaternion camRot, bool useHandles) //{ // if (useHandles) // { // DrawWireSphere(spos, rot, sradius, camRot, true); // DrawWireSphere(epos, rot, eradius, camRot, true); // var ps = spos + camRot * Vector3.up * sradius; // var es = epos + camRot * Vector3.up * eradius; // Handles.DrawLine(ps, es); // } // else // { // } //} /// /// ワイヤーボックスを描画する /// /// /// /// /// public static void DrawWireCube(Vector3 pos, Quaternion rot, Vector3 size, bool useHandles) { if (useHandles) { Handles.DrawWireCube(pos, size); // 何故かカラーが反映しない!バグっぽい } else { Gizmos.DrawWireCube(pos, size); } } public static void DrawCross(Vector3 pos, Quaternion rot, float size, bool useHandles) { if (useHandles) { Handles.color = Color.red; Handles.DrawLine(pos, pos + rot * Vector3.right * size); Handles.color = Color.green; Handles.DrawLine(pos, pos + rot * Vector3.up * size); Handles.color = Color.blue; Handles.DrawLine(pos, pos + rot * Vector3.forward * size); } else { Gizmos.color = Color.red; Gizmos.DrawLine(pos, pos + rot * Vector3.right * size); Gizmos.color = Color.green; Gizmos.DrawLine(pos, pos + rot * Vector3.up * size); Gizmos.color = Color.blue; Gizmos.DrawLine(pos, pos + rot * Vector3.forward * size); } } //========================================================================================= public static void DrawCollider(ColliderComponent collider, Quaternion camRot, bool selected) { if (collider == null) return; Handles.color = selected ? ColorCollider : ColorCollider * 0.5f; // Main var ct = collider.transform; //var cpos = ct.TransformPoint(collider.center); float3 cpos = ct.position; quaternion crot = ct.rotation; float3 cscl = ct.lossyScale; // マイナススケール float3 sclSign = math.sign(cscl); // オフセット cpos += math.mul(crot, collider.center * sclSign) * cscl * sclSign; // カメラ回転をコライダーのローカル回転に変換 var camRotN = Quaternion.Inverse(crot) * camRot; DrawColliderInternal(collider, camRotN, cpos, crot, cscl, 1.0f); // Symmetry // 実行時と同じ計算をして表示 ColliderSymmetryMode? smode = ColliderSymmetryMode.None; Transform symmetryParent = null; if (EditorApplication.isPlaying) { smode = collider.ActiveSymmetryMode; symmetryParent = collider.ActiveSymmetryTarget; } if (smode.HasValue == false || smode == ColliderSymmetryMode.None) smode = collider.CalcSymmetryMode(out symmetryParent); if (smode != ColliderSymmetryMode.None && symmetryParent) { float3 lpos = ct.localPosition; //float3 lerot = ct.localEulerAngles; float3 lerot = MathUtility.ToEuler(ct.localRotation); float3 lscl = ct.localScale; float3 center = collider.center; switch (smode) { case ColliderSymmetryMode.X_Symmetry: lpos.x = -lpos.x; center.x = -center.x; lerot.y = -lerot.y; lerot.z = -lerot.z; break; case ColliderSymmetryMode.Y_Symmetry: lpos.y = -lpos.y; center.y = -center.y; lerot.x = -lerot.x; lerot.z = -lerot.z; break; case ColliderSymmetryMode.Z_Symmetry: lpos.z = -lpos.z; center.z = -center.z; lerot.x = -lerot.x; lerot.y = -lerot.y; break; case ColliderSymmetryMode.XYZ_Symmetry: lpos = -lpos; center = -center; break; default: return; } // 方向性 float direction = 1.0f; if (collider is MagicaCapsuleCollider) { var ccol = collider as MagicaCapsuleCollider; if (smode == ColliderSymmetryMode.X_Symmetry && ccol.direction == MagicaCapsuleCollider.Direction.X) direction = -1.0f; else if (smode == ColliderSymmetryMode.Y_Symmetry && ccol.direction == MagicaCapsuleCollider.Direction.Y) direction = -1.0f; else if (smode == ColliderSymmetryMode.Z_Symmetry && ccol.direction == MagicaCapsuleCollider.Direction.Z) direction = -1.0f; else if (smode == ColliderSymmetryMode.XYZ_Symmetry) direction = -1.0f; } else if (collider is MagicaPlaneCollider) { switch (smode) { case ColliderSymmetryMode.Y_Symmetry: case ColliderSymmetryMode.XYZ_Symmetry: direction = -1.0f; break; } } // シンメトリーの親 float3 ppos = symmetryParent.position; quaternion prot = symmetryParent.rotation; float3 pscl = symmetryParent.lossyScale; // マイナススケール sclSign = math.sign(pscl); float3 sclEulerSign = 1; if (pscl.x < 0 || pscl.y < 0 || pscl.z < 0) sclEulerSign = sclSign * -1; // シンメトリーコライダーの姿勢 float3 wpos = MathUtility.TransformPoint(lpos, ppos, prot, pscl); quaternion wrot = math.mul(prot, quaternion.Euler(math.radians(lerot * sclEulerSign))); float3 wscl = pscl * lscl; wpos += math.mul(wrot, center * sclSign) * wscl * sclSign; // カメラ回転をコライダーのローカル回転に変換 var camRotS = Quaternion.Inverse(wrot) * camRot; Handles.color = selected ? ColorSymmetryCollider : ColorSymmetryCollider * 0.5f; DrawColliderInternal(collider, camRotS, wpos, wrot, wscl, direction); } } static void DrawColliderInternal(ColliderComponent collider, Quaternion camRot, Vector3 cpos, Quaternion crot, Vector3 cscl, float direction) { var size = collider.GetSize(); Handles.matrix = Matrix4x4.TRS(cpos, crot, cscl); if (collider is MagicaSphereCollider) { DrawWireSphere(Vector3.zero, Quaternion.identity, size.x, camRot, true); } else if (collider is MagicaPlaneCollider) { DrawWireCube(Vector3.zero, Quaternion.identity, new Vector3(1.0f, 0.0f, 1.0f) * 1.0f, true); DrawLine(Vector3.zero, Vector3.up * 0.25f * direction, true); } else if (collider is MagicaCapsuleCollider) { var c = collider as MagicaCapsuleCollider; var ldir = c.GetLocalDir() * direction; var lup = c.GetLocalUp(); DrawWireCapsule(Vector3.zero, Quaternion.identity, ldir, lup, size.x, size.y, size.z, c.alignedOnCenter, camRot, true); } } /// /// ワイヤーカプセルを描画する /// UnityのCapsuleColliderと同じ /// /// 基準座標 /// 基準回転 /// カプセルの方向 /// カプセルの上方向 /// カプセルの長さ /// 始点の半径 /// 終点の半径 public static void DrawWireCapsule( Vector3 pos, Quaternion rot, Vector3 scl, Vector3 ldir, Vector3 lup, float length, float startRadius, float endRadius, bool resetMatrix = true ) { Gizmos.matrix = Matrix4x4.TRS(pos, rot, scl); float slen = length * 0.5f; float elen = length * 0.5f; slen = Mathf.Max(slen - startRadius, 0.0f); elen = Mathf.Max(elen - endRadius, 0.0f); var sl = ldir * slen; var el = -ldir * elen; Gizmos.DrawWireSphere(sl, startRadius); Gizmos.DrawWireSphere(el, endRadius); for (int i = 0; i < 360; i += 45) { var q = Quaternion.AngleAxis(i, ldir); var up1 = q * (lup * startRadius); var up2 = q * (lup * endRadius); Gizmos.DrawLine(sl + up1, el + up2); } // 45度ずらしてもう1回球を描く Gizmos.matrix = Matrix4x4.TRS(pos, rot * Quaternion.AngleAxis(45, ldir), scl); Gizmos.DrawWireSphere(sl, startRadius); Gizmos.DrawWireSphere(el, endRadius); if (resetMatrix) Gizmos.matrix = Matrix4x4.identity; } /// /// ワイヤー球を描画する /// UnityのSphereColliderと同じ /// /// 基準座標 /// 基準回転 /// 半径 /// public static void DrawWireSphere( Vector3 pos, Quaternion rot, Vector3 scl, float radius, bool drawSphere, bool drawAxis, bool resetMatrix = true) { //Gizmos.matrix = Matrix4x4.TRS(pos, rot, Vector3.one); Gizmos.matrix = Matrix4x4.TRS(pos, rot, scl); // 球 if (drawSphere) Gizmos.DrawWireSphere(Vector3.zero, radius); // 軸 if (drawAxis) { const float axisRadius = 0.03f; Gizmos.color = Color.red; Gizmos.DrawLine(Vector3.zero, Vector3.right * axisRadius); Gizmos.color = Color.green; Gizmos.DrawLine(Vector3.zero, Vector3.up * axisRadius); Gizmos.color = Color.blue; Gizmos.DrawLine(Vector3.zero, Vector3.forward * axisRadius); } // 45度ずらしてもう1回球を描く //Gizmos.matrix = Matrix4x4.TRS(pos, rot * Quaternion.AngleAxis(45, Vector3.up), Vector3.one); //Gizmos.DrawWireSphere(Vector3.zero, radius); if (resetMatrix) Gizmos.matrix = Matrix4x4.identity; } #if false /// /// ワイヤーボックスを描画する /// /// /// /// /// public static void DrawWireCube(Vector3 pos, Quaternion rot, Vector3 size, bool resetMatrix = true) { Gizmos.matrix = Matrix4x4.TRS(pos, rot, Vector3.one); Gizmos.DrawWireCube(Vector3.zero, size); if (resetMatrix) Gizmos.matrix = Matrix4x4.identity; } #endif public static void DrawWireCone(Vector3 pos, Quaternion rot, float length, float radius, int div = 8) { Gizmos.matrix = Matrix4x4.TRS(pos, rot, Vector3.one); var epos = Vector3.forward * length; Vector3 oldpos = epos; for (int i = 0; i < div; i++) { float t = (float)i / (float)div; var q = Quaternion.AngleAxis(t * 360.0f, Vector3.forward); var x = q * Vector3.right * radius; Gizmos.DrawLine(Vector3.zero, epos + x); Gizmos.DrawLine(epos, epos + x); if (i > 0) Gizmos.DrawLine(oldpos, epos + x); oldpos = epos + x; } Gizmos.DrawLine(oldpos, epos + Vector3.right * radius); Gizmos.matrix = Matrix4x4.identity; } /// /// ワイヤー矢印を描画する /// /// /// /// /// 十字描画 public static void DrawWireArrow(Vector3 pos, Quaternion rot, Vector3 size, bool cross = false) { //Gizmos.matrix = Matrix4x4.TRS(pos, rot, size); Handles.matrix = Matrix4x4.TRS(pos, rot, size); Vector3[] points = new Vector3[] { new Vector3(0.0f, 0.0f, -1.0f), new Vector3(0.0f, 0.5f, -1.0f), new Vector3(0.0f, 0.5f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f), new Vector3(0.0f, 0.0f, 1.0f), }; float addAngle = cross ? 90.0f : 180.0f; int loop = cross ? 4 : 2; for (int j = 0; j < loop; j++) { for (int i = 0; i < points.Length - 1; i++) { //Gizmos.DrawLine(points[i], points[i + 1]); Handles.DrawLine(points[i], points[i + 1]); } rot = rot * Quaternion.AngleAxis(addAngle, Vector3.forward); //Gizmos.matrix = Matrix4x4.TRS(pos, rot, size); Handles.matrix = Matrix4x4.TRS(pos, rot, size); } //Gizmos.matrix = Matrix4x4.identity; Handles.matrix = Matrix4x4.identity; } /// /// XYZ軸を描画する /// /// /// /// /// public static void DrawAxis(Vector3 pos, Quaternion rot, float size, bool resetMatrix = true) { Gizmos.matrix = Matrix4x4.TRS(pos, rot, Vector3.one); Gizmos.color = Color.red; Gizmos.DrawRay(Vector3.zero, Vector3.right * size); Gizmos.color = Color.green; Gizmos.DrawRay(Vector3.zero, Vector3.up * size); Gizmos.color = Color.blue; Gizmos.DrawRay(Vector3.zero, Vector3.forward * size); if (resetMatrix) Gizmos.matrix = Matrix4x4.identity; } /// /// ボーン形状を描画する /// /// /// /// public static void DrawBone(Vector3 pos, Vector3 tpos, float size) { var v = tpos - pos; var rot = Quaternion.FromToRotation(Vector3.forward, v); Gizmos.matrix = Matrix4x4.TRS(pos, rot, Vector3.one); Gizmos.color = ColorSkinningBone; Gizmos.DrawWireSphere(Vector3.zero, size); //Gizmos.DrawLine(Vector3.zero, Vector3.forward * v.magnitude); float bsize = size * 0.8f; float zoff = size; var gpos = Vector3.forward * v.magnitude; var p0 = new Vector3(bsize, bsize, zoff); var p1 = new Vector3(bsize, -bsize, zoff); var p2 = new Vector3(-bsize, -bsize, zoff); var p3 = new Vector3(-bsize, bsize, zoff); Gizmos.DrawLine(p0, gpos); Gizmos.DrawLine(p1, gpos); Gizmos.DrawLine(p2, gpos); Gizmos.DrawLine(p3, gpos); Gizmos.DrawLine(p0, p1); Gizmos.DrawLine(p1, p2); Gizmos.DrawLine(p2, p3); Gizmos.DrawLine(p3, p0); Gizmos.matrix = Matrix4x4.identity; } //========================================================================================= /// /// Handlesによるコーンの描画 /// /// /// /// /// 角度(deg) /// /// /// public static void ConeHandle( Vector3 pos, Quaternion rot, float size, float angle, Color coneColor, Color wireColor, float wireThickness = 1.0f, int controllId = 0 ) { // デフォルトのコーン描画は角度や直径が決まっているので描画角度によりそれをスケーリングさせる // サイズ1のオリジナルコーンの情報 // 底面の半径0.4, 直径0.8 // 高さ1.2(中心から底面まで0.5, 中心から始点まで0.7) // 角度18.434948度 //float size = scr.size; float rad = Mathf.Deg2Rad * angle; float z = Mathf.Cos(rad); float xy = Mathf.Sin(rad); // 角度18.434948の倍率 const float cosBaseScl = 1.0f / 0.94868329f; const float sinBaseScl = 1.0f / 0.31622775f; z *= cosBaseScl; xy *= sinBaseScl; //Debug.Log(z); float zoffset = size * 0.7f; //readonly Quaternion flipQ = Quaternion.AngleAxis(180.0f, Vector3.up); //Handles.matrix = Matrix4x4.TRS(Vector3.zero, rot, new Vector3(xy, xy, z)); Handles.matrix = Matrix4x4.TRS(pos, rot * FlipZ, new Vector3(xy, xy, z)); //var pos = transform.position; // cone Handles.color = coneColor; Vector3 offset = new Vector3(0.0f, 0.0f, -zoffset); Handles.ConeHandleCap( controllId, //pos + offset, offset, Quaternion.identity, size, EventType.Repaint ); // wire dist Handles.color = wireColor; Handles.DrawWireDisc( //pos + new Vector3(0.0f, 0.0f, -size * 1.2f), new Vector3(0.0f, 0.0f, -size * 1.2f), Vector3.forward, size * (0.4f / 1.0f), wireThickness ); // wire //Handles.color = scr.wireColor; //var spos = pos; const int div = 6; const float angStep = 360.0f / div; for (int i = 0; i < div; i++) { float ang = Mathf.Deg2Rad * i * angStep; float x = Mathf.Sin(ang); float y = Mathf.Cos(ang); x *= size * (0.4f / 1.0f); y *= size * (0.4f / 1.0f); //Handles.DrawLine(spos, new Vector3(x, y, -size * 1.2f), wireThickness); Handles.DrawLine(Vector3.zero, new Vector3(x, y, -size * 1.2f), wireThickness); } Handles.matrix = Matrix4x4.identity; } /// /// 扇を描画する /// /// /// /// /// /// /// /// /// public static void ArcHandle( Vector3 pos, Quaternion rot, float angle, float size, float wireThickness, Color arcColor1, Color arcColor2, Color wireColor ) { var up = rot * Vector3.up; var right = rot * Vector3.right; var forward = rot * Vector3.forward; Handles.color = arcColor1; Handles.DrawSolidArc(pos, up, forward, angle, size); Handles.DrawSolidArc(pos, -up, forward, angle, size); Handles.color = arcColor2; Handles.DrawSolidArc(pos, right, forward, angle, size); Handles.DrawSolidArc(pos, -right, forward, angle, size); Handles.color = wireColor; Handles.DrawLine(pos, pos + forward * size, wireThickness); } //========================================================================================= public static void DrawWindZone(MagicaWindZone windZone, Quaternion camRot, bool selected) { if (windZone == null) return; // ゾーン Handles.matrix = windZone.transform.localToWorldMatrix; Handles.color = selected ? ColorWindZone : ColorWindZone * 0.5f; switch (windZone.mode) { case MagicaWindZone.Mode.GlobalDirection: break; case MagicaWindZone.Mode.BoxDirection: DrawWireCube(Vector3.zero, Quaternion.identity, windZone.size, true); break; case MagicaWindZone.Mode.SphereDirection: case MagicaWindZone.Mode.SphereRadial: // カメラ回転をコライダーのローカル回転に変換 camRot = Quaternion.Inverse(windZone.transform.rotation) * camRot; DrawWireSphere(Vector3.zero, Quaternion.identity, windZone.radius, camRot, true); break; } // 方向 Handles.color = selected ? ColorWindArrow : ColorWindArrow * 0.5f; var pos = windZone.transform.position; const float gsize = 0.5f; if (windZone.IsDirection()) { var rot = MathUtility.AxisQuaternion(windZone.GetWindDirection()); DrawWireArrow(pos, rot, new Vector3(gsize, gsize, gsize * 2), true); } else if (windZone.IsRadial()) { DrawLine(new Vector3(0, -gsize, 0), new Vector3(0, gsize, 0), true); DrawLine(new Vector3(-gsize, 0, 0), new Vector3(gsize, 0, 0), true); DrawLine(new Vector3(0, 0, -gsize), new Vector3(0, 0, gsize), true); } Handles.matrix = Matrix4x4.identity; } } }