308 lines
11 KiB
C#
308 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Animations.Rigging;
|
|
|
|
namespace UnityEditor.Animations.Rigging
|
|
{
|
|
[Serializable]
|
|
internal class RigEffector : ScriptableObject, IRigEffector
|
|
{
|
|
[SerializeField] private RigEffectorData m_Data;
|
|
|
|
public Transform transform
|
|
{
|
|
get => m_Data.transform;
|
|
}
|
|
|
|
public bool visible
|
|
{
|
|
get => m_Data.visible;
|
|
set => m_Data.visible = value;
|
|
}
|
|
|
|
private static int s_ButtonHash = "RigEffector".GetHashCode();
|
|
|
|
public void Initialize(RigEffectorData data)
|
|
{
|
|
m_Data = data;
|
|
}
|
|
|
|
private static Material s_Material;
|
|
|
|
public static Material material
|
|
{
|
|
get
|
|
{
|
|
if (!s_Material)
|
|
{
|
|
var shader = EditorHelper.LoadShader("BoneHandles.shader");
|
|
s_Material = new Material(shader);
|
|
s_Material.hideFlags = HideFlags.HideAndDontSave;
|
|
}
|
|
|
|
return s_Material;
|
|
}
|
|
}
|
|
|
|
public static RigEffectorData.Style defaultStyle
|
|
{
|
|
get
|
|
{
|
|
var style = new RigEffectorData.Style()
|
|
{
|
|
shape = EditorHelper.LoadShape("LocatorEffector.asset"),
|
|
color = new Color(1f, 0f, 0f, 0.5f),
|
|
size = 0.10f,
|
|
position = Vector3.zero,
|
|
rotation = Vector3.zero
|
|
};
|
|
|
|
return style;
|
|
}
|
|
}
|
|
|
|
public void OnSceneGUI()
|
|
{
|
|
if (!m_Data.visible)
|
|
return;
|
|
|
|
// Might happen if we delete transform while effector still exists.
|
|
if (transform == null)
|
|
return;
|
|
|
|
var style = m_Data.style;
|
|
|
|
// Disregard effectors without shapes.
|
|
if (style.shape == null)
|
|
return;
|
|
|
|
if (SceneVisibilityManager.instance.IsHidden(transform.gameObject, false))
|
|
return;
|
|
|
|
var mask = UnityEditor.Tools.visibleLayers;
|
|
if ((mask & (1 << transform.gameObject.layer)) == 0)
|
|
return;
|
|
|
|
int id = GUIUtility.GetControlID(s_ButtonHash, FocusType.Passive);
|
|
Event evt = Event.current;
|
|
|
|
switch (evt.GetTypeForControl(id))
|
|
{
|
|
case EventType.Layout:
|
|
{
|
|
HandleUtility.AddControl(id, DistanceToEffector(transform, style.shape, style.position, style.rotation, style.size));
|
|
break;
|
|
}
|
|
case EventType.MouseDown:
|
|
{
|
|
if (evt.alt)
|
|
break;
|
|
|
|
if (HandleUtility.nearestControl == id && evt.button == 0)
|
|
{
|
|
GameObject targetGameObject = transform.gameObject;
|
|
if (!SceneVisibilityManager.instance.IsPickingDisabled(targetGameObject, false))
|
|
{
|
|
GUIUtility.hotControl = id; // Grab mouse focus
|
|
EditorHelper.HandleClickSelection(targetGameObject, evt);
|
|
evt.Use();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case EventType.MouseDrag:
|
|
{
|
|
if (!evt.alt && GUIUtility.hotControl == id)
|
|
{
|
|
GameObject targetGameObject = transform.gameObject;
|
|
if (!SceneVisibilityManager.instance.IsPickingDisabled(targetGameObject, false))
|
|
{
|
|
DragAndDrop.PrepareStartDrag();
|
|
DragAndDrop.objectReferences = new UnityEngine.Object[] {transform};
|
|
DragAndDrop.StartDrag(ObjectNames.GetDragAndDropTitle(transform));
|
|
|
|
GUIUtility.hotControl = 0;
|
|
|
|
evt.Use();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case EventType.MouseUp:
|
|
{
|
|
if (GUIUtility.hotControl == id && (evt.button == 0 || evt.button == 2))
|
|
{
|
|
GUIUtility.hotControl = 0;
|
|
evt.Use();
|
|
}
|
|
break;
|
|
}
|
|
case EventType.Repaint:
|
|
{
|
|
Matrix4x4 matrix = GetEffectorMatrix(transform, style.position, style.rotation, style.size);
|
|
|
|
Color highlight = style.color;
|
|
|
|
bool hoveringEffector = GUIUtility.hotControl == 0 && HandleUtility.nearestControl == id;
|
|
hoveringEffector = hoveringEffector &&
|
|
!SceneVisibilityManager.instance.IsPickingDisabled(transform.gameObject, false);
|
|
|
|
if (hoveringEffector)
|
|
{
|
|
highlight = Handles.preselectionColor;
|
|
}
|
|
else if (Selection.Contains(transform.gameObject) || Selection.activeObject == transform.gameObject)
|
|
{
|
|
highlight = Handles.selectedColor;
|
|
}
|
|
|
|
try
|
|
{
|
|
Material mat = material;
|
|
|
|
var shapeHighlight = MeshHasWireframeShapes(style.shape) ? style.color : highlight;
|
|
var wireHighlight = new Color(highlight.r, highlight.g, highlight.b, 1f);
|
|
|
|
if (style.shape.subMeshCount > 0)
|
|
{
|
|
// Draw every sub meshes separately to control highlight vs shape colors.
|
|
for (int i = 0; i < style.shape.subMeshCount; ++i)
|
|
{
|
|
MeshTopology topology = style.shape.GetTopology(i);
|
|
bool isFilled = (topology == MeshTopology.Triangles || topology == MeshTopology.Quads);
|
|
|
|
mat.SetColor("_Color", isFilled ? shapeHighlight : wireHighlight);
|
|
mat.SetPass(0);
|
|
|
|
Graphics.DrawMeshNow(style.shape, matrix, i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MeshTopology topology = style.shape.GetTopology(0);
|
|
bool isFilled = (topology == MeshTopology.Triangles || topology == MeshTopology.Quads);
|
|
|
|
mat.SetColor("_Color", isFilled ? shapeHighlight : wireHighlight);
|
|
mat.SetPass(0);
|
|
|
|
Graphics.DrawMeshNow(style.shape, matrix);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Debug.LogException(exception);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private bool FindMeshTopology(Mesh shape, IEnumerable<MeshTopology> topologies)
|
|
{
|
|
if (shape.subMeshCount > 0)
|
|
{
|
|
for (int i = 0; i < shape.subMeshCount; ++i)
|
|
{
|
|
MeshTopology topology = shape.GetTopology(i);
|
|
foreach (var topologyQuery in topologies)
|
|
{
|
|
if (topologyQuery == topology)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var topology = shape.GetTopology(0);
|
|
foreach (var topologyQuery in topologies)
|
|
{
|
|
if (topologyQuery == topology)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool MeshHasFilledShapes(Mesh shape) =>
|
|
FindMeshTopology(shape, new MeshTopology[] {MeshTopology.Triangles, MeshTopology.Quads});
|
|
|
|
private bool MeshHasWireframeShapes(Mesh shape) =>
|
|
FindMeshTopology(shape, new MeshTopology[] {MeshTopology.Lines, MeshTopology.LineStrip});
|
|
|
|
private Matrix4x4 GetEffectorMatrix(Transform transform, Vector3 position, Vector3 rotation, float size)
|
|
{
|
|
return Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one) * Matrix4x4.TRS(position, Quaternion.Euler(rotation), new Vector3(size, size, size));
|
|
}
|
|
|
|
private float DistanceToEffector(Transform transform, Mesh shape, Vector3 position, Vector3 rotation, float size)
|
|
{
|
|
Matrix4x4 matrix = GetEffectorMatrix(transform, position, rotation, size);
|
|
Vector3 origin = matrix.MultiplyPoint(Vector3.zero);
|
|
|
|
if (shape == null)
|
|
return HandleUtility.DistanceToCircle(origin, size * 0.5f);
|
|
|
|
if (MeshHasFilledShapes(shape))
|
|
{
|
|
var bounds = shape.bounds;
|
|
var extents = Mathf.Max( Mathf.Max(bounds.extents.x, bounds.extents.y), bounds.extents.z);
|
|
return HandleUtility.DistanceToCircle(origin + bounds.center * size, extents * size);
|
|
}
|
|
|
|
float nearestDistance = Mathf.Infinity;
|
|
|
|
Vector3[] vertices = shape.vertices;
|
|
|
|
for (int i = 0; i < shape.subMeshCount; ++i)
|
|
{
|
|
MeshTopology topology = shape.GetTopology(i);
|
|
if (topology == MeshTopology.Lines)
|
|
{
|
|
int[] indices = shape.GetIndices(i);
|
|
|
|
int count = 0;
|
|
while (count < indices.Length)
|
|
{
|
|
float d = HandleUtility.DistanceToLine(matrix.MultiplyPoint(vertices[indices[count]]), matrix.MultiplyPoint(vertices[indices[count+1]]));
|
|
if (d < nearestDistance)
|
|
nearestDistance = d;
|
|
|
|
count += 2;
|
|
}
|
|
}
|
|
else if (topology == MeshTopology.LineStrip)
|
|
{
|
|
int[] indices = shape.GetIndices(i);
|
|
|
|
if (indices.Length > 0)
|
|
{
|
|
int count = 0;
|
|
float d = 0f;
|
|
Vector3 v0 = matrix.MultiplyPoint(vertices[indices[count]]);
|
|
Vector3 v1 = v0;
|
|
|
|
while (++count < indices.Length)
|
|
{
|
|
Vector3 v2 = matrix.MultiplyPoint(vertices[indices[count]]);
|
|
|
|
d = HandleUtility.DistanceToLine(v1, v2);
|
|
if (d < nearestDistance)
|
|
nearestDistance = d;
|
|
|
|
v1 = v2;
|
|
}
|
|
|
|
d = HandleUtility.DistanceToLine(v1, v0);
|
|
if (d < nearestDistance)
|
|
nearestDistance = d;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nearestDistance;
|
|
}
|
|
}
|
|
}
|