123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- using System;
- using UnityEngine;
- using System.Reflection;
- namespace UnityEditor.Rendering
- {
- /// <summary>
- /// Provide a gizmo/handle representing a box where all face can be moved independently.
- /// Also add a contained sub gizmo/handle box if contained is used at creation.
- /// </summary>
- /// <example>
- /// <code>
- /// class MyComponentEditor : Editor
- /// {
- /// static HierarchicalBox box;
- /// static HierarchicalBox containedBox;
- ///
- /// static MyComponentEditor()
- /// {
- /// Color[] handleColors = new Color[]
- /// {
- /// Color.red,
- /// Color.green,
- /// Color.Blue,
- /// new Color(0.5f, 0f, 0f, 1f),
- /// new Color(0f, 0.5f, 0f, 1f),
- /// new Color(0f, 0f, 0.5f, 1f)
- /// };
- /// box = new HierarchicalBox(new Color(1f, 1f, 1f, 0.25), handleColors);
- /// containedBox = new HierarchicalBox(new Color(1f, 0f, 1f, 0.25), handleColors, container: box);
- /// }
- ///
- /// [DrawGizmo(GizmoType.Selected|GizmoType.Active)]
- /// void DrawGizmo(MyComponent comp, GizmoType gizmoType)
- /// {
- /// box.center = comp.transform.position;
- /// box.size = comp.transform.scale;
- /// box.DrawHull(gizmoType == GizmoType.Selected);
- ///
- /// containedBox.center = comp.innerposition;
- /// containedBox.size = comp.innerScale;
- /// containedBox.DrawHull(gizmoType == GizmoType.Selected);
- /// }
- ///
- /// void OnSceneGUI()
- /// {
- /// EditorGUI.BeginChangeCheck();
- ///
- /// //container box must be also set for contained box for clamping
- /// box.center = comp.transform.position;
- /// box.size = comp.transform.scale;
- /// box.DrawHandle();
- ///
- /// containedBox.DrawHandle();
- /// containedBox.center = comp.innerposition;
- /// containedBox.size = comp.innerScale;
- ///
- /// if(EditorGUI.EndChangeCheck())
- /// {
- /// comp.innerposition = containedBox.center;
- /// comp.innersize = containedBox.size;
- /// }
- /// }
- /// }
- /// </code>
- /// </example>
- public class HierarchicalBox
- {
- const float k_HandleSizeCoef = 0.05f;
- static Material k_Material_Cache;
- static Material k_Material => (k_Material_Cache == null || k_Material_Cache.Equals(null) ? (k_Material_Cache = new Material(Shader.Find("Hidden/UnlitTransparentColored"))) : k_Material_Cache);
- static Mesh k_MeshQuad_Cache;
- static Mesh k_MeshQuad => k_MeshQuad_Cache == null || k_MeshQuad_Cache.Equals(null) ? (k_MeshQuad_Cache = Resources.GetBuiltinResource<Mesh>("Quad.fbx")) : k_MeshQuad_Cache;
- enum NamedFace { Right, Top, Front, Left, Bottom, Back, None }
- Material m_Material;
- readonly Color[] m_PolychromeHandleColor;
- readonly HierarchicalBox m_Parent;
- Color m_MonochromeFillColor;
- Color m_MonochromeHandleColor;
- Color m_WireframeColor;
- Color m_WireframeColorBehind;
- int[] m_ControlIDs = new int[6] { 0, 0, 0, 0, 0, 0 };
- bool m_MonoHandle = true;
- Material material
- {
- get
- {
- if (m_Material == null || m_Material.Equals(null))
- m_Material = new Material(k_Material);
- //material can be lost when exiting play mode so gather the color again when reconstructing it
- m_Material.color = m_MonochromeFillColor;
- return m_Material;
- }
- }
- /// <summary>
- /// Allow to switch between the mode where all axis are controlled together or not
- /// Note that if there is several handles, they will use the polychrome colors.
- /// </summary>
- public bool monoHandle { get => m_MonoHandle; set => m_MonoHandle = value; }
- /// <summary>The position of the center of the box in Handle.matrix space.</summary>
- public Vector3 center { get; set; }
- /// <summary>The size of the box in Handle.matrix space.</summary>
- public Vector3 size { get; set; }
- /// <summary>The baseColor used to fill hull. All other colors are deduced from it except specific handle colors.</summary>
- public Color baseColor
- {
- get { return material.color; }
- set
- {
- value.a = 8f / 255;
- m_MonochromeFillColor = value;
- material.color = m_MonochromeFillColor;
- value.a = 1f;
- m_MonochromeHandleColor = value;
- value.a = 0.7f;
- m_WireframeColor = value;
- value.a = 0.2f;
- m_WireframeColorBehind = value;
- }
- }
- //Note: Handles.Slider not allow to use a specific ControlID.
- //Thus Slider1D is used (with reflection)
- static Type k_Slider1D = Type.GetType("UnityEditorInternal.Slider1D, UnityEditor");
- static MethodInfo k_Slider1D_Do = k_Slider1D
- .GetMethod(
- "Do",
- BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
- null,
- CallingConventions.Any,
- new[] { typeof(int), typeof(Vector3), typeof(Vector3), typeof(float), typeof(Handles.CapFunction), typeof(float) },
- null);
- static void Slider1D(int controlID, ref Vector3 handlePosition, Vector3 handleOrientation, float snapScale, Color color)
- {
- using (new Handles.DrawingScope(color))
- {
- handlePosition = (Vector3)k_Slider1D_Do.Invoke(null, new object[]
- {
- controlID,
- handlePosition,
- handleOrientation,
- HandleUtility.GetHandleSize(handlePosition) * k_HandleSizeCoef,
- new Handles.CapFunction(Handles.DotHandleCap),
- snapScale
- });
- }
- }
- /// <summary>Constructor. Used to setup colors and also the container if any.</summary>
- /// <param name="baseColor">The color of each face of the box. Other colors are deduced from it.</param>
- /// <param name="polychromeHandleColors">The color of handle when they are separated. When they are grouped, they use a variation of the faceColor instead.</param>
- /// <param name="parent">The HierarchicalBox containing this box. If null, the box will not be limited in size.</param>
- public HierarchicalBox(Color baseColor, Color[] polychromeHandleColors = null, HierarchicalBox parent = null)
- {
- if (polychromeHandleColors != null && polychromeHandleColors.Length != 6)
- throw new ArgumentException("polychromeHandleColors must be null or have a size of 6.");
- m_Parent = parent;
- m_Material = new Material(k_Material);
- this.baseColor = baseColor;
- m_PolychromeHandleColor = polychromeHandleColors ?? new Color[]
- {
- Handles.xAxisColor, Handles.yAxisColor, Handles.zAxisColor,
- Handles.xAxisColor, Handles.yAxisColor, Handles.zAxisColor
- };
- }
- /// <summary>Draw the hull which means the boxes without the handles</summary>
- /// <param name="filled">If true, also fill the faces of the hull</param>
- public void DrawHull(bool filled)
- {
- Color previousColor = Handles.color;
- if (filled)
- {
- // Draw the hull
- var xSize = new Vector3(size.z, size.y, 1f);
- material.SetPass(0);
- Graphics.DrawMeshNow(k_MeshQuad, Handles.matrix * Matrix4x4.TRS(center + size.x * .5f * Vector3.left, Quaternion.FromToRotation(Vector3.forward, Vector3.left), xSize));
- Graphics.DrawMeshNow(k_MeshQuad, Handles.matrix * Matrix4x4.TRS(center + size.x * .5f * Vector3.right, Quaternion.FromToRotation(Vector3.forward, Vector3.right), xSize));
- var ySize = new Vector3(size.x, size.z, 1f);
- Graphics.DrawMeshNow(k_MeshQuad, Handles.matrix * Matrix4x4.TRS(center + size.y * .5f * Vector3.up, Quaternion.FromToRotation(Vector3.forward, Vector3.up), ySize));
- Graphics.DrawMeshNow(k_MeshQuad, Handles.matrix * Matrix4x4.TRS(center + size.y * .5f * Vector3.down, Quaternion.FromToRotation(Vector3.forward, Vector3.down), ySize));
- var zSize = new Vector3(size.x, size.y, 1f);
- Graphics.DrawMeshNow(k_MeshQuad, Handles.matrix * Matrix4x4.TRS(center + size.z * .5f * Vector3.forward, Quaternion.identity, zSize));
- Graphics.DrawMeshNow(k_MeshQuad, Handles.matrix * Matrix4x4.TRS(center + size.z * .5f * Vector3.back, Quaternion.FromToRotation(Vector3.forward, Vector3.back), zSize));
- //if as a parent, also draw handle distance to the parent
- if (m_Parent != null)
- {
- var centerDiff = center - m_Parent.center;
- var xRecal = centerDiff;
- var yRecal = centerDiff;
- var zRecal = centerDiff;
- xRecal.x = 0;
- yRecal.y = 0;
- zRecal.z = 0;
- Handles.color = GetHandleColor(NamedFace.Left);
- Handles.DrawLine(m_Parent.center + xRecal + m_Parent.size.x * .5f * Vector3.left, center + size.x * .5f * Vector3.left);
- Handles.color = GetHandleColor(NamedFace.Right);
- Handles.DrawLine(m_Parent.center + xRecal + m_Parent.size.x * .5f * Vector3.right, center + size.x * .5f * Vector3.right);
- Handles.color = GetHandleColor(NamedFace.Top);
- Handles.DrawLine(m_Parent.center + yRecal + m_Parent.size.y * .5f * Vector3.up, center + size.y * .5f * Vector3.up);
- Handles.color = GetHandleColor(NamedFace.Bottom);
- Handles.DrawLine(m_Parent.center + yRecal + m_Parent.size.y * .5f * Vector3.down, center + size.y * .5f * Vector3.down);
- Handles.color = GetHandleColor(NamedFace.Front);
- Handles.DrawLine(m_Parent.center + zRecal + m_Parent.size.z * .5f * Vector3.forward, center + size.z * .5f * Vector3.forward);
- Handles.color = GetHandleColor(NamedFace.Back);
- Handles.DrawLine(m_Parent.center + zRecal + m_Parent.size.z * .5f * Vector3.back, center + size.z * .5f * Vector3.back);
- }
- }
- Handles.color = m_WireframeColor;
- Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
- Handles.DrawWireCube(center, size);
- Handles.color = m_WireframeColorBehind;
- Handles.zTest = UnityEngine.Rendering.CompareFunction.Greater;
- Handles.DrawWireCube(center, size);
- Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
- Handles.color = previousColor;
- }
- /// <summary>Draw the manipulable handles</summary>
- public void DrawHandle()
- {
- Event evt = Event.current;
- bool useHomothety = evt.shift;
- bool useSymetry = evt.alt || evt.command;
- // Note: snapping is handled natively on ctrl for each Slider1D
- for (int i = 0, count = m_ControlIDs.Length; i < count; ++i)
- m_ControlIDs[i] = GUIUtility.GetControlID("HierarchicalBox".GetHashCode() + i, FocusType.Passive);
-
- var leftPosition = center + size.x * .5f * Vector3.left;
- var rightPosition = center + size.x * .5f * Vector3.right;
- var topPosition = center + size.y * .5f * Vector3.up;
- var bottomPosition = center + size.y * .5f * Vector3.down;
- var frontPosition = center + size.z * .5f * Vector3.forward;
- var backPosition = center + size.z * .5f * Vector3.back;
- var theChangedFace = NamedFace.None;
- EditorGUI.BeginChangeCheck();
- EditorGUI.BeginChangeCheck();
- Slider1D(m_ControlIDs[(int)NamedFace.Left], ref leftPosition, Vector3.left, EditorSnapSettings.scale, GetHandleColor(NamedFace.Left));
- if (EditorGUI.EndChangeCheck())
- theChangedFace = NamedFace.Left;
- EditorGUI.BeginChangeCheck();
- Slider1D(m_ControlIDs[(int)NamedFace.Right], ref rightPosition, Vector3.right, EditorSnapSettings.scale, GetHandleColor(NamedFace.Right));
- if (EditorGUI.EndChangeCheck())
- theChangedFace = NamedFace.Right;
- EditorGUI.BeginChangeCheck();
- Slider1D(m_ControlIDs[(int)NamedFace.Top], ref topPosition, Vector3.up, EditorSnapSettings.scale, GetHandleColor(NamedFace.Top));
- if (EditorGUI.EndChangeCheck())
- theChangedFace = NamedFace.Top;
- EditorGUI.BeginChangeCheck();
- Slider1D(m_ControlIDs[(int)NamedFace.Bottom], ref bottomPosition, Vector3.down, EditorSnapSettings.scale, GetHandleColor(NamedFace.Bottom));
- if (EditorGUI.EndChangeCheck())
- theChangedFace = NamedFace.Bottom;
- EditorGUI.BeginChangeCheck();
- Slider1D(m_ControlIDs[(int)NamedFace.Front], ref frontPosition, Vector3.forward, EditorSnapSettings.scale, GetHandleColor(NamedFace.Front));
- if (EditorGUI.EndChangeCheck())
- theChangedFace = NamedFace.Front;
- EditorGUI.BeginChangeCheck();
- Slider1D(m_ControlIDs[(int)NamedFace.Back], ref backPosition, Vector3.back, EditorSnapSettings.scale, GetHandleColor(NamedFace.Back));
- if (EditorGUI.EndChangeCheck())
- theChangedFace = NamedFace.Back;
- if (EditorGUI.EndChangeCheck())
- {
- float delta = 0f;
- switch (theChangedFace)
- {
- case NamedFace.Left: delta = (leftPosition - center - size.x * .5f * Vector3.left).x; break;
- case NamedFace.Right: delta = -(rightPosition - center - size.x * .5f * Vector3.right).x; break;
- case NamedFace.Top: delta = -(topPosition - center - size.y * .5f * Vector3.up).y; break;
- case NamedFace.Bottom: delta = (bottomPosition - center - size.y * .5f * Vector3.down).y; break;
- case NamedFace.Front: delta = -(frontPosition - center - size.z * .5f * Vector3.forward).z; break;
- case NamedFace.Back: delta = (backPosition - center - size.z * .5f * Vector3.back).z; break;
- }
- if (monoHandle || useHomothety && useSymetry)
- {
- var tempSize = size - Vector3.one * delta;
- //ensure that the box face are still facing outside
- for (int axis = 0; axis < 3; ++axis)
- {
- if (tempSize[axis] < 0)
- {
- delta += tempSize[axis];
- tempSize = size - Vector3.one * delta;
- }
- }
- //ensure containedBox do not exit container
- if (m_Parent != null)
- {
- for (int axis = 0; axis < 3; ++axis)
- {
- if (tempSize[axis] > m_Parent.size[axis])
- tempSize[axis] = m_Parent.size[axis];
- }
- }
- size = tempSize;
- }
- else
- {
- if (useSymetry)
- {
- switch (theChangedFace)
- {
- case NamedFace.Left: rightPosition.x -= delta; break;
- case NamedFace.Right: leftPosition.x += delta; break;
- case NamedFace.Top: bottomPosition.y += delta; break;
- case NamedFace.Bottom: topPosition.y -= delta; break;
- case NamedFace.Front: backPosition.z += delta; break;
- case NamedFace.Back: frontPosition.z -= delta; break;
- }
-
- //ensure that the box face are still facing outside
- switch (theChangedFace)
- {
- case NamedFace.Left:
- case NamedFace.Right:
- if (rightPosition.x < leftPosition.x)
- rightPosition.x = leftPosition.x = center.x;
- break;
- case NamedFace.Top:
- case NamedFace.Bottom:
- if (topPosition.y < bottomPosition.y)
- topPosition.y = bottomPosition.y = center.y;
- break;
- case NamedFace.Front:
- case NamedFace.Back:
- if (frontPosition.z < backPosition.z)
- frontPosition.z = backPosition.z = center.z;
- break;
- }
- }
- if (useHomothety)
- {
- float halfDelta = delta * 0.5f;
- switch (theChangedFace)
- {
- case NamedFace.Left:
- case NamedFace.Right:
- bottomPosition.y += halfDelta;
- topPosition.y -= halfDelta;
- backPosition.z += halfDelta;
- frontPosition.z -= halfDelta;
- break;
- case NamedFace.Top:
- case NamedFace.Bottom:
- rightPosition.x -= halfDelta;
- leftPosition.x += halfDelta;
- backPosition.z += halfDelta;
- frontPosition.z -= halfDelta;
- break;
- case NamedFace.Front:
- case NamedFace.Back:
- rightPosition.x -= halfDelta;
- leftPosition.x += halfDelta;
- bottomPosition.y += halfDelta;
- topPosition.y -= halfDelta;
- break;
- }
-
- //ensure that the box face are still facing outside
- switch (theChangedFace)
- {
- case NamedFace.Left:
- if (rightPosition.x < leftPosition.x)
- leftPosition.x = rightPosition.x;
- if (topPosition.y < bottomPosition.y)
- topPosition.y = bottomPosition.y = center.y;
- if (frontPosition.z < backPosition.z)
- frontPosition.z = backPosition.z = center.z;
- break;
- case NamedFace.Right:
- if (rightPosition.x < leftPosition.x)
- rightPosition.x = leftPosition.x;
- if (topPosition.y < bottomPosition.y)
- topPosition.y = bottomPosition.y = center.y;
- if (frontPosition.z < backPosition.z)
- frontPosition.z = backPosition.z = center.z;
- break;
- case NamedFace.Top:
- if (topPosition.y < bottomPosition.y)
- topPosition.y = bottomPosition.y;
- if (rightPosition.x < leftPosition.x)
- rightPosition.x = leftPosition.x = center.x;
- if (frontPosition.z < backPosition.z)
- frontPosition.z = backPosition.z = center.z;
- break;
- case NamedFace.Bottom:
- if (topPosition.y < bottomPosition.y)
- bottomPosition.y = topPosition.y;
- if (rightPosition.x < leftPosition.x)
- rightPosition.x = leftPosition.x = center.x;
- if (frontPosition.z < backPosition.z)
- frontPosition.z = backPosition.z = center.z;
- break;
- case NamedFace.Front:
- if (frontPosition.z < backPosition.z)
- frontPosition.z = backPosition.z;
- if (rightPosition.x < leftPosition.x)
- rightPosition.x = leftPosition.x = center.x;
- if (topPosition.y < bottomPosition.y)
- topPosition.y = bottomPosition.y = center.y;
- break;
- case NamedFace.Back:
- if (frontPosition.z < backPosition.z)
- backPosition.z = frontPosition.z;
- if (rightPosition.x < leftPosition.x)
- rightPosition.x = leftPosition.x = center.x;
- if (topPosition.y < bottomPosition.y)
- topPosition.y = bottomPosition.y = center.y;
- break;
- }
- }
- var max = new Vector3(rightPosition.x, topPosition.y, frontPosition.z);
- var min = new Vector3(leftPosition.x, bottomPosition.y, backPosition.z);
- if (!useSymetry && !useHomothety)
- {
- //ensure that the box face are still facing outside
- for (int axis = 0; axis < 3; ++axis)
- {
- if (min[axis] > max[axis])
- {
- // Control IDs in m_ControlIDs[0-3[ are for positive axes
- if (GUIUtility.hotControl == m_ControlIDs[axis])
- max[axis] = min[axis];
- else
- min[axis] = max[axis];
- }
- }
- }
- //ensure containedBox do not exit container
- if (m_Parent != null)
- {
- for (int axis = 0; axis < 3; ++axis)
- {
- if (min[axis] < m_Parent.center[axis] - m_Parent.size[axis] * 0.5f)
- min[axis] = m_Parent.center[axis] - m_Parent.size[axis] * 0.5f;
- if (max[axis] > m_Parent.center[axis] + m_Parent.size[axis] * 0.5f)
- max[axis] = m_Parent.center[axis] + m_Parent.size[axis] * 0.5f;
- }
- }
- center = (max + min) * .5f;
- size = max - min;
- }
- }
- }
- Color GetHandleColor(NamedFace name) => monoHandle ? m_MonochromeHandleColor : m_PolychromeHandleColor[(int)name];
- }
- }
|