using Unity.Mathematics; using UnityEditor.SettingsManagement; using UnityEngine; using UnityEngine.Rendering; namespace UnityEditor.Splines { static class DirectManipulation { [UserSetting("Tweak Mode", "Plane Color")] static readonly Pref s_GuidePlaneColor = new Pref("Handles.DirectManipulation.PlaneColor", new Color(1f, 1f, 1f, 5f/255f)); [UserSetting("Tweak Mode", "Snap to Guide Enabled")] static readonly Pref s_SnapToGuide = new Pref("Handles.DirectManipulation.SnapToGuide", true); [UserSetting("Tweak Mode", "Snap to Guide Distance")] static readonly Pref s_SnapToGuideDistance = new Pref("Handles.DirectManipulation.SnapToGuideDistance", 7f); static readonly Vector3[] s_VertexBuffer = new Vector3[4]; public static bool IsDragging => s_IsDragging; static readonly Vector3 k_GuidePlaneZTestOffset = new Vector3(0.001f, 0.001f, 0.001f); static Vector3 s_InitialPosition; static Quaternion s_InitialRotation; static Vector2 s_InitialMousePosition; static bool s_IsDragging; #if UNITY_2022_2_OR_NEWER static bool IncrementalSnapActive => EditorSnapSettings.incrementalSnapActive; #else static bool IncrementalSnapActive => false; #endif const float k_HandleColorAlphaFactor = 0.3f; static bool ShouldMoveOnNormal(int controlId) => GUIUtility.hotControl == controlId && Event.current.alt; public static void BeginDrag(Vector3 position, Quaternion rotation) { s_InitialPosition = position; s_InitialRotation = rotation; s_InitialMousePosition = Event.current.mousePosition; } public static Vector3 UpdateDrag(int controlId) { var position = ShouldMoveOnNormal(controlId) ? MoveOnNormal(Event.current.mousePosition, s_InitialPosition, s_InitialRotation) : MoveOnPlane(Event.current.mousePosition, s_InitialPosition, s_InitialRotation, IncrementalSnapActive); s_IsDragging = true; return position; } public static void EndDrag() { s_IsDragging = false; } public static void DrawHandles(int controlId, Vector3 position) { if (GUIUtility.hotControl != controlId || !s_IsDragging) return; EditorGUIUtility.AddCursorRect(new Rect(0, 0, 100000, 10000), MouseCursor.MoveArrow); if (ShouldMoveOnNormal(controlId)) { var yDir = s_InitialRotation * Vector3.up; DrawGuideAxis(s_InitialPosition, yDir, Handles.yAxisColor); } else { var zDir = s_InitialRotation * Vector3.forward; var xDir = s_InitialRotation * Vector3.right; DrawGuidePlane(s_InitialPosition, xDir, zDir, position); DrawGuideDottedLine(s_InitialPosition, zDir, position); DrawGuideDottedLine(s_InitialPosition, xDir, position); DrawGuideAxis(s_InitialPosition, zDir, Handles.zAxisColor); DrawGuideAxis(s_InitialPosition, xDir, Handles.xAxisColor); } } static (Vector3 projection, float distance) GetSnapToGuideData(Vector3 current, Vector3 origin, Vector3 axis) { var projection = Vector3.Project(current - origin, axis); var screenPos = HandleUtility.WorldToGUIPoint(origin + projection); var distance = Vector2.Distance(screenPos, Event.current.mousePosition); return (projection, distance); } static Vector3 MoveOnPlane(Vector2 mousePosition, Vector3 origin, Quaternion rotation, bool snapping) { var ray = HandleUtility.GUIPointToWorldRay(mousePosition); var manipPlane = new Plane(rotation * Vector3.up, origin); var position = manipPlane.Raycast(ray, out float distance) ? ray.origin + ray.direction * distance : origin; var dir = position - origin; var forward = GetSnapToGuideData(position, origin, rotation * Vector3.forward); var right = GetSnapToGuideData(position, origin, rotation * Vector3.right); if (!snapping && s_SnapToGuide) { if (forward.distance < s_SnapToGuideDistance || right.distance < s_SnapToGuideDistance) { var snapToForward = forward.distance < right.distance; var axis = (snapToForward ? forward : right).projection; return origin + axis; } } if(Mathf.Approximately(dir.magnitude, 0f)) dir = Vector3.forward; var translation = Handles.SnapValue(Quaternion.Inverse(rotation) * dir, new Vector3(EditorSnapSettings.move.x, 0, EditorSnapSettings.move.z)); return origin + rotation * translation; } static Vector3 MoveOnNormal(Vector2 mousePosition, Vector3 origin, Quaternion rotation) { var upAxis = rotation * Vector3.up; var translation = upAxis * Handles.SnapValue(HandleUtility.CalcLineTranslation(s_InitialMousePosition, mousePosition, origin, upAxis), EditorSnapSettings.move.y); return origin + translation; } static void DrawGuideAxis(Vector3 origin, Vector3 axis, Color color) { var start = origin - axis.normalized * 10000f; var end = origin + axis.normalized * 10000f; using (new ZTestScope(CompareFunction.Less)) using (new Handles.DrawingScope(color)) { Handles.DrawLine(origin, start, 0f); Handles.DrawLine(origin, end, 0f); } color = new Color(color.r, color.g, color.b, color.a * k_HandleColorAlphaFactor); using (new ZTestScope(CompareFunction.Greater)) using (new Handles.DrawingScope(color)) { Handles.DrawLine(origin, start, 0f); Handles.DrawLine(origin, end, 0f); } } static void DrawGuidePlane(Vector3 origin, Vector3 axisX, Vector3 axisZ, Vector3 position) { var xAxisProjection = Vector3.Project(position - origin, axisX); var zAxisProjection = Vector3.Project(position - origin, axisZ); var cross = math.cross(xAxisProjection, zAxisProjection); var normal = math.normalizesafe(cross); var scaledOffset = k_GuidePlaneZTestOffset * HandleUtility.GetHandleSize(origin); var calculatedOffset = new Vector3(scaledOffset.x * normal.x, scaledOffset.y * normal.y, scaledOffset.z * normal.z); position += calculatedOffset; origin += calculatedOffset; s_VertexBuffer[0] = origin; s_VertexBuffer[1] = origin + Vector3.Project(position - origin, axisX); s_VertexBuffer[2] = position; s_VertexBuffer[3] = origin + Vector3.Project(position - origin, axisZ); DrawGuidePlane(Matrix4x4.identity); } static void DrawGuidePlane(Matrix4x4 matrix) { var color = s_GuidePlaneColor.value; using (new ZTestScope(CompareFunction.Less)) using (new Handles.DrawingScope(matrix)) Handles.DrawSolidRectangleWithOutline(s_VertexBuffer, color, Color.clear); color = new Color(s_GuidePlaneColor.value.r, s_GuidePlaneColor.value.g, s_GuidePlaneColor.value.b, s_GuidePlaneColor.value.a * k_HandleColorAlphaFactor); using (new ZTestScope(CompareFunction.Greater)) using (new Handles.DrawingScope(matrix)) Handles.DrawSolidRectangleWithOutline(s_VertexBuffer, color, Color.clear); } static void DrawGuideDottedLine(Vector3 origin, Vector3 axis, Vector3 position) { using (new ZTestScope(CompareFunction.Less)) Handles.DrawDottedLine(origin + Vector3.Project(position - origin, axis), position, 3f); var color = new Color(Handles.color.r, Handles.color.g, Handles.color.b, Handles.color.a * k_HandleColorAlphaFactor); using (new ZTestScope(CompareFunction.Greater)) using (new Handles.DrawingScope(color)) Handles.DrawDottedLine(origin + Vector3.Project(position - origin, axis), position, 3f); } } }