WeightPainterTool.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. using UnityEngine;
  2. using UnityEditor.U2D.Layout;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. namespace UnityEditor.U2D.Animation
  7. {
  8. internal enum WeightPainterMode
  9. {
  10. Brush,
  11. Slider
  12. }
  13. internal class WeightPainterTool : MeshToolWrapper
  14. {
  15. private WeightPainterPanel m_WeightPainterPanel;
  16. private WeightEditor m_WeightEditor = new WeightEditor();
  17. private Brush m_Brush = new Brush(new GUIWrapper());
  18. private ISelection<int> m_BrushSelection = new IndexedSelection();
  19. private CircleVertexSelector m_CircleVertexSelector = new CircleVertexSelector();
  20. public WeightPainterMode paintMode
  21. {
  22. get { return m_WeightPainterPanel.paintMode; }
  23. set { m_WeightPainterPanel.paintMode = value; }
  24. }
  25. public override int defaultControlID
  26. {
  27. get { return m_Brush.controlID; }
  28. }
  29. internal override void OnCreate()
  30. {
  31. m_WeightEditor.cacheUndo = skinningCache;
  32. m_Brush.onMove += (brush) =>
  33. {
  34. UpdateBrushSelection(brush);
  35. };
  36. m_Brush.onRepaint += (brush) =>
  37. {
  38. DrawBrush(brush);
  39. };
  40. m_Brush.onSize += (brush) =>
  41. {
  42. UpdateBrushSelection(brush);
  43. m_WeightPainterPanel.size = Mathf.RoundToInt(brush.size);
  44. };
  45. m_Brush.onStrokeBegin += (brush) =>
  46. {
  47. UpdateBrushSelection(brush);
  48. EditStart(m_BrushSelection, true);
  49. };
  50. m_Brush.onStrokeDelta += (brush) =>
  51. {
  52. if (m_BrushSelection.Count > 0)
  53. meshTool.UpdateWeights();
  54. };
  55. m_Brush.onStrokeStep += (brush) =>
  56. {
  57. UpdateBrushSelection(brush);
  58. var hardness = brush.hardness / 100f;
  59. if (EditorGUI.actionKey)
  60. hardness *= -1f;
  61. EditWeights(hardness, false);
  62. };
  63. m_Brush.onStrokeEnd += (brush) =>
  64. {
  65. EditEnd();
  66. };
  67. }
  68. public string panelTitle
  69. {
  70. set { m_WeightPainterPanel.title = value; }
  71. }
  72. protected override void OnActivate()
  73. {
  74. base.OnActivate();
  75. m_WeightPainterPanel.SetHiddenFromLayout(false);
  76. skinningCache.events.selectedSpriteChanged.AddListener(OnSelectedSpriteChanged);
  77. skinningCache.events.skinningModeChanged.AddListener(OnSkinningModeChanged);
  78. skinningCache.events.boneSelectionChanged.AddListener(OnBoneSelectionChanged);
  79. m_Brush.size = skinningCache.brushSize;
  80. m_Brush.hardness = skinningCache.brushHardness;
  81. m_Brush.step = skinningCache.brushStep;
  82. m_WeightPainterPanel.size = (int) m_Brush.size;
  83. m_WeightPainterPanel.hardness = (int) m_Brush.hardness;
  84. m_WeightPainterPanel.step = (int) m_Brush.step;
  85. UpdatePanel();
  86. }
  87. protected override void OnDeactivate()
  88. {
  89. base.OnDeactivate();
  90. skinningCache.events.selectedSpriteChanged.RemoveListener(OnSelectedSpriteChanged);
  91. skinningCache.events.skinningModeChanged.RemoveListener(OnSkinningModeChanged);
  92. skinningCache.events.boneSelectionChanged.RemoveListener(OnBoneSelectionChanged);
  93. m_WeightPainterPanel.SetHiddenFromLayout(true);
  94. }
  95. private void OnBoneSelectionChanged()
  96. {
  97. UpdateSelectedBone();
  98. }
  99. private void OnSelectedSpriteChanged(SpriteCache sprite)
  100. {
  101. UpdatePanel();
  102. }
  103. private void OnSkinningModeChanged(SkinningMode mode)
  104. {
  105. UpdatePanel();
  106. }
  107. private string[] GetSkeletonBonesNames()
  108. {
  109. var names = new List<string>() { WeightPainterPanel.kNone };
  110. var skeleton = skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite);
  111. if (skeleton != null)
  112. names.AddRange(GetUniqueBoneNames(skeleton.bones, skeleton));
  113. return names.ToArray();
  114. }
  115. private string[] GetMeshBoneNames()
  116. {
  117. var mesh = meshTool.mesh;
  118. var skeleton = skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite);
  119. if (mesh != null && skeleton != null)
  120. {
  121. var bones = meshTool.mesh.bones.ToSpriteSheetIfNeeded();
  122. return GetUniqueBoneNames(bones, skeleton);
  123. }
  124. return new string[0];
  125. }
  126. private string[] GetUniqueBoneNames(BoneCache[] bones, SkeletonCache skeleton)
  127. {
  128. return Array.ConvertAll(bones, b => skeleton.GetUniqueName(b));
  129. }
  130. private void UpdatePanel()
  131. {
  132. m_WeightPainterPanel.SetActive(skinningCache.selectedSprite != null);
  133. m_WeightPainterPanel.UpdateWeightInspector(meshTool.mesh, GetMeshBoneNames(), skinningCache.vertexSelection, skinningCache);
  134. m_WeightPainterPanel.UpdatePanel(GetSkeletonBonesNames());
  135. UpdateSelectedBone();
  136. }
  137. private void UpdateSelectedBone()
  138. {
  139. var boneName = WeightPainterPanel.kNone;
  140. var bone = skinningCache.skeletonSelection.activeElement.ToSpriteSheetIfNeeded();
  141. var skeleton = skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite);
  142. if (skeleton != null && skeleton.Contains(bone))
  143. boneName = skeleton.GetUniqueName(bone);
  144. m_WeightPainterPanel.SetBoneSelectionByName(boneName);
  145. }
  146. public override void Initialize(LayoutOverlay layout)
  147. {
  148. base.Initialize(layout);
  149. m_WeightPainterPanel = WeightPainterPanel.GenerateFromUXML();
  150. m_WeightPainterPanel.SetHiddenFromLayout(true);
  151. layout.rightOverlay.Add(m_WeightPainterPanel);
  152. m_WeightPainterPanel.sliderStarted += () =>
  153. {
  154. EditStart(skinningCache.vertexSelection, false);
  155. };
  156. m_WeightPainterPanel.sliderChanged += (value) =>
  157. {
  158. EditWeights(value, true);
  159. meshTool.UpdateWeights();
  160. };
  161. m_WeightPainterPanel.sliderEnded += () =>
  162. {
  163. EditEnd();
  164. };
  165. m_WeightPainterPanel.bonePopupChanged += (i) =>
  166. {
  167. var skeleton = skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite);
  168. if (skeleton != null)
  169. {
  170. BoneCache bone = null;
  171. if (i != -1)
  172. bone = skeleton.GetBone(i).ToCharacterIfNeeded();
  173. if(bone != skinningCache.skeletonSelection.activeElement)
  174. {
  175. using (skinningCache.UndoScope(TextContent.boneSelection))
  176. {
  177. skinningCache.skeletonSelection.activeElement = bone;
  178. InvokeBoneSelectionChanged();
  179. }
  180. }
  181. }
  182. };
  183. m_WeightPainterPanel.weightsChanged += () => meshTool.UpdateWeights();
  184. }
  185. internal void SetWeightPainterPanelTitle(string title)
  186. {
  187. m_WeightPainterPanel.title = title;
  188. }
  189. private void AssociateSelectedBoneToCharacterPart()
  190. {
  191. var mesh = meshTool.mesh;
  192. if (skinningCache.hasCharacter
  193. && skinningCache.mode == SkinningMode.Character
  194. && m_WeightPainterPanel.boneIndex != -1
  195. && mesh != null)
  196. {
  197. var skeleton = skinningCache.character.skeleton;
  198. Debug.Assert(skeleton != null);
  199. var bone = skeleton.GetBone(m_WeightPainterPanel.boneIndex);
  200. if (!mesh.ContainsBone(bone))
  201. {
  202. using (skinningCache.UndoScope(TextContent.addBoneInfluence))
  203. {
  204. var characterPart = mesh.sprite.GetCharacterPart();
  205. var characterBones = characterPart.bones.ToList();
  206. characterBones.Add(bone);
  207. characterPart.bones = characterBones.ToArray();
  208. skinningCache.events.characterPartChanged.Invoke(characterPart);
  209. m_WeightPainterPanel.UpdateWeightInspector(meshTool.mesh, GetMeshBoneNames(), skinningCache.vertexSelection, skinningCache);
  210. }
  211. }
  212. }
  213. }
  214. private void EditStart(ISelection<int> selection, bool relative)
  215. {
  216. AssociateSelectedBoneToCharacterPart();
  217. SetupWeightEditor(selection);
  218. if (m_WeightEditor.spriteMeshData != null)
  219. m_WeightEditor.OnEditStart(relative);
  220. }
  221. private void EditWeights(float hardness, bool emptySelectionEditsAll)
  222. {
  223. m_WeightEditor.emptySelectionEditsAll = emptySelectionEditsAll;
  224. if (m_WeightEditor.spriteMeshData != null)
  225. m_WeightEditor.DoEdit(hardness);
  226. }
  227. private void EditEnd()
  228. {
  229. if (m_WeightEditor.spriteMeshData != null)
  230. {
  231. m_WeightEditor.OnEditEnd();
  232. meshTool.UpdateWeights();
  233. }
  234. }
  235. private void InvokeBoneSelectionChanged()
  236. {
  237. skinningCache.events.boneSelectionChanged.RemoveListener(OnBoneSelectionChanged);
  238. skinningCache.events.boneSelectionChanged.Invoke();
  239. skinningCache.events.boneSelectionChanged.AddListener(OnBoneSelectionChanged);
  240. }
  241. private int ConvertBoneIndex(int index)
  242. {
  243. if (index != -1 && meshTool.mesh != null)
  244. {
  245. var skeleton = skinningCache.GetEffectiveSkeleton(meshTool.mesh.sprite);
  246. if (skeleton != null)
  247. {
  248. var bone = skeleton.GetBone(index).ToCharacterIfNeeded();
  249. index = Array.IndexOf(meshTool.mesh.bones, bone);
  250. }
  251. }
  252. return index;
  253. }
  254. private void SetupWeightEditor(ISelection<int> selection)
  255. {
  256. m_WeightEditor.spriteMeshData = meshTool.mesh;
  257. m_WeightEditor.mode = m_WeightPainterPanel.mode;
  258. m_WeightEditor.boneIndex = ConvertBoneIndex(m_WeightPainterPanel.boneIndex);
  259. m_WeightEditor.autoNormalize = m_WeightPainterPanel.normalize;
  260. m_WeightEditor.selection = selection;
  261. m_WeightEditor.emptySelectionEditsAll = true;
  262. }
  263. private void UpdateBrushSelection(Brush brush)
  264. {
  265. m_BrushSelection.Clear();
  266. m_CircleVertexSelector.spriteMeshData = meshTool.mesh;
  267. m_CircleVertexSelector.position = brush.position;
  268. m_CircleVertexSelector.radius = brush.size;
  269. m_CircleVertexSelector.selection = m_BrushSelection;
  270. m_CircleVertexSelector.Select();
  271. }
  272. private void DrawBrush(Brush brush)
  273. {
  274. var oldColor = Handles.color;
  275. Handles.color = Color.white;
  276. if (EditorGUI.actionKey)
  277. Handles.color = Color.red;
  278. if (brush.isHot)
  279. Handles.color = Color.yellow;
  280. Handles.DrawWireDisc(brush.position, Vector3.forward, brush.size);
  281. Handles.color = oldColor;
  282. }
  283. protected override void OnGUI()
  284. {
  285. m_MeshPreviewBehaviour.showWeightMap = true;
  286. m_MeshPreviewBehaviour.overlaySelected = true;
  287. skeletonTool.skeletonStyle = SkeletonStyles.WeightMap;
  288. skeletonMode = SkeletonMode.EditPose;
  289. meshMode = SpriteMeshViewMode.EditGeometry;
  290. disableMeshEditor = true;
  291. var isBoneHovered = skeletonTool.hoveredBone != null && !m_Brush.isHot;
  292. var useBrush = paintMode == WeightPainterMode.Brush;
  293. meshTool.selectionOverride = null;
  294. if (useBrush)
  295. meshTool.selectionOverride = m_BrushSelection;
  296. DoSkeletonGUI();
  297. DoMeshGUI();
  298. if (useBrush && !isBoneHovered)
  299. {
  300. var handlesMatrix = Handles.matrix;
  301. var selectedSprite = skinningCache.selectedSprite;
  302. var matrix = Matrix4x4.identity;
  303. if (selectedSprite != null)
  304. matrix = selectedSprite.GetLocalToWorldMatrixFromMode();
  305. Handles.matrix *= matrix;
  306. skinningCache.brushSize = m_Brush.size = m_WeightPainterPanel.size;
  307. skinningCache.brushHardness = m_Brush.hardness = m_WeightPainterPanel.hardness;
  308. skinningCache.brushStep = m_Brush.step = m_WeightPainterPanel.step;
  309. if (m_Brush.isHot || !skinningCache.IsOnVisualElement())
  310. {
  311. meshTool.BeginPositionOverride();
  312. m_Brush.OnGUI();
  313. meshTool.EndPositionOverride();
  314. }
  315. Handles.matrix = handlesMatrix;
  316. }
  317. }
  318. }
  319. }