Last active
June 29, 2017 15:28
-
-
Save mitu217/a1ac977cea29ee370dbd to your computer and use it in GitHub Desktop.
PolyMesh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections.Generic; | |
[ExecuteInEditMode] | |
[RequireComponent(typeof(MeshRenderer))] | |
[RequireComponent(typeof(MeshFilter))] | |
public class GenerateMesh : MonoBehaviour | |
{ | |
/// <summary> | |
/// パス座標 | |
/// </summary> | |
public List<Vector3> keyPoints = new List<Vector3>(); | |
/// <summary> | |
/// 頂点パス座標 | |
/// </summary> | |
public List<Vector3> curvePoints = new List<Vector3>(); | |
/// <summary> | |
/// 各頂点間が曲線を持つかどうか | |
/// </summary> | |
public List<bool> isCurve = new List<bool>(); | |
/// <summary> | |
/// Mesh Collider | |
/// </summary> | |
public MeshCollider meshCollider; | |
[Range(0.01f, 1)] public float curveDetail = 0.1f; | |
public float colliderDepth = 1; | |
public bool buildColliderEdges = true; | |
public bool buildColliderFront; | |
/// <summary> | |
/// テクスチャの描画位置 | |
/// </summary> | |
public Vector2 uvPosition; | |
/// <summary> | |
/// UVの拡大率 | |
/// </summary> | |
public float uvScale = 1; | |
/// <summary> | |
/// UVの回転率 | |
/// </summary> | |
public float uvRotation; | |
public List<Vector3> GetEdgePoints() { | |
//Build the point list and calculate curves | |
var points = new List<Vector3>(); | |
for (int i = 0; i < keyPoints.Count; i++) { | |
if (isCurve[i]) { | |
//Get the curve control point | |
var a = keyPoints[i]; | |
var c = keyPoints[(i + 1) % keyPoints.Count]; | |
var b = Bezier.Control(a, c, curvePoints[i]); | |
//Build the curve | |
var count = Mathf.Ceil(1 / curveDetail); | |
for (int j = 0; j < count; j++) | |
{ | |
var t = (float)j / count; | |
points.Add(Bezier.Curve(a, b, c, t)); | |
} | |
} | |
else | |
points.Add(keyPoints[i]); | |
} | |
return points; | |
} | |
public void BuildMesh() | |
{ | |
var points = GetEdgePoints(); | |
var vertices = points.ToArray(); | |
//Build the index array | |
var indices = new List<int>(); | |
while (indices.Count < points.Count) | |
indices.Add(indices.Count); | |
//Build the triangle array | |
var triangles = Triangulate.Points(points); | |
//Build the uv array | |
var scale = uvScale != 0 ? (1 / uvScale) : 0; | |
var matrix = Matrix4x4.TRS(-uvPosition, Quaternion.Euler(0, 0, uvRotation), new Vector3(scale, scale, 1)); | |
var uv = new Vector2[points.Count]; | |
for (int i = 0; i < uv.Length; i++) | |
{ | |
var p = matrix.MultiplyPoint(points[i]); | |
uv[i] = new Vector2(p.x, p.y); | |
} | |
//Find the mesh (create it if it doesn't exist) | |
var meshFilter = GetComponent<MeshFilter>(); | |
var mesh = meshFilter.sharedMesh; | |
if (mesh == null) | |
{ | |
mesh = new Mesh(); | |
mesh.name = "PolySprite_Mesh"; | |
meshFilter.mesh = mesh; | |
} | |
//Update the mesh | |
mesh.Clear(); | |
mesh.vertices = vertices; | |
mesh.uv = uv; | |
mesh.triangles = triangles; | |
mesh.RecalculateNormals(); | |
mesh.Optimize(); | |
//Update collider after the mesh is updated | |
UpdateCollider(points, triangles); | |
} | |
void UpdateCollider(List<Vector3> points, int[] tris) | |
{ | |
//Update the mesh collider if there is one | |
if (meshCollider != null) | |
{ | |
var vertices = new List<Vector3>(); | |
var triangles = new List<int>(); | |
if (buildColliderEdges) | |
{ | |
//Build vertices array | |
var offset = new Vector3(0, 0, colliderDepth / 2); | |
for (int i = 0; i < points.Count; i++) | |
{ | |
vertices.Add(points[i] + offset); | |
vertices.Add(points[i] - offset); | |
} | |
//Build triangles array | |
for (int a = 0; a < vertices.Count; a += 2) | |
{ | |
var b = (a + 1) % vertices.Count; | |
var c = (a + 2) % vertices.Count; | |
var d = (a + 3) % vertices.Count; | |
triangles.Add(a); | |
triangles.Add(c); | |
triangles.Add(b); | |
triangles.Add(c); | |
triangles.Add(d); | |
triangles.Add(b); | |
} | |
} | |
if (buildColliderFront) | |
{ | |
for (int i = 0; i < tris.Length; i++) | |
tris[i] += vertices.Count; | |
vertices.AddRange(points); | |
triangles.AddRange(tris); | |
} | |
//Find the mesh (create it if it doesn't exist) | |
var mesh = meshCollider.sharedMesh; | |
if (mesh == null) | |
{ | |
mesh = new Mesh(); | |
mesh.name = "PolySprite_Collider"; | |
} | |
//Update the mesh | |
mesh.Clear(); | |
mesh.vertices = vertices.ToArray(); | |
mesh.triangles = triangles.ToArray(); | |
mesh.RecalculateNormals(); | |
mesh.Optimize(); | |
meshCollider.sharedMesh = null; | |
meshCollider.sharedMesh = mesh; | |
} | |
} | |
bool IsRightTurn(List<Vector3> points, int a, int b, int c) | |
{ | |
var ab = points[b] - points[a]; | |
var bc = points[c] - points[b]; | |
return (ab.x * bc.y - ab.y * bc.x) < 0; | |
} | |
bool IntersectsExistingLines(List<Vector3> points, Vector3 a, Vector3 b) | |
{ | |
for (int i = 0; i < points.Count; i++) | |
if (LinesIntersect(points, a, b, points[i], points[(i + 1) % points.Count])) | |
return true; | |
return false; | |
} | |
bool LinesIntersect(List<Vector3> points, Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4) | |
{ | |
if (point1 == point3 || point1 == point4 || point2 == point3 || point2 == point4) | |
return false; | |
float ua = (point4.x - point3.x) * (point1.y - point3.y) - (point4.y - point3.y) * (point1.x - point3.x); | |
float ub = (point2.x - point1.x) * (point1.y - point3.y) - (point2.y - point1.y) * (point1.x - point3.x); | |
float denominator = (point4.y - point3.y) * (point2.x - point1.x) - (point4.x - point3.x) * (point2.y - point1.y); | |
if (Mathf.Abs(denominator) <= 0.00001f) | |
{ | |
if (Mathf.Abs(ua) <= 0.00001f && Mathf.Abs(ub) <= 0.00001f) | |
return true; | |
} | |
else | |
{ | |
ua /= denominator; | |
ub /= denominator; | |
if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) | |
return true; | |
} | |
return false; | |
} | |
} | |
public static class Bezier | |
{ | |
public static float Curve(float from, float control, float to, float t) | |
{ | |
return from * (1 - t) * (1 - t) + control * 2 * (1 - t) * t + to * t * t; | |
} | |
public static Vector3 Curve(Vector3 from, Vector3 control, Vector3 to, float t) | |
{ | |
from.x = Curve(from.x, control.x, to.x, t); | |
from.y = Curve(from.y, control.y, to.y, t); | |
from.z = Curve(from.z, control.z, to.z, t); | |
return from; | |
} | |
public static Vector3 Control(Vector3 from, Vector3 to, Vector3 curve) | |
{ | |
//var center = Vector3.Lerp(from, to, 0.5f); | |
//return center + (curve - center) * 2; | |
var axis = Vector3.Normalize(to - from); | |
var dot = Vector3.Dot(axis, curve - from); | |
var linePoint = from + axis * dot; | |
return linePoint + (curve - linePoint) * 2; | |
} | |
} | |
public static class Triangulate | |
{ | |
public static int[] Points(List<Vector3> points) | |
{ | |
var indices = new List<int>(); | |
int n = points.Count; | |
if (n < 3) | |
return indices.ToArray(); | |
int[] V = new int[n]; | |
if (Area(points) > 0) | |
{ | |
for (int v = 0; v < n; v++) | |
V[v] = v; | |
} | |
else | |
{ | |
for (int v = 0; v < n; v++) | |
V[v] = (n - 1) - v; | |
} | |
int nv = n; | |
int count = 2 * nv; | |
for (int m = 0, v = nv - 1; nv > 2; ) | |
{ | |
if ((count--) <= 0) | |
return indices.ToArray(); | |
int u = v; | |
if (nv <= u) | |
u = 0; | |
v = u + 1; | |
if (nv <= v) | |
v = 0; | |
int w = v + 1; | |
if (nv <= w) | |
w = 0; | |
if (Snip(points, u, v, w, nv, V)) | |
{ | |
int a, b, c, s, t; | |
a = V[u]; | |
b = V[v]; | |
c = V[w]; | |
indices.Add(a); | |
indices.Add(b); | |
indices.Add(c); | |
m++; | |
for (s = v, t = v + 1; t < nv; s++, t++) | |
V[s] = V[t]; | |
nv--; | |
count = 2 * nv; | |
} | |
} | |
indices.Reverse(); | |
return indices.ToArray(); | |
} | |
static float Area(List<Vector3> points) | |
{ | |
int n = points.Count; | |
float A = 0.0f; | |
for (int p = n - 1, q = 0; q < n; p = q++) | |
{ | |
Vector3 pval = points[p]; | |
Vector3 qval = points[q]; | |
A += pval.x * qval.y - qval.x * pval.y; | |
} | |
return (A * 0.5f); | |
} | |
static bool Snip(List<Vector3> points, int u, int v, int w, int n, int[] V) | |
{ | |
int p; | |
Vector3 A = points[V[u]]; | |
Vector3 B = points[V[v]]; | |
Vector3 C = points[V[w]]; | |
if (Mathf.Epsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x)))) | |
return false; | |
for (p = 0; p < n; p++) | |
{ | |
if ((p == u) || (p == v) || (p == w)) | |
continue; | |
Vector3 P = points[V[p]]; | |
if (InsideTriangle(A, B, C, P)) | |
return false; | |
} | |
return true; | |
} | |
static bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P) | |
{ | |
float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; | |
float cCROSSap, bCROSScp, aCROSSbp; | |
ax = C.x - B.x; ay = C.y - B.y; | |
bx = A.x - C.x; by = A.y - C.y; | |
cx = B.x - A.x; cy = B.y - A.y; | |
apx = P.x - A.x; apy = P.y - A.y; | |
bpx = P.x - B.x; bpy = P.y - B.y; | |
cpx = P.x - C.x; cpy = P.y - C.y; | |
aCROSSbp = ax * bpy - ay * bpx; | |
cCROSSap = cx * apy - cy * apx; | |
bCROSScp = bx * cpy - by * cpx; | |
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections.Generic; | |
//コンポーネントを自動的に追加 | |
[RequireComponent(typeof(MeshFilter))] | |
public class PolyMesh : MonoBehaviour | |
{ | |
/// <summary> | |
/// | |
/// </summary> | |
public List<Vector3> keyPoints = new List<Vector3>(); | |
/// <summary> | |
/// | |
/// </summary> | |
public List<Vector3> curvePoints = new List<Vector3>(); | |
/// <summary> | |
/// | |
/// </summary> | |
public List<bool> isCurve = new List<bool>(); | |
/// <summary> | |
/// | |
/// </summary> | |
public MeshCollider meshCollider; | |
/// <summary> | |
/// | |
/// </summary> | |
[Range(0.01f, 1)] public float curveDetail = 0.1f; | |
/// <summary> | |
/// | |
/// </summary> | |
public float colliderDepth = 1; | |
/// <summary> | |
/// | |
/// </summary> | |
public bool buildColliderEdges = true; | |
/// <summary> | |
/// | |
/// </summary> | |
public bool buildColliderFront; | |
/// <summary> | |
/// | |
/// </summary> | |
public Vector2 uvPosition; | |
/// <summary> | |
/// | |
/// </summary> | |
public float uvScale = 1; | |
/// <summary> | |
/// | |
/// </summary> | |
public float uvRotation; | |
public List<Vector3> GetEdgePoints() | |
{ | |
//Build the point list and calculate curves | |
var points = new List<Vector3>(); | |
for (int i = 0; i < keyPoints.Count; i++) | |
{ | |
if (isCurve[i]) | |
{ | |
//Get the curve control point | |
var a = keyPoints[i]; | |
var c = keyPoints[(i + 1) % keyPoints.Count]; | |
var b = Bezier.Control(a, c, curvePoints[i]); | |
//Build the curve | |
var count = Mathf.Ceil(1 / curveDetail); | |
for (int j = 0; j < count; j++) | |
{ | |
var t = (float)j / count; | |
points.Add(Bezier.Curve(a, b, c, t)); | |
} | |
} | |
else | |
points.Add(keyPoints[i]); | |
} | |
return points; | |
} | |
public void BuildMesh() | |
{ | |
var points = GetEdgePoints(); | |
var vertices = points.ToArray(); | |
//Build the index array | |
var indices = new List<int>(); | |
while (indices.Count < points.Count) | |
indices.Add(indices.Count); | |
//Build the triangle array | |
var triangles = Triangulate.Points(points); | |
//Build the uv array | |
var scale = uvScale != 0 ? (1 / uvScale) : 0; | |
var matrix = Matrix4x4.TRS(-uvPosition, Quaternion.Euler(0, 0, uvRotation), new Vector3(scale, scale, 1)); | |
var uv = new Vector2[points.Count]; | |
for (int i = 0; i < uv.Length; i++) | |
{ | |
var p = matrix.MultiplyPoint(points[i]); | |
uv[i] = new Vector2(p.x, p.y); | |
} | |
//Find the mesh (create it if it doesn't exist) | |
var meshFilter = GetComponent<MeshFilter>(); | |
var mesh = meshFilter.sharedMesh; | |
if (mesh == null) | |
{ | |
mesh = new Mesh(); | |
mesh.name = "PolySprite_Mesh"; | |
meshFilter.mesh = mesh; | |
} | |
//Update the mesh | |
mesh.Clear(); | |
mesh.vertices = vertices; | |
mesh.uv = uv; | |
mesh.triangles = triangles; | |
mesh.RecalculateNormals(); | |
mesh.Optimize(); | |
//Update collider after the mesh is updated | |
UpdateCollider(points, triangles); | |
} | |
void UpdateCollider(List<Vector3> points, int[] tris) | |
{ | |
//Update the mesh collider if there is one | |
if (meshCollider != null) | |
{ | |
var vertices = new List<Vector3>(); | |
var triangles = new List<int>(); | |
if (buildColliderEdges) | |
{ | |
//Build vertices array | |
var offset = new Vector3(0, 0, colliderDepth / 2); | |
for (int i = 0; i < points.Count; i++) | |
{ | |
vertices.Add(points[i] + offset); | |
vertices.Add(points[i] - offset); | |
} | |
//Build triangles array | |
for (int a = 0; a < vertices.Count; a += 2) | |
{ | |
var b = (a + 1) % vertices.Count; | |
var c = (a + 2) % vertices.Count; | |
var d = (a + 3) % vertices.Count; | |
triangles.Add(a); | |
triangles.Add(c); | |
triangles.Add(b); | |
triangles.Add(c); | |
triangles.Add(d); | |
triangles.Add(b); | |
} | |
} | |
if (buildColliderFront) | |
{ | |
for (int i = 0; i < tris.Length; i++) | |
tris[i] += vertices.Count; | |
vertices.AddRange(points); | |
triangles.AddRange(tris); | |
} | |
//Find the mesh (create it if it doesn't exist) | |
var mesh = meshCollider.sharedMesh; | |
if (mesh == null) | |
{ | |
mesh = new Mesh(); | |
mesh.name = "PolySprite_Collider"; | |
} | |
//Update the mesh | |
mesh.Clear(); | |
mesh.vertices = vertices.ToArray(); | |
mesh.triangles = triangles.ToArray(); | |
mesh.RecalculateNormals(); | |
mesh.Optimize(); | |
meshCollider.sharedMesh = null; | |
meshCollider.sharedMesh = mesh; | |
} | |
} | |
bool IsRightTurn(List<Vector3> points, int a, int b, int c) | |
{ | |
var ab = points[b] - points[a]; | |
var bc = points[c] - points[b]; | |
return (ab.x * bc.y - ab.y * bc.x) < 0; | |
} | |
bool IntersectsExistingLines(List<Vector3> points, Vector3 a, Vector3 b) | |
{ | |
for (int i = 0; i < points.Count; i++) | |
if (LinesIntersect(points, a, b, points[i], points[(i + 1) % points.Count])) | |
return true; | |
return false; | |
} | |
bool LinesIntersect(List<Vector3> points, Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4) | |
{ | |
if (point1 == point3 || point1 == point4 || point2 == point3 || point2 == point4) | |
return false; | |
float ua = (point4.x - point3.x) * (point1.y - point3.y) - (point4.y - point3.y) * (point1.x - point3.x); | |
float ub = (point2.x - point1.x) * (point1.y - point3.y) - (point2.y - point1.y) * (point1.x - point3.x); | |
float denominator = (point4.y - point3.y) * (point2.x - point1.x) - (point4.x - point3.x) * (point2.y - point1.y); | |
if (Mathf.Abs(denominator) <= 0.00001f) | |
{ | |
if (Mathf.Abs(ua) <= 0.00001f && Mathf.Abs(ub) <= 0.00001f) | |
return true; | |
} | |
else | |
{ | |
ua /= denominator; | |
ub /= denominator; | |
if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) | |
return true; | |
} | |
return false; | |
} | |
} | |
public static class Bezier | |
{ | |
public static float Curve(float from, float control, float to, float t) | |
{ | |
return from * (1 - t) * (1 - t) + control * 2 * (1 - t) * t + to * t * t; | |
} | |
public static Vector3 Curve(Vector3 from, Vector3 control, Vector3 to, float t) | |
{ | |
from.x = Curve(from.x, control.x, to.x, t); | |
from.y = Curve(from.y, control.y, to.y, t); | |
from.z = Curve(from.z, control.z, to.z, t); | |
return from; | |
} | |
public static Vector3 Control(Vector3 from, Vector3 to, Vector3 curve) | |
{ | |
//var center = Vector3.Lerp(from, to, 0.5f); | |
//return center + (curve - center) * 2; | |
var axis = Vector3.Normalize(to - from); | |
var dot = Vector3.Dot(axis, curve - from); | |
var linePoint = from + axis * dot; | |
return linePoint + (curve - linePoint) * 2; | |
} | |
} | |
public static class Triangulate | |
{ | |
public static int[] Points(List<Vector3> points) | |
{ | |
var indices = new List<int>(); | |
int n = points.Count; | |
if (n < 3) | |
return indices.ToArray(); | |
int[] V = new int[n]; | |
if (Area(points) > 0) | |
{ | |
for (int v = 0; v < n; v++) | |
V[v] = v; | |
} | |
else | |
{ | |
for (int v = 0; v < n; v++) | |
V[v] = (n - 1) - v; | |
} | |
int nv = n; | |
int count = 2 * nv; | |
for (int m = 0, v = nv - 1; nv > 2; ) | |
{ | |
if ((count--) <= 0) | |
return indices.ToArray(); | |
int u = v; | |
if (nv <= u) | |
u = 0; | |
v = u + 1; | |
if (nv <= v) | |
v = 0; | |
int w = v + 1; | |
if (nv <= w) | |
w = 0; | |
if (Snip(points, u, v, w, nv, V)) | |
{ | |
int a, b, c, s, t; | |
a = V[u]; | |
b = V[v]; | |
c = V[w]; | |
indices.Add(a); | |
indices.Add(b); | |
indices.Add(c); | |
m++; | |
for (s = v, t = v + 1; t < nv; s++, t++) | |
V[s] = V[t]; | |
nv--; | |
count = 2 * nv; | |
} | |
} | |
indices.Reverse(); | |
return indices.ToArray(); | |
} | |
static float Area(List<Vector3> points) | |
{ | |
int n = points.Count; | |
float A = 0.0f; | |
for (int p = n - 1, q = 0; q < n; p = q++) | |
{ | |
Vector3 pval = points[p]; | |
Vector3 qval = points[q]; | |
A += pval.x * qval.y - qval.x * pval.y; | |
} | |
return (A * 0.5f); | |
} | |
static bool Snip(List<Vector3> points, int u, int v, int w, int n, int[] V) | |
{ | |
int p; | |
Vector3 A = points[V[u]]; | |
Vector3 B = points[V[v]]; | |
Vector3 C = points[V[w]]; | |
if (Mathf.Epsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x)))) | |
return false; | |
for (p = 0; p < n; p++) | |
{ | |
if ((p == u) || (p == v) || (p == w)) | |
continue; | |
Vector3 P = points[V[p]]; | |
if (InsideTriangle(A, B, C, P)) | |
return false; | |
} | |
return true; | |
} | |
static bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P) | |
{ | |
float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; | |
float cCROSSap, bCROSScp, aCROSSbp; | |
ax = C.x - B.x; ay = C.y - B.y; | |
bx = A.x - C.x; by = A.y - C.y; | |
cx = B.x - A.x; cy = B.y - A.y; | |
apx = P.x - A.x; apy = P.y - A.y; | |
bpx = P.x - B.x; bpy = P.y - B.y; | |
cpx = P.x - C.x; cpy = P.y - C.y; | |
aCROSSbp = ax * bpy - ay * bpx; | |
cCROSSap = cx * apy - cy * apx; | |
bCROSScp = bx * cpy - by * cpx; | |
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using UnityEditor; | |
using System.Collections.Generic; | |
using System.Reflection; | |
[CustomEditor(typeof(PolyMesh))] | |
public class PolyMeshEditor : Editor | |
{ | |
/// <summary> | |
/// Sceneビュー上でのステータスを示す構造体 | |
/// </summary> | |
enum State { Hover, Drag, BoxSelect, DragSelected, RotateSelected, ScaleSelected, Extrude } | |
/// <summary> | |
/// クリック判定領域 | |
/// </summary> | |
const float clickRadius = 0.12f; | |
/// <summary> | |
/// ??? | |
/// </summary> | |
FieldInfo undoCallback; | |
/// <summary> | |
/// Editモードかどうか | |
/// </summary> | |
bool editing; | |
/// <summary> | |
/// ??? | |
/// </summary> | |
bool tabDown; | |
/// <summary> | |
/// ??? | |
/// </summary> | |
State state; | |
/// <summary> | |
/// 直線パス | |
/// </summary> | |
List<Vector3> keyPoints; | |
/// <summary> | |
/// 曲線パス | |
/// </summary> | |
List<Vector3> curvePoints; | |
/// <summary> | |
/// カーブするかどうか??? | |
/// </summary> | |
List<bool> isCurve; | |
/// <summary> | |
/// global座標をlocal座標で持つ??? | |
/// </summary> | |
Matrix4x4 worldToLocal; | |
/// <summary> | |
/// 回転角度 Quaternionでもつの??? | |
/// </summary> | |
Quaternion inverseRotation; | |
/// <summary> | |
/// マウス座標 | |
/// </sumamry> | |
Vector3 mousePosition; | |
/// <summary> | |
/// クリック座標 | |
/// </summary> | |
Vector3 clickPosition; | |
/// <summary> | |
/// スクリーン上でのマウスの座標 | |
/// </summary> | |
Vector3 screenMousePosition; | |
/// <summary> | |
/// マウスカーソルの状態をArrowに指定 | |
/// </summary> | |
MouseCursor mouseCursor = MouseCursor.Arrow; | |
/// <summary> | |
/// スナップ??? | |
/// </summary> | |
float snap; | |
/// <summary> | |
/// ??? | |
/// </summary> | |
int dragIndex; | |
/// <summary> | |
/// ??? | |
/// </summary> | |
List<int> selectedIndices = new List<int>(); | |
/// <summary> | |
/// ??? | |
/// </summary> | |
int nearestLine; | |
/// <summary> | |
/// ??? | |
/// </summary> | |
Vector3 splitPosition; | |
/// <summary> | |
/// ??? | |
/// </summary> | |
bool extrudeKeyDown; | |
/// <summary> | |
/// ??? | |
/// </summary> | |
bool doExtrudeUpdate; | |
/// <summary> | |
/// ??? | |
/// </summary> | |
bool draggingCurve; | |
#region Inspector GUI | |
public override void OnInspectorGUI() { | |
if (target == null) { | |
return; | |
} | |
if (polyMesh.keyPoints.Count == 0) { | |
CreateSquare(polyMesh, 0.5f); | |
} | |
// Editモードに入るかどうか | |
if (editing) { | |
if (GUILayout.Button("Stop Editing")) | |
{ | |
editing = false; | |
HideWireframe(false); | |
} | |
} | |
else if (GUILayout.Button("Edit PolyMesh")) { | |
editing = true; | |
HideWireframe(hideWireframe); | |
} | |
// UV設定 | |
if (uvSettings = EditorGUILayout.Foldout(uvSettings, "UVs")) { | |
var uvPosition = EditorGUILayout.Vector2Field("Position", polyMesh.uvPosition); | |
var uvScale = EditorGUILayout.FloatField("Scale", polyMesh.uvScale); | |
var uvRotation = EditorGUILayout.Slider("Rotation", polyMesh.uvRotation, -180, 180) % 360; | |
if (uvRotation < -180) | |
uvRotation += 360; | |
if (GUI.changed) { | |
RecordUndo(); | |
polyMesh.uvPosition = uvPosition; | |
polyMesh.uvScale = uvScale; | |
polyMesh.uvRotation = uvRotation; | |
} | |
if (GUILayout.Button("Reset UVs")) { | |
polyMesh.uvPosition = Vector3.zero; | |
polyMesh.uvScale = 1; | |
polyMesh.uvRotation = 0; | |
} | |
} | |
// Mesh設定 | |
if (meshSettings = EditorGUILayout.Foldout(meshSettings, "Mesh")) { | |
var curveDetail = EditorGUILayout.Slider("Curve Detail", polyMesh.curveDetail, 0.01f, 1f); | |
curveDetail = Mathf.Clamp(curveDetail, 0.01f, 1f); | |
if (GUI.changed) { | |
RecordUndo(); | |
polyMesh.curveDetail = curveDetail; | |
} | |
//Buttons | |
EditorGUILayout.BeginHorizontal(); | |
if (GUILayout.Button("Build Mesh")) { | |
polyMesh.BuildMesh(); | |
} | |
if (GUILayout.Button("Make Mesh Unique")) { | |
RecordUndo(); | |
polyMesh.GetComponent<MeshFilter>().mesh = null; | |
polyMesh.BuildMesh(); | |
} | |
EditorGUILayout.EndHorizontal(); | |
} | |
// Colliderの生成 | |
if (colliderSettings = EditorGUILayout.Foldout(colliderSettings, "Collider")) { | |
// Colliderの深さ設定(z軸の長さ) | |
var colliderDepth = EditorGUILayout.FloatField("Depth", polyMesh.colliderDepth); | |
colliderDepth = Mathf.Max(colliderDepth, 0.01f); | |
var buildColliderEdges = EditorGUILayout.Toggle("Build Edges", polyMesh.buildColliderEdges); | |
var buildColliderFront = EditorGUILayout.Toggle("Build Font", polyMesh.buildColliderFront); | |
if (GUI.changed) { | |
RecordUndo(); | |
polyMesh.colliderDepth = colliderDepth; | |
polyMesh.buildColliderEdges = buildColliderEdges; | |
polyMesh.buildColliderFront = buildColliderFront; | |
} | |
// Colliderの破壊 | |
if (polyMesh.meshCollider == null) { | |
if (GUILayout.Button("Create Collider")) { | |
RecordDeepUndo(); | |
var obj = new GameObject("Collider", typeof(MeshCollider)); | |
polyMesh.meshCollider = obj.GetComponent<MeshCollider>(); | |
obj.transform.parent = polyMesh.transform; | |
obj.transform.localPosition = Vector3.zero; | |
} | |
} else if (GUILayout.Button("Destroy Collider")) { | |
RecordDeepUndo(); | |
DestroyImmediate(polyMesh.meshCollider.gameObject); | |
} | |
} | |
//Update mesh | |
if (GUI.changed) { | |
polyMesh.BuildMesh(); | |
} | |
//Editor settings | |
if (editorSettings = EditorGUILayout.Foldout(editorSettings, "Editor")) { | |
gridSnap = EditorGUILayout.FloatField("Grid Snap", gridSnap); | |
autoSnap = EditorGUILayout.Toggle("Auto Snap", autoSnap); | |
globalSnap = EditorGUILayout.Toggle("Global Snap", globalSnap); | |
EditorGUI.BeginChangeCheck(); | |
hideWireframe = EditorGUILayout.Toggle("Hide Wireframe", hideWireframe); | |
if (EditorGUI.EndChangeCheck()) { | |
HideWireframe(hideWireframe); | |
} | |
editKey = (KeyCode)EditorGUILayout.EnumPopup("[Toggle Edit] Key", editKey); | |
selectAllKey = (KeyCode)EditorGUILayout.EnumPopup("[Select All] Key", selectAllKey); | |
splitKey = (KeyCode)EditorGUILayout.EnumPopup("[Split] Key", splitKey); | |
extrudeKey = (KeyCode)EditorGUILayout.EnumPopup("[Extrude] Key", extrudeKey); | |
} | |
} | |
#endregion | |
#region Scene GUI | |
void OnSceneGUI() { | |
if (target == null) { | |
return; | |
} | |
if (KeyPressed(editKey)) { | |
editing = !editing; | |
} | |
if (editing) { | |
// 各種パスを取得 | |
if (keyPoints == null) { | |
keyPoints = new List<Vector3>(polyMesh.keyPoints); | |
curvePoints = new List<Vector3>(polyMesh.curvePoints); | |
isCurve = new List<bool>(polyMesh.isCurve); | |
} | |
// Undo時の動作を設定 | |
if (undoCallback == null) { | |
undoCallback = typeof(EditorApplication).GetField("undoRedoPerformed", BindingFlags.NonPublic | BindingFlags.Static); | |
if (undoCallback != null) { | |
undoCallback.SetValue(null, new EditorApplication.CallbackFunction(OnUndoRedo)); | |
} | |
} | |
// ハンドルの座標を読み込む | |
Handles.matrix = polyMesh.transform.localToWorldMatrix; | |
// パスと線の描画 | |
DrawAxis(); | |
Handles.color = Color.white; | |
for (int i = 0; i < keyPoints.Count; i++) { | |
// PolyMeshの外枠を描画 | |
Handles.color = nearestLine == i ? Color.green : Color.white; | |
DrawSegment(i); | |
// 選択中のパスの描画 | |
if (selectedIndices.Contains(i)) { | |
Handles.color = Color.green; | |
DrawCircle(keyPoints[i], 0.08f); | |
} else { | |
Handles.color = Color.white; | |
} | |
DrawKeyPoint(i); | |
// 曲線パスについての描画??? | |
if (isCurve[i]) { | |
Handles.color = (draggingCurve && dragIndex == i) ? Color.white : Color.blue; | |
DrawCurvePoint(i); | |
} | |
} | |
// ツール切り替え時はキャンセル | |
if (e.type == EventType.KeyDown) { | |
switch (e.keyCode) { | |
case KeyCode.Q: | |
case KeyCode.W: | |
case KeyCode.E: | |
case KeyCode.R: | |
return; | |
} | |
} | |
//パニング中、もしくはカメラがシーン中にない場合はキャンセル | |
if (Tools.current == Tool.View || (e.isMouse && e.button > 0) || Camera.current == null || e.type == EventType.ScrollWheel) { | |
return; | |
} | |
//レイアウトの更新時はキャンセル | |
if (e.type == EventType.Layout) { | |
HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive)); | |
return; | |
} | |
//Cursor rectangle | |
EditorGUIUtility.AddCursorRect(new Rect(0, 0, Camera.current.pixelWidth, Camera.current.pixelHeight), mouseCursor); | |
mouseCursor = MouseCursor.Arrow; | |
//Extrude key state | |
if (e.keyCode == extrudeKey) { | |
if (extrudeKeyDown) { | |
if (e.type == EventType.KeyUp) | |
extrudeKeyDown = false; | |
} else if (e.type == EventType.KeyDown) { | |
extrudeKeyDown = true; | |
} | |
} | |
//Update matrices and snap | |
worldToLocal = polyMesh.transform.worldToLocalMatrix; | |
inverseRotation = Quaternion.Inverse(polyMesh.transform.rotation) * Camera.current.transform.rotation; | |
snap = gridSnap; | |
//Update mouse position | |
screenMousePosition = new Vector3(e.mousePosition.x, Camera.current.pixelHeight - e.mousePosition.y); | |
var plane = new Plane(-polyMesh.transform.forward, polyMesh.transform.position); | |
var ray = Camera.current.ScreenPointToRay(screenMousePosition); | |
float hit; | |
if (plane.Raycast(ray, out hit)) { | |
mousePosition = worldToLocal.MultiplyPoint(ray.GetPoint(hit)); | |
} else { | |
return; | |
} | |
//Update nearest line and split position | |
nearestLine = NearestLine(out splitPosition); | |
//Update the state and repaint | |
var newState = UpdateState(); | |
if (state != newState) | |
SetState(newState); | |
HandleUtility.Repaint(); | |
e.Use(); | |
} | |
} | |
void HideWireframe(bool hide) | |
{ | |
Renderer renderer = polyMesh.GetComponent<Renderer>(); | |
if (renderer != null) { | |
EditorUtility.SetSelectedWireframeHidden(renderer, hide); | |
} | |
} | |
void RecordUndo() { | |
Undo.RecordObject(target, "PolyMesh Changed"); | |
} | |
void RecordDeepUndo() { | |
Undo.RegisterFullObjectHierarchyUndo(target, "PolyMesh Changed"); | |
} | |
#endregion | |
#region State Control | |
//Initialize state | |
void SetState(State newState) { | |
state = newState; | |
switch (state) { | |
case State.Hover: | |
break; | |
} | |
} | |
//Update state | |
State UpdateState() { | |
switch (state) { | |
//Hovering | |
case State.Hover: | |
DrawNearestLineAndSplit(); | |
if (Tools.current == Tool.Move && TryDragSelected()) | |
return State.DragSelected; | |
if (Tools.current == Tool.Rotate && TryRotateSelected()) | |
return State.RotateSelected; | |
if (Tools.current == Tool.Scale && TryScaleSelected()) | |
return State.ScaleSelected; | |
if (Tools.current == Tool.Move && TryExtrude()) | |
return State.Extrude; | |
if (TrySelectAll()) | |
return State.Hover; | |
if (TrySplitLine()) | |
return State.Hover; | |
if (TryDeleteSelected()) | |
return State.Hover; | |
if (TryHoverCurvePoint(out dragIndex) && TryDragCurvePoint(dragIndex)) | |
return State.Drag; | |
if (TryHoverKeyPoint(out dragIndex) && TryDragKeyPoint(dragIndex)) | |
return State.Drag; | |
if (TryBoxSelect()) | |
return State.BoxSelect; | |
break; | |
//Dragging | |
case State.Drag: | |
mouseCursor = MouseCursor.MoveArrow; | |
DrawCircle(keyPoints[dragIndex], clickRadius); | |
if (draggingCurve) | |
MoveCurvePoint(dragIndex, mousePosition - clickPosition); | |
else | |
MoveKeyPoint(dragIndex, mousePosition - clickPosition); | |
if (TryStopDrag()) | |
return State.Hover; | |
break; | |
//Box Selecting | |
case State.BoxSelect: | |
if (TryBoxSelectEnd()) | |
return State.Hover; | |
break; | |
//Dragging selected | |
case State.DragSelected: | |
mouseCursor = MouseCursor.MoveArrow; | |
MoveSelected(mousePosition - clickPosition); | |
if (TryStopDrag()) | |
return State.Hover; | |
break; | |
//Rotating selected | |
case State.RotateSelected: | |
mouseCursor = MouseCursor.RotateArrow; | |
RotateSelected(); | |
if (TryStopDrag()) | |
return State.Hover; | |
break; | |
//Scaling selected | |
case State.ScaleSelected: | |
mouseCursor = MouseCursor.ScaleArrow; | |
ScaleSelected(); | |
if (TryStopDrag()) | |
return State.Hover; | |
break; | |
//Extruding | |
case State.Extrude: | |
mouseCursor = MouseCursor.MoveArrow; | |
MoveSelected(mousePosition - clickPosition); | |
if (doExtrudeUpdate && mousePosition != clickPosition) | |
{ | |
UpdatePoly(false, false); | |
doExtrudeUpdate = false; | |
} | |
if (TryStopDrag()) | |
return State.Hover; | |
break; | |
} | |
return state; | |
} | |
//Update the mesh on undo/redo | |
void OnUndoRedo() { | |
keyPoints = new List<Vector3>(polyMesh.keyPoints); | |
curvePoints = new List<Vector3>(polyMesh.curvePoints); | |
isCurve = new List<bool>(polyMesh.isCurve); | |
polyMesh.BuildMesh(); | |
} | |
void LoadPoly() { | |
for (int i = 0; i < keyPoints.Count; i++) { | |
keyPoints[i] = polyMesh.keyPoints[i]; | |
curvePoints[i] = polyMesh.curvePoints[i]; | |
isCurve[i] = polyMesh.isCurve[i]; | |
} | |
} | |
void TransformPoly(Matrix4x4 matrix) { | |
for (int i = 0; i < keyPoints.Count; i++) { | |
keyPoints[i] = matrix.MultiplyPoint(polyMesh.keyPoints[i]); | |
curvePoints[i] = matrix.MultiplyPoint(polyMesh.curvePoints[i]); | |
} | |
} | |
void UpdatePoly(bool sizeChanged, bool recordUndo) { | |
if (recordUndo) { | |
RecordUndo(); | |
} | |
if (sizeChanged) { | |
polyMesh.keyPoints = new List<Vector3>(keyPoints); | |
polyMesh.curvePoints = new List<Vector3>(curvePoints); | |
polyMesh.isCurve = new List<bool>(isCurve); | |
} else { | |
for (int i = 0; i < keyPoints.Count; i++) { | |
polyMesh.keyPoints[i] = keyPoints[i]; | |
polyMesh.curvePoints[i] = curvePoints[i]; | |
polyMesh.isCurve[i] = isCurve[i]; | |
} | |
} | |
for (int i = 0; i < keyPoints.Count; i++){ | |
if (!isCurve[i]) { | |
polyMesh.curvePoints[i] = curvePoints[i] = Vector3.Lerp(keyPoints[i], keyPoints[(i + 1) % keyPoints.Count], 0.5f); | |
} | |
} | |
polyMesh.BuildMesh(); | |
} | |
void MoveKeyPoint(int index, Vector3 amount) { | |
var moveCurve = selectedIndices.Contains((index + 1) % keyPoints.Count); | |
if (doSnap) { | |
if (globalSnap) { | |
keyPoints[index] = Snap(polyMesh.keyPoints[index] + amount); | |
if (moveCurve){ | |
curvePoints[index] = Snap(polyMesh.curvePoints[index] + amount); | |
} | |
} else { | |
amount = Snap(amount); | |
keyPoints[index] = polyMesh.keyPoints[index] + amount; | |
if (moveCurve) { | |
curvePoints[index] = polyMesh.curvePoints[index] + amount; | |
} | |
} | |
} else { | |
keyPoints[index] = polyMesh.keyPoints[index] + amount; | |
if (moveCurve) { | |
curvePoints[index] = polyMesh.curvePoints[index] + amount; | |
} | |
} | |
} | |
void MoveCurvePoint(int index, Vector3 amount) { | |
isCurve[index] = true; | |
if (doSnap) { | |
if (globalSnap){ | |
curvePoints[index] = Snap(polyMesh.curvePoints[index] + amount); | |
} else { | |
curvePoints[index] = polyMesh.curvePoints[index] + amount; | |
} | |
} else { | |
curvePoints[index] = polyMesh.curvePoints[index] + amount; | |
} | |
} | |
void MoveSelected(Vector3 amount) { | |
foreach (var i in selectedIndices) { | |
MoveKeyPoint(i, amount); | |
} | |
} | |
void RotateSelected() { | |
var center = GetSelectionCenter(); | |
Handles.color = Color.white; | |
Handles.DrawLine(center, clickPosition); | |
Handles.color = Color.green; | |
Handles.DrawLine(center, mousePosition); | |
var clickOffset = clickPosition - center; | |
var mouseOffset = mousePosition - center; | |
var clickAngle = Mathf.Atan2(clickOffset.y, clickOffset.x); | |
var mouseAngle = Mathf.Atan2(mouseOffset.y, mouseOffset.x); | |
var angleOffset = mouseAngle - clickAngle; | |
foreach (var i in selectedIndices) { | |
var point = polyMesh.keyPoints[i]; | |
var pointOffset = point - center; | |
var a = Mathf.Atan2(pointOffset.y, pointOffset.x) + angleOffset; | |
var d = pointOffset.magnitude; | |
keyPoints[i] = center + new Vector3(Mathf.Cos(a) * d, Mathf.Sin(a) * d); | |
} | |
} | |
void ScaleSelected() { | |
Handles.color = Color.green; | |
Handles.DrawLine(clickPosition, mousePosition); | |
var center = GetSelectionCenter(); | |
var scale = mousePosition - clickPosition; | |
//Uniform scaling if shift pressed | |
if (e.shift) { | |
if (Mathf.Abs(scale.x) > Mathf.Abs(scale.y)) { | |
scale.y = scale.x; | |
} else { | |
scale.x = scale.y; | |
} | |
} | |
//Determine direction of scaling | |
if (scale.x < 0) { | |
scale.x = 1 / (-scale.x + 1); | |
}else { | |
scale.x = 1 + scale.x; | |
} | |
if (scale.y < 0) { | |
scale.y = 1 / (-scale.y + 1); | |
}else { | |
scale.y = 1 + scale.y; | |
} | |
foreach (var i in selectedIndices) { | |
var point = polyMesh.keyPoints[i]; | |
var offset = point - center; | |
offset.x *= scale.x; | |
offset.y *= scale.y; | |
keyPoints[i] = center + offset; | |
} | |
} | |
#endregion | |
#region Drawing | |
void DrawAxis() { | |
Handles.color = Color.red; | |
var size = HandleUtility.GetHandleSize(Vector3.zero) * 0.1f; | |
Handles.DrawLine(new Vector3(-size, 0), new Vector3(size, 0)); | |
Handles.DrawLine(new Vector3(0, -size), new Vector2(0, size)); | |
} | |
void DrawKeyPoint(int index) { | |
Handles.DotCap(0, keyPoints[index], Quaternion.identity, HandleUtility.GetHandleSize(keyPoints[index]) * 0.03f); | |
} | |
void DrawCurvePoint(int index) { | |
Handles.DotCap(0, curvePoints[index], Quaternion.identity, HandleUtility.GetHandleSize(keyPoints[index]) * 0.03f); | |
} | |
void DrawSegment(int index) { | |
var from = keyPoints[index]; | |
var to = keyPoints[(index + 1) % keyPoints.Count]; | |
if (isCurve[index]) { | |
var control = Bezier.Control(from, to, curvePoints[index]); | |
var count = Mathf.Ceil(1 / polyMesh.curveDetail); | |
for (int i = 0; i < count; i++) { | |
Handles.DrawLine(Bezier.Curve(from, control, to, i / count), Bezier.Curve(from, control, to, (i + 1) / count)); | |
} | |
} else { | |
Handles.DrawLine(from, to); | |
} | |
} | |
void DrawCircle(Vector3 position, float size) { | |
Handles.CircleCap(0, position, inverseRotation, HandleUtility.GetHandleSize(position) * size); | |
} | |
void DrawNearestLineAndSplit() { | |
if (nearestLine >= 0) { | |
Handles.color = Color.green; | |
DrawSegment(nearestLine); | |
Handles.color = Color.red; | |
Handles.DotCap(0, splitPosition, Quaternion.identity, HandleUtility.GetHandleSize(splitPosition) * 0.03f); | |
} | |
} | |
#endregion | |
#region State Checking | |
bool TryHoverKeyPoint(out int index) { | |
if (TryHover(keyPoints, Color.white, out index)) { | |
mouseCursor = MouseCursor.MoveArrow; | |
return true; | |
} | |
return false; | |
} | |
bool TryHoverCurvePoint(out int index) { | |
if (TryHover(curvePoints, Color.white, out index)) { | |
mouseCursor = MouseCursor.MoveArrow; | |
return true; | |
} | |
return false; | |
} | |
bool TryDragKeyPoint(int index) { | |
if (TryDrag(keyPoints, index)) { | |
draggingCurve = false; | |
return true; | |
} | |
return false; | |
} | |
bool TryDragCurvePoint(int index) { | |
if (TryDrag(curvePoints, index)) { | |
draggingCurve = true; | |
return true; | |
} | |
return false; | |
} | |
bool TryHover(List<Vector3> points, Color color, out int index) { | |
if (Tools.current == Tool.Move) { | |
index = NearestPoint(points); | |
if (index >= 0 && IsHovering(points[index])) { | |
Handles.color = color; | |
DrawCircle(points[index], clickRadius); | |
return true; | |
} | |
} | |
index = -1; | |
return false; | |
} | |
bool TryDrag(List<Vector3> points, int index) { | |
if (e.type == EventType.MouseDown && IsHovering(points[index])) { | |
clickPosition = mousePosition; | |
return true; | |
} | |
return false; | |
} | |
bool TryStopDrag() { | |
if (e.type == EventType.MouseUp) { | |
dragIndex = -1; | |
UpdatePoly(false, state != State.Extrude); | |
return true; | |
} | |
return false; | |
} | |
bool TryBoxSelect() { | |
if (e.type == EventType.MouseDown) { | |
clickPosition = mousePosition; | |
return true; | |
} | |
return false; | |
} | |
bool TryBoxSelectEnd() { | |
var min = new Vector3(Mathf.Min(clickPosition.x, mousePosition.x), Mathf.Min(clickPosition.y, mousePosition.y)); | |
var max = new Vector3(Mathf.Max(clickPosition.x, mousePosition.x), Mathf.Max(clickPosition.y, mousePosition.y)); | |
Handles.color = Color.white; | |
Handles.DrawLine(new Vector3(min.x, min.y), new Vector3(max.x, min.y)); | |
Handles.DrawLine(new Vector3(min.x, max.y), new Vector3(max.x, max.y)); | |
Handles.DrawLine(new Vector3(min.x, min.y), new Vector3(min.x, max.y)); | |
Handles.DrawLine(new Vector3(max.x, min.y), new Vector3(max.x, max.y)); | |
if (e.type == EventType.MouseUp) { | |
var rect = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); | |
if (!control) { | |
selectedIndices.Clear(); | |
} | |
for (int i = 0; i < keyPoints.Count; i++){ | |
if (rect.Contains(keyPoints[i])) { | |
selectedIndices.Add(i); | |
} | |
} | |
return true; | |
} | |
return false; | |
} | |
bool TryDragSelected() { | |
if (selectedIndices.Count > 0 && TryDragButton(GetSelectionCenter(), 0.2f)) { | |
clickPosition = mousePosition; | |
return true; | |
} | |
return false; | |
} | |
bool TryRotateSelected() { | |
if (selectedIndices.Count > 0 && TryRotateButton(GetSelectionCenter(), 0.3f)) { | |
clickPosition = mousePosition; | |
return true; | |
} | |
return false; | |
} | |
bool TryScaleSelected() { | |
if (selectedIndices.Count > 0 && TryScaleButton(GetSelectionCenter(), 0.3f)) { | |
clickPosition = mousePosition; | |
return true; | |
} | |
return false; | |
} | |
bool TryDragButton(Vector3 position, float size) { | |
size *= HandleUtility.GetHandleSize(position); | |
if (Vector3.Distance(mousePosition, position) < size) { | |
if (e.type == EventType.MouseDown) { | |
return true; | |
} else { | |
mouseCursor = MouseCursor.MoveArrow; | |
Handles.color = Color.green; | |
} | |
} else { | |
Handles.color = Color.white; | |
} | |
var buffer = size / 2; | |
Handles.DrawLine(new Vector3(position.x - buffer, position.y), new Vector3(position.x + buffer, position.y)); | |
Handles.DrawLine(new Vector3(position.x, position.y - buffer), new Vector3(position.x, position.y + buffer)); | |
Handles.RectangleCap(0, position, Quaternion.identity, size); | |
return false; | |
} | |
bool TryRotateButton(Vector3 position, float size) { | |
size *= HandleUtility.GetHandleSize(position); | |
var dist = Vector3.Distance(mousePosition, position); | |
var buffer = size / 4; | |
if (dist < size + buffer && dist > size - buffer) { | |
if (e.type == EventType.MouseDown) { | |
return true; | |
}else { | |
mouseCursor = MouseCursor.RotateArrow; | |
Handles.color = Color.green; | |
} | |
} else { | |
Handles.color = Color.white; | |
} | |
Handles.CircleCap(0, position, inverseRotation, size - buffer / 2); | |
Handles.CircleCap(0, position, inverseRotation, size + buffer / 2); | |
return false; | |
} | |
bool TryScaleButton(Vector3 position, float size) { | |
size *= HandleUtility.GetHandleSize(position); | |
if (Vector3.Distance(mousePosition, position) < size) { | |
if (e.type == EventType.MouseDown) { | |
return true; | |
}else { | |
mouseCursor = MouseCursor.ScaleArrow; | |
Handles.color = Color.green; | |
} | |
} | |
else | |
Handles.color = Color.white; | |
var buffer = size / 4; | |
Handles.DrawLine(new Vector3(position.x - size - buffer, position.y), new Vector3(position.x - size + buffer, position.y)); | |
Handles.DrawLine(new Vector3(position.x + size - buffer, position.y), new Vector3(position.x + size + buffer, position.y)); | |
Handles.DrawLine(new Vector3(position.x, position.y - size - buffer), new Vector3(position.x, position.y - size + buffer)); | |
Handles.DrawLine(new Vector3(position.x, position.y + size - buffer), new Vector3(position.x, position.y + size + buffer)); | |
Handles.RectangleCap(0, position, Quaternion.identity, size); | |
return false; | |
} | |
bool TrySelectAll(){ | |
if (KeyPressed(selectAllKey)) { | |
selectedIndices.Clear(); | |
for (int i = 0; i < keyPoints.Count; i++){ | |
selectedIndices.Add(i); | |
} | |
return true; | |
} | |
return false; | |
} | |
bool TrySplitLine() { | |
if (nearestLine >= 0 && KeyPressed(splitKey)) { | |
if (nearestLine == keyPoints.Count - 1) { | |
keyPoints.Add(splitPosition); | |
curvePoints.Add(Vector3.zero); | |
isCurve.Add(false); | |
} else { | |
keyPoints.Insert(nearestLine + 1, splitPosition); | |
curvePoints.Insert(nearestLine + 1, Vector3.zero); | |
isCurve.Insert(nearestLine + 1, false); | |
} | |
isCurve[nearestLine] = false; | |
UpdatePoly(true, true); | |
return true; | |
} | |
return false; | |
} | |
bool TryExtrude() { | |
if (nearestLine >= 0 && extrudeKeyDown && e.type == EventType.MouseDown) { | |
var a = nearestLine; | |
var b = (nearestLine + 1) % keyPoints.Count; | |
if (b == 0 && a == keyPoints.Count - 1) { | |
//Extrude between the first and last points | |
keyPoints.Add(polyMesh.keyPoints[a]); | |
keyPoints.Add(polyMesh.keyPoints[b]); | |
curvePoints.Add(Vector3.zero); | |
curvePoints.Add(Vector3.zero); | |
isCurve.Add(false); | |
isCurve.Add(false); | |
selectedIndices.Clear(); | |
selectedIndices.Add(keyPoints.Count - 2); | |
selectedIndices.Add(keyPoints.Count - 1); | |
} else { | |
//Extrude between two inner points | |
var pointA = keyPoints[a]; | |
var pointB = keyPoints[b]; | |
keyPoints.Insert(a + 1, pointA); | |
keyPoints.Insert(a + 2, pointB); | |
curvePoints.Insert(a + 1, Vector3.zero); | |
curvePoints.Insert(a + 2, Vector3.zero); | |
isCurve.Insert(a + 1, false); | |
isCurve.Insert(a + 2, false); | |
selectedIndices.Clear(); | |
selectedIndices.Add(a + 1); | |
selectedIndices.Add(a + 2); | |
} | |
isCurve[nearestLine] = false; | |
clickPosition = mousePosition; | |
doExtrudeUpdate = true; | |
UpdatePoly(true, true); | |
return true; | |
} | |
return false; | |
} | |
bool TryDeleteSelected() { | |
if (KeyPressed(KeyCode.Backspace) || KeyPressed(KeyCode.Delete)) { | |
if (selectedIndices.Count > 0) { | |
if (keyPoints.Count - selectedIndices.Count >= 3) { | |
for (int i = selectedIndices.Count - 1; i >= 0; i--) { | |
var index = selectedIndices[i]; | |
keyPoints.RemoveAt(index); | |
curvePoints.RemoveAt(index); | |
isCurve.RemoveAt(index); | |
} | |
selectedIndices.Clear(); | |
UpdatePoly(true, true); | |
return true; | |
} | |
} else if (IsHovering(curvePoints[nearestLine])) { | |
isCurve[nearestLine] = false; | |
UpdatePoly(false, true); | |
} | |
} | |
return false; | |
} | |
bool IsHovering(Vector3 point) { | |
return Vector3.Distance(mousePosition, point) < HandleUtility.GetHandleSize(point) * clickRadius; | |
} | |
int NearestPoint(List<Vector3> points) { | |
var near = -1; | |
var nearDist = float.MaxValue; | |
for (int i = 0; i < points.Count; i++) { | |
var dist = Vector3.Distance(points[i], mousePosition); | |
if (dist < nearDist) { | |
nearDist = dist; | |
near = i; | |
} | |
} | |
return near; | |
} | |
int NearestLine(out Vector3 position) { | |
var near = -1; | |
var nearDist = float.MaxValue; | |
position = keyPoints[0]; | |
var linePos = Vector3.zero; | |
for (int i = 0; i < keyPoints.Count; i++) { | |
var j = (i + 1) % keyPoints.Count; | |
var line = keyPoints[j] - keyPoints[i]; | |
var offset = mousePosition - keyPoints[i]; | |
var dot = Vector3.Dot(line.normalized, offset); | |
if (dot >= 0 && dot <= line.magnitude) { | |
if (isCurve[i]) { | |
linePos = Bezier.Curve(keyPoints[i], Bezier.Control(keyPoints[i], keyPoints[j], curvePoints[i]), keyPoints[j], dot / line.magnitude); | |
} else { | |
linePos = keyPoints[i] + line.normalized * dot; | |
} | |
var dist = Vector3.Distance(linePos, mousePosition); | |
if (dist < nearDist) { | |
nearDist = dist; | |
position = linePos; | |
near = i; | |
} | |
} | |
} | |
return near; | |
} | |
bool KeyPressed(KeyCode key) { | |
return e.type == EventType.KeyDown && e.keyCode == key; | |
} | |
bool KeyReleased(KeyCode key) { | |
return e.type == EventType.KeyUp && e.keyCode == key; | |
} | |
Vector3 Snap(Vector3 value) { | |
value.x = Mathf.Round(value.x / snap) * snap; | |
value.y = Mathf.Round(value.y / snap) * snap; | |
return value; | |
} | |
Vector3 GetSelectionCenter() { | |
var center = Vector3.zero; | |
foreach (var i in selectedIndices) | |
center += polyMesh.keyPoints[i]; | |
return center / selectedIndices.Count; | |
} | |
#endregion | |
#region Properties | |
PolyMesh polyMesh { | |
get { return (PolyMesh)target; } | |
} | |
Event e { | |
get { return Event.current; } | |
} | |
bool control { | |
get { return Application.platform == RuntimePlatform.OSXEditor ? e.command : e.control; } | |
} | |
bool doSnap { | |
get { return autoSnap ? !control : control; } | |
} | |
static bool meshSettings { | |
get { return EditorPrefs.GetBool("PolyMeshEditor_meshSettings", false); } | |
set { EditorPrefs.SetBool("PolyMeshEditor_meshSettings", value); } | |
} | |
static bool colliderSettings { | |
get { return EditorPrefs.GetBool("PolyMeshEditor_colliderSettings", false); } | |
set { EditorPrefs.SetBool("PolyMeshEditor_colliderSettings", value); } | |
} | |
static bool uvSettings { | |
get { return EditorPrefs.GetBool("PolyMeshEditor_uvSettings", false); } | |
set { EditorPrefs.SetBool("PolyMeshEditor_uvSettings", value); } | |
} | |
static bool editorSettings { | |
get { return EditorPrefs.GetBool("PolyMeshEditor_editorSettings", false); } | |
set { EditorPrefs.SetBool("PolyMeshEditor_editorSettings", value); } | |
} | |
static bool autoSnap { | |
get { return EditorPrefs.GetBool("PolyMeshEditor_autoSnap", false); } | |
set { EditorPrefs.SetBool("PolyMeshEditor_autoSnap", value); } | |
} | |
static bool globalSnap { | |
get { return EditorPrefs.GetBool("PolyMeshEditor_globalSnap", false); } | |
set { EditorPrefs.SetBool("PolyMeshEditor_globalSnap", value); } | |
} | |
static float gridSnap { | |
get { return EditorPrefs.GetFloat("PolyMeshEditor_gridSnap", 1); } | |
set { EditorPrefs.SetFloat("PolyMeshEditor_gridSnap", value); } | |
} | |
static bool hideWireframe { | |
get { return EditorPrefs.GetBool("PolyMeshEditor_hideWireframe", true); } | |
set { EditorPrefs.SetBool("PolyMeshEditor_hideWireframe", value); } | |
} | |
public KeyCode editKey { | |
get { return (KeyCode)EditorPrefs.GetInt("PolyMeshEditor_editKey", (int)KeyCode.Tab); } | |
set { EditorPrefs.SetInt("PolyMeshEditor_editKey", (int)value); } | |
} | |
public KeyCode selectAllKey { | |
get { return (KeyCode)EditorPrefs.GetInt("PolyMeshEditor_selectAllKey", (int)KeyCode.A); } | |
set { EditorPrefs.SetInt("PolyMeshEditor_selectAllKey", (int)value); } | |
} | |
public KeyCode splitKey { | |
get { return (KeyCode)EditorPrefs.GetInt("PolyMeshEditor_splitKey", (int)KeyCode.S); } | |
set { EditorPrefs.SetInt("PolyMeshEditor_splitKey", (int)value); } | |
} | |
public KeyCode extrudeKey { | |
get { return (KeyCode)EditorPrefs.GetInt("PolyMeshEditor_extrudeKey", (int)KeyCode.D); } | |
set { EditorPrefs.SetInt("PolyMeshEditor_extrudeKey", (int)value); } | |
} | |
#endregion | |
#region Menu Items | |
[MenuItem("GameObject/Create Other/PolyMesh", false, 1000)] | |
static void CreatePolyMesh() { | |
var obj = new GameObject("PolyMesh", typeof(MeshFilter), typeof(MeshRenderer)); | |
var polyMesh = obj.AddComponent<PolyMesh>(); | |
CreateSquare(polyMesh, 0.5f); | |
} | |
static void CreateSquare(PolyMesh polyMesh, float size) { | |
polyMesh.keyPoints.AddRange(new Vector3[] { new Vector3(size, size), new Vector3(size, -size), new Vector3(-size, -size), new Vector3(-size, size)} ); | |
polyMesh.curvePoints.AddRange(new Vector3[] { Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero } ); | |
polyMesh.isCurve.AddRange(new bool[] { false, false, false, false } ); | |
polyMesh.BuildMesh(); | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment