using UnityEngine;
namespace UnityEditor.Rendering
{
///
/// 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.
///
///
///
/// 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;
/// }
/// }
/// }
///
///
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("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;
/// The position of the center of the box in Handle.matrix space.
public Vector3 center { get; set; }
/// The size of the box in Handle.matrix space.
public float radius { get; set; }
/// The baseColor used to fill hull. All other colors are deduced from it.
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;
}
}
/// Constructor. Used to setup colors and also the container if any.
/// The color of filling. All other colors are deduced from it.
/// The HierarchicalSphere containing this sphere. If null, the sphere will not be limited in size.
public HierarchicalSphere(Color baseColor, HierarchicalSphere parent = null)
{
m_Parent = parent;
m_Material = new Material(k_Material);
this.baseColor = baseColor;
}
/// Draw the hull which means the boxes without the handles
/// If true, also draw the surface of the hull's sphere
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;
}
}
/// Draw the manipulable handles
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));
}
}
}
}