testss
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Rendering
|
||||
{
|
||||
public static class GizmoUtility
|
||||
{
|
||||
public static Color GetHandleColor(Color baseColor)
|
||||
{
|
||||
baseColor.a = 1f;
|
||||
return baseColor;
|
||||
}
|
||||
|
||||
public static Color GetWireframeColor(Color baseColor)
|
||||
{
|
||||
baseColor.a = .7f;
|
||||
return baseColor;
|
||||
}
|
||||
|
||||
public static Color GetWireframeColorBehindObjects(Color baseColor)
|
||||
{
|
||||
baseColor.a = .2f;
|
||||
return baseColor;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,491 @@
|
||||
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;
|
||||
SetBaseColor(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the baseColor used to fill hull. All other colors are deduced from it except specific handle colors.
|
||||
/// Instead of <see cref="baseColor">baseColor</see> set, this will not force the opacity and keep what is provided for the filled faces.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to use</param>
|
||||
public void SetBaseColor(Color color)
|
||||
{
|
||||
m_MonochromeFillColor = color;
|
||||
material.color = m_MonochromeFillColor;
|
||||
m_MonochromeHandleColor = GizmoUtility.GetHandleColor(color);
|
||||
m_WireframeColor = GizmoUtility.GetWireframeColor(color);
|
||||
m_WireframeColorBehind = GizmoUtility.GetWireframeColorBehindObjects(color);
|
||||
}
|
||||
|
||||
//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];
|
||||
}
|
||||
}
|
@@ -0,0 +1,176 @@
|
||||
using UnityEngine;
|
||||
|
||||
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 HierarchicalSphere sphere;
|
||||
/// static HierarchicalSphere containedSphere;
|
||||
///
|
||||
/// 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)
|
||||
/// };
|
||||
/// sphere = new HierarchicalSphere(new Color(1f, 1f, 1f, 0.25));
|
||||
/// containedSphere = new HierarchicalSphere(new Color(1f, 0f, 1f, 0.25), container: sphere);
|
||||
/// }
|
||||
///
|
||||
/// [DrawGizmo(GizmoType.Selected|GizmoType.Active)]
|
||||
/// void DrawGizmo(MyComponent comp, GizmoType gizmoType)
|
||||
/// {
|
||||
/// sphere.center = comp.transform.position;
|
||||
/// sphere.size = comp.transform.scale;
|
||||
/// sphere.DrawHull(gizmoType == GizmoType.Selected);
|
||||
///
|
||||
/// containedSphere.center = comp.innerposition;
|
||||
/// containedSphere.size = comp.innerScale;
|
||||
/// containedSphere.DrawHull(gizmoType == GizmoType.Selected);
|
||||
/// }
|
||||
///
|
||||
/// void OnSceneGUI()
|
||||
/// {
|
||||
/// EditorGUI.BeginChangeCheck();
|
||||
///
|
||||
/// //container sphere must be also set for contained sphere for clamping
|
||||
/// sphere.center = comp.transform.position;
|
||||
/// sphere.size = comp.transform.scale;
|
||||
/// sphere.DrawHandle();
|
||||
///
|
||||
/// containedSphere.center = comp.innerposition;
|
||||
/// containedSphere.size = comp.innerScale;
|
||||
/// containedSphere.DrawHandle();
|
||||
///
|
||||
/// if(EditorGUI.EndChangeCheck())
|
||||
/// {
|
||||
/// comp.innerposition = containedSphere.center;
|
||||
/// comp.innersize = containedSphere.size;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class HierarchicalSphere
|
||||
{
|
||||
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_MeshSphere_Cache;
|
||||
static Mesh k_MeshSphere => k_MeshSphere_Cache == null || k_MeshSphere_Cache.Equals(null) ? (k_MeshSphere_Cache = Resources.GetBuiltinResource<Mesh>("New-Sphere.fbx")) : k_MeshSphere_Cache;
|
||||
|
||||
Material m_Material;
|
||||
readonly HierarchicalSphere m_Parent;
|
||||
Color m_HandleColor;
|
||||
Color m_WireframeColor;
|
||||
Color m_WireframeColorBehind;
|
||||
|
||||
Material material => m_Material == null || m_Material.Equals(null)
|
||||
? (m_Material = new Material(k_Material))
|
||||
: m_Material;
|
||||
|
||||
/// <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 float radius { get; set; }
|
||||
|
||||
/// <summary>The baseColor used to fill hull. All other colors are deduced from it.</summary>
|
||||
public Color baseColor
|
||||
{
|
||||
get { return material.color; }
|
||||
set
|
||||
{
|
||||
value.a = 8f / 255;
|
||||
material.color = value;
|
||||
value.a = 1f;
|
||||
m_HandleColor = value;
|
||||
value.a = 0.7f;
|
||||
m_WireframeColor = value;
|
||||
value.a = 0.2f;
|
||||
m_WireframeColorBehind = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Constructor. Used to setup colors and also the container if any.</summary>
|
||||
/// <param name="baseColor">The color of filling. All other colors are deduced from it.</param>
|
||||
/// <param name="parent">The HierarchicalSphere containing this sphere. If null, the sphere will not be limited in size.</param>
|
||||
public HierarchicalSphere(Color baseColor, HierarchicalSphere parent = null)
|
||||
{
|
||||
m_Parent = parent;
|
||||
m_Material = new Material(k_Material);
|
||||
this.baseColor = baseColor;
|
||||
}
|
||||
|
||||
/// <summary>Draw the hull which means the boxes without the handles</summary>
|
||||
/// <param name="filled">If true, also draw the surface of the hull's sphere</param>
|
||||
public void DrawHull(bool filled)
|
||||
{
|
||||
Color wireframeColor = m_HandleColor;
|
||||
wireframeColor.a = 0.8f;
|
||||
using (new Handles.DrawingScope(m_WireframeColor, Matrix4x4.TRS((Vector3)Handles.matrix.GetColumn(3) + center, Quaternion.identity, Vector3.one)))
|
||||
{
|
||||
if (filled)
|
||||
{
|
||||
material.SetPass(0);
|
||||
Matrix4x4 drawMatrix = Matrix4x4.TRS((Vector3)Handles.matrix.GetColumn(3), Quaternion.identity, Vector3.one * radius * 2f);
|
||||
Graphics.DrawMeshNow(k_MeshSphere, drawMatrix);
|
||||
}
|
||||
|
||||
var drawCenter = Vector3.zero;
|
||||
var viewPlaneNormal = Vector3.zero;
|
||||
var drawnRadius = radius;
|
||||
if (Camera.current.orthographic)
|
||||
viewPlaneNormal = Camera.current.transform.forward;
|
||||
else
|
||||
{
|
||||
viewPlaneNormal = (Vector3)Handles.matrix.GetColumn(3) - Camera.current.transform.position;
|
||||
var sqrDist = viewPlaneNormal.sqrMagnitude; // squared distance from camera to center
|
||||
var sqrRadius = radius * radius; // squared radius
|
||||
var sqrOffset = sqrRadius * sqrRadius / sqrDist; // squared distance from actual center to drawn disc center
|
||||
var insideAmount = sqrOffset / sqrRadius;
|
||||
|
||||
// If we are not inside the sphere, calculate where to draw the periphery
|
||||
if (insideAmount < 1)
|
||||
{
|
||||
drawnRadius = Mathf.Sqrt(sqrRadius - sqrOffset); // the radius of the drawn disc
|
||||
drawCenter -= (sqrRadius / sqrDist) * viewPlaneNormal;
|
||||
}
|
||||
}
|
||||
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
|
||||
Handles.DrawWireDisc(Vector3.zero, Vector3.up, radius);
|
||||
Handles.DrawWireDisc(drawCenter, viewPlaneNormal, drawnRadius);
|
||||
|
||||
Handles.color = m_WireframeColorBehind;
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.Greater;
|
||||
Handles.DrawWireDisc(Vector3.zero, Vector3.up, radius);
|
||||
Handles.DrawWireDisc(drawCenter, viewPlaneNormal, drawnRadius);
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Draw the manipulable handles</summary>
|
||||
public void DrawHandle()
|
||||
{
|
||||
using (new Handles.DrawingScope(m_HandleColor))
|
||||
{
|
||||
radius = Handles.RadiusHandle(Quaternion.identity, center, radius, handlesOnly: true);
|
||||
if (m_Parent != null)
|
||||
radius = Mathf.Min(radius, m_Parent.radius - Vector3.Distance(center, m_Parent.center));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
Shader "Hidden/UnlitTransparentColored" {
|
||||
Properties {
|
||||
_Color ("Main Color", Color) = (1,1,1,1)
|
||||
}
|
||||
|
||||
SubShader {
|
||||
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
|
||||
|
||||
ZWrite Off
|
||||
Lighting Off
|
||||
Fog { Mode Off }
|
||||
Cull Off
|
||||
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
|
||||
Pass {
|
||||
Color [_Color]
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user