IKEditorManager.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using UnityEditor.SceneManagement;
  4. using UnityEngine;
  5. using UnityEngine.U2D.Common;
  6. using UnityEngine.U2D.IK;
  7. using UnityEngine.Profiling;
  8. namespace UnityEditor.U2D.IK
  9. {
  10. internal class IKEditorManager : ScriptableSingleton<IKEditorManager>
  11. {
  12. private readonly HashSet<IKManager2D> m_DirtyManagers = new HashSet<IKManager2D>();
  13. private readonly HashSet<Solver2D> m_IKSolvers = new HashSet<Solver2D>();
  14. private readonly List<IKManager2D> m_IKManagers = new List<IKManager2D>();
  15. private readonly Dictionary<IKChain2D, Vector3> m_ChainPositionOverrides = new Dictionary<IKChain2D, Vector3>();
  16. private readonly List<Vector3> m_TargetPositions = new List<Vector3>();
  17. private GameObject m_Helper;
  18. private GameObject[] m_SelectedGameobjects;
  19. private bool m_IgnorePostProcessModifications = false;
  20. private HashSet<Transform> m_IgnoreTransformsOnUndo = new HashSet<Transform>();
  21. internal bool isDraggingATool { get; private set; }
  22. internal bool isDragging { get { return IKGizmos.instance.isDragging || isDraggingATool; } }
  23. [InitializeOnLoadMethod]
  24. private static void Setup()
  25. {
  26. instance.Create();
  27. }
  28. private void Create() {}
  29. private void OnEnable()
  30. {
  31. SetupLateUpdateHelper();
  32. RegisterCallbacks();
  33. Initialize();
  34. }
  35. private void OnDisable()
  36. {
  37. UnregisterCallbacks();
  38. DestroyLateUpdateHelper();
  39. }
  40. private void RegisterCallbacks()
  41. {
  42. EditorApplication.hierarchyChanged += Initialize;
  43. Undo.postprocessModifications += OnPostProcessModifications;
  44. #if UNITY_2019_1_OR_NEWER
  45. SceneView.duringSceneGui += OnSceneGUI;
  46. #else
  47. SceneView.onSceneGUIDelegate += OnSceneGUI;
  48. #endif
  49. Selection.selectionChanged += OnSelectionChanged;
  50. }
  51. private void UnregisterCallbacks()
  52. {
  53. EditorApplication.hierarchyChanged -= Initialize;
  54. Undo.postprocessModifications -= OnPostProcessModifications;
  55. #if UNITY_2019_1_OR_NEWER
  56. SceneView.duringSceneGui -= OnSceneGUI;
  57. #else
  58. SceneView.onSceneGUIDelegate -= OnSceneGUI;
  59. #endif
  60. Selection.selectionChanged -= OnSelectionChanged;
  61. }
  62. private bool m_EnableGizmos;
  63. private bool m_CurrentEnableGizmoState;
  64. void OnDrawGizmos()
  65. {
  66. m_EnableGizmos = true;
  67. IKManager2D.onDrawGizmos.RemoveListener(OnDrawGizmos);
  68. }
  69. public void CheckGizmoToggle()
  70. {
  71. //Ignore events other than Repaint
  72. if (Event.current.type != EventType.Repaint)
  73. return;
  74. if (m_CurrentEnableGizmoState != m_EnableGizmos)
  75. SceneView.RepaintAll();
  76. m_CurrentEnableGizmoState = m_EnableGizmos;
  77. //Assume the Gizmo toggle is disabled and listen to the event again
  78. m_EnableGizmos = false;
  79. IKManager2D.onDrawGizmos.RemoveListener(OnDrawGizmos);
  80. IKManager2D.onDrawGizmos.AddListener(OnDrawGizmos);
  81. }
  82. private void OnSelectionChanged()
  83. {
  84. m_SelectedGameobjects = null;
  85. }
  86. private void SetupLateUpdateHelper()
  87. {
  88. if (m_Helper != null)
  89. return;
  90. m_Helper = new GameObject("IKEditorManagerHelper");
  91. m_Helper.hideFlags = HideFlags.HideAndDontSave;
  92. var helper = m_Helper.AddComponent<IKEditorManagerHelper>();
  93. helper.onLateUpdate.AddListener(OnLateUpdate);
  94. }
  95. private void DestroyLateUpdateHelper()
  96. {
  97. if (m_Helper != null)
  98. GameObject.DestroyImmediate(m_Helper);
  99. }
  100. public void Initialize()
  101. {
  102. m_IKManagers.Clear();
  103. m_IKSolvers.Clear();
  104. m_DirtyManagers.Clear();
  105. m_ChainPositionOverrides.Clear();
  106. var currentStage = StageUtility.GetCurrentStageHandle();
  107. var managers = currentStage.FindComponentsOfType<IKManager2D>().Where(x => x.gameObject.scene.isLoaded).ToArray();
  108. m_IKManagers.AddRange(managers);
  109. foreach (IKManager2D manager in m_IKManagers)
  110. {
  111. foreach (Solver2D solver in manager.solvers)
  112. {
  113. if (solver)
  114. m_IKSolvers.Add(solver);
  115. }
  116. }
  117. }
  118. public IKManager2D FindManager(Solver2D solver)
  119. {
  120. foreach (IKManager2D manager in m_IKManagers)
  121. {
  122. if (manager == null)
  123. continue;
  124. foreach (Solver2D s in manager.solvers)
  125. {
  126. if (s == null)
  127. continue;
  128. if (s == solver)
  129. return manager;
  130. }
  131. }
  132. return null;
  133. }
  134. public void Record(Solver2D solver, string undoName)
  135. {
  136. var manager = FindManager(solver);
  137. DoUndo(manager, undoName, true);
  138. }
  139. public void RegisterUndo(Solver2D solver, string undoName)
  140. {
  141. var manager = FindManager(solver);
  142. DoUndo(manager, undoName, false);
  143. }
  144. public void Record(IKManager2D manager, string undoName)
  145. {
  146. DoUndo(manager, undoName, true);
  147. }
  148. public void RegisterUndo(IKManager2D manager, string undoName)
  149. {
  150. DoUndo(manager, undoName, false);
  151. }
  152. private void DoUndo(IKManager2D manager, string undoName, bool record)
  153. {
  154. if (manager == null)
  155. return;
  156. foreach (var solver in manager.solvers)
  157. {
  158. if (solver == null || !solver.isActiveAndEnabled)
  159. continue;
  160. if (!solver.isValid)
  161. solver.Initialize();
  162. if (!solver.isValid)
  163. continue;
  164. for (int i = 0; i < solver.chainCount; ++i)
  165. {
  166. var chain = solver.GetChain(i);
  167. if (record)
  168. {
  169. foreach(var t in chain.transforms)
  170. {
  171. if(m_IgnoreTransformsOnUndo.Contains(t))
  172. continue;
  173. Undo.RecordObject(t, undoName);
  174. }
  175. if(chain.target && !m_IgnoreTransformsOnUndo.Contains(chain.target))
  176. Undo.RecordObject(chain.target, undoName);
  177. }
  178. else
  179. {
  180. foreach(var t in chain.transforms)
  181. {
  182. if(m_IgnoreTransformsOnUndo.Contains(t))
  183. continue;
  184. Undo.RegisterCompleteObjectUndo(t, undoName);
  185. }
  186. if(chain.target && !m_IgnoreTransformsOnUndo.Contains(chain.target))
  187. Undo.RegisterCompleteObjectUndo(chain.target, undoName);
  188. }
  189. }
  190. m_IgnorePostProcessModifications = true;
  191. }
  192. }
  193. public void UpdateManagerImmediate(IKManager2D manager, bool recordRootLoops)
  194. {
  195. SetManagerDirty(manager);
  196. UpdateDirtyManagers(recordRootLoops);
  197. }
  198. public void UpdateSolverImmediate(Solver2D solver, bool recordRootLoops)
  199. {
  200. SetSolverDirty(solver);
  201. UpdateDirtyManagers(recordRootLoops);
  202. }
  203. public void UpdateHierarchyImmediate(Transform hierarchyRoot, bool recordRootLoops)
  204. {
  205. SetDirtyUnderHierarchy(hierarchyRoot);
  206. UpdateDirtyManagers(recordRootLoops);
  207. }
  208. public void SetChainPositionOverride(IKChain2D chain, Vector3 position)
  209. {
  210. m_ChainPositionOverrides[chain] = position;
  211. }
  212. private bool IsViewToolActive()
  213. {
  214. int button = Event.current.button;
  215. return Tools.current == Tool.View || Event.current.alt || (button == 1) || (button == 2);
  216. }
  217. private bool IsDraggingATool()
  218. {
  219. //If a tool has used EventType.MouseDrag, we won't be able to detect it. Instead we check for delta magnitude
  220. return GUIUtility.hotControl != 0 && Event.current.button == 0 && Event.current.delta.sqrMagnitude > 0f && !IsViewToolActive();
  221. }
  222. private void OnSceneGUI(SceneView sceneView)
  223. {
  224. CheckGizmoToggle();
  225. if (!m_CurrentEnableGizmoState)
  226. return;
  227. if (m_SelectedGameobjects == null)
  228. m_SelectedGameobjects = Selection.gameObjects;
  229. foreach (Solver2D solver in m_IKSolvers)
  230. IKGizmos.instance.DoSolverGUI(solver);
  231. if (!IKGizmos.instance.isDragging && IsDraggingATool())
  232. {
  233. //We expect the object to be selected while dragged
  234. foreach (var gameObject in m_SelectedGameobjects)
  235. {
  236. if (gameObject != null && gameObject.transform != null)
  237. SetDirtySolversAffectedByTransform(gameObject.transform);
  238. }
  239. if(m_DirtyManagers.Count > 0 && !isDraggingATool)
  240. {
  241. isDraggingATool = true;
  242. Undo.SetCurrentGroupName("IK Update");
  243. RegisterUndoForDirtyManagers();
  244. }
  245. }
  246. if(GUIUtility.hotControl == 0)
  247. isDraggingATool = false;
  248. }
  249. internal void OnLateUpdate()
  250. {
  251. if (Application.isPlaying)
  252. return;
  253. Profiler.BeginSample("IKEditorManager.OnLateUpdate");
  254. SetAllManagersDirty();
  255. UpdateDirtyManagers(false);
  256. Profiler.EndSample();
  257. }
  258. private bool ProcessTransformPropertyModification(UndoPropertyModification modification, out Transform transform)
  259. {
  260. transform = null;
  261. var targetType = modification.currentValue.target.GetType();
  262. if ((targetType == typeof(Transform) || targetType.IsSubclassOf(typeof(Transform))))
  263. {
  264. transform = (Transform)modification.currentValue.target;
  265. return true;
  266. }
  267. return false;
  268. }
  269. private UndoPropertyModification[] OnPostProcessModifications(UndoPropertyModification[] modifications)
  270. {
  271. if(!m_IgnorePostProcessModifications && !isDragging)
  272. {
  273. //Prepare transforms that already have an undo modification
  274. foreach (var modification in modifications)
  275. {
  276. if (modification.currentValue == null)
  277. continue;
  278. Transform transform;
  279. if (ProcessTransformPropertyModification(modification, out transform))
  280. m_IgnoreTransformsOnUndo.Add(transform);
  281. }
  282. var processedObjectList = new HashSet<Object>();
  283. foreach (var modification in modifications)
  284. {
  285. if (modification.currentValue == null)
  286. continue;
  287. var target = modification.currentValue.target;
  288. if(processedObjectList.Contains(target))
  289. continue;
  290. processedObjectList.Add(target);
  291. var targetType = target.GetType();
  292. Transform transform;
  293. if (ProcessTransformPropertyModification(modification, out transform))
  294. {
  295. SetDirtySolversAffectedByTransform(transform);
  296. RegisterUndoForDirtyManagers();
  297. }
  298. if (targetType == typeof(Solver2D) || targetType.IsSubclassOf(typeof(Solver2D)))
  299. {
  300. var solver = (Solver2D)modification.currentValue.target;
  301. SetSolverDirty(solver);
  302. RegisterUndoForDirtyManagers();
  303. }
  304. if (targetType == typeof(IKManager2D))
  305. {
  306. var dirtyManager = (IKManager2D)modification.currentValue.target;
  307. SetManagerDirty(dirtyManager);
  308. RegisterUndoForDirtyManagers();
  309. }
  310. }
  311. m_IgnoreTransformsOnUndo.Clear();
  312. }
  313. m_IgnorePostProcessModifications = false;
  314. return modifications;
  315. }
  316. private void SetSolverDirty(Solver2D solver)
  317. {
  318. if (solver && solver.isValid && solver.isActiveAndEnabled)
  319. SetManagerDirty(FindManager(solver));
  320. }
  321. private void SetManagerDirty(IKManager2D manager)
  322. {
  323. if (manager && manager.isActiveAndEnabled)
  324. m_DirtyManagers.Add(manager);
  325. }
  326. private void SetAllManagersDirty()
  327. {
  328. m_DirtyManagers.Clear();
  329. foreach (IKManager2D manager in m_IKManagers)
  330. SetManagerDirty(manager);
  331. }
  332. private void SetDirtyUnderHierarchy(Transform hierarchyRoot)
  333. {
  334. if (hierarchyRoot == null)
  335. return;
  336. foreach (Solver2D solver in m_IKSolvers)
  337. {
  338. if (solver.isValid)
  339. {
  340. for (int i = 0; i < solver.chainCount; ++i)
  341. {
  342. var chain = solver.GetChain(i);
  343. if(chain.target == null)
  344. continue;
  345. if (hierarchyRoot == chain.target ||
  346. IKUtility.IsDescendentOf(chain.target, hierarchyRoot) ||
  347. IKUtility.IsDescendentOf(chain.effector, hierarchyRoot))
  348. {
  349. SetSolverDirty(solver);
  350. break;
  351. }
  352. }
  353. }
  354. }
  355. }
  356. private void SetDirtySolversAffectedByTransform(Transform transform)
  357. {
  358. foreach (Solver2D solver in m_IKSolvers)
  359. {
  360. if (solver.isValid)
  361. {
  362. for (int i = 0; i < solver.chainCount; ++i)
  363. {
  364. var chain = solver.GetChain(i);
  365. if(chain.target == null)
  366. continue;
  367. if (!(IKUtility.IsDescendentOf(chain.target, transform) && IKUtility.IsDescendentOf(chain.rootTransform, transform)) &&
  368. (chain.target == transform || IKUtility.IsDescendentOf(chain.target, transform) || IKUtility.IsDescendentOf(chain.effector, transform)))
  369. {
  370. SetSolverDirty(solver);
  371. break;
  372. }
  373. }
  374. }
  375. }
  376. }
  377. private void RegisterUndoForDirtyManagers()
  378. {
  379. foreach (var manager in m_DirtyManagers)
  380. RegisterUndo(manager, Undo.GetCurrentGroupName());
  381. }
  382. private void UpdateDirtyManagers(bool recordRootLoops)
  383. {
  384. foreach (var manager in m_DirtyManagers)
  385. {
  386. if (manager == null || !manager.isActiveAndEnabled)
  387. continue;
  388. foreach (var solver in manager.solvers)
  389. {
  390. if (solver == null || !solver.isActiveAndEnabled)
  391. continue;
  392. if (!solver.isValid)
  393. solver.Initialize();
  394. if (!solver.isValid)
  395. continue;
  396. if(solver.allChainsHaveTargets)
  397. solver.UpdateIK(manager.weight);
  398. else if(PrepareTargetOverrides(solver))
  399. solver.UpdateIK(m_TargetPositions, manager.weight);
  400. for (int i = 0; i < solver.chainCount; ++i)
  401. {
  402. var chain = solver.GetChain(i);
  403. if (recordRootLoops)
  404. InternalEngineBridge.SetLocalEulerHint(chain.rootTransform);
  405. if(solver.constrainRotation && chain.target != null)
  406. InternalEngineBridge.SetLocalEulerHint(chain.effector);
  407. }
  408. }
  409. }
  410. m_DirtyManagers.Clear();
  411. m_ChainPositionOverrides.Clear();
  412. }
  413. private bool PrepareTargetOverrides(Solver2D solver)
  414. {
  415. m_TargetPositions.Clear();
  416. for (int i = 0; i < solver.chainCount; ++i)
  417. {
  418. var chain = solver.GetChain(i);
  419. Vector3 positionOverride;
  420. if (!m_ChainPositionOverrides.TryGetValue(chain, out positionOverride))
  421. {
  422. m_TargetPositions.Clear();
  423. return false;
  424. }
  425. m_TargetPositions.Add(positionOverride);
  426. }
  427. return true;
  428. }
  429. }
  430. }