272 lines
11 KiB
C#
272 lines
11 KiB
C#
|
using UnityEngine;
|
||
|
using UnityEditor;
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
|
||
|
namespace Obi
|
||
|
{
|
||
|
|
||
|
public class ObiPathHandles
|
||
|
{
|
||
|
|
||
|
static int splineSelectorHash = "ObiPathSelectorHash".GetHashCode();
|
||
|
const int minSelectionDistance = 5;
|
||
|
|
||
|
static Vector2 startPos;
|
||
|
static Vector2 currentPos;
|
||
|
static bool dragging = false;
|
||
|
static Rect marquee;
|
||
|
|
||
|
public static bool SplineCPSelector(ObiPath path, bool[] selectionStatus)
|
||
|
{
|
||
|
|
||
|
int controlID = GUIUtility.GetControlID(splineSelectorHash, FocusType.Passive);
|
||
|
int selectedCPIndex = -1;
|
||
|
bool selectionStatusChanged = false;
|
||
|
|
||
|
// select vertex on mouse click:
|
||
|
switch (Event.current.GetTypeForControl(controlID))
|
||
|
{
|
||
|
case EventType.Layout:
|
||
|
case EventType.MouseMove:
|
||
|
|
||
|
float minSqrDistance = System.Single.MaxValue;
|
||
|
float sqrMinSelectionDistance = minSelectionDistance * minSelectionDistance;
|
||
|
|
||
|
for (int i = 0; i < path.ControlPointCount; i++)
|
||
|
{
|
||
|
|
||
|
// get particle position in gui space:
|
||
|
Vector2 pos = HandleUtility.WorldToGUIPoint(path.points[i].position);
|
||
|
|
||
|
// get distance from mouse position to particle position:
|
||
|
float sqrDistance = Vector2.SqrMagnitude(Event.current.mousePosition - pos);
|
||
|
|
||
|
// check if this control point is closer to the cursor that any previously considered point.
|
||
|
if (sqrDistance < sqrMinSelectionDistance && sqrDistance < minSqrDistance)
|
||
|
{
|
||
|
minSqrDistance = sqrDistance;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
HandleUtility.AddControl(controlID, Mathf.Sqrt(minSqrDistance));
|
||
|
|
||
|
break;
|
||
|
|
||
|
case EventType.MouseDown:
|
||
|
|
||
|
marquee.Set(0, 0, 0, 0);
|
||
|
startPos = Event.current.mousePosition;
|
||
|
|
||
|
if (Event.current.button == 0)
|
||
|
{
|
||
|
|
||
|
if (HandleUtility.nearestControl == controlID)
|
||
|
{
|
||
|
GUIUtility.hotControl = controlID;
|
||
|
|
||
|
// If the user is pressing shift or ctrl, accumulate selection.
|
||
|
if ((Event.current.modifiers & (EventModifiers.Shift | EventModifiers.Control)) == 0 && (Event.current.modifiers & EventModifiers.Alt) == 0)
|
||
|
{
|
||
|
for (int i = 0; i < selectionStatus.Length; i++)
|
||
|
selectionStatus[i] = false;
|
||
|
|
||
|
selectionStatusChanged = true;
|
||
|
}
|
||
|
|
||
|
minSqrDistance = System.Single.MaxValue;
|
||
|
sqrMinSelectionDistance = minSelectionDistance * minSelectionDistance;
|
||
|
|
||
|
for (int i = 0; i < path.ControlPointCount; i++)
|
||
|
{
|
||
|
|
||
|
// get particle position in gui space:
|
||
|
Vector2 pos = HandleUtility.WorldToGUIPoint(path.points[i].position);
|
||
|
|
||
|
// get distance from mouse position to particle position:
|
||
|
float sqrDistance = Vector2.SqrMagnitude(startPos - pos);
|
||
|
|
||
|
// check if this control point is closer to the cursor that any previously considered point.
|
||
|
if (sqrDistance < sqrMinSelectionDistance && sqrDistance < minSqrDistance)
|
||
|
{
|
||
|
minSqrDistance = sqrDistance;
|
||
|
selectedCPIndex = i;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (selectedCPIndex >= 0)
|
||
|
{ // toggle particle selection status.
|
||
|
|
||
|
selectionStatus[selectedCPIndex] = !selectionStatus[selectedCPIndex];
|
||
|
selectionStatusChanged = true;
|
||
|
|
||
|
// Prevent spline deselection if we have selected a particle:
|
||
|
Event.current.Use();
|
||
|
|
||
|
}
|
||
|
}
|
||
|
else if ((Event.current.modifiers & (EventModifiers.Shift | EventModifiers.Control)) == 0 && (Event.current.modifiers & EventModifiers.Alt) == 0)
|
||
|
{
|
||
|
for (int i = 0; i < selectionStatus.Length; i++)
|
||
|
selectionStatus[i] = false;
|
||
|
|
||
|
selectionStatusChanged = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case EventType.MouseDrag:
|
||
|
|
||
|
if (Event.current.button == 0 && (Event.current.modifiers & EventModifiers.Alt) == 0)
|
||
|
{
|
||
|
currentPos = Event.current.mousePosition;
|
||
|
if (!dragging && Vector2.Distance(startPos, currentPos) > 5)
|
||
|
{
|
||
|
dragging = true;
|
||
|
}
|
||
|
|
||
|
if (dragging)
|
||
|
{
|
||
|
GUIUtility.hotControl = controlID;
|
||
|
Event.current.Use();
|
||
|
}
|
||
|
|
||
|
//update marquee rect:
|
||
|
float left = Mathf.Min(startPos.x, currentPos.x);
|
||
|
float right = Mathf.Max(startPos.x, currentPos.x);
|
||
|
float bottom = Mathf.Min(startPos.y, currentPos.y);
|
||
|
float top = Mathf.Max(startPos.y, currentPos.y);
|
||
|
|
||
|
marquee = new Rect(left, bottom, right - left, top - bottom);
|
||
|
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case EventType.MouseUp:
|
||
|
|
||
|
if (GUIUtility.hotControl == controlID)
|
||
|
{
|
||
|
|
||
|
dragging = false;
|
||
|
|
||
|
for (int i = 0; i < path.ControlPointCount; i++)
|
||
|
{
|
||
|
|
||
|
// get particle position in gui space:
|
||
|
Vector2 pos = HandleUtility.WorldToGUIPoint(path.points[i].position);
|
||
|
|
||
|
if (pos.x > marquee.xMin && pos.x < marquee.xMax && pos.y > marquee.yMin && pos.y < marquee.yMax)
|
||
|
{
|
||
|
selectionStatus[i] = true;
|
||
|
selectionStatusChanged = true;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
GUIUtility.hotControl = 0;
|
||
|
Event.current.Use();
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case EventType.Repaint:
|
||
|
|
||
|
if (dragging)
|
||
|
{
|
||
|
GUISkin oldSkin = GUI.skin;
|
||
|
GUI.skin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene);
|
||
|
Handles.BeginGUI();
|
||
|
GUI.Box(new Rect(marquee.xMin, marquee.yMin, marquee.width, marquee.height), "");
|
||
|
Handles.EndGUI();
|
||
|
GUI.skin = oldSkin;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
return selectionStatusChanged;
|
||
|
}
|
||
|
|
||
|
private static void DrawControlPointArcs(ObiPath path, float thicknessScale)
|
||
|
{
|
||
|
for (int i = 0; i < path.ControlPointCount; ++i)
|
||
|
{
|
||
|
Vector3 position = path.points[i].position;
|
||
|
Vector3 tangent = path.points.GetTangent(i);
|
||
|
Vector3 right = Vector3.Cross(tangent, path.normals[i]).normalized;
|
||
|
float thickness = path.thicknesses[i] * thicknessScale + 0.05f;
|
||
|
|
||
|
Handles.DrawWireArc(position, tangent, right, -180, thickness);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void DrawPathPolylines(Vector3[] samples, Vector3[] leftSamples, Vector3[] rightSamples, Vector3[] upSamples, bool drawOrientation)
|
||
|
{
|
||
|
Handles.DrawPolyLine(samples);
|
||
|
if (drawOrientation)
|
||
|
{
|
||
|
Handles.DrawPolyLine(leftSamples);
|
||
|
Handles.DrawPolyLine(upSamples);
|
||
|
Handles.DrawPolyLine(rightSamples);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static void DrawPathHandle(ObiPath path, Matrix4x4 referenceFrame, float thicknessScale, int resolution, bool drawOrientation = true)
|
||
|
{
|
||
|
|
||
|
if (path == null || path.GetSpanCount() == 0) return;
|
||
|
|
||
|
Matrix4x4 prevMatrix = Handles.matrix;
|
||
|
Handles.matrix = referenceFrame;
|
||
|
|
||
|
// Draw the curve:
|
||
|
int curveSegments = path.GetSpanCount() * resolution;
|
||
|
Vector3[] samples = new Vector3[curveSegments + 1];
|
||
|
Vector3[] leftSamples = new Vector3[curveSegments + 1];
|
||
|
Vector3[] rightSamples = new Vector3[curveSegments + 1];
|
||
|
Vector3[] upSamples = new Vector3[curveSegments + 1];
|
||
|
|
||
|
for (int i = 0; i <= curveSegments; ++i)
|
||
|
{
|
||
|
|
||
|
float mu = i / (float)curveSegments;
|
||
|
samples[i] = path.points.GetPositionAtMu(path.Closed,mu);
|
||
|
|
||
|
if (drawOrientation)
|
||
|
{
|
||
|
Vector3 tangent = path.points.GetTangentAtMu(path.Closed,mu);
|
||
|
Vector3 right = Vector3.Cross(tangent, path.normals.GetAtMu(path.Closed,mu)).normalized;
|
||
|
Vector3 up = Vector3.Cross(right, tangent).normalized;
|
||
|
float thickness = path.thicknesses.GetAtMu(path.Closed,mu) * thicknessScale + 0.05f;
|
||
|
|
||
|
leftSamples[i] = samples[i] - right * thickness;
|
||
|
rightSamples[i] = samples[i] + right * thickness;
|
||
|
upSamples[i] = samples[i] + up * thickness;
|
||
|
|
||
|
if (i % 5 == 0)
|
||
|
{
|
||
|
Handles.DrawLine(leftSamples[i], rightSamples[i]);
|
||
|
Handles.DrawLine(samples[i], samples[i] + up * thickness);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (drawOrientation)
|
||
|
DrawControlPointArcs(path, thicknessScale);
|
||
|
|
||
|
DrawPathPolylines(samples, leftSamples, rightSamples, upSamples, drawOrientation);
|
||
|
DrawPathPolylines(samples, leftSamples, rightSamples, upSamples, drawOrientation);
|
||
|
|
||
|
Handles.matrix = prevMatrix;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|