539 lines
23 KiB
C#
539 lines
23 KiB
C#
// =====================================================================
|
|
// Copyright 2013-2017 Fluffy Underware
|
|
// All rights reserved
|
|
//
|
|
// http://www.fluffyunderware.com
|
|
// =====================================================================
|
|
|
|
using System;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.Collections.Generic;
|
|
|
|
namespace FluffyUnderware.DevToolsEditor
|
|
{
|
|
public static class DTHandles
|
|
{
|
|
public static class SnapSettings
|
|
{
|
|
private static float s_MoveSnapX;
|
|
private static float s_MoveSnapY;
|
|
private static float s_MoveSnapZ;
|
|
private static float s_ScaleSnap;
|
|
private static float s_RotationSnap;
|
|
private static bool s_Initialized;
|
|
|
|
private static void Initialize()
|
|
{
|
|
if (!s_Initialized)
|
|
{
|
|
s_MoveSnapX = EditorPrefs.GetFloat("MoveSnapX", 1f);
|
|
s_MoveSnapY = EditorPrefs.GetFloat("MoveSnapY", 1f);
|
|
s_MoveSnapZ = EditorPrefs.GetFloat("MoveSnapZ", 1f);
|
|
s_ScaleSnap = EditorPrefs.GetFloat("ScaleSnap", 0.1f);
|
|
s_RotationSnap = EditorPrefs.GetFloat("RotationSnap", 15f);
|
|
s_Initialized = true;
|
|
}
|
|
}
|
|
|
|
public static Vector3 Move
|
|
{
|
|
get { return new Vector3(MoveX, MoveY, MoveZ); }
|
|
}
|
|
public static float MoveX
|
|
{
|
|
get { Initialize(); return s_MoveSnapX; }
|
|
}
|
|
public static float MoveY
|
|
{
|
|
get { Initialize(); return s_MoveSnapY; }
|
|
}
|
|
public static float MoveZ
|
|
{
|
|
get { Initialize(); return s_MoveSnapZ; }
|
|
}
|
|
public static float Rotation
|
|
{
|
|
get { Initialize(); return s_RotationSnap; }
|
|
}
|
|
public static float ScaleSnap
|
|
{
|
|
get { Initialize(); return s_ScaleSnap; }
|
|
}
|
|
}
|
|
|
|
public static bool MouseOverSceneView
|
|
{
|
|
get
|
|
{
|
|
return (SceneView.currentDrawingSceneView != null && Event.current != null) ?
|
|
SceneView.currentDrawingSceneView.position.Contains(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)) : false;
|
|
}
|
|
}
|
|
|
|
public static bool SceneViewIsSelected
|
|
{
|
|
get
|
|
{
|
|
return SceneView.focusedWindow == SceneView.currentDrawingSceneView;
|
|
}
|
|
}
|
|
|
|
static Stack<Color> mHandlesColorstack = new Stack<Color>();
|
|
|
|
public static void PushHandlesColor(Color col)
|
|
{
|
|
mHandlesColorstack.Push(GUI.color);
|
|
Handles.color = col;
|
|
}
|
|
|
|
public static void PopHandlesColor()
|
|
{
|
|
Handles.color = mHandlesColorstack.Pop();
|
|
}
|
|
|
|
static Stack<Matrix4x4> mHandlesMatrixstack = new Stack<Matrix4x4>();
|
|
|
|
public static void PushMatrix(Matrix4x4 matrix)
|
|
{
|
|
mHandlesMatrixstack.Push(matrix);
|
|
Handles.matrix = matrix;
|
|
}
|
|
|
|
public static void PopMatrix()
|
|
{
|
|
Handles.matrix = mHandlesMatrixstack.Pop();
|
|
}
|
|
|
|
|
|
public static void LabelIcon(Vector3 position, string text, Color labelColor)
|
|
{
|
|
GUIStyle iconStyle = GUI.skin.button;
|
|
iconStyle.normal.textColor = labelColor;
|
|
Handles.Label(position, text, iconStyle);
|
|
}
|
|
|
|
public static bool Button(int id, Vector3 position, Quaternion direction, float size, float pickSize, bool useHandleSize, Handles.CapFunction capFunc, Color hoverCol, out bool isHovering)
|
|
{
|
|
Event current = Event.current;
|
|
isHovering = false;
|
|
if (useHandleSize)
|
|
{
|
|
float s = HandleUtility.GetHandleSize(position);
|
|
size *= s;
|
|
pickSize *= s;
|
|
}
|
|
switch (current.GetTypeForControl(id))
|
|
{
|
|
case EventType.MouseDown:
|
|
if (HandleUtility.nearestControl == id)
|
|
{
|
|
GUIUtility.hotControl = id;
|
|
current.Use();
|
|
}
|
|
break;
|
|
case EventType.MouseUp:
|
|
if ((current.button == 0 || current.button == 2) && GUIUtility.hotControl == id)
|
|
{
|
|
GUIUtility.hotControl = 0;
|
|
current.Use();
|
|
if (HandleUtility.nearestControl == id)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
case EventType.MouseMove:
|
|
if ((HandleUtility.nearestControl == id && current.button == 0) || (GUIUtility.keyboardControl == id && current.button == 2))
|
|
{
|
|
HandleUtility.Repaint();
|
|
}
|
|
break;
|
|
case EventType.Repaint:
|
|
{
|
|
Color color = Handles.color;
|
|
if (HandleUtility.nearestControl == id && GUI.enabled)
|
|
{
|
|
isHovering = true;
|
|
Handles.color = hoverCol;
|
|
}
|
|
|
|
capFunc(id, position, Quaternion.identity, size, EventType.Repaint);
|
|
Handles.color = color;
|
|
break;
|
|
}
|
|
case EventType.Layout:
|
|
if (GUI.enabled)
|
|
{
|
|
HandleUtility.AddControl(id, HandleUtility.DistanceToCircle(position, pickSize));
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static bool PositionHandle(Vector3 position, Quaternion rotation, out Vector3 delta)
|
|
{
|
|
Vector3 p = Handles.PositionHandle(position, rotation);
|
|
delta = p - position;
|
|
return delta != Vector3.zero;
|
|
}
|
|
|
|
public static bool RotationHandle(Vector3 position, Quaternion rotation, out Quaternion newRotation)
|
|
{
|
|
newRotation = Handles.RotationHandle(rotation, position);
|
|
return (newRotation != rotation);
|
|
}
|
|
|
|
static Vector2 s_StartMousePosition;
|
|
static Vector2 s_CurrentMousePosition;
|
|
static float s_StartScale;
|
|
|
|
public static bool ScaleSlider(float scale, Vector3 position, Vector3 direction, Quaternion rotation, float length, float size, float snap, out float delta)
|
|
{
|
|
int id = GUIUtility.GetControlID("ScaleSliderHash".GetHashCode(), FocusType.Keyboard);
|
|
float newScale = scale;
|
|
Event current = Event.current;
|
|
switch (current.GetTypeForControl(id))
|
|
{
|
|
case EventType.MouseDown:
|
|
if ((HandleUtility.nearestControl == id && current.button == 0) || (GUIUtility.keyboardControl == id && current.button == 2))
|
|
{
|
|
GUIUtility.keyboardControl = id;
|
|
GUIUtility.hotControl = id;
|
|
s_CurrentMousePosition = (s_StartMousePosition = current.mousePosition);
|
|
s_StartScale = scale;
|
|
current.Use();
|
|
EditorGUIUtility.SetWantsMouseJumping(1);
|
|
}
|
|
break;
|
|
case EventType.MouseUp:
|
|
if (GUIUtility.hotControl == id && (current.button == 0 || current.button == 2))
|
|
{
|
|
GUIUtility.hotControl = 0;
|
|
current.Use();
|
|
EditorGUIUtility.SetWantsMouseJumping(0);
|
|
}
|
|
break;
|
|
case EventType.MouseDrag:
|
|
if (GUIUtility.hotControl == id)
|
|
{
|
|
s_CurrentMousePosition += current.delta;
|
|
float num = 1f + HandleUtility.CalcLineTranslation(s_StartMousePosition, s_CurrentMousePosition, position, direction) * 0.1f;
|
|
num = Handles.SnapValue(num, snap);
|
|
newScale = s_StartScale * num;
|
|
GUI.changed = true;
|
|
current.Use();
|
|
}
|
|
break;
|
|
case EventType.Repaint:
|
|
{
|
|
Color color = Color.white;
|
|
if (id == GUIUtility.keyboardControl)
|
|
{
|
|
color = Handles.color;
|
|
Handles.color = Handles.selectedColor;
|
|
}
|
|
//float num2 = size;
|
|
//if (GUIUtility.hotControl == id)
|
|
//{
|
|
// num2 = size * scale / s_StartScale;
|
|
//}
|
|
Handles.CubeHandleCap(id, position + direction * length, rotation, HandleUtility.GetHandleSize(position) * size, EventType.Repaint);
|
|
|
|
Handles.DrawLine(position, position + direction * length);
|
|
if (id == GUIUtility.keyboardControl)
|
|
{
|
|
Handles.color = color;
|
|
}
|
|
break;
|
|
}
|
|
case EventType.Layout:
|
|
HandleUtility.AddControl(id, HandleUtility.DistanceToLine(position, position + direction * length));
|
|
HandleUtility.AddControl(id, HandleUtility.DistanceToCircle(position + direction * length, size));
|
|
break;
|
|
}
|
|
delta = newScale - scale;
|
|
|
|
return (scale != newScale);
|
|
}
|
|
|
|
public static Quaternion RotationHandle(Quaternion rotation, Vector3 position, float size)
|
|
{
|
|
Color staticColor = new Color(0.5f, 0.5f, 0.5f, 0f);
|
|
float staticBlend = 0.6f;
|
|
float handleSize = HandleUtility.GetHandleSize(position) * size;
|
|
Color color = Handles.color;
|
|
bool flag = !Tools.hidden && EditorApplication.isPlaying && ContainsStatic(Selection.gameObjects);
|
|
Handles.color = ((!flag) ? Handles.xAxisColor : Color.Lerp(Handles.xAxisColor, staticColor, staticBlend));
|
|
rotation = Handles.Disc(rotation, position, rotation * Vector3.right, handleSize, true, SnapSettings.Rotation);
|
|
Handles.color = ((!flag) ? Handles.yAxisColor : Color.Lerp(Handles.yAxisColor, staticColor, staticBlend));
|
|
rotation = Handles.Disc(rotation, position, rotation * Vector3.up, handleSize, true, SnapSettings.Rotation);
|
|
Handles.color = ((!flag) ? Handles.zAxisColor : Color.Lerp(Handles.zAxisColor, staticColor, staticBlend));
|
|
rotation = Handles.Disc(rotation, position, rotation * Vector3.forward, handleSize, true, SnapSettings.Rotation);
|
|
if (!flag)
|
|
{
|
|
Handles.color = Handles.centerColor;
|
|
rotation = Handles.Disc(rotation, position, Camera.current.transform.forward, handleSize * 1.1f, false, 0f);
|
|
rotation = Handles.FreeRotateHandle(rotation, position, handleSize);
|
|
}
|
|
Handles.color = color;
|
|
return rotation;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws a 3D position handle
|
|
/// </summary>
|
|
/// <returns>The updated position</returns>
|
|
/// <seealso cref="GUIUtility.GetControlID(int,FocusType)"/>
|
|
public static Vector3 PositionHandle(int controlId1, int controlId2, int controlId3, Vector3 position, Quaternion rotation, float handleSize, float rectangleToSliderRatio = 0.15f, Color additionalColor = new Color())
|
|
{
|
|
float sliderSize = HandleUtility.GetHandleSize(position) * handleSize;
|
|
float rectangleSize = sliderSize * rectangleToSliderRatio;
|
|
|
|
Vector3 snap = SnapSettings.Move;
|
|
|
|
Vector3 axis1 = rotation * Vector3.right;
|
|
Vector3 axis2 = rotation * Vector3.up;
|
|
Vector3 axis3 = rotation * Vector3.forward;
|
|
Color axis1Color = new Color(0.9f, 0.3f, 0.1f) + additionalColor;
|
|
Color axis2Color = new Color(0.6f, 0.9f, 0.3f) + additionalColor;
|
|
Color axis3Color = new Color(0.2f, 0.4f, 0.9f) + additionalColor;
|
|
|
|
Vector3 updatedPosition = position;
|
|
|
|
Handles.color = axis1Color;
|
|
updatedPosition += Handles.Slider(position, axis1, sliderSize, Handles.ArrowHandleCap, snap.x) - position;
|
|
updatedPosition += Handles.Slider2D(controlId1, position, rectangleSize * (axis3 + axis2), Vector3.Cross(axis3, axis2), axis2, axis3, rectangleSize, Handles.RectangleHandleCap, new Vector2(snap.y, snap.z)) - position;
|
|
|
|
Handles.color = axis2Color;
|
|
updatedPosition += Handles.Slider(position, axis2, sliderSize, Handles.ArrowHandleCap, snap.y) - position;
|
|
updatedPosition += Handles.Slider2D(controlId2, position, rectangleSize * (axis1 + axis3), Vector3.Cross(axis1, axis3), axis3, axis1, rectangleSize, Handles.RectangleHandleCap, new Vector2(snap.z, snap.x)) - position;
|
|
|
|
Handles.color = axis3Color;
|
|
updatedPosition += Handles.Slider(position, axis3, sliderSize, Handles.ArrowHandleCap, snap.z) - position;
|
|
updatedPosition += Handles.Slider2D(controlId3, position, rectangleSize * (axis1 + axis2), Vector3.Cross(axis1, axis2), axis2, axis1, rectangleSize, Handles.RectangleHandleCap, new Vector2(snap.y, snap.x)) - position;
|
|
|
|
return updatedPosition;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws a 2D position handle
|
|
/// </summary>
|
|
/// <param name="size"></param>
|
|
/// <param name="plane">0 for XY, 1 for XZ and 2 for YZ</param>
|
|
/// <param name="controlId"><see cref="GUIUtility.GetControlID(int,FocusType)"/></param>
|
|
/// <param name="position"></param>
|
|
/// <param name="rotation"></param>
|
|
/// <returns>The updated position</returns>
|
|
public static Vector3 PositionHandle2D(int controlId, Vector3 position, Quaternion rotation, float size, int plane = 0)
|
|
{
|
|
float sliderSize = HandleUtility.GetHandleSize(position) * size;
|
|
float rectSize = sliderSize * 0.15f;
|
|
|
|
Vector3 snap = SnapSettings.Move;
|
|
|
|
Vector3 axis1;
|
|
Vector3 axis2;
|
|
Color axis1Color;
|
|
Color axis2Color;
|
|
Color planeColor;
|
|
{
|
|
Color xColor = new Color(0.9f, 0.3f, 0.1f);
|
|
Color yColor = new Color(0.6f, 0.9f, 0.3f);
|
|
Color zColor = new Color(0.2f, 0.4f, 0.9f);
|
|
|
|
switch (plane)
|
|
{
|
|
//XY
|
|
case 0:
|
|
axis1 = rotation * Vector3.right;
|
|
axis2 = rotation * Vector3.up;
|
|
axis1Color = xColor;
|
|
axis2Color = yColor;
|
|
planeColor = zColor;
|
|
break;
|
|
//XZ
|
|
case 1:
|
|
axis1 = rotation * Vector3.right;
|
|
axis2 = rotation * Vector3.forward;
|
|
axis1Color = xColor;
|
|
axis2Color = zColor;
|
|
planeColor = yColor;
|
|
break;
|
|
//YZ
|
|
case 2:
|
|
axis1 = rotation * Vector3.up;
|
|
axis2 = rotation * Vector3.forward;
|
|
axis1Color = yColor;
|
|
axis2Color = zColor;
|
|
planeColor = xColor;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(plane));
|
|
}
|
|
}
|
|
|
|
Vector3 updatedPosition = position;
|
|
|
|
Handles.color = axis1Color;
|
|
updatedPosition += Handles.Slider(position, axis1, sliderSize, Handles.ArrowHandleCap, snap.x) - position;
|
|
|
|
Handles.color = axis2Color;
|
|
updatedPosition += Handles.Slider(position, axis2, sliderSize, Handles.ArrowHandleCap, snap.y) - position;
|
|
|
|
Handles.color = planeColor;
|
|
updatedPosition += Handles.Slider2D(controlId, position, rectSize * (axis1 + axis2), Vector3.Cross(axis1, axis2), axis2, axis1, rectSize, Handles.RectangleHandleCap, new Vector2(snap.x, snap.y)) - position;
|
|
|
|
return updatedPosition;
|
|
}
|
|
|
|
public static Vector3 TinyHandle2D(int id, Vector3 position, Quaternion rotation, float size, Handles.CapFunction func = null)
|
|
{
|
|
return TinyHandle2D(id, position, rotation * Vector3.forward, rotation * Vector3.up, rotation * Vector3.right, size, func);
|
|
}
|
|
|
|
public static Vector3 TinyHandle2D(int id, Transform transform, float size, Handles.CapFunction func = null)
|
|
{
|
|
return TinyHandle2D(id, transform.position, transform.forward, transform.up, transform.right, size, func);
|
|
}
|
|
|
|
public static Vector3 TinyHandle2D(int id, Vector3 pos, Vector3 forward, Vector3 up, Vector3 right, float size, Handles.CapFunction func = null)
|
|
{
|
|
return Handles.Slider2D(id, pos, forward, up, right, HandleUtility.GetHandleSize(pos) * size, func, SnapSettings.Move);
|
|
}
|
|
|
|
public static void DrawSolidRectangleWithOutline(Vector2 center, Vector2 extends, Color backgroundColor, Color outlineColor)
|
|
{
|
|
DrawSolidRectangleWithOutline(new Rect(center.x - extends.x / 2, center.y - extends.y / 2, extends.x, extends.y), backgroundColor, outlineColor);
|
|
}
|
|
|
|
public static void DrawSolidRectangleWithOutline(Rect r, Color backgroundColor, Color outlineColor)
|
|
{
|
|
Vector3[] verts = new Vector3[4]
|
|
{
|
|
new Vector3(r.xMin,r.yMin,0),
|
|
new Vector3(r.xMax,r.yMin,0),
|
|
new Vector3(r.xMax,r.yMax,0),
|
|
new Vector3(r.xMin,r.yMax,0)
|
|
};
|
|
Handles.DrawSolidRectangleWithOutline(verts, backgroundColor, outlineColor);
|
|
}
|
|
|
|
public static void DrawOutline(Rect r, Color outlineColor)
|
|
{
|
|
DrawSolidRectangleWithOutline(r, new Color(0, 0, 0, 0), outlineColor);
|
|
}
|
|
|
|
public static void WireCubeCap(Vector3 position, Vector3 size)
|
|
{
|
|
Vector3 half = size / 2;
|
|
// draw front
|
|
Handles.DrawLine(position + new Vector3(-half.x, -half.y, half.z), position + new Vector3(half.x, -half.y, half.z));
|
|
Handles.DrawLine(position + new Vector3(-half.x, -half.y, half.z), position + new Vector3(-half.x, half.y, half.z));
|
|
Handles.DrawLine(position + new Vector3(half.x, half.y, half.z), position + new Vector3(half.x, -half.y, half.z));
|
|
Handles.DrawLine(position + new Vector3(half.x, half.y, half.z), position + new Vector3(-half.x, half.y, half.z));
|
|
// draw back
|
|
Handles.DrawLine(position + new Vector3(-half.x, -half.y, -half.z), position + new Vector3(half.x, -half.y, -half.z));
|
|
Handles.DrawLine(position + new Vector3(-half.x, -half.y, -half.z), position + new Vector3(-half.x, half.y, -half.z));
|
|
Handles.DrawLine(position + new Vector3(half.x, half.y, -half.z), position + new Vector3(half.x, -half.y, -half.z));
|
|
Handles.DrawLine(position + new Vector3(half.x, half.y, -half.z), position + new Vector3(-half.x, half.y, -half.z));
|
|
// draw corners
|
|
Handles.DrawLine(position + new Vector3(-half.x, -half.y, -half.z), position + new Vector3(-half.x, -half.y, half.z));
|
|
Handles.DrawLine(position + new Vector3(half.x, -half.y, -half.z), position + new Vector3(half.x, -half.y, half.z));
|
|
Handles.DrawLine(position + new Vector3(-half.x, half.y, -half.z), position + new Vector3(-half.x, half.y, half.z));
|
|
Handles.DrawLine(position + new Vector3(half.x, half.y, -half.z), position + new Vector3(half.x, half.y, half.z));
|
|
}
|
|
|
|
public static void ArrowCap(Vector3 position, Vector3 direction, Color outlineColor, float length, float lineWidth = 0.4f, float headWidth = 1f, float headRatio = 0.3f, bool useHandleSize = true)
|
|
{
|
|
float size = (useHandleSize) ? HandleUtility.GetHandleSize(position) : 1;
|
|
|
|
ArrowCap(position, direction, Vector3.up, outlineColor, length, lineWidth, headWidth, headRatio, size);
|
|
}
|
|
|
|
public static void ArrowCap(Vector3 position, Vector3 direction, Vector3 upDirection, Color outlineColor, float length, float lineWidth, float headWidth, float headRatio, float sizeMultiplier)
|
|
{
|
|
Vector3[] arrow = new Vector3[8];
|
|
Vector3 start = position;
|
|
length *= sizeMultiplier;
|
|
Vector3 end = position + direction * length;
|
|
|
|
Vector3 right = Vector3.Cross(upDirection, direction).normalized;
|
|
|
|
float hh = length * headRatio;
|
|
Vector3 rlw2 = right * lineWidth / 2 * sizeMultiplier;
|
|
Vector3 rhw2 = right * headWidth / 2 * sizeMultiplier;
|
|
arrow[0] = start - rlw2;
|
|
arrow[1] = end - direction * hh - rlw2;
|
|
arrow[2] = end - direction * hh - rhw2;
|
|
arrow[3] = end;
|
|
arrow[4] = end - direction * hh + rhw2;
|
|
arrow[5] = end - direction * hh + rlw2;
|
|
arrow[6] = start + rlw2;
|
|
arrow[7] = arrow[0];
|
|
|
|
Handles.DrawAAConvexPolygon(arrow[2], arrow[3], arrow[4]);
|
|
Handles.DrawAAConvexPolygon(arrow[0], arrow[1], arrow[5], arrow[6]);
|
|
if (Handles.color != outlineColor)
|
|
{
|
|
Color c = Handles.color;
|
|
Handles.color = outlineColor;
|
|
Handles.DrawAAPolyLine(arrow);
|
|
Handles.color = c;
|
|
}
|
|
}
|
|
|
|
public static void BoundsCap(Bounds b)
|
|
{
|
|
WireCubeCap(b.center, b.size);
|
|
}
|
|
|
|
public static Vector3 TranslateByPixel(Vector3 position, float x, float y)
|
|
{
|
|
return TranslateByPixel(position, new Vector3(x, y));
|
|
}
|
|
public static Vector3 TranslateByPixel(Vector3 position, Vector3 translateBy)
|
|
{
|
|
Camera cam = SceneView.currentDrawingSceneView.camera;
|
|
if (cam)
|
|
return cam.ScreenToWorldPoint(cam.WorldToScreenPoint(position) + translateBy);
|
|
else
|
|
return position;
|
|
}
|
|
|
|
public static Vector3 TranslateAligned(Vector3 position, GUIContent content, TextAlignment alignment = TextAlignment.Center, GUIStyle style = null)
|
|
{
|
|
if (style == null)
|
|
style = EditorStyles.label;
|
|
float w = style.CalcSize(content).x;
|
|
switch (alignment)
|
|
{
|
|
case TextAlignment.Center:
|
|
return TranslateByPixel(position, new Vector3(-w / 2, 0, 0));
|
|
case TextAlignment.Right:
|
|
return TranslateByPixel(position, new Vector3(w / 2, 0, 0));
|
|
default:
|
|
return position;
|
|
}
|
|
|
|
}
|
|
|
|
static bool ContainsStatic(GameObject[] objects)
|
|
{
|
|
if (objects == null || objects.Length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
for (int i = 0; i < objects.Length; i++)
|
|
{
|
|
if (objects[i] != null && objects[i].isStatic)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|