using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace TowerDefense.MeshCreator.Editor
{
[CustomEditor(typeof(AreaMeshCreator))]
public class AreaMeshCreatorEditor : UnityEditor.Editor
{
protected AreaMeshCreator m_AreaMeshCreator;
protected MeshObject m_CurrentMeshObject;
protected float m_SquareSideLength = 1;
protected float m_OctagonRadius = 1;
///
/// Recreates the mesh when this script becomes active
///
protected void OnEnable()
{
m_AreaMeshCreator = (AreaMeshCreator)target;
CreateMesh();
}
///
/// Inspector GUI
///
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUILayout.BeginVertical();
ForcePointsFlat();
GUILayout.Space(10.0f);
EditorGUILayout.HelpBox("SHIFT: Delete Points", MessageType.Info);
GUILayout.Space(10.0f);
GUILayout.Label("Preset Shapes");
GUILayout.Label("Square");
m_SquareSideLength = EditorGUILayout.FloatField("Side Length", m_SquareSideLength);
if (GUILayout.Button("Square"))
{
SetSquare(m_SquareSideLength);
}
GUILayout.Space(10.0f);
GUILayout.Label("Octagon");
m_OctagonRadius = EditorGUILayout.FloatField("Radius", m_OctagonRadius);
if (GUILayout.Button("Octagon"))
{
SetOctagon(m_OctagonRadius);
}
GUILayout.EndVertical();
}
///
/// Creates the mesh from the points currently in the mesh creator
///
protected void CreateMesh()
{
List vertices3D = m_AreaMeshCreator.GetPoints();
Vector2[] vertices2D = new Vector2[vertices3D.Count];
for (int i = 0; i < vertices3D.Count; i++)
{
Vector3 v = m_AreaMeshCreator.transform.InverseTransformPoint(vertices3D[i]);
vertices2D[i] = new Vector2(v.x, v.z);
}
// Use the triangulator to get indices for creating triangles
var tr = new Triangulator(vertices2D);
int[] indices = tr.Triangulate();
// Create the Vector3 vertices
Vector3[] vertices = new Vector3[vertices2D.Length];
for (int i = 0; i < vertices.Length; i++)
{
vertices[i] = new Vector3(vertices2D[i].x, 0, vertices2D[i].y);
}
// Create the mesh
var msh = new Mesh();
msh.vertices = vertices;
msh.triangles = indices;
msh.RecalculateNormals();
msh.RecalculateBounds();
var filter = m_AreaMeshCreator.GetComponent();
if (m_AreaMeshCreator.GetComponent() == null)
{
filter = m_AreaMeshCreator.gameObject.AddComponent();
}
filter.mesh = msh;
Mesh mesh = msh;
int numberTriangles = mesh.triangles.Length / 3;
int[] triangles = mesh.triangles;
List trianglesList = new List();
for (int i = 0; i < numberTriangles; i++)
{
Vector3 v0 = mesh.vertices[triangles[i * 3]];
Vector3 v1 = mesh.vertices[triangles[i * 3 + 1]];
Vector3 v2 = mesh.vertices[triangles[i * 3 + 2]];
trianglesList.Add(new Triangle(v0, v1, v2));
}
m_AreaMeshCreator.meshObject = new MeshObject(trianglesList);
}
///
/// Makes points coplanar
///
protected void ForcePointsFlat()
{
m_AreaMeshCreator.ForcePointsFlat();
}
///
/// Adds a new point at the midpoint of 2 other points
///
/// First point
/// Second point
protected void AddPoint(Transform point1, Transform point2)
{
Vector3 first = point1.position, last = point2.position, midpoint = Midpoint(first, last);
GameObject p = Instantiate(m_AreaMeshCreator.pointsTransforms[0].gameObject, midpoint, Quaternion.identity);
p.name = "point";
p.transform.SetParent(m_AreaMeshCreator.transform.GetChild(0));
int index = Mathf.Min(point1.GetSiblingIndex(), point2.GetSiblingIndex()) + 1;
if (index == 1 && (point1.GetSiblingIndex() == m_AreaMeshCreator.pointsTransforms.Length - 2 ||
point2.GetSiblingIndex() == m_AreaMeshCreator.pointsTransforms.Length - 2))
{
p.transform.SetAsLastSibling();
}
else
{
p.transform.SetSiblingIndex(index);
}
CreateMesh();
Undo.RegisterCreatedObjectUndo(p, "Created point");
}
///
/// Draws and handles input for manipulating the mesh in scene
///
protected void OnSceneGUI()
{
if (m_AreaMeshCreator.pointsTransforms == null || m_AreaMeshCreator.pointsTransforms.Length < 3)
{
SetSquare(1);
}
if (Event.current.shift && m_AreaMeshCreator.GetPoints().Count > 3)
{
List allPoints = m_AreaMeshCreator.GetPoints();
var plane = new Plane(allPoints[0], allPoints[1], allPoints[2]);
Vector2 mousePos = Event.current.mousePosition;
Ray ray = HandleUtility.GUIPointToWorldRay(mousePos);
float rayDistance;
if (plane.Raycast(ray, out rayDistance))
{
Transform closestPoint = GetClosetsPoint(ray.GetPoint(rayDistance));
if (DeleteButton(closestPoint.position))
{
DeletePoint(closestPoint);
}
}
}
else
{
Transform[] points = m_AreaMeshCreator.pointsTransforms;
if (points == null)
{
return;
}
int length = points.Length;
for (int i = 0; i < length; i++)
{
Transform t = points[i];
// Vector3 newPosition = Handles.PositionHandle(t.position, Quaternion.identity);
float size = HandleUtility.GetHandleSize(t.position) * 0.125f;
Vector3 snap = Vector3.one * 0.5f;
Vector3 newPosition = Handles.FreeMoveHandle(t.position, Quaternion.LookRotation(Vector3.up), size, snap,
Handles.RectangleHandleCap);
newPosition.y = t.position.y;
if (newPosition != t.position)
{
t.position = newPosition;
Undo.RecordObject(t, string.Format("Moved {0}", t.name));
EditorUtility.SetDirty(t);
CreateMesh();
}
}
List allPoints = m_AreaMeshCreator.GetPoints();
var plane = new Plane(allPoints[0], allPoints[1], allPoints[2]);
Vector2 mousePos = Event.current.mousePosition;
Ray ray = HandleUtility.GUIPointToWorldRay(mousePos);
float rayDistance;
if (plane.Raycast(ray, out rayDistance))
{
Transform[] closestPoints = GetClosestTwoPoints(ray.GetPoint(rayDistance));
Vector3 position = Midpoint(closestPoints[0].position, closestPoints[1].position);
if (AddButton(position))
{
AddPoint(closestPoints[0], closestPoints[1]);
}
}
ForcePointsFlat();
}
// maintain selection of object
Selection.activeGameObject = m_AreaMeshCreator.gameObject;
}
///
/// Gets the 2 closest points to the specified (cursor) position
///
/// The position of the cursor
/// The 2 closest points to the cursor
protected Transform[] GetClosestTwoPoints(Vector3 position)
{
Transform[] points = new Transform[2];
points[0] = GetClosetsPoint(position);
int index = points[0].GetSiblingIndex(), length = m_AreaMeshCreator.pointsTransforms.Length;
int previousIndex = index > 0 ? index - 1 : length - 1;
Transform previous = points[0].parent.GetChild(previousIndex);
int nextIndex = index < length - 1 ? index + 1 : 0;
Transform next = points[0].parent.GetChild(nextIndex);
float previousDistance = Vector3.Distance(previous.position, position);
float nextDistance = Vector3.Distance(next.position, position);
points[1] = previousDistance < nextDistance ? previous : next;
return points;
}
///
/// Finds the closest point to the specified (cursor) position
///
/// The position of the cursor
/// The closest point to the cursor
protected Transform GetClosetsPoint(Vector3 position)
{
Transform[] ordererPoints = m_AreaMeshCreator.pointsTransforms.OrderBy(x => Vector3.Distance(x.position, position))
.ToArray();
return ordererPoints[0];
}
///
/// Deletes the selected point Transform.
///
protected void DeletePoint(Transform point)
{
Undo.DestroyObjectImmediate(point.gameObject);
CreateMesh();
}
///
/// Gets the midpoint between 2 Vector3's
///
/// First point
/// Last point
protected static Vector3 Midpoint(Vector3 first, Vector3 last)
{
return (first + last) * 0.5f;
}
///
/// Destroys the current points in the mesh
///
protected void ClearCurrentPoints()
{
Transform[] points = m_AreaMeshCreator.pointsTransforms;
int length = points.Length;
for (int i = 0; i < length; i++)
{
Undo.DestroyObjectImmediate(points[i].gameObject);
}
}
///
/// Creates a new point at the specified position
///
/// Position to create the point at
protected void CreateNewPoint(Vector3 position)
{
var point = new GameObject("point");
point.transform.position = position;
point.transform.SetParent(m_AreaMeshCreator.pointsCenter);
Undo.RegisterCreatedObjectUndo(point, "Created point");
}
///
/// Creates a basic square
///
/// Length of the sides of the square
protected void SetSquare(float sideLength)
{
ClearCurrentPoints();
Vector3 center = m_AreaMeshCreator.pointsCenter.position;
float halfSide = sideLength / 2f;
CreateNewPoint(center + new Vector3(halfSide, 0, halfSide));
CreateNewPoint(center + new Vector3(-halfSide, 0, halfSide));
CreateNewPoint(center + new Vector3(-halfSide, 0, -halfSide));
CreateNewPoint(center + new Vector3(halfSide, 0, -halfSide));
CreateMesh();
}
///
/// Creates an octagon
///
/// Radius of the Octagon
protected void SetOctagon(float radius)
{
ClearCurrentPoints();
Vector3 center = m_AreaMeshCreator.pointsCenter.position;
for (int i = 0; i < 8; i++)
{
float angle = 2 * Mathf.PI * (i + 1) / 8;
float x = center.x + radius * Mathf.Cos(angle);
float y = center.z + radius * Mathf.Sin(angle);
CreateNewPoint(new Vector3(x, 0, y));
}
CreateMesh();
}
bool AddButton(Vector3 position)
{
return HandleButton(position, "ADD", 50, 25);
}
bool DeleteButton(Vector3 position)
{
return HandleButton(position, "DELETE", 50, 25);
}
bool HandleButton(Vector3 position, string text, float width, float height)
{
Vector2 pos2D = HandleUtility.WorldToGUIPoint(position);
Handles.BeginGUI();
bool clicked = GUI.Button(new Rect(pos2D.x - width * 0.5f, pos2D.y - height * 0.5f, width, height), text);
Handles.EndGUI();
HandleUtility.Repaint();
return clicked;
}
}
}