You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

582 lines
21 KiB
C#

/******************************************************************************/
/*
Project - MudBun
Publisher - Long Bunny Labs
http://LongBunnyLabs.com
Author - Ming-Lun "Allen" Chou
http://AllenChou.net
*/
/******************************************************************************/
using UnityEngine;
namespace MudBun
{
public class GizmosUtil
{
public static readonly Color OutlineDefault = new Color(1.0f, 1.0f, 1.0f, 0.5f);
public static readonly Color OutlineSelected = new Color(1.0f, 0.7f, 0.1f, 0.5f);
public static readonly Color Transparent = new Color(1.0f, 1.0f, 1.0f, 0.0f);
public static void DrawLine(Vector3 a, Vector3 b)
{
Gizmos.DrawLine(a, b);
}
public static void DrawCircle(float radius, Vector3 center, Quaternion rotation)
{
int numSegments = 32;
float t = 0.0f;
float dt = MathUtil.TwoPi / numSegments;
Vector3 prev = center + rotation * new Vector3(radius, 0.0f, 0.0f);
for (int i = 0; i < numSegments; ++i)
{
t += dt;
Vector3 curr = center + rotation * new Vector3(radius * Mathf.Cos(t), 0.0f, radius * Mathf.Sin(t));
DrawLine(prev, curr);
prev = curr;
}
}
public static void DrawWireBox(Vector3 center, Vector3 size, Quaternion rotation)
{
Matrix4x4 prevMatrix = Gizmos.matrix;
Gizmos.matrix *= Matrix4x4.TRS(center, rotation, size);
Gizmos.DrawWireCube(Vector3.zero, Vector3.one);
Gizmos.matrix = prevMatrix;
}
public static void DrawInvisibleBox(Vector3 center, Vector3 size, Quaternion rotation)
{
Color prevColor = Gizmos.color;
Gizmos.color = Transparent;
Matrix4x4 prevMatrix = Gizmos.matrix;
Gizmos.matrix *= Matrix4x4.TRS(center, rotation, size);
Gizmos.DrawCube(Vector3.zero, Vector3.one);
Gizmos.matrix = prevMatrix;
Gizmos.color = prevColor;
}
public static void DrawWireSphere(Vector3 center, float radius, Vector3 scale, Quaternion rotation)
{
Matrix4x4 prevMatrix = Gizmos.matrix;
Gizmos.matrix *= Matrix4x4.TRS(center, rotation, scale);
Gizmos.DrawWireSphere(Vector3.zero, radius);
Gizmos.matrix = prevMatrix;
}
public static void DrawInvisibleSphere(Vector3 center, float radius, Vector3 scale, Quaternion rotation)
{
Color prevColor = Gizmos.color;
Gizmos.color = Transparent;
Matrix4x4 prevMatrix = Gizmos.matrix;
Gizmos.matrix *= Matrix4x4.TRS(center, rotation, scale);
Gizmos.DrawSphere(Vector3.zero, radius);
Gizmos.matrix = prevMatrix;
Gizmos.color = prevColor;
}
public static void DrawWireCylinder(Vector3 center, float radius, float topRadiusOffset, float height, Quaternion rotation)
{
float topRadius = Mathf.Max(0.0f, radius + topRadiusOffset);
Vector3 hh = new Vector3(0.0f, 0.5f * height, 0.0f);
Matrix4x4 prevMatrix = Gizmos.matrix;
Gizmos.matrix *= Matrix4x4.TRS(center, rotation, Vector3.one);
DrawCircle(radius, -hh, Quaternion.identity);
DrawCircle(topRadius, hh, Quaternion.identity);
DrawLine(new Vector3(-radius, 0.0f, 0.0f) - hh, new Vector3(-topRadius, 0.0f, 0.0f) + hh);
DrawLine(new Vector3(radius, 0.0f, 0.0f) - hh, new Vector3(topRadius, 0.0f, 0.0f) + hh);
DrawLine(new Vector3(0.0f, 0.0f, -radius) - hh, new Vector3(0.0f, 0.0f, -topRadius) + hh);
DrawLine(new Vector3(0.0f, 0.0f, radius) - hh, new Vector3(0.0f, 0.0f, topRadius) + hh);
Gizmos.matrix = prevMatrix;
}
public static void DrawInvisibleCylinder(Vector3 center, float radius, float height, Quaternion rotation)
{
Color prevColor = Gizmos.color;
Gizmos.color = Transparent;
Matrix4x4 prevMatrix = Gizmos.matrix;
Gizmos.matrix *= Matrix4x4.TRS(center, rotation, new Vector3(radius, height, radius));
Gizmos.DrawMesh(CylinderMesh);
Gizmos.matrix = prevMatrix;
Gizmos.color = prevColor;
}
public static void DrawWireCone(Vector3 baseCenter, float radius, float height, Quaternion rotation)
{
Vector3 h = new Vector3(0.0f, height, 0.0f);
Matrix4x4 prevMatrix = Gizmos.matrix;
Gizmos.matrix *= Matrix4x4.TRS(baseCenter, rotation, Vector3.one);
DrawCircle(radius, Vector3.zero, Quaternion.identity);
DrawLine(h, new Vector3(-radius, 0.0f, 0.0f));
DrawLine(h, new Vector3(radius, 0.0f, 0.0f));
DrawLine(h, new Vector3(0.0f, 0.0f, -radius));
DrawLine(h, new Vector3(0.0f, 0.0f, radius));
Gizmos.matrix = prevMatrix;
}
public static void DrawInvisibleCone(Vector3 baseCenter, float radius, float height, Quaternion rotation)
{
Color prevColor = Gizmos.color;
Gizmos.color = Transparent;
Matrix4x4 prevMatrix = Gizmos.matrix;
Gizmos.matrix *= Matrix4x4.TRS(baseCenter, rotation, new Vector3(radius, height, radius));
Gizmos.DrawMesh(ConeMesh);
Gizmos.matrix = prevMatrix;
Gizmos.color = prevColor;
}
public static void DrawWireSolidAngle(Vector3 center, float radius, float angle, Quaternion rotation)
{
int numSegments = 32;
float t = 0.0f;
float dt = angle / numSegments;
float s = Mathf.Sin(angle);
float c = Mathf.Cos(angle);
Vector3 h = new Vector3(0.0f, radius * c, 0.0f);
Matrix4x4 prevMatrix = Gizmos.matrix;
Gizmos.matrix *= Matrix4x4.TRS(center, rotation, Vector3.one);
DrawCircle(radius * s, h, Quaternion.identity);
float prevS = 0.0f;
float prevC = 1.0f;
for (int i = 0; i < numSegments; ++i)
{
t += dt;
float currS = Mathf.Sin(t);
float currC = Mathf.Cos(t);
DrawLine(new Vector3(-radius * prevS, radius * prevC, 0.0f), new Vector3(-radius * currS, radius * currC, 0.0f));
DrawLine(new Vector3(radius * prevS, radius * prevC, 0.0f), new Vector3(radius * currS, radius * currC, 0.0f));
DrawLine(new Vector3(0.0f, radius * prevC, -radius * prevS), new Vector3(0.0f, radius * currC, -radius * currS));
DrawLine(new Vector3(0.0f, radius * prevC, radius * prevS), new Vector3(0.0f, radius * currC, radius * currS));
prevS = currS;
prevC = currC;
}
if (angle < MathUtil.Pi - MathUtil.Epsilon)
{
DrawLine(Vector3.zero, new Vector3(-radius * prevS, radius * prevC, 0.0f));
DrawLine(Vector3.zero, new Vector3(radius * prevS, radius * prevC, 0.0f));
DrawLine(Vector3.zero, new Vector3(0.0f, radius * prevC, -radius * prevS));
DrawLine(Vector3.zero, new Vector3(0.0f, radius * prevC, radius * prevS));
}
if (angle > MathUtil.HalfPi)
{
DrawCircle(radius, Vector3.zero, Quaternion.identity);
}
Gizmos.matrix = prevMatrix;
}
public static void DrawBezierQuad(Vector3 a, Vector3 b, Vector3 controlPoint)
{
int numSegments = 32;
float t = 0;
float dt = 1.0f / numSegments;
Vector3 prev = a;
for (int i = 0; i < numSegments; ++i)
{
t += dt;
Vector3 curr = VectorUtil.BezierQuad(a, b, controlPoint, t);
DrawLine(prev, curr);
prev = curr;
}
}
public static void DrawWireCatmullRom(Vector3 [] aPoint, float [] aRadius, Vector3 headControlPoint, Vector3 tailControlPoint)
{
for (int i = 0; i < aPoint.Length; ++i)
{
DrawWireSphere(aPoint[i], aRadius[i], Vector3.one, Quaternion.identity);
}
if (VectorUtil.IsValid(headControlPoint))
{
DrawWireSphere(headControlPoint, aRadius[0], Vector3.one, Quaternion.identity);
}
if (VectorUtil.IsValid(tailControlPoint))
{
DrawWireSphere(tailControlPoint, aRadius[aPoint.Length - 1], Vector3.one, Quaternion.identity);
}
if (aPoint.Length == 1)
return;
var head = aPoint[0];
var postHead = aPoint[1];
Vector3 preHeadPos =
VectorUtil.IsValid(headControlPoint)
? headControlPoint
: 2.0f * head - postHead;
var tail = aPoint[aPoint.Length - 1];
var preTail = aPoint[aPoint.Length - 2];
Vector3 postTailPos =
VectorUtil.IsValid(tailControlPoint)
? tailControlPoint
: 2.0f * tail - preTail;
int numSegments = 16;
float t = 0.0f;
float dt = 1.0f / numSegments;
float prevT = 0.0f;
for (int i = 0; i < numSegments; ++i)
{
t += dt;
float currT = t;
DrawLine
(
VectorUtil.CatmullRom(preHeadPos, aPoint[0], aPoint[1], aPoint.Length > 2 ? aPoint[2] : postTailPos, prevT),
VectorUtil.CatmullRom(preHeadPos, aPoint[0], aPoint[1], aPoint.Length > 2 ? aPoint[2] : postTailPos, currT)
);
if (aPoint.Length > 2)
{
DrawLine
(
VectorUtil.CatmullRom(aPoint.Length > 2 ? aPoint[aPoint.Length - 3] : preHeadPos, aPoint[aPoint.Length - 2], aPoint[aPoint.Length - 1], postTailPos, prevT),
VectorUtil.CatmullRom(aPoint.Length > 2 ? aPoint[aPoint.Length - 3] : preHeadPos, aPoint[aPoint.Length - 2], aPoint[aPoint.Length - 1], postTailPos, currT)
);
}
for (int j = 0; j < aPoint.Length - 3; ++j)
{
DrawLine
(
VectorUtil.CatmullRom(aPoint[j], aPoint[j + 1], aPoint[j + 2], aPoint[j + 3], prevT),
VectorUtil.CatmullRom(aPoint[j], aPoint[j + 1], aPoint[j + 2], aPoint[j + 3], currT)
);
}
prevT = currT;
}
}
public static void DrawInvisibleCatmullRom(Vector3 [] aPoint, float [] aRadius, Vector3 headControlPoint, Vector3 tailControlPoint)
{
for (int i = 0; i < aPoint.Length; ++i)
{
DrawInvisibleSphere(aPoint[i], aRadius[i], Vector3.one, Quaternion.identity);
}
if (aPoint.Length == 1)
return;
var head = aPoint[0];
var postHead = aPoint[1];
Vector3 preHeadPos =
VectorUtil.IsValid(headControlPoint)
? headControlPoint
: 2.0f * head - postHead;
var tail = aPoint[aPoint.Length - 1];
var preTail = aPoint[aPoint.Length - 2];
Vector3 postTailPos =
VectorUtil.IsValid(tailControlPoint)
? tailControlPoint
: 2.0f * tail - preTail;
int numSegments = 4;
float t = 0.0f;
float dt = 1.0f / numSegments;
float prevT = 0.0f;
for (int i = 0; i < numSegments; ++i)
{
t += dt;
float currT = t;
DrawInvisibleSphere
(
VectorUtil.CatmullRom(preHeadPos, aPoint[0], aPoint[1], aPoint.Length > 2 ? aPoint[2] : postTailPos, prevT),
Mathf.Lerp(aRadius[0], aRadius[1], prevT),
Vector3.one,
Quaternion.identity
);
if (aPoint.Length > 2)
{
DrawInvisibleSphere
(
VectorUtil.CatmullRom(aPoint.Length > 2 ? aPoint[aPoint.Length - 3] : preHeadPos, aPoint[aPoint.Length - 2], aPoint[aPoint.Length - 1], postTailPos, prevT),
Mathf.Lerp(aRadius[aPoint.Length - 2], aRadius[aPoint.Length - 1], prevT),
Vector3.one,
Quaternion.identity
);
}
prevT = currT;
}
}
public static void DrawWireTorus(Vector3 center, float radius, float width, float depth, Quaternion rotation)
{
width = Mathf.Abs(width);
depth = Mathf.Abs(depth);
DrawCircle(radius, center + 0.5f * (width - radius) * (rotation * Vector3.right), rotation * Quaternion.Euler(90.0f, 0.0f, 0.0f));
DrawCircle(radius, center - 0.5f * (width - radius) * (rotation * Vector3.right), rotation * Quaternion.Euler(90.0f, 0.0f, 0.0f));
DrawCircle(radius, center + 0.5f * (depth - radius) * (rotation * Vector3.forward), rotation * Quaternion.Euler(0.0f, 0.0f, 90.0f));
DrawCircle(radius, center - 0.5f * (depth - radius) * (rotation * Vector3.forward), rotation * Quaternion.Euler(0.0f, 0.0f, 90.0f));
int numSegments = 16;
float dt = MathUtil.Pi / numSegments;
float dimDiff = Mathf.Abs(width - depth);
float r = 0.5f * (Mathf.Min(width, depth) - radius);
Vector3 axisA =
width > depth
? rotation * Vector3.right
: rotation * Vector3.forward;
Vector3 axisB =
width > depth
? rotation * Vector3.forward
: rotation * Vector3.right;
float a = (width > depth ? width : depth) - 2.0f * r - 0.5f * radius;
{
float t = 0.0f;
Vector3 c = center + 0.5f * a * axisA;
Vector3 prev = c + r * axisB;
for (int i = 0; i < numSegments; ++i)
{
t += dt;
Vector3 curr = c + (r * (Mathf.Sin(t) * axisA + Mathf.Cos(t) * axisB));
DrawLine(prev, curr);
prev = curr;
}
}
{
float t = Mathf.PI;
Vector3 c = center - 0.5f * a * axisA;
Vector3 prev = c - r * axisB;
for (int i = 0; i < numSegments; ++i)
{
t += dt;
Vector3 curr = c + (r * (Mathf.Sin(t) * axisA + Mathf.Cos(t) * axisB));
DrawLine(prev, curr);
prev = curr;
}
}
DrawLine(center + 0.5f * a * axisA + r * axisB, center - 0.5f * a * axisA + r * axisB);
DrawLine(center + 0.5f * a * axisA - r * axisB, center - 0.5f * a * axisA - r * axisB);
}
public static void DrawInvisibleTorus(Vector3 center, float radius, float width, float depth, Quaternion rotation)
{
width = Mathf.Abs(width);
depth = Mathf.Abs(depth);
Color prevColor = Gizmos.color;
Gizmos.color = Transparent;
/*
DrawInvisibleSphere(center + 0.5f * (width - radius) * (rotation * Vector3.right), radius, Vector3.one, Quaternion.identity);
DrawInvisibleSphere(center - 0.5f * (width - radius) * (rotation * Vector3.right), radius, Vector3.one, Quaternion.identity);
DrawInvisibleSphere(center + 0.5f * (depth - radius) * (rotation * Vector3.forward), radius, Vector3.one, Quaternion.identity);
DrawInvisibleSphere(center - 0.5f * (depth - radius) * (rotation * Vector3.forward), radius, Vector3.one, Quaternion.identity);
*/
int numSegments = 6;
float dt = MathUtil.Pi / numSegments;
float dimDiff = Mathf.Abs(width - depth);
float r = 0.5f * (Mathf.Min(width, depth) - radius);
Vector3 axisA =
width > depth
? rotation * Vector3.right
: rotation * Vector3.forward;
Vector3 axisB =
width > depth
? rotation * Vector3.forward
: rotation * Vector3.right;
float a = (width > depth ? width : depth) - 2.0f * r - 0.5f * radius;
{
float t = 0.0f;
Vector3 c = center + 0.5f * a * axisA;
for (int i = 0; i < numSegments; ++i)
{
t += dt;
Vector3 curr = c + (r * (Mathf.Sin(t) * axisA + Mathf.Cos(t) * axisB));
DrawInvisibleSphere(curr, radius, Vector3.one, Quaternion.identity);
}
}
{
float t = Mathf.PI;
Vector3 c = center - 0.5f * a * axisA;
for (int i = 0; i < numSegments; ++i)
{
t += dt;
Vector3 curr = c + (r * (Mathf.Sin(t) * axisA + Mathf.Cos(t) * axisB));
DrawInvisibleSphere(curr, radius, Vector3.one, Quaternion.identity);
}
}
{
numSegments = 4;
float t = 0.0f;
dt = 1.0f / numSegments;
for (int i = 0; i <= numSegments; ++i)
{
DrawInvisibleSphere(Vector3.Lerp(center + 0.5f * a * axisA + (r * axisB), center - 0.5f * a * axisA + (r * axisB), t), radius, Vector3.one, Quaternion.identity);
DrawInvisibleSphere(Vector3.Lerp(center + 0.5f * a * axisA - (r * axisB), center - 0.5f * a * axisA - (r * axisB), t), radius, Vector3.one, Quaternion.identity);
t += dt;
}
}
Gizmos.color = prevColor;
}
private static Mesh s_cylinderMesh;
private static Mesh CylinderMesh
{
get
{
if (s_cylinderMesh != null)
return s_cylinderMesh;
int numSegments = 16;
s_cylinderMesh = new Mesh();
Vector3[] aVert = new Vector3[numSegments * 6 + 2];
Vector3[] aNormal = new Vector3[aVert.Length];
int[] aIndex = new int[numSegments * 12];
Vector3 bottom = new Vector3(0.0f, -0.5f, 0.0f);
Vector3 top = new Vector3(0.0f, 0.5f, 0.0f);
int iBottomCapStart = 0;
int iTopCapStart = numSegments;
int iSideStart = numSegments * 2;
int iBottom = numSegments * 6;
int iTop = numSegments * 6 + 1;
aVert[iBottom] = bottom;
aVert[iTop] = top;
aNormal[iBottom] = new Vector3(0.0f, -1.0f, 0.0f);
aNormal[iTop] = new Vector3(0.0f, 1.0f, 0.0f);
int iIndex = 0;
float angleIncrement = 2.0f * Mathf.PI / numSegments;
float angle = 0.0f;
for (int i = 0; i < numSegments; ++i)
{
// caps
Vector3 offset = Mathf.Cos(angle) * Vector3.right + Mathf.Sin(angle) * Vector3.forward;
aVert[iBottomCapStart + i] = bottom + offset;
aVert[iTopCapStart + i] = top + offset;
aNormal[iBottomCapStart + i] = new Vector3(0.0f, -1.0f, 0.0f);
aNormal[iTopCapStart + i] = new Vector3(0.0f, 1.0f, 0.0f);
aIndex[iIndex++] = iBottom;
aIndex[iIndex++] = iBottomCapStart + i;
aIndex[iIndex++] = iBottomCapStart + ((i + 1) % numSegments);
aIndex[iIndex++] = iTop;
aIndex[iIndex++] = iTopCapStart + ((i + 1) % numSegments);
aIndex[iIndex++] = iTopCapStart + i;
angle += angleIncrement;
// sides
Vector3 offsetNext = Mathf.Cos(angle) * Vector3.right + Mathf.Sin(angle) * Vector3.forward;
aVert[iSideStart + i * 4] = bottom + offset;
aVert[iSideStart + i * 4 + 1] = top + offset;
aVert[iSideStart + i * 4 + 2] = bottom + offsetNext;
aVert[iSideStart + i * 4 + 3] = top + offsetNext;
Vector3 sideNormal = Vector3.Cross(top - bottom, offsetNext - offset).normalized;
aNormal[iSideStart + i * 4] = sideNormal;
aNormal[iSideStart + i * 4 + 1] = sideNormal;
aNormal[iSideStart + i * 4 + 2] = sideNormal;
aNormal[iSideStart + i * 4 + 3] = sideNormal;
aIndex[iIndex++] = iSideStart + i * 4;
aIndex[iIndex++] = iSideStart + i * 4 + 3;
aIndex[iIndex++] = iSideStart + i * 4 + 2;
aIndex[iIndex++] = iSideStart + i * 4;
aIndex[iIndex++] = iSideStart + i * 4 + 1;
aIndex[iIndex++] = iSideStart + i * 4 + 3;
}
s_cylinderMesh.vertices = aVert;
s_cylinderMesh.normals = aNormal;
s_cylinderMesh.SetIndices(aIndex, MeshTopology.Triangles, 0);
return s_cylinderMesh;
}
}
private static Mesh s_coneMesh;
private static Mesh ConeMesh
{
get
{
if (s_coneMesh != null)
return s_coneMesh;
int numSegments = 16;
s_coneMesh = new Mesh();
Vector3[] aVert = new Vector3[numSegments * 3 + numSegments];
Vector3[] aNormal = new Vector3[aVert.Length];
int[] aIndex = new int[numSegments * 3 + (numSegments - 2) * 3];
Vector3 top = new Vector3(0.0f, 1.0f, 0.0f);
Vector3[] aBaseVert = new Vector3[numSegments];
float angleIncrement = 2.0f * Mathf.PI / numSegments;
float angle = 0.0f;
for (int i = 0; i < numSegments; ++i)
{
aBaseVert[i] = Mathf.Cos(angle) * Vector3.right + Mathf.Sin(angle) * Vector3.forward;
angle += angleIncrement;
}
int iVert = 0;
int iIndex = 0;
int iNormal = 0;
for (int i = 0; i < numSegments; ++i)
{
int iSideTriStart = iVert;
aVert[iVert++] = top;
aVert[iVert++] = aBaseVert[i];
aVert[iVert++] = aBaseVert[(i + 1) % numSegments];
Vector3 sideTriNormal = Vector3.Cross(aVert[iSideTriStart + 2] - aVert[iSideTriStart], aVert[iSideTriStart + 1] - aVert[iSideTriStart]).normalized;
aNormal[iNormal++] = sideTriNormal;
aNormal[iNormal++] = sideTriNormal;
aNormal[iNormal++] = sideTriNormal;
aIndex[iIndex++] = iSideTriStart;
aIndex[iIndex++] = iSideTriStart + 2;
aIndex[iIndex++] = iSideTriStart + 1;
}
int iBaseStart = iVert;
for (int i = 0; i < numSegments; ++i)
{
aVert[iVert++] = aBaseVert[i];
aNormal[iNormal++] = new Vector3(0.0f, -1.0f, 0.0f);
if (i >= 2)
{
aIndex[iIndex++] = iBaseStart;
aIndex[iIndex++] = iBaseStart + i - 1;
aIndex[iIndex++] = iBaseStart + i;
}
}
s_coneMesh.vertices = aVert;
s_coneMesh.normals = aNormal;
s_coneMesh.SetIndices(aIndex, MeshTopology.Triangles, 0);
return s_coneMesh;
}
}
}
}