You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

397 lines
11 KiB
C#

using System.Collections.Generic;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace MudBun
{
[ExecuteAlways]
public class SelectionManager
{
#if UNITY_EDITOR
private static Ray s_lastRay;
private static Sdf.Contact s_lastHit;
internal static void NotifyRendererDisabled(MudRendererBase renderer)
{
if (s_lastHoveredRenderer == renderer)
s_lastHoveredRenderer = null;
if (s_lastMouseDownRenderer == renderer)
s_lastMouseDownRenderer = null;
System.Func<Object, bool> filter =
x =>
(x != null)
&& (x is GameObject)
&& ((GameObject) x).TryGetComponent(out MudRendererBase r)
&& r != renderer;
s_selectedObjectsOnMouseDown = s_selectedObjectsOnMouseDown.Where(filter).ToList();
s_lastSelectedObjects = s_lastSelectedObjects.Where(filter).ToList();
}
internal static void NotifyBrushDisabled(MudBrushBase brush)
{
if (s_lastHoveredBrush == brush)
s_lastHoveredBrush = null;
if (s_lastMouseDownBrush == brush)
s_lastMouseDownBrush = null;
System.Func<Object, bool> filter =
x =>
(x != null)
&& (x is GameObject)
&& ((GameObject) x).TryGetComponent(out MudBrushBase b)
&& b != brush;
s_selectedObjectsOnMouseDown = s_selectedObjectsOnMouseDown.Where(filter).ToList();
s_lastSelectedObjects = s_lastSelectedObjects.Where(filter).ToList();
}
internal static void Init()
{
SceneView.duringSceneGui += OnScene;
Selection.selectionChanged += OnSelectionChanged;
EditorApplication.update += Update;
}
internal static void Dispose()
{
SceneView.duringSceneGui -= OnScene;
Selection.selectionChanged -= OnSelectionChanged;
EditorApplication.update -= Update;
}
//private static int s_lastMouseDownFrame = -1;
private static int s_lastMouseMoveFrame = -1;
private static float s_lastMouseMoveTime = 0;
private static MudRendererBase s_lastHoveredRenderer;
private static MudRendererBase s_lastMouseDownRenderer;
private static MudBrushBase s_lastHoveredBrush;
private static MudBrushBase s_lastMouseDownBrush;
private static Vector2 s_mouseMovePos;
private static Vector2 s_mouseDownPos;
private static List<Object> s_selectedObjectsOnMouseDown = new List<Object>();
private static List<Object> s_lastSelectedObjects = new List<Object>();
#if UNITY_2022_3_OR_NEWER
private static bool s_lastSelectedObjectsConsumed = true;
#endif
private static void OnScene(SceneView sceneView)
{
/*
if (Application.isPlaying)
return;
if (!UnityEditorInternal.InternalEditorUtility.isApplicationActive)
return;
*/
if (EditorApplication.isPaused)
return;
Event e = Event.current;
if (e.alt)
return;
int frame = Time.renderedFrameCount;
float ppp = EditorGUIUtility.pixelsPerPoint;
int mouseScreenX = (int) (e.mousePosition.x * ppp);
int mouseScreenY = (int) (e.mousePosition.y * ppp);
if (mouseScreenX < 0
|| mouseScreenX >= sceneView.camera.pixelWidth
|| mouseScreenY < 0
|| mouseScreenY >= sceneView.camera.pixelHeight)
return;
int controlID = GUIUtility.GetControlID(FocusType.Passive);
var controlEventType = e.GetTypeForControl(controlID);
switch (e.type)
{
case EventType.Repaint:
sceneView.Repaint();
break;
#if UNITY_2022_3_OR_NEWER
case EventType.MouseUp:
/*
if (controlID != GUIUtility.hotControl)
break;
GUIUtility.hotControl = 0;
*/
if ((e.mousePosition - s_mouseDownPos).magnitude > 3.0f)
break;
#else
case EventType.Used:
#endif
if (s_lastMouseDownBrush == null)
break;
if (s_lastMouseDownRenderer == null)
break;
// stabilize original selection
var selection = s_lastSelectedObjects;
foreach (var obj in s_selectedObjectsOnMouseDown)
{
if (!selection.Contains(obj))
{
selection.Add(obj);
}
}
var brushGo = s_lastMouseDownBrush.gameObject;
var rendererGo = s_lastMouseDownRenderer.gameObject;
var selectedGo = brushGo;
System.Func<Object, bool> filter =
x =>
(x != null)
&& (x is GameObject)
&& ((GameObject) x).TryGetComponent(out MudBrushBase b)
&& (b.Renderer != null)
&& (b.Renderer == s_lastMouseDownRenderer);
if (!selection.Contains(rendererGo)
&& !selection.Any(filter))
{
selectedGo = rendererGo;
}
if (e.shift || e.control)
{
if (selection.Contains(selectedGo))
selection.Remove(selectedGo);
else
selection.Add(selectedGo);
}
else
{
selection.Clear();
selection.Add(selectedGo);
}
Selection.objects = selection.ToArray();
s_lastMouseDownRenderer.MarkNeedsCompute();
EditorApplication.QueuePlayerLoopUpdate();
s_lastSelectedObjects = selection;
#if UNITY_2022_3_OR_NEWER
s_lastSelectedObjectsConsumed = false;
#endif
s_lastMouseDownBrush = null;
s_lastMouseDownRenderer = null;
s_selectedObjectsOnMouseDown.Clear();
break;
case EventType.MouseDown:
if (e.button != 0)
break;
if (s_lastHoveredBrush == null)
break;
if (s_lastHoveredRenderer == null)
break;
#if !UNITY_2022_3_OR_NEWER
if (!e.shift && !e.control)
#endif
s_mouseDownPos = e.mousePosition;
/*
if (!e.shift
&& !e.control
&& Selection.objects.Contains(s_lastHoveredBrush.gameObject))
break;
GUIUtility.hotControl = controlID;
*/
s_lastMouseDownBrush = s_lastHoveredBrush;
s_lastMouseDownRenderer = s_lastHoveredRenderer;
s_selectedObjectsOnMouseDown = Selection.objects.ToList();
break;
case EventType.MouseMove:
s_mouseMovePos = e.mousePosition;
if (s_lastMouseMoveFrame == Time.renderedFrameCount)
break;
s_lastMouseMoveFrame = frame;
s_lastMouseMoveTime = Time.realtimeSinceStartup;
var ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
var contact = MudRenderer.RaycastClickSelection(ray.origin, ray.direction, 1000.0f);
s_lastRay = ray;
// hit normal collision before brush?
if (contact.Hit)
{
float maxDistance = (contact.Position - ray.origin).magnitude;
if (Physics.Raycast(ray, out var hitInfo, maxDistance)
&& hitInfo.collider != null)
{
bool hitMudBunObject = false;
var t = hitInfo.collider.transform;
while (t != null)
{
if (t.GetComponent<MudBrushBase>() != null
|| t.GetComponent<MudRenderer>() != null)
{
hitMudBunObject = true;
break;
}
t = t.parent;
}
if (!hitMudBunObject)
contact.Hit = false;
}
}
MudBrushBase hoveredBrush = null;
if (contact.Hit)
{
s_lastHit = contact;
hoveredBrush = MudRendererBase.LookupBrush(Mathf.Abs(contact.Material.EmissionHash.a));
}
if (MudRenderer.HoveredBrush != hoveredBrush)
{
if (MudRenderer.HoveredBrush != null
&& MudRenderer.HoveredBrush.Renderer != null)
{
MudRenderer.HoveredBrush.Renderer.MarkNeedsCompute();
}
MudRenderer.HoveredBrush = hoveredBrush;
var renderer = hoveredBrush != null ? hoveredBrush.Renderer : null;
if (renderer != null)
{
s_lastHoveredBrush = hoveredBrush;
s_lastHoveredRenderer = renderer;
renderer.MarkNeedsCompute();
}
else if (s_lastHoveredRenderer != null)
{
s_lastHoveredRenderer.MarkNeedsCompute();
s_lastHoveredBrush = null;
s_lastHoveredRenderer = null;
}
}
EditorApplication.QueuePlayerLoopUpdate();
break;
}
}
private static List<UnityEngine.Object> s_restoreSelection;
private static void OnSelectionChanged()
{
if (Time.realtimeSinceStartup - s_lastMouseMoveTime > 0.5f)
{
s_restoreSelection = null;
return;
}
// HACK: someone a single-pixel mouse move during click selection can nuke the selection
if ((s_mouseDownPos - s_mouseMovePos).magnitude < 3.0f)
{
var selection = Selection.objects.ToList();
foreach (var obj in s_lastSelectedObjects)
{
var go = obj as GameObject;
if (go == null)
continue;
if (go.GetComponent<MudBrushBase>() == null
&& go.GetComponent<MudRenderer>() == null)
continue;
if (!selection.Contains(go))
{
if (s_restoreSelection == null)
s_restoreSelection = new List<Object>();
s_restoreSelection.Add(go);
}
}
if (s_restoreSelection != null)
EditorApplication.QueuePlayerLoopUpdate();
}
bool markedNeedsCompute = false;
foreach (var obj in s_lastSelectedObjects)
{
var go = obj as GameObject;
if (go == null)
continue;
if (Selection.objects.Contains(go))
continue;
var brush = go.GetComponent<MudBrushBase>();
if (brush == null)
continue;
var renderer = brush.Renderer;
if (renderer == null)
continue;
renderer.MarkNeedsCompute();
markedNeedsCompute = true;
}
if (markedNeedsCompute)
EditorApplication.QueuePlayerLoopUpdate();
s_lastSelectedObjects = Selection.objects.ToList();
}
private static void Update()
{
#if UNITY_2022_3_OR_NEWER
if (!s_lastSelectedObjectsConsumed)
{
Selection.objects = s_lastSelectedObjects.ToArray();
s_lastSelectedObjectsConsumed = true;
}
#endif
if (s_restoreSelection != null)
{
var selection = Selection.objects.ToList();
foreach (var go in s_restoreSelection)
{
if (!selection.Contains(go))
selection.Add(go);
}
Selection.objects = selection.ToArray();
s_restoreSelection = null;
}
}
#endif
}
}