ShapeEditor.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEditor;
  5. public class ShapeEditor
  6. {
  7. public enum TangentMode { Linear, Continuous, Broken }
  8. private class Styles
  9. {
  10. public readonly GUIStyle pointNormalStyle;
  11. public readonly GUIStyle pointHoveredStyle;
  12. public readonly GUIStyle pointSelectedStyle;
  13. public readonly GUIStyle pointPreviewStyle;
  14. public readonly GUIStyle tangentNormalStyle;
  15. public readonly GUIStyle tangentHoveredStyle;
  16. public Styles()
  17. {
  18. Texture2D pointNormal = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Editor/UI/ShapeEditor/Icon/ss_pointNormal.png");
  19. Texture2D pointHovered = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Editor/UI/ShapeEditor/Icon/ss_pointHovered.png");
  20. Texture2D pointSelected = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Editor/UI/ShapeEditor/Icon/ss_pointSelected.png");
  21. Texture2D pointPreview = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Editor/UI/ShapeEditor/Icon/ss_pointPreview.png");
  22. Texture2D tangentNormal = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Editor/UI/ShapeEditor/Icon/ss_tangentNormal.png");
  23. pointNormalStyle = new GUIStyle();
  24. pointNormalStyle.normal.background = pointNormal;
  25. pointNormalStyle.fixedWidth = 12f;
  26. pointNormalStyle.fixedHeight = 12f;
  27. pointHoveredStyle = new GUIStyle();
  28. pointHoveredStyle.normal.background = pointHovered;
  29. pointHoveredStyle.fixedWidth = 12f;
  30. pointHoveredStyle.fixedHeight = 12f;
  31. pointSelectedStyle = new GUIStyle();
  32. pointSelectedStyle.normal.background = pointSelected;
  33. pointSelectedStyle.fixedWidth = 12f;
  34. pointSelectedStyle.fixedHeight = 12f;
  35. pointPreviewStyle = new GUIStyle();
  36. pointPreviewStyle.normal.background = pointPreview;
  37. pointPreviewStyle.fixedWidth = 12f;
  38. pointPreviewStyle.fixedHeight = 12f;
  39. tangentNormalStyle = new GUIStyle();
  40. tangentNormalStyle.normal.background = tangentNormal;
  41. tangentNormalStyle.fixedWidth = 8f;
  42. tangentNormalStyle.fixedHeight = 8f;
  43. tangentHoveredStyle = new GUIStyle();
  44. tangentHoveredStyle.normal.background = pointHovered;
  45. tangentHoveredStyle.fixedWidth = 10f;
  46. tangentHoveredStyle.fixedHeight = 10f;
  47. }
  48. }
  49. private Styles m_Styles;
  50. private Styles styles
  51. {
  52. get
  53. {
  54. if (m_Styles == null)
  55. m_Styles = new Styles();
  56. return m_Styles;
  57. }
  58. }
  59. public Func<int, Vector3> GetPosition = i => Vector3.zero;
  60. public Action<int, Vector3> SetPosition = (i, p) => {};
  61. public Func<int, Vector3> GetLeftTangent = i => Vector3.zero;
  62. public Action<int, Vector3> SetLeftTangent = (i, p) => {};
  63. public Func<int, Vector3> GetRightTangent = i => Vector3.zero;
  64. public Action<int, Vector3> SetRightTangent = (i, p) => {};
  65. public Func<int, TangentMode> GetTangentMode = i => TangentMode.Linear;
  66. public Action<int, TangentMode> SetTangentMode = (i, m) => {};
  67. public Action<int, Vector3> InsertPointAt = (i, p) => {};
  68. public Action<int> RemovePointAt = i => {};
  69. public Func<int> GetPointCount = () => 0;
  70. // --- Transforms
  71. public Func<Vector2, Vector3> ScreenToWorld = i => i;
  72. public Func<Vector3, Vector2> WorldToScreen = i => i;
  73. public Func<Matrix4x4> LocalToWorldMatrix = () => Matrix4x4.identity;
  74. public Func<Vector3> GetForwardVector = () => Vector3.forward;
  75. public Func<Vector3> GetUpVector = () => Vector3.up;
  76. public Func<Vector3> GetRightVector = () => Vector3.right;
  77. // --- Other
  78. public Action Repaint = () => {};
  79. public Action RecordUndo = () => {};
  80. public Func<Vector3, Vector3> Snap = i => i;
  81. public Action<Bounds> Frame = b => {};
  82. public Func<bool> OpenEnded = () => false;
  83. public Func<Vector3, float> GetHandleSize;
  84. public Handles.CapFunction GetPointCapNormal;
  85. public Handles.CapFunction GetPointCapHovered;
  86. public Handles.CapFunction GetPointCapSelected;
  87. public Handles.CapFunction GetPointCapActive;
  88. public Handles.CapFunction GetPointCapPreview;
  89. public Handles.CapFunction GetTangentCapNormal;
  90. public Handles.CapFunction GetTangentCapHovered;
  91. public Handles.CapFunction GetTangentCapActive;
  92. // --- END
  93. public Texture2D lineTexture { get; set; }
  94. int m_ActiveEdgeIndex = -1;
  95. Vector3 m_SliderPosition;
  96. Vector3 m_ClosestPoint;
  97. float m_ClosestPointT;
  98. int m_DragPointControlId = -1;
  99. int m_DragEdgeControlId = -1;
  100. int m_DragTangentControlId = -1;
  101. int m_ActiveTangentPointIndex = -1;
  102. bool m_DragLeftTangent = false;
  103. int m_ActivePointIndex = -1;
  104. int m_HoveredPointIndex = -1;
  105. int m_HoveredEdgeIndex = -1;
  106. int m_HoveredTangentPoint = -1;
  107. bool m_HoveredLeftTangent = false;
  108. bool m_EdgePointsDirty = true;
  109. private List<Vector3[]> m_EdgePoints = new List<Vector3[]>();
  110. private static readonly Color kTangentColor = Handles.selectedColor;
  111. private static readonly Color kTangentColorAlternative = Handles.selectedColor;
  112. private const float kEdgeWidth = 2f;
  113. private const float kActiveEdgeWidth = 6f;
  114. private const int kBezierPatch = 40;
  115. Event currentEvent {get; set; }
  116. public ShapeEditor()
  117. {
  118. GetHandleSize = GetDefaultHandleSizeForPoint;
  119. GetPointCapNormal = PointCapNormal;
  120. GetPointCapHovered = PointCapHovered;
  121. GetPointCapSelected = PointCapSelected;
  122. GetPointCapActive = PointCapHovered;
  123. GetPointCapPreview = PointCapPreview;
  124. GetTangentCapNormal = TangentCapNormal;
  125. GetTangentCapHovered = TangentCapHovered;
  126. GetTangentCapActive = TangentCapHovered;
  127. }
  128. public void SetDirty()
  129. {
  130. m_EdgePointsDirty = true;
  131. }
  132. public void OnGUI()
  133. {
  134. Color currentColor = Handles.color;
  135. Matrix4x4 currentMatrix = Handles.matrix;
  136. Handles.matrix = LocalToWorldMatrix();
  137. currentEvent = Event.current;
  138. if (currentEvent.type == EventType.Layout)
  139. {
  140. if (m_DragPointControlId == -1)
  141. m_DragPointControlId = GUIUtility.GetControlID("DragPoint".GetHashCode(), FocusType.Passive);
  142. if (m_DragEdgeControlId == -1)
  143. m_DragEdgeControlId = GUIUtility.GetControlID("DragPoint".GetHashCode(), FocusType.Passive);
  144. if (m_DragTangentControlId == -1)
  145. m_DragTangentControlId = GUIUtility.GetControlID("DragTangent".GetHashCode(), FocusType.Passive);
  146. m_HoveredPointIndex = -1;
  147. m_HoveredEdgeIndex = -1;
  148. m_HoveredTangentPoint = -1;
  149. }
  150. LayoutPoints();
  151. LayoutTangents();
  152. LayoutEdges();
  153. DrawEdges();
  154. DrawPoints();
  155. DrawCreatePointPreview();
  156. DrawTangents();
  157. HandleDragEdge();
  158. HandleDragTangent();
  159. HandleInsertPointToEdge();
  160. HandleSelectPoint();
  161. HandleDragPoints();
  162. HandleDeletePoints();
  163. HandleUnselect();
  164. Framing();
  165. Handles.color = currentColor;
  166. Handles.matrix = currentMatrix;
  167. }
  168. private void PrepareEdgePoints()
  169. {
  170. int pointCount = GetPointCount();
  171. int edgeCount = OpenEnded() ? pointCount - 1 : pointCount;
  172. if (m_EdgePoints.Count != edgeCount)
  173. m_EdgePointsDirty = true;
  174. if (!m_EdgePointsDirty)
  175. return;
  176. m_EdgePoints.Clear();
  177. for (int index = 0; index < edgeCount; ++index)
  178. {
  179. int nextIndex = SplineUtility.NextIndex(index, pointCount);
  180. Vector3 position0 = GetPosition(index);
  181. Vector3 position1 = GetPosition(nextIndex);
  182. if (GetTangentMode(index) == TangentMode.Linear && GetTangentMode(nextIndex) == TangentMode.Linear)
  183. {
  184. m_EdgePoints.Add(new Vector3[] { position0, position1 });
  185. }
  186. else
  187. {
  188. var tangent0 = GetRightTangent(index) + position0;
  189. var tangent1 = GetLeftTangent(nextIndex) + position1;
  190. m_EdgePoints.Add(Handles.MakeBezierPoints(position0, position1, tangent0, tangent1, kBezierPatch));
  191. }
  192. }
  193. m_EdgePointsDirty = false;
  194. }
  195. private void LayoutPoints()
  196. {
  197. for (int index = 0; index < GetPointCount(); index++)
  198. {
  199. int id = GUIUtility.GetControlID("Point".GetHashCode(), FocusType.Passive);
  200. if (currentEvent.type == EventType.Layout)
  201. {
  202. Vector3 position = GetPosition(index);
  203. GetPointCapNormal(id, position, Quaternion.LookRotation(GetForwardVector(), GetUpVector()), GetHandleSize(position), EventType.Layout);
  204. if (m_HoveredEdgeIndex == -1)
  205. {
  206. if (HandleUtility.nearestControl == id)
  207. {
  208. m_HoveredPointIndex = index;
  209. }
  210. }
  211. }
  212. }
  213. }
  214. private void DrawPoints()
  215. {
  216. if (currentEvent.type != EventType.Repaint)
  217. return;
  218. for (int index = 0; index < GetPointCount(); index++)
  219. {
  220. var position = GetPosition(index);
  221. if (m_ActivePointIndex == index)
  222. GetPointCapActive(0, position, Quaternion.LookRotation(GetForwardVector(), GetUpVector()), GetHandleSize(position), currentEvent.type);
  223. else if (m_HoveredPointIndex == index && GUIUtility.hotControl == 0)
  224. GetPointCapHovered(0, position, Quaternion.LookRotation(GetForwardVector(), GetUpVector()), GetHandleSize(position), currentEvent.type);
  225. else if (ShapeEditorCache.GetSelection().IsSelected(index))
  226. GetPointCapSelected(0, position, Quaternion.LookRotation(GetForwardVector(), GetUpVector()), GetHandleSize(position), currentEvent.type);
  227. else
  228. GetPointCapNormal(0, position, Quaternion.LookRotation(GetForwardVector(), GetUpVector()), GetHandleSize(position), currentEvent.type);
  229. }
  230. }
  231. private void DrawCreatePointPreview()
  232. {
  233. if (GUIUtility.hotControl == 0 && m_HoveredTangentPoint == -1 && m_HoveredPointIndex == -1 && !currentEvent.shift && !EdgeDragModifiersActive())
  234. {
  235. if (m_HoveredEdgeIndex != -1)
  236. GetPointCapHovered(0, m_ClosestPoint, Quaternion.LookRotation(GetForwardVector(), GetUpVector()), GetHandleSize(m_ClosestPoint), currentEvent.type);
  237. else
  238. GetPointCapPreview(0, m_ClosestPoint, Quaternion.LookRotation(GetForwardVector(), GetUpVector()), GetHandleSize(m_ClosestPoint), currentEvent.type);
  239. }
  240. }
  241. private void HandleSelectPoint()
  242. {
  243. EventType eventType = currentEvent.GetTypeForControl(m_DragPointControlId);
  244. if (GUIUtility.hotControl == 0 && m_HoveredPointIndex != -1 && eventType == EventType.MouseDown && currentEvent.button == 0)
  245. {
  246. SelectPoint(m_HoveredPointIndex);
  247. }
  248. }
  249. private void HandleDeletePoints()
  250. {
  251. bool wantsDelete = GUIUtility.hotControl == 0 &&
  252. (currentEvent.type == EventType.ExecuteCommand || currentEvent.type == EventType.ValidateCommand)
  253. && (currentEvent.commandName == "SoftDelete" || currentEvent.commandName == "Delete");
  254. if (wantsDelete)
  255. {
  256. if (currentEvent.type == EventType.ValidateCommand)
  257. {
  258. currentEvent.Use();
  259. }
  260. else if (currentEvent.type == EventType.ExecuteCommand)
  261. {
  262. DeleteSelected();
  263. currentEvent.Use();
  264. }
  265. }
  266. }
  267. private void HandleDragPoints()
  268. {
  269. EventType eventType = currentEvent.GetTypeForControl(m_DragPointControlId);
  270. if ((!currentEvent.alt && GUIUtility.hotControl == 0 && m_HoveredPointIndex != -1 && !EditorGUI.actionKey) ||
  271. GUIUtility.hotControl == m_DragPointControlId)
  272. {
  273. if (eventType == EventType.Layout && GUIUtility.hotControl == 0)
  274. GetPointCapNormal(m_DragPointControlId, GetPosition(m_HoveredPointIndex), Quaternion.LookRotation(GetForwardVector(), GetUpVector()), GetHandleSize(GetPosition(m_HoveredPointIndex)), EventType.Layout);
  275. if (eventType == EventType.MouseDown && currentEvent.button == 0)
  276. {
  277. m_ActivePointIndex = m_HoveredPointIndex;
  278. m_SliderPosition = GetPosition(m_HoveredPointIndex);
  279. }
  280. if (eventType == EventType.MouseUp && currentEvent.button == 0)
  281. m_ActivePointIndex = -1;
  282. EditorGUI.BeginChangeCheck();
  283. Vector3 newPosition = DoSlider(m_DragPointControlId, m_SliderPosition, GetUpVector(), GetRightVector(), GetHandleSize(m_SliderPosition), (int cid, Vector3 p, Quaternion q, float s, EventType et) => {});
  284. if (EditorGUI.EndChangeCheck())
  285. {
  286. newPosition = Snap(newPosition);
  287. MoveSelectedPoints(newPosition - m_SliderPosition);
  288. }
  289. m_SliderPosition = newPosition;
  290. }
  291. }
  292. private void LayoutEdges()
  293. {
  294. if (currentEvent.type != EventType.Layout)
  295. return;
  296. PrepareEdgePoints();
  297. int pointCount = GetPointCount();
  298. int edgeCount = OpenEnded() ? pointCount - 1 : pointCount;
  299. float minDistance = float.MaxValue;
  300. for (int index = 0; index < edgeCount; ++index)
  301. {
  302. int id = GUIUtility.GetControlID("Edge".GetHashCode(), FocusType.Passive);
  303. if (currentEvent.type == EventType.Layout)
  304. {
  305. int nextIndex = SplineUtility.NextIndex(index, pointCount);
  306. Vector3 position0 = GetPosition(index);
  307. Vector3 position1 = GetPosition(nextIndex);
  308. Vector3 tangent0 = GetRightTangent(index) + position0;
  309. Vector3 tangent1 = GetLeftTangent(nextIndex) + position1;
  310. float t;
  311. Vector3 closestPoint = BezierUtility.ClosestPointOnCurve(ScreenToWorld(currentEvent.mousePosition), position0, position1, tangent0, tangent1, out t);
  312. Vector2 guiPosition = HandleUtility.WorldToGUIPoint(closestPoint);
  313. float distance = (currentEvent.mousePosition - guiPosition).magnitude;
  314. if (m_HoveredPointIndex == -1)
  315. {
  316. HandleUtility.AddControl(id, distance);
  317. if (HandleUtility.nearestControl == id)
  318. {
  319. m_HoveredEdgeIndex = index;
  320. m_HoveredPointIndex = -1;
  321. m_HoveredTangentPoint = -1;
  322. }
  323. }
  324. if (distance < minDistance)
  325. {
  326. m_ClosestPoint = closestPoint;
  327. m_ClosestPointT = t;
  328. minDistance = distance;
  329. }
  330. }
  331. }
  332. }
  333. private void DrawEdges()
  334. {
  335. PrepareEdgePoints();
  336. if (currentEvent.type != EventType.Repaint)
  337. return;
  338. Color handlesOldColor = Handles.color;
  339. var pointCount = GetPointCount();
  340. int edgeCount = OpenEnded() ? pointCount - 1 : pointCount;
  341. for (int index = 0; index < edgeCount; index++)
  342. {
  343. Color edgeColor = Color.white;
  344. if ((m_HoveredEdgeIndex == index && HandleUtility.nearestControl == m_DragEdgeControlId) ||
  345. (m_ActiveEdgeIndex == index && GUIUtility.hotControl == m_DragEdgeControlId))
  346. edgeColor = Handles.selectedColor;
  347. float edgeWidth = kEdgeWidth;
  348. if ((m_ActiveEdgeIndex == index && GUIUtility.hotControl == m_DragEdgeControlId) ||
  349. (GUIUtility.hotControl == 0 && m_HoveredEdgeIndex == index))
  350. edgeWidth = kActiveEdgeWidth;
  351. Handles.color = edgeColor;
  352. Handles.DrawAAPolyLine(lineTexture, edgeWidth, m_EdgePoints[index]);
  353. }
  354. Handles.color = handlesOldColor;
  355. }
  356. private void HandleUnselect()
  357. {
  358. EventType eventType = currentEvent.type;
  359. if (GUIUtility.hotControl == 0 && eventType == EventType.MouseDown && currentEvent.button == 1)
  360. {
  361. ClearSelection();
  362. }
  363. }
  364. private void HandleInsertPointToEdge()
  365. {
  366. EventType eventType = currentEvent.GetTypeForControl(m_DragPointControlId);
  367. if (m_HoveredPointIndex == -1 && m_HoveredEdgeIndex != -1 && GUIUtility.hotControl == 0 && eventType == EventType.MouseDown && currentEvent.button == 0 && !currentEvent.shift)
  368. {
  369. RecordUndo();
  370. ClearSelection();
  371. int nextIndex = SplineUtility.NextIndex(m_HoveredEdgeIndex, GetPointCount());
  372. TangentMode leftTangentMode = GetTangentMode(m_HoveredEdgeIndex);
  373. TangentMode rightTangentMode = GetTangentMode(nextIndex);
  374. Vector3 leftStartPosition;
  375. Vector3 leftEndPosition;
  376. Vector3 leftStartTangent;
  377. Vector3 leftEndTangent;
  378. Vector3 rightStartPosition;
  379. Vector3 rightEndPosition;
  380. Vector3 rightStartTangent;
  381. Vector3 rightEndTangent;
  382. Vector3 position0 = GetPosition(m_HoveredEdgeIndex);
  383. Vector3 position1 = GetPosition(nextIndex);
  384. Vector3 tangent0 = GetRightTangent(m_HoveredEdgeIndex) + position0;
  385. Vector3 tangent1 = GetLeftTangent(nextIndex) + position1;
  386. BezierUtility.SplitBezier(m_ClosestPointT, position0, position1, tangent0, tangent1,
  387. out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
  388. out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
  389. if (leftTangentMode != TangentMode.Linear)
  390. {
  391. SetTangentMode(m_HoveredEdgeIndex, TangentMode.Broken);
  392. SetRightTangent(m_HoveredEdgeIndex, leftStartTangent - leftStartPosition);
  393. }
  394. if (rightTangentMode != TangentMode.Linear)
  395. {
  396. SetTangentMode(nextIndex, TangentMode.Broken);
  397. SetLeftTangent(nextIndex, rightEndTangent - rightEndPosition);
  398. }
  399. InsertPointAt(nextIndex, m_ClosestPoint);
  400. if (leftTangentMode != TangentMode.Linear || rightTangentMode != TangentMode.Linear)
  401. {
  402. SetTangentMode(nextIndex, TangentMode.Broken);
  403. if (m_ClosestPointT == 0.5f)
  404. SetTangentMode(nextIndex, TangentMode.Continuous);
  405. SetLeftTangent(nextIndex, leftEndTangent - leftEndPosition);
  406. SetRightTangent(nextIndex, rightStartTangent - rightStartPosition);
  407. }
  408. m_HoveredPointIndex = nextIndex;
  409. m_HoveredEdgeIndex = -1;
  410. HandleUtility.nearestControl = m_DragPointControlId;
  411. }
  412. }
  413. private void HandleDragEdge()
  414. {
  415. EventType eventType = currentEvent.GetTypeForControl(m_DragEdgeControlId);
  416. if ((m_HoveredEdgeIndex != -1 && GUIUtility.hotControl == 0 && EdgeDragModifiersActive()) ||
  417. GUIUtility.hotControl == m_DragEdgeControlId)
  418. {
  419. if (eventType == EventType.MouseDown && currentEvent.button == 0)
  420. {
  421. m_ActiveEdgeIndex = m_HoveredEdgeIndex;
  422. m_SliderPosition = GetPosition(m_HoveredEdgeIndex);
  423. }
  424. if (eventType == EventType.MouseUp && currentEvent.button == 0)
  425. {
  426. m_ActiveEdgeIndex = -1;
  427. }
  428. if (eventType == EventType.Layout && GUIUtility.hotControl == 0)
  429. HandleUtility.AddControl(m_DragEdgeControlId, 0f);
  430. EditorGUI.BeginChangeCheck();
  431. Vector3 newPosition = DoSlider(m_DragEdgeControlId, m_SliderPosition, GetUpVector(), GetRightVector(), GetHandleSize(m_SliderPosition), (int cid, Vector3 p, Quaternion q, float s, EventType et) => {});
  432. if (EditorGUI.EndChangeCheck())
  433. {
  434. Vector3 snappedDelta = Snap(newPosition) - m_SliderPosition;
  435. int nextIndex = SplineUtility.NextIndex(m_ActiveEdgeIndex, GetPointCount());
  436. RecordUndo();
  437. SetPosition(m_ActiveEdgeIndex, GetPosition(m_ActiveEdgeIndex) + snappedDelta);
  438. SetPosition(nextIndex, GetPosition(nextIndex) + snappedDelta);
  439. m_EdgePointsDirty = true;
  440. }
  441. m_SliderPosition = newPosition;
  442. }
  443. }
  444. private void LayoutTangents()
  445. {
  446. ISelection selection = ShapeEditorCache.GetSelection();
  447. int selectedPoint = selection.single;
  448. if (selectedPoint == -1 || GetTangentMode(selectedPoint) == TangentMode.Linear)
  449. return;
  450. int tangentId = GUIUtility.GetControlID("Tangent".GetHashCode(), FocusType.Passive);
  451. Vector3 position = GetPosition(selectedPoint);
  452. if (currentEvent.type == EventType.Layout)
  453. {
  454. Vector3 leftTangentPosition = GetLeftTangent(selectedPoint);
  455. GetTangentCapNormal(tangentId, leftTangentPosition + position, Quaternion.identity, GetHandleSize(leftTangentPosition + position), EventType.Layout);
  456. if (HandleUtility.nearestControl == tangentId)
  457. {
  458. m_HoveredTangentPoint = selectedPoint;
  459. m_HoveredLeftTangent = true;
  460. m_HoveredPointIndex = -1;
  461. }
  462. }
  463. tangentId = GUIUtility.GetControlID("Tangent".GetHashCode(), FocusType.Passive);
  464. if (currentEvent.type == EventType.Layout)
  465. {
  466. Vector3 rightTangentPosition = GetRightTangent(selection.single);
  467. GetTangentCapNormal(tangentId, rightTangentPosition + position, Quaternion.identity, GetHandleSize(rightTangentPosition + position), EventType.Layout);
  468. if (HandleUtility.nearestControl == tangentId)
  469. {
  470. m_HoveredTangentPoint = selectedPoint;
  471. m_HoveredLeftTangent = false;
  472. m_HoveredPointIndex = -1;
  473. }
  474. }
  475. }
  476. private void DrawTangentLine(Vector3 position, Vector3 tangent, Color color)
  477. {
  478. if (currentEvent.type != EventType.Repaint)
  479. return;
  480. Handles.color = color;
  481. Handles.DrawAAPolyLine(lineTexture, 3f, new Vector3[] { position, position + tangent });
  482. }
  483. private void DrawTangents()
  484. {
  485. ISelection selection = ShapeEditorCache.GetSelection();
  486. int selectedPoint = selection.single;
  487. if (selectedPoint == -1 || GetTangentMode(selectedPoint) == TangentMode.Linear || currentEvent.type != EventType.Repaint)
  488. return;
  489. Vector3 position = GetPosition(selectedPoint);
  490. Vector3 leftTangent = GetLeftTangent(selectedPoint);
  491. Vector3 rightTangent = GetRightTangent(selection.single);
  492. Color color = kTangentColor;
  493. DrawTangentLine(position, leftTangent, color);
  494. if (GetTangentMode(selectedPoint) == TangentMode.Broken)
  495. color = kTangentColorAlternative;
  496. DrawTangentLine(position, rightTangent, color);
  497. if (m_ActiveTangentPointIndex != -1)
  498. {
  499. if (m_DragLeftTangent)
  500. {
  501. GetTangentCapActive(0, leftTangent + position, Quaternion.identity, GetHandleSize(leftTangent + position), EventType.Repaint);
  502. GetTangentCapNormal(0, rightTangent + position, Quaternion.identity, GetHandleSize(rightTangent + position), EventType.Repaint);
  503. }
  504. else
  505. {
  506. GetTangentCapNormal(0, leftTangent + position, Quaternion.identity, GetHandleSize(leftTangent + position), EventType.Repaint);
  507. GetTangentCapActive(0, rightTangent + position, Quaternion.identity, GetHandleSize(rightTangent + position), EventType.Repaint);
  508. }
  509. }
  510. else if (GUIUtility.hotControl == 0 && m_HoveredTangentPoint != -1)
  511. {
  512. if (m_HoveredLeftTangent)
  513. {
  514. GetTangentCapHovered(0, leftTangent + position, Quaternion.identity, GetHandleSize(leftTangent + position), EventType.Repaint);
  515. GetTangentCapNormal(0, rightTangent + position, Quaternion.identity, GetHandleSize(rightTangent + position), EventType.Repaint);
  516. }
  517. else
  518. {
  519. GetTangentCapNormal(0, leftTangent + position, Quaternion.identity, GetHandleSize(leftTangent + position), EventType.Repaint);
  520. GetTangentCapHovered(0, rightTangent + position, Quaternion.identity, GetHandleSize(rightTangent + position), EventType.Repaint);
  521. }
  522. }
  523. else
  524. {
  525. GetTangentCapNormal(0, leftTangent + position, Quaternion.identity, GetHandleSize(leftTangent + position), EventType.Repaint);
  526. GetTangentCapNormal(0, rightTangent + position, Quaternion.identity, GetHandleSize(rightTangent + position), EventType.Repaint);
  527. }
  528. }
  529. private void HandleDragTangent()
  530. {
  531. EventType eventType = currentEvent.GetTypeForControl(m_DragTangentControlId);
  532. if ((!currentEvent.alt && GUIUtility.hotControl == 0 && m_HoveredTangentPoint != -1 && !EditorGUI.actionKey) ||
  533. GUIUtility.hotControl == m_DragTangentControlId)
  534. {
  535. if (eventType == EventType.MouseDown && currentEvent.button == 0)
  536. {
  537. m_ActiveTangentPointIndex = m_HoveredTangentPoint;
  538. m_DragLeftTangent = m_HoveredLeftTangent;
  539. if (m_DragLeftTangent)
  540. m_SliderPosition = GetLeftTangent(m_HoveredTangentPoint) + GetPosition(m_HoveredTangentPoint);
  541. else
  542. m_SliderPosition = GetRightTangent(m_HoveredTangentPoint) + GetPosition(m_HoveredTangentPoint);
  543. }
  544. if (eventType == EventType.MouseUp && currentEvent.button == 0)
  545. {
  546. Vector3 leftTangent = GetLeftTangent(m_ActiveTangentPointIndex);
  547. Vector3 rightTangent = GetRightTangent(m_ActiveTangentPointIndex);
  548. if (leftTangent.sqrMagnitude == 0f && rightTangent.sqrMagnitude == 0f)
  549. SetTangentMode(m_ActiveTangentPointIndex, TangentMode.Linear);
  550. m_ActiveTangentPointIndex = -1;
  551. }
  552. if (eventType == EventType.Layout && GUIUtility.hotControl == 0)
  553. HandleUtility.AddControl(m_DragTangentControlId, 0f);
  554. EditorGUI.BeginChangeCheck();
  555. Vector3 newPosition = DoSlider(m_DragTangentControlId, m_SliderPosition, GetUpVector(), GetRightVector(), GetHandleSize(m_SliderPosition), (int cid, Vector3 p, Quaternion q, float s, EventType et) => {});
  556. if (EditorGUI.EndChangeCheck())
  557. {
  558. RecordUndo();
  559. Vector3 tangent = newPosition - GetPosition(m_ActiveTangentPointIndex);
  560. if (tangent.magnitude < GetHandleSize(GetPosition(m_ActiveTangentPointIndex)))
  561. tangent = Vector3.zero;
  562. if (m_DragLeftTangent)
  563. SetLeftTangent(m_ActiveTangentPointIndex, tangent);
  564. else
  565. SetRightTangent(m_ActiveTangentPointIndex, tangent);
  566. ValidateTangents(!m_DragLeftTangent);
  567. }
  568. }
  569. }
  570. private void Framing()
  571. {
  572. if (GetPointCount() == 0)
  573. return;
  574. ISelection selection = ShapeEditorCache.GetSelection();
  575. if (currentEvent.commandName == "FrameSelected" && selection.Count > 0)
  576. {
  577. switch (currentEvent.type)
  578. {
  579. case EventType.ExecuteCommand:
  580. Bounds bounds = default(Bounds);
  581. if (selection.Count == 0)
  582. {
  583. bounds = new Bounds(GetPosition(0), Vector3.zero);
  584. for (int index = 1; index < GetPointCount(); ++index)
  585. {
  586. bounds.Encapsulate(GetPosition(index));
  587. }
  588. }
  589. else
  590. {
  591. bounds = new Bounds(GetPosition(selection.any), Vector3.zero);
  592. foreach (int index in selection)
  593. bounds.Encapsulate(GetPosition(index));
  594. }
  595. Frame(bounds);
  596. currentEvent.Use();
  597. break;
  598. case EventType.ValidateCommand:
  599. currentEvent.Use();
  600. break;
  601. }
  602. }
  603. }
  604. private float GetDefaultHandleSizeForPoint(Vector3 position)
  605. {
  606. return Camera.current != null ? HandleUtility.GetHandleSize(position) * 0.09f : 5f;
  607. }
  608. private float GetTangentSizeForPoint(int index)
  609. {
  610. return GetHandleSize(GetPosition(index)) * 0.7f;
  611. }
  612. private void ValidateTangents(bool rightTangentChanged)
  613. {
  614. ISelection selection = ShapeEditorCache.GetSelection();
  615. TangentMode mode = GetTangentMode(selection.single);
  616. Vector3 leftTangent = GetLeftTangent(selection.single);
  617. Vector3 rightTangent = GetRightTangent(selection.single);
  618. if (mode == TangentMode.Continuous)
  619. {
  620. if (rightTangentChanged)
  621. leftTangent = -rightTangent;
  622. else
  623. rightTangent = -leftTangent;
  624. }
  625. SetLeftTangent(selection.single, leftTangent);
  626. SetRightTangent(selection.single, rightTangent);
  627. ShapeEditorCache.RecordUndo();
  628. ShapeEditorCache.instance.rightTangentChanged = rightTangentChanged;
  629. if (rightTangentChanged)
  630. ShapeEditorCache.instance.rightTangent = rightTangent;
  631. else
  632. ShapeEditorCache.instance.leftTangent = leftTangent;
  633. }
  634. private bool EdgeDragModifiersActive()
  635. {
  636. return currentEvent.modifiers == EventModifiers.Control || currentEvent.modifiers == EventModifiers.Command;
  637. }
  638. private static Vector3 DoSlider(int id, Vector3 position, Vector3 slide1, Vector3 slide2, float s, Handles.CapFunction cap)
  639. {
  640. return Handles.Slider2D(id, position, Vector3.zero, Vector3.Cross(slide1, slide2), slide1, slide2, s, cap, Vector2.zero, false);
  641. }
  642. private void ClearSelection()
  643. {
  644. ShapeEditorCache.RecordUndo("Edit Selection");
  645. ShapeEditorCache.ClearSelection();
  646. GUI.changed = true;
  647. }
  648. private void DeleteSelected()
  649. {
  650. ISelection selection = ShapeEditorCache.GetSelection();
  651. if (GetPointCount() <= 2)
  652. return;
  653. RecordUndo();
  654. List<int> indices = new List<int>(selection);
  655. indices.Sort();
  656. for (int i = indices.Count - 1; i >= 0; --i)
  657. RemovePointAt(indices[i]);
  658. ClearSelection();
  659. GUI.changed = true;
  660. }
  661. private void MoveSelectedPoints(Vector3 delta)
  662. {
  663. ISelection selection = ShapeEditorCache.GetSelection();
  664. RecordUndo();
  665. if (delta.sqrMagnitude < float.Epsilon)
  666. return;
  667. foreach (int index in selection)
  668. SetPosition(index, GetPosition(index) + delta);
  669. }
  670. private void SelectPoint(int index)
  671. {
  672. ISelection selection = ShapeEditorCache.GetSelection();
  673. bool additive = currentEvent.shift;
  674. bool subtractive = EditorGUI.actionKey;
  675. ShapeEditorCache.RecordUndo("Edit Selection");
  676. if (!additive && !subtractive)
  677. ShapeEditorCache.ClearSelection();
  678. selection.Select(index, (!selection.IsSelected(index) || additive) && !subtractive);
  679. HandleSinglePointSelection();
  680. GUI.changed = true;
  681. }
  682. public void HandleSinglePointSelection()
  683. {
  684. ISelection selection = ShapeEditorCache.GetSelection();
  685. if (selection.single != -1)
  686. {
  687. TangentMode mode = GetTangentMode(selection.single);
  688. SetTangentMode(selection.single, TangentMode.Broken);
  689. Vector3 leftTangent = GetLeftTangent(selection.single);
  690. Vector3 rightTangent = GetRightTangent(selection.single);
  691. ShapeEditorCache.instance.leftTangent = leftTangent;
  692. ShapeEditorCache.instance.rightTangent = rightTangent;
  693. ShapeEditorCache.instance.rightTangentChanged = true;
  694. if (rightTangent.magnitude == 0f)
  695. ShapeEditorCache.instance.rightTangentChanged = false;
  696. SetTangentMode(selection.single, mode);
  697. }
  698. }
  699. public void PointCapNormal(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
  700. {
  701. if (eventType == EventType.Layout)
  702. HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(position, size * 0.5f));
  703. else if (eventType == EventType.Repaint)
  704. DrawGUIStyleCap(controlID, position, rotation, size, styles.pointNormalStyle);
  705. }
  706. public void PointCapHovered(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
  707. {
  708. if (eventType == EventType.Layout)
  709. HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(position, size * 0.5f));
  710. else if (eventType == EventType.Repaint)
  711. DrawGUIStyleCap(controlID, position, rotation, size, styles.pointHoveredStyle);
  712. }
  713. public void PointCapSelected(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
  714. {
  715. if (eventType == EventType.Layout)
  716. HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(position, size * 0.5f));
  717. else if (eventType == EventType.Repaint)
  718. DrawGUIStyleCap(controlID, position, rotation, size, styles.pointSelectedStyle);
  719. }
  720. public void PointCapPreview(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
  721. {
  722. if (eventType == EventType.Layout)
  723. HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(position, size * 0.5f));
  724. else if (eventType == EventType.Repaint)
  725. DrawGUIStyleCap(controlID, position, rotation, size, styles.pointPreviewStyle);
  726. }
  727. public void DrawGUIStyleCap(int controlID, Vector3 position, Quaternion rotation, float size, GUIStyle guiStyle)
  728. {
  729. if (Camera.current && Vector3.Dot(position - Camera.current.transform.position, Camera.current.transform.forward) < 0f)
  730. return;
  731. Handles.BeginGUI();
  732. guiStyle.Draw(GetGUIStyleRect(guiStyle, position), GUIContent.none, controlID);
  733. Handles.EndGUI();
  734. }
  735. public void TangentCapNormal(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
  736. {
  737. if (eventType == EventType.Layout)
  738. HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(position, size * 0.5f));
  739. else if (eventType == EventType.Repaint)
  740. DrawGUIStyleCap(controlID, position, rotation, size, styles.tangentNormalStyle);
  741. }
  742. public void TangentCapHovered(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
  743. {
  744. if (eventType == EventType.Layout)
  745. HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(position, size * 0.5f));
  746. else if (eventType == EventType.Repaint)
  747. DrawGUIStyleCap(controlID, position, rotation, size, styles.tangentHoveredStyle);
  748. }
  749. public void TangentCapActive(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
  750. {
  751. if (eventType == EventType.Layout)
  752. HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(position, size * 0.5f));
  753. else if (eventType == EventType.Repaint)
  754. DrawGUIStyleCap(controlID, position, rotation, size, styles.tangentHoveredStyle);
  755. }
  756. private Rect GetGUIStyleRect(GUIStyle style, Vector3 position)
  757. {
  758. Vector2 vector = HandleUtility.WorldToGUIPoint(position);
  759. float fixedWidth = style.fixedWidth;
  760. float fixedHeight = style.fixedHeight;
  761. return new Rect(vector.x - fixedWidth / 2f, vector.y - fixedHeight / 2f - 2f, fixedWidth, fixedHeight);
  762. }
  763. }