using System; using System.Linq.Expressions; using System.Reflection; using UnityEditor; namespace UnityEngine.Rendering.Universal { // Copy of UnityEditor.Rendering.HighDefinition.DisplacableRectHandles class ProjectedTransform { struct PositionHandleIds { static int s_xAxisMoveHandleHash = "xAxisDecalPivot".GetHashCode(); static int s_yAxisMoveHandleHash = "yAxisDecalPivot".GetHashCode(); static int s_zAxisMoveHandleHash = "zAxisDecalPivot".GetHashCode(); static int s_xyAxisMoveHandleHash = "xyAxisDecalPivot".GetHashCode(); public static PositionHandleIds @default { get { return new PositionHandleIds( GUIUtility.GetControlID(s_xAxisMoveHandleHash, FocusType.Passive), GUIUtility.GetControlID(s_yAxisMoveHandleHash, FocusType.Passive), GUIUtility.GetControlID(s_zAxisMoveHandleHash, FocusType.Passive), GUIUtility.GetControlID(s_xyAxisMoveHandleHash, FocusType.Passive) ); } } public readonly int x, y, z, xy; public int this[int index] { get { switch (index) { case 0: return x; case 1: return y; case 2: return z; case 3: return xy; } return -1; } } public bool Has(int id) { return x == id || y == id || z == id || xy == id; } public PositionHandleIds(int x, int y, int z, int xy) { this.x = x; this.y = y; this.z = z; this.xy = xy; } public override int GetHashCode() { return x ^ y ^ z ^ xy; } public override bool Equals(object obj) { if (!(obj is PositionHandleIds o)) return false; return o.x == x && o.y == y && o.z == z && o.xy == xy; } } struct PositionHandleParam { public static PositionHandleParam defaultHandleXY = new PositionHandleParam( Handle.X | Handle.Y | Handle.XY, Vector3.zero, Vector3.one, Vector3.zero, Vector3.one * .25f, Orientation.Signed, Orientation.Camera); public static PositionHandleParam defaultHandleZ = new PositionHandleParam( Handle.Z, Vector3.zero, Vector3.one, Vector3.zero, Vector3.one * .25f, Orientation.Signed, Orientation.Camera); [Flags] public enum Handle { None = 0, X = 1 << 0, Y = 1 << 1, Z = 1 << 2, XY = 1 << 3, All = ~None } public enum Orientation { Signed, Camera } public readonly Vector3 axisOffset; public readonly Vector3 axisSize; public readonly Vector3 planeOffset; public readonly Vector3 planeSize; public readonly Handle handles; public readonly Orientation axesOrientation; public readonly Orientation planeOrientation; public bool ShouldShow(int axis) { return (handles & (Handle)(1 << axis)) != 0; } public bool ShouldShow(Handle handle) { return (handles & handle) != 0; } public PositionHandleParam( Handle handles, Vector3 axisOffset, Vector3 axisSize, Vector3 planeOffset, Vector3 planeSize, Orientation axesOrientation, Orientation planeOrientation) { this.axisOffset = axisOffset; this.axisSize = axisSize; this.planeOffset = planeOffset; this.planeSize = planeSize; this.handles = handles; this.axesOrientation = axesOrientation; this.planeOrientation = planeOrientation; } } static PositionHandleParam paramXY = PositionHandleParam.defaultHandleXY; static PositionHandleParam paramZ = PositionHandleParam.defaultHandleZ; static PositionHandleIds ids = PositionHandleIds.@default; static int[] s_DoPositionHandle_Internal_NextIndex = { 1, 2, 0 }; static int[] s_DoPositionHandle_Internal_PrevIndex = { 2, 0, 1 }; static Vector3[] verts = { Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero }; static Func s_IsGridSnappingActive; static ProjectedTransform() { //We need to know if grid snaping is active or not in Editor. Sadly this is internal so we must grab it by reflection. Type gridSnappingType = typeof(Handles).Assembly.GetType("UnityEditor.GridSnapping"); PropertyInfo activePropertyInfo = gridSnappingType.GetProperty("active", BindingFlags.Public | BindingFlags.Static); MethodCallExpression activePropertyGetCall = Expression.Call(null, activePropertyInfo.GetGetMethod()); var activeGetLambda = Expression.Lambda>(activePropertyGetCall); s_IsGridSnappingActive = activeGetLambda.Compile(); } static bool IsHovering(int controlID, Event evt) { return controlID == HandleUtility.nearestControl && GUIUtility.hotControl == 0 && !Tools.viewToolActive; } public static Vector3 DrawHandles(Vector3 position, float zProjectionDistance, Quaternion rotation) { var isHot = ids.Has(GUIUtility.hotControl); var planeSize = isHot ? paramXY.planeSize + paramXY.planeOffset : paramXY.planeSize; var planarSize = Mathf.Max(planeSize[0], planeSize[s_DoPositionHandle_Internal_NextIndex[0]]); Vector3 sliderRotatedWorldPos = Quaternion.Inverse(rotation) * position; var size1D = HandleUtility.GetHandleSize(sliderRotatedWorldPos); var size2D = HandleUtility.GetHandleSize(sliderRotatedWorldPos - new Vector3(0, 0, zProjectionDistance)) * planarSize * .5f; Vector3 depthSlider = sliderRotatedWorldPos; EditorGUI.BeginChangeCheck(); { // dot offset = transform position seen as a sphere EditorGUI.BeginChangeCheck(); depthSlider = Handles.Slider(depthSlider, Vector3.forward, size1D * .1f, Handles.SphereHandleCap, -1); if (EditorGUI.EndChangeCheck()) sliderRotatedWorldPos.z = depthSlider.z; // 2D slider: square xy-axis Vector3 sliderFaceProjected = sliderRotatedWorldPos - new Vector3(0, 0, zProjectionDistance); sliderFaceProjected.x += size2D; sliderFaceProjected.y += size2D; using (new Handles.DrawingScope(Handles.zAxisColor)) { verts[0] = sliderFaceProjected + (Vector3.right + Vector3.up) * size2D; verts[1] = sliderFaceProjected + (-Vector3.right + Vector3.up) * size2D; verts[2] = sliderFaceProjected + (-Vector3.right - Vector3.up) * size2D; verts[3] = sliderFaceProjected + (Vector3.right - Vector3.up) * size2D; int id = GUIUtility.GetControlID(ids.xy, FocusType.Passive); float faceOpacity = 0.8f; if (GUIUtility.hotControl == id) Handles.color = Handles.selectedColor; else if (IsHovering(id, Event.current)) faceOpacity = 0.4f; else faceOpacity = 0.1f; Color faceColor = new Color(Handles.zAxisColor.r, Handles.zAxisColor.g, Handles.zAxisColor.b, Handles.zAxisColor.a * faceOpacity); Handles.DrawSolidRectangleWithOutline(verts, faceColor, Color.clear); EditorGUI.BeginChangeCheck(); sliderFaceProjected = Handles.Slider2D(id, sliderFaceProjected, Vector3.forward, Vector3.right, Vector3.up, size2D, Handles.RectangleHandleCap, s_IsGridSnappingActive() ? Vector2.zero : new Vector2(EditorSnapSettings.move[0], EditorSnapSettings.move[1]), false); if (EditorGUI.EndChangeCheck()) { sliderRotatedWorldPos.x = sliderFaceProjected.x; sliderRotatedWorldPos.y = sliderFaceProjected.y; } } sliderFaceProjected.x -= size2D; sliderFaceProjected.y -= size2D; // 2D slider: x-axis EditorGUI.BeginChangeCheck(); using (new Handles.DrawingScope(Handles.xAxisColor)) sliderFaceProjected = Handles.Slider(sliderFaceProjected, Vector3.right); if (EditorGUI.EndChangeCheck()) sliderRotatedWorldPos.x = sliderFaceProjected.x; // 2D slider: y-axis EditorGUI.BeginChangeCheck(); using (new Handles.DrawingScope(Handles.yAxisColor)) sliderFaceProjected = Handles.Slider(sliderFaceProjected, Vector3.up); if (EditorGUI.EndChangeCheck()) sliderRotatedWorldPos.y = sliderFaceProjected.y; // depth: z-axis EditorGUI.BeginChangeCheck(); using (new Handles.DrawingScope(Handles.zAxisColor)) depthSlider = Handles.Slider(depthSlider, Vector3.forward); if (EditorGUI.EndChangeCheck()) sliderRotatedWorldPos.z = depthSlider.z; } if (EditorGUI.EndChangeCheck()) { position = rotation * sliderRotatedWorldPos; } return position; } } }