PO/Library/PackageCache/com.unity.2d.animation@5.0.7/IK/Editor/IKEditorManager.cs

528 lines
17 KiB
C#
Raw Normal View History

using System.Collections.Generic;
using System.Linq;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.U2D.Common;
using UnityEngine.U2D.IK;
using UnityEngine.Profiling;
namespace UnityEditor.U2D.IK
{
internal class IKEditorManager : ScriptableSingleton<IKEditorManager>
{
private readonly HashSet<IKManager2D> m_DirtyManagers = new HashSet<IKManager2D>();
private readonly HashSet<Solver2D> m_IKSolvers = new HashSet<Solver2D>();
private readonly List<IKManager2D> m_IKManagers = new List<IKManager2D>();
private readonly Dictionary<IKChain2D, Vector3> m_ChainPositionOverrides = new Dictionary<IKChain2D, Vector3>();
private readonly List<Vector3> m_TargetPositions = new List<Vector3>();
private GameObject m_Helper;
private GameObject[] m_SelectedGameobjects;
private bool m_IgnorePostProcessModifications = false;
private HashSet<Transform> m_IgnoreTransformsOnUndo = new HashSet<Transform>();
internal bool isDraggingATool { get; private set; }
internal bool isDragging { get { return IKGizmos.instance.isDragging || isDraggingATool; } }
[InitializeOnLoadMethod]
private static void Setup()
{
instance.Create();
}
private void Create() {}
private void OnEnable()
{
SetupLateUpdateHelper();
RegisterCallbacks();
Initialize();
}
private void OnDisable()
{
UnregisterCallbacks();
DestroyLateUpdateHelper();
}
private void RegisterCallbacks()
{
EditorApplication.hierarchyChanged += Initialize;
Undo.postprocessModifications += OnPostProcessModifications;
#if UNITY_2019_1_OR_NEWER
SceneView.duringSceneGui += OnSceneGUI;
#else
SceneView.onSceneGUIDelegate += OnSceneGUI;
#endif
Selection.selectionChanged += OnSelectionChanged;
}
private void UnregisterCallbacks()
{
EditorApplication.hierarchyChanged -= Initialize;
Undo.postprocessModifications -= OnPostProcessModifications;
#if UNITY_2019_1_OR_NEWER
SceneView.duringSceneGui -= OnSceneGUI;
#else
SceneView.onSceneGUIDelegate -= OnSceneGUI;
#endif
Selection.selectionChanged -= OnSelectionChanged;
}
private bool m_EnableGizmos;
private bool m_CurrentEnableGizmoState;
void OnDrawGizmos()
{
m_EnableGizmos = true;
IKManager2D.onDrawGizmos.RemoveListener(OnDrawGizmos);
}
public void CheckGizmoToggle()
{
//Ignore events other than Repaint
if (Event.current.type != EventType.Repaint)
return;
if (m_CurrentEnableGizmoState != m_EnableGizmos)
SceneView.RepaintAll();
m_CurrentEnableGizmoState = m_EnableGizmos;
//Assume the Gizmo toggle is disabled and listen to the event again
m_EnableGizmos = false;
IKManager2D.onDrawGizmos.RemoveListener(OnDrawGizmos);
IKManager2D.onDrawGizmos.AddListener(OnDrawGizmos);
}
private void OnSelectionChanged()
{
m_SelectedGameobjects = null;
}
private void SetupLateUpdateHelper()
{
if (m_Helper != null)
return;
m_Helper = new GameObject("IKEditorManagerHelper");
m_Helper.hideFlags = HideFlags.HideAndDontSave;
var helper = m_Helper.AddComponent<IKEditorManagerHelper>();
helper.onLateUpdate.AddListener(OnLateUpdate);
}
private void DestroyLateUpdateHelper()
{
if (m_Helper != null)
GameObject.DestroyImmediate(m_Helper);
}
public void Initialize()
{
m_IKManagers.Clear();
m_IKSolvers.Clear();
m_DirtyManagers.Clear();
m_ChainPositionOverrides.Clear();
var currentStage = StageUtility.GetCurrentStageHandle();
var managers = currentStage.FindComponentsOfType<IKManager2D>().Where(x => x.gameObject.scene.isLoaded).ToArray();
m_IKManagers.AddRange(managers);
foreach (IKManager2D manager in m_IKManagers)
{
foreach (Solver2D solver in manager.solvers)
{
if (solver)
m_IKSolvers.Add(solver);
}
}
}
public IKManager2D FindManager(Solver2D solver)
{
foreach (IKManager2D manager in m_IKManagers)
{
if (manager == null)
continue;
foreach (Solver2D s in manager.solvers)
{
if (s == null)
continue;
if (s == solver)
return manager;
}
}
return null;
}
public void Record(Solver2D solver, string undoName)
{
var manager = FindManager(solver);
DoUndo(manager, undoName, true);
}
public void RegisterUndo(Solver2D solver, string undoName)
{
var manager = FindManager(solver);
DoUndo(manager, undoName, false);
}
public void Record(IKManager2D manager, string undoName)
{
DoUndo(manager, undoName, true);
}
public void RegisterUndo(IKManager2D manager, string undoName)
{
DoUndo(manager, undoName, false);
}
private void DoUndo(IKManager2D manager, string undoName, bool record)
{
if (manager == null)
return;
foreach (var solver in manager.solvers)
{
if (solver == null || !solver.isActiveAndEnabled)
continue;
if (!solver.isValid)
solver.Initialize();
if (!solver.isValid)
continue;
for (int i = 0; i < solver.chainCount; ++i)
{
var chain = solver.GetChain(i);
if (record)
{
foreach(var t in chain.transforms)
{
if(m_IgnoreTransformsOnUndo.Contains(t))
continue;
Undo.RecordObject(t, undoName);
}
if(chain.target && !m_IgnoreTransformsOnUndo.Contains(chain.target))
Undo.RecordObject(chain.target, undoName);
}
else
{
foreach(var t in chain.transforms)
{
if(m_IgnoreTransformsOnUndo.Contains(t))
continue;
Undo.RegisterCompleteObjectUndo(t, undoName);
}
if(chain.target && !m_IgnoreTransformsOnUndo.Contains(chain.target))
Undo.RegisterCompleteObjectUndo(chain.target, undoName);
}
}
m_IgnorePostProcessModifications = true;
}
}
public void UpdateManagerImmediate(IKManager2D manager, bool recordRootLoops)
{
SetManagerDirty(manager);
UpdateDirtyManagers(recordRootLoops);
}
public void UpdateSolverImmediate(Solver2D solver, bool recordRootLoops)
{
SetSolverDirty(solver);
UpdateDirtyManagers(recordRootLoops);
}
public void UpdateHierarchyImmediate(Transform hierarchyRoot, bool recordRootLoops)
{
SetDirtyUnderHierarchy(hierarchyRoot);
UpdateDirtyManagers(recordRootLoops);
}
public void SetChainPositionOverride(IKChain2D chain, Vector3 position)
{
m_ChainPositionOverrides[chain] = position;
}
private bool IsViewToolActive()
{
int button = Event.current.button;
return Tools.current == Tool.View || Event.current.alt || (button == 1) || (button == 2);
}
private bool IsDraggingATool()
{
//If a tool has used EventType.MouseDrag, we won't be able to detect it. Instead we check for delta magnitude
return GUIUtility.hotControl != 0 && Event.current.button == 0 && Event.current.delta.sqrMagnitude > 0f && !IsViewToolActive();
}
private void OnSceneGUI(SceneView sceneView)
{
CheckGizmoToggle();
if (!m_CurrentEnableGizmoState)
return;
if (m_SelectedGameobjects == null)
m_SelectedGameobjects = Selection.gameObjects;
foreach (Solver2D solver in m_IKSolvers)
IKGizmos.instance.DoSolverGUI(solver);
if (!IKGizmos.instance.isDragging && IsDraggingATool())
{
//We expect the object to be selected while dragged
foreach (var gameObject in m_SelectedGameobjects)
{
if (gameObject != null && gameObject.transform != null)
SetDirtySolversAffectedByTransform(gameObject.transform);
}
if(m_DirtyManagers.Count > 0 && !isDraggingATool)
{
isDraggingATool = true;
Undo.SetCurrentGroupName("IK Update");
RegisterUndoForDirtyManagers();
}
}
if(GUIUtility.hotControl == 0)
isDraggingATool = false;
}
internal void OnLateUpdate()
{
if (Application.isPlaying)
return;
Profiler.BeginSample("IKEditorManager.OnLateUpdate");
SetAllManagersDirty();
UpdateDirtyManagers(false);
Profiler.EndSample();
}
private bool ProcessTransformPropertyModification(UndoPropertyModification modification, out Transform transform)
{
transform = null;
var targetType = modification.currentValue.target.GetType();
if ((targetType == typeof(Transform) || targetType.IsSubclassOf(typeof(Transform))))
{
transform = (Transform)modification.currentValue.target;
return true;
}
return false;
}
private UndoPropertyModification[] OnPostProcessModifications(UndoPropertyModification[] modifications)
{
if(!m_IgnorePostProcessModifications && !isDragging)
{
//Prepare transforms that already have an undo modification
foreach (var modification in modifications)
{
if (modification.currentValue == null)
continue;
Transform transform;
if (ProcessTransformPropertyModification(modification, out transform))
m_IgnoreTransformsOnUndo.Add(transform);
}
var processedObjectList = new HashSet<Object>();
foreach (var modification in modifications)
{
if (modification.currentValue == null)
continue;
var target = modification.currentValue.target;
if(processedObjectList.Contains(target))
continue;
processedObjectList.Add(target);
var targetType = target.GetType();
Transform transform;
if (ProcessTransformPropertyModification(modification, out transform))
{
SetDirtySolversAffectedByTransform(transform);
RegisterUndoForDirtyManagers();
}
if (targetType == typeof(Solver2D) || targetType.IsSubclassOf(typeof(Solver2D)))
{
var solver = (Solver2D)modification.currentValue.target;
SetSolverDirty(solver);
RegisterUndoForDirtyManagers();
}
if (targetType == typeof(IKManager2D))
{
var dirtyManager = (IKManager2D)modification.currentValue.target;
SetManagerDirty(dirtyManager);
RegisterUndoForDirtyManagers();
}
}
m_IgnoreTransformsOnUndo.Clear();
}
m_IgnorePostProcessModifications = false;
return modifications;
}
private void SetSolverDirty(Solver2D solver)
{
if (solver && solver.isValid && solver.isActiveAndEnabled)
SetManagerDirty(FindManager(solver));
}
private void SetManagerDirty(IKManager2D manager)
{
if (manager && manager.isActiveAndEnabled)
m_DirtyManagers.Add(manager);
}
private void SetAllManagersDirty()
{
m_DirtyManagers.Clear();
foreach (IKManager2D manager in m_IKManagers)
SetManagerDirty(manager);
}
private void SetDirtyUnderHierarchy(Transform hierarchyRoot)
{
if (hierarchyRoot == null)
return;
foreach (Solver2D solver in m_IKSolvers)
{
if (solver.isValid)
{
for (int i = 0; i < solver.chainCount; ++i)
{
var chain = solver.GetChain(i);
if(chain.target == null)
continue;
if (hierarchyRoot == chain.target ||
IKUtility.IsDescendentOf(chain.target, hierarchyRoot) ||
IKUtility.IsDescendentOf(chain.effector, hierarchyRoot))
{
SetSolverDirty(solver);
break;
}
}
}
}
}
private void SetDirtySolversAffectedByTransform(Transform transform)
{
foreach (Solver2D solver in m_IKSolvers)
{
if (solver.isValid)
{
for (int i = 0; i < solver.chainCount; ++i)
{
var chain = solver.GetChain(i);
if(chain.target == null)
continue;
if (!(IKUtility.IsDescendentOf(chain.target, transform) && IKUtility.IsDescendentOf(chain.rootTransform, transform)) &&
(chain.target == transform || IKUtility.IsDescendentOf(chain.target, transform) || IKUtility.IsDescendentOf(chain.effector, transform)))
{
SetSolverDirty(solver);
break;
}
}
}
}
}
private void RegisterUndoForDirtyManagers()
{
foreach (var manager in m_DirtyManagers)
RegisterUndo(manager, Undo.GetCurrentGroupName());
}
private void UpdateDirtyManagers(bool recordRootLoops)
{
foreach (var manager in m_DirtyManagers)
{
if (manager == null || !manager.isActiveAndEnabled)
continue;
foreach (var solver in manager.solvers)
{
if (solver == null || !solver.isActiveAndEnabled)
continue;
if (!solver.isValid)
solver.Initialize();
if (!solver.isValid)
continue;
if(solver.allChainsHaveTargets)
solver.UpdateIK(manager.weight);
else if(PrepareTargetOverrides(solver))
solver.UpdateIK(m_TargetPositions, manager.weight);
for (int i = 0; i < solver.chainCount; ++i)
{
var chain = solver.GetChain(i);
if (recordRootLoops)
InternalEngineBridge.SetLocalEulerHint(chain.rootTransform);
if(solver.constrainRotation && chain.target != null)
InternalEngineBridge.SetLocalEulerHint(chain.effector);
}
}
}
m_DirtyManagers.Clear();
m_ChainPositionOverrides.Clear();
}
private bool PrepareTargetOverrides(Solver2D solver)
{
m_TargetPositions.Clear();
for (int i = 0; i < solver.chainCount; ++i)
{
var chain = solver.GetChain(i);
Vector3 positionOverride;
if (!m_ChainPositionOverrides.TryGetValue(chain, out positionOverride))
{
m_TargetPositions.Clear();
return false;
}
m_TargetPositions.Add(positionOverride);
}
return true;
}
}
}